Skip to content

Commit 460b9b3

Browse files
committed
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.
1 parent 7ffeecb commit 460b9b3

File tree

1 file changed

+27
-11
lines changed

1 file changed

+27
-11
lines changed

roundup/password.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,53 @@
2424
from base64 import b64encode, b64decode
2525
from hashlib import md5, sha1
2626

27-
from roundup.anypy.strings import us2s, s2b
27+
from roundup.anypy.strings import us2s, b2s, s2b
2828

2929
try:
3030
import crypt
3131
except ImportError:
3232
crypt = None
3333

34-
_bempty = ""
34+
_bempty = b""
3535
_bjoin = _bempty.join
3636

37+
def bchr(c):
38+
if bytes == str:
39+
# Python 2.
40+
return chr(c)
41+
else:
42+
# Python 3.
43+
return bytes((c,))
44+
45+
def bord(c):
46+
if bytes == str:
47+
# Python 2.
48+
return ord(c)
49+
else:
50+
# Python 3. Elements of bytes are integers.
51+
return c
52+
3753
def getrandbytes(count):
38-
return _bjoin(chr(random.randint(0,255)) for i in range(count))
54+
return _bjoin(bchr(random.randint(0,255)) for i in range(count))
3955

4056
#NOTE: PBKDF2 hash is using this variant of base64 to minimize encoding size,
4157
# and have charset that's compatible w/ unix crypt variants
4258
def h64encode(data):
4359
"""encode using variant of base64"""
44-
return b64encode(data, "./").strip("=\n")
60+
return b2s(b64encode(data, b"./").strip(b"=\n"))
4561

4662
def h64decode(data):
4763
"""decode using variant of base64"""
64+
data = s2b(data)
4865
off = len(data) % 4
4966
if off == 0:
50-
return b64decode(data, "./")
67+
return b64decode(data, b"./")
5168
elif off == 1:
5269
raise ValueError("Invalid base64 input")
5370
elif off == 2:
54-
return b64decode(data + "==", "./")
71+
return b64decode(data + b"==", b"./")
5572
else:
56-
return b64decode(data + "=", "./")
73+
return b64decode(data + b"=", b"./")
5774

5875
try:
5976
from M2Crypto.EVP import pbkdf2 as _pbkdf2
@@ -64,7 +81,7 @@ def h64decode(data):
6481

6582
def xor_bytes(left, right):
6683
"perform bitwise-xor of two byte-strings"
67-
return _bjoin(chr(ord(l) ^ ord(r)) for l, r in zip(left, right))
84+
return _bjoin(bchr(bord(l) ^ bord(r)) for l, r in zip(left, right))
6885

6986
def _pbkdf2(password, salt, rounds, keylen):
7087
digest_size = 20 # sha1 generates 20-byte blocks
@@ -98,7 +115,7 @@ def pbkdf2(password, salt, rounds, keylen):
98115
"""pkcs#5 password-based key derivation v2.0
99116
100117
:arg password: passphrase to use to generate key (if unicode, converted to utf-8)
101-
:arg salt: salt string to use when generating key (if unicode, converted to utf-8)
118+
:arg salt: salt bytes to use when generating key
102119
:param rounds: number of rounds to use to generate key
103120
:arg keylen: number of bytes to generate
104121
@@ -108,7 +125,6 @@ def pbkdf2(password, salt, rounds, keylen):
108125
raw bytes of generated key
109126
"""
110127
password = s2b(us2s(password))
111-
salt = s2b(us2s(salt))
112128
if keylen > 40:
113129
#NOTE: pbkdf2 allows up to (2**31-1)*20 bytes,
114130
# but m2crypto has issues on some platforms above 40,
@@ -351,7 +367,7 @@ def test():
351367

352368
# PBKDF2 - low level function
353369
from binascii import unhexlify
354-
k = pbkdf2("password", "ATHENA.MIT.EDUraeburn", 1200, 32)
370+
k = pbkdf2("password", b"ATHENA.MIT.EDUraeburn", 1200, 32)
355371
assert k == unhexlify("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13")
356372

357373
# PBKDF2 - hash function

0 commit comments

Comments
 (0)