isso/isso/utils/crypto.py
Martin Zimmermann 6e85c54a2e refactor views and introduce an API for notifications
Keep Isso modular, not monolithic. Make it easy to integrate a
web interface or add XMPP notifications.

This refactorization includes minor bugfixes and changes:

* CORS middleware did not work properly due to wrong unit tests
* more type checks on JSON input
* new detection for origin and public url, closes #28
* new activation and delete url (no redirect for old urls, but you can
  convert the old urls: copy hash after `/activate/` (or delete) and
  open `/id/<id of comment>/activate/<hash>`
* move crypto.py to utils/

With this commit, SMTP is no longer automatically configured: add
`notify = smtp` to the `[general]` section to use SMTP.
2013-11-11 12:09:19 +01:00

91 lines
2.5 KiB
Python

# -*- encoding: utf-8 -*-
#
# Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved.
import hmac
import struct
import base64
import hashlib
import binascii
import operator
from functools import reduce
def _bin_to_long(x):
"""
Convert a binary string into a long integer
This is a clever optimization for fast xor vector math
"""
return int(binascii.hexlify(x), 16)
def _long_to_bin(x, hex_format_string):
"""
Convert a long integer into a binary string.
hex_format_string is like "%020x" for padding 10 characters.
"""
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
def _fast_hmac(key, msg, digest):
"""
A trimmed down version of Python's HMAC implementation.
This function operates on bytes.
"""
dig1, dig2 = digest(), digest()
if len(key) > dig1.block_size:
key = digest(key).digest()
key += b'\x00' * (dig1.block_size - len(key))
dig1.update(key.translate(hmac.trans_36))
dig1.update(msg)
dig2.update(key.translate(hmac.trans_5C))
dig2.update(dig1.digest())
return dig2
def _pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""
Implements PBKDF2 as defined in RFC 2898, section 5.2
HMAC+SHA256 is used as the default pseudo random function.
Right now 10,000 iterations is the recommended default which takes
100ms on a 2.2Ghz Core 2 Duo. This is probably the bare minimum
for security given 1000 iterations was recommended in 2001. This
code is very well optimized for CPython and is only four times
slower than openssl's implementation.
"""
assert iterations > 0
if not digest:
digest = hashlib.sha1
password = b'' + password
salt = b'' + salt
hlen = digest().digest_size
if not dklen:
dklen = hlen
if dklen > (2 ** 32 - 1) * hlen:
raise OverflowError('dklen too big')
l = -(-dklen // hlen)
r = dklen - (l - 1) * hlen
hex_format_string = "%%0%ix" % (hlen * 2)
def F(i):
def U():
u = salt + struct.pack(b'>I', i)
for j in range(int(iterations)):
u = _fast_hmac(password, u, digest).digest()
yield _bin_to_long(u)
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
T = [F(x) for x in range(1, l + 1)]
return b''.join(T[:-1]) + T[-1][:r]
pbkdf2 = lambda text, salt, iterations, dklen: base64.b16encode(
_pbkdf2(text.encode('utf-8'), salt, iterations, dklen)).lower()