Mercurial > p > roundup > code
diff roundup/password.py @ 5428:1f1899658115
Python 3 preparation: use bytes more in password handling.
This patch adds enough fixes to use bytes to work for PBKDF2
passwords, but probably not for the other hash algorithms supported.
It would make sense for the PBKDF2 code to use the Python hashlib
support for PBKDF2 as the first choice before M2Crypto and the local
PBKDF2 implementation (hashlib supports it in 2.7.8, 3.4 and later),
but it still seems appropriate to fix the local code to handle bytes
properly anyway.
| author | Joseph Myers <jsm@polyomino.org.uk> |
|---|---|
| date | Wed, 25 Jul 2018 09:55:10 +0000 |
| parents | 56c9bcdea47f |
| children | fbbcbfc6dad0 |
line wrap: on
line diff
--- a/roundup/password.py Wed Jul 25 09:54:08 2018 +0000 +++ b/roundup/password.py Wed Jul 25 09:55:10 2018 +0000 @@ -24,36 +24,53 @@ from base64 import b64encode, b64decode from hashlib import md5, sha1 -from roundup.anypy.strings import us2s, s2b +from roundup.anypy.strings import us2s, b2s, s2b try: import crypt except ImportError: crypt = None -_bempty = "" +_bempty = b"" _bjoin = _bempty.join +def bchr(c): + if bytes == str: + # Python 2. + return chr(c) + else: + # Python 3. + return bytes((c,)) + +def bord(c): + if bytes == str: + # Python 2. + return ord(c) + else: + # Python 3. Elements of bytes are integers. + return c + def getrandbytes(count): - return _bjoin(chr(random.randint(0,255)) for i in range(count)) + return _bjoin(bchr(random.randint(0,255)) for i in range(count)) #NOTE: PBKDF2 hash is using this variant of base64 to minimize encoding size, # and have charset that's compatible w/ unix crypt variants def h64encode(data): """encode using variant of base64""" - return b64encode(data, "./").strip("=\n") + return b2s(b64encode(data, b"./").strip(b"=\n")) def h64decode(data): """decode using variant of base64""" + data = s2b(data) off = len(data) % 4 if off == 0: - return b64decode(data, "./") + return b64decode(data, b"./") elif off == 1: raise ValueError("Invalid base64 input") elif off == 2: - return b64decode(data + "==", "./") + return b64decode(data + b"==", b"./") else: - return b64decode(data + "=", "./") + return b64decode(data + b"=", b"./") try: from M2Crypto.EVP import pbkdf2 as _pbkdf2 @@ -64,7 +81,7 @@ def xor_bytes(left, right): "perform bitwise-xor of two byte-strings" - return _bjoin(chr(ord(l) ^ ord(r)) for l, r in zip(left, right)) + return _bjoin(bchr(bord(l) ^ bord(r)) for l, r in zip(left, right)) def _pbkdf2(password, salt, rounds, keylen): digest_size = 20 # sha1 generates 20-byte blocks @@ -98,7 +115,7 @@ """pkcs#5 password-based key derivation v2.0 :arg password: passphrase to use to generate key (if unicode, converted to utf-8) - :arg salt: salt string to use when generating key (if unicode, converted to utf-8) + :arg salt: salt bytes to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate @@ -108,7 +125,6 @@ raw bytes of generated key """ password = s2b(us2s(password)) - salt = s2b(us2s(salt)) if keylen > 40: #NOTE: pbkdf2 allows up to (2**31-1)*20 bytes, # but m2crypto has issues on some platforms above 40, @@ -351,7 +367,7 @@ # PBKDF2 - low level function from binascii import unhexlify - k = pbkdf2("password", "ATHENA.MIT.EDUraeburn", 1200, 32) + k = pbkdf2("password", b"ATHENA.MIT.EDUraeburn", 1200, 32) assert k == unhexlify("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13") # PBKDF2 - hash function
