summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2011-06-17 23:26:28 -0400
committerEli Collins <elic@assurancetechnologies.com>2011-06-17 23:26:28 -0400
commit4e26763cd57da1e7feeaa5568f535acc85418948 (patch)
treebd5a4241e7ecb0af06495fdaf74d89970e3d40a7
parente1cad3f31308c0d6bc2928978a771bfc885a3e5f (diff)
downloadpasslib-4e26763cd57da1e7feeaa5568f535acc85418948.tar.gz
pbkdf2 handlers now py3 compat
-rw-r--r--passlib/handlers/pbkdf2.py84
-rw-r--r--passlib/tests/test_drivers.py28
-rw-r--r--passlib/utils/__init__.py12
-rw-r--r--passlib/utils/handlers.py6
4 files changed, 76 insertions, 54 deletions
diff --git a/passlib/handlers/pbkdf2.py b/passlib/handlers/pbkdf2.py
index 981758d..5283dfb 100644
--- a/passlib/handlers/pbkdf2.py
+++ b/passlib/handlers/pbkdf2.py
@@ -10,7 +10,8 @@ import logging; log = logging.getLogger(__name__)
from warnings import warn
#site
#libs
-from passlib.utils import adapted_b64_encode, adapted_b64_decode, ALL_BYTE_VALUES, handlers as uh
+from passlib.utils import adapted_b64_encode, adapted_b64_decode, \
+ handlers as uh, to_hash_str, to_unicode, bytes, b
from passlib.utils.pbkdf2 import pbkdf2
#pkg
#local
@@ -66,10 +67,10 @@ class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gen
raise ValueError("no hash specified")
rounds, salt, chk = uh.parse_mc3(hash, cls.ident, cls.name)
int_rounds = int(rounds)
- if rounds != str(int_rounds): #forbid zero padding, etc.
+ if rounds != unicode(int_rounds): #forbid zero padding, etc.
raise ValueError("invalid %s hash" % (cls.name,))
- raw_salt = adapted_b64_decode(salt)
- raw_chk = adapted_b64_decode(chk) if chk else None
+ raw_salt = adapted_b64_decode(salt.encode("ascii"))
+ raw_chk = adapted_b64_decode(chk.encode("ascii")) if chk else None
return cls(
rounds=int_rounds,
salt=raw_salt,
@@ -78,11 +79,13 @@ class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gen
)
def to_string(self, withchk=True):
- salt = adapted_b64_encode(self.salt)
+ salt = adapted_b64_encode(self.salt).decode("ascii")
if withchk and self.checksum:
- return '%s%d$%s$%s' % (self.ident, self.rounds, salt, adapted_b64_encode(self.checksum))
+ chk = adapted_b64_encode(self.checksum).decode("ascii")
+ hash = u'%s%d$%s$%s' % (self.ident, self.rounds, salt, chk)
else:
- return '%s%d$%s' % (self.ident, self.rounds, salt)
+ hash = u'%s%d$%s' % (self.ident, self.rounds, salt)
+ return to_hash_str(hash)
def calc_checksum(self, secret):
if isinstance(secret, unicode):
@@ -93,7 +96,7 @@ def create_pbkdf2_hash(hash_name, digest_size, ident=None):
"create new Pbkdf2DigestHandler subclass for a specific hash"
name = 'pbkdf2_' + hash_name
if ident is None:
- ident = "$pbkdf2-%s$" % (hash_name,)
+ ident = u"$pbkdf2-%s$" % (hash_name,)
prf = "hmac-%s" % (hash_name,)
base = Pbkdf2DigestHandler
return type(name, (base,), dict(
@@ -126,7 +129,7 @@ def create_pbkdf2_hash(hash_name, digest_size, ident=None):
#---------------------------------------------------------
#derived handlers
#---------------------------------------------------------
-pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, ident="$pbkdf2$")
+pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, ident=u"$pbkdf2$")
pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32)
pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64)
@@ -137,6 +140,10 @@ ldap_pbkdf2_sha512 = uh.PrefixWrapper("ldap_pbkdf2_sha512", pbkdf2_sha512, "{PBK
#=========================================================
#cryptacular's pbkdf2 hash
#=========================================================
+
+#: bytes used by cta hash for base64 values 63 & 64
+CTA_ALTCHARS = b("-_")
+
class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
"""This class implements Cryptacular's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`.
@@ -164,7 +171,7 @@ class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Generic
#--GenericHandler--
name = "cta_pbkdf2_sha1"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = "$p5k2$"
+ ident = u"$p5k2$"
#NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
# underlying algorithm (and reference implementation) allow effectively unbounded values for both of these.
@@ -201,9 +208,9 @@ class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Generic
#left-padded with zeroes
raise ValueError("invalid cta_pbkdf2_sha1 hash")
rounds = int(rounds, 16)
- salt = b64decode(salt, "-_")
+ salt = b64decode(salt.encode("ascii"), CTA_ALTCHARS)
if chk:
- chk = b64decode(chk, "-_")
+ chk = b64decode(chk.encode("ascii"), CTA_ALTCHARS)
return cls(
rounds=rounds,
salt=salt,
@@ -212,10 +219,12 @@ class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Generic
)
def to_string(self, withchk=True):
- out = '$p5k2$%x$%s' % (self.rounds, b64encode(self.salt, "-_"))
+ out = u'$p5k2$%x$%s' % (self.rounds,
+ b64encode(self.salt, CTA_ALTCHARS).decode("ascii"))
if withchk and self.checksum:
- out = "%s$%s" % (out,b64encode(self.checksum,"-_"))
- return out
+ out = u"%s$%s" % (out,
+ b64encode(self.checksum, CTA_ALTCHARS).decode("ascii"))
+ return to_hash_str(out)
#=========================================================
#backend
@@ -259,7 +268,7 @@ class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
#--GenericHandler--
name = "dlitz_pbkdf2_sha1"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = "$p5k2$"
+ ident = u"$p5k2$"
#NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
# underlying algorithm (and reference implementation) allow effectively unbounded values for both of these.
@@ -302,14 +311,14 @@ class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
strict=bool(chk),
)
- def to_string(self, withchk=True):
+ def to_string(self, withchk=True, native=True):
if self.rounds == 400:
- out = '$p5k2$$%s' % (self.salt,)
+ out = u'$p5k2$$%s' % (self.salt,)
else:
- out = '$p5k2$%x$%s' % (self.rounds, self.salt)
+ out = u'$p5k2$%x$%s' % (self.rounds, self.salt)
if withchk and self.checksum:
- out = "%s$%s" % (out,self.checksum)
- return out
+ out = u"%s$%s" % (out,self.checksum)
+ return to_hash_str(out) if native else out
#=========================================================
#backend
@@ -317,9 +326,9 @@ class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
def calc_checksum(self, secret):
if isinstance(secret, unicode):
secret = secret.encode("utf-8")
- salt = self.to_string(withchk=False)
+ salt = self.to_string(withchk=False, native=False).encode("ascii")
result = pbkdf2(secret, salt, self.rounds, 24, "hmac-sha1")
- return adapted_b64_encode(result)
+ return adapted_b64_encode(result).decode("ascii")
#=========================================================
#eoc
@@ -343,10 +352,10 @@ class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler)
#--GenericHandler--
name = "atlassian_pbkdf2_sha1"
setting_kwds =("salt",)
- ident = "{PKCS5S2}"
+ ident = u"{PKCS5S2}"
checksum_size = 32
- _stub_checksum = "\x00" * 32
+ _stub_checksum = b("\x00") * 32
#--HasRawSalt--
min_salt_size = max_salt_size = 16
@@ -355,18 +364,19 @@ class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler)
def from_string(cls, hash):
if not hash:
raise ValueError("no hash specified")
- if isinstance(hash, unicode):
- hash = hash.encode("ascii")
+ if isinstance(hash, bytes):
+ hash = hash.decode("ascii")
ident = cls.ident
if not hash.startswith(ident):
raise ValueError("invalid %s hash" % (cls.name,))
- data = b64decode(hash[len(ident):])
+ data = b64decode(hash[len(ident):].encode("ascii"))
salt, chk = data[:16], data[16:]
return cls(salt=salt, checksum=chk, strict=True)
def to_string(self):
data = self.salt + (self.checksum or self._stub_checksum)
- return self.ident + b64encode(data)
+ hash = self.ident + b64encode(data).decode("ascii")
+ return to_hash_str(hash)
def calc_checksum(self, secret):
#TODO: find out what crowd's policy is re: unicode
@@ -401,7 +411,7 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
name = "grub_pbkdf2_sha512"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = "grub.pbkdf2.sha512."
+ ident = u"grub.pbkdf2.sha512."
#NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
# the underlying pbkdf2 specifies no bounds for either,
@@ -420,12 +430,12 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
def from_string(cls, hash):
if not hash:
raise ValueError("no hash specified")
- rounds, salt, chk = uh.parse_mc3(hash, cls.ident, cls.name, sep=".")
+ rounds, salt, chk = uh.parse_mc3(hash, cls.ident, cls.name, sep=u".")
int_rounds = int(rounds)
if rounds != str(int_rounds): #forbid zero padding, etc.
raise ValueError("invalid %s hash" % (cls.name,))
- raw_salt = unhexlify(salt)
- raw_chk = unhexlify(chk) if chk else None
+ raw_salt = unhexlify(salt.encode("ascii"))
+ raw_chk = unhexlify(chk.encode("ascii")) if chk else None
return cls(
rounds=int_rounds,
salt=raw_salt,
@@ -434,11 +444,13 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
)
def to_string(self, withchk=True):
- salt = hexlify(self.salt).upper()
+ salt = hexlify(self.salt).decode("ascii").upper()
if withchk and self.checksum:
- return '%s%d.%s.%s' % (self.ident, self.rounds, salt, hexlify(self.checksum).upper())
+ chk = hexlify(self.checksum).decode("ascii").upper()
+ hash = u'%s%d.%s.%s' % (self.ident, self.rounds, salt, chk)
else:
- return '%s%d.%s' % (self.ident, self.rounds, salt)
+ hash = u'%s%d.%s' % (self.ident, self.rounds, salt)
+ return to_hash_str(hash)
def calc_checksum(self, secret):
#TODO: find out what grub's policy is re: unicode
diff --git a/passlib/tests/test_drivers.py b/passlib/tests/test_drivers.py
index 49234c5..65589a6 100644
--- a/passlib/tests/test_drivers.py
+++ b/passlib/tests/test_drivers.py
@@ -519,11 +519,11 @@ from passlib.handlers import pbkdf2 as pk2
class AtlassianPbkdf2Sha1Test(HandlerCase):
handler = pk2.atlassian_pbkdf2_sha1
- known_correct_hashes = (
+ known_correct_hashes = [
("admin", '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/p'),
(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
"{PKCS5S2}cE9Yq6Am5tQGdHSHhky2XLeOnURwzaLBG2sur7FHKpvy2u0qDn6GcVGRjlmJoIUy"),
- )
+ ]
known_malformed_hashes = [
#bad char
@@ -538,26 +538,26 @@ class AtlassianPbkdf2Sha1Test(HandlerCase):
class Pbkdf2Sha1Test(HandlerCase):
handler = pk2.pbkdf2_sha1
- known_correct_hashes = (
+ known_correct_hashes = [
("password", '$pbkdf2$1212$OB.dtnSEXZK8U5cgxU/GYQ$y5LKPOplRmok7CZp/aqVDVg8zGI'),
(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$pbkdf2$1212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc'),
- )
+ ]
class Pbkdf2Sha256Test(HandlerCase):
handler = pk2.pbkdf2_sha256
- known_correct_hashes = (
+ known_correct_hashes = [
("password",
'$pbkdf2-sha256$1212$4vjV83LKPjQzk31VI4E0Vw$hsYF68OiOUPdDZ1Fg.fJPeq1h/gXXY7acBp9/6c.tmQ'
),
(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$pbkdf2-sha256$1212$3SABFJGDtyhrQMVt1uABPw$WyaUoqCLgvz97s523nF4iuOqZNbp5Nt8do/cuaa7AiI'
),
- )
+ ]
class Pbkdf2Sha512Test(HandlerCase):
handler = pk2.pbkdf2_sha512
- known_correct_hashes = (
+ known_correct_hashes = [
("password",
'$pbkdf2-sha512$1212$RHY0Fr3IDMSVO/RSZyb5ow$eNLfBK.eVozomMr.1gYa1'
'7k9B7KIK25NOEshvhrSX.esqY3s.FvWZViXz4KoLlQI.BzY/YTNJOiKc5gBYFYGww'
@@ -566,11 +566,11 @@ class Pbkdf2Sha512Test(HandlerCase):
'$pbkdf2-sha512$1212$KkbvoKGsAIcF8IslDR6skQ$8be/PRmd88Ps8fmPowCJt'
'tH9G3vgxpG.Krjt3KT.NP6cKJ0V4Prarqf.HBwz0dCkJ6xgWnSj2ynXSV7MlvMa8Q'
),
- )
+ ]
class CtaPbkdf2Sha1Test(HandlerCase):
handler = pk2.cta_pbkdf2_sha1
- known_correct_hashes = (
+ known_correct_hashes = [
#test vectors from original implementation
(u"hashy the \N{SNOWMAN}", '$p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0='),
@@ -578,11 +578,11 @@ class CtaPbkdf2Sha1Test(HandlerCase):
("password", "$p5k2$1$$h1TDLGSw9ST8UMAPeIE13i0t12c="),
(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
"$p5k2$4321$OTg3NjU0MzIx$jINJrSvZ3LXeIbUdrJkRpN62_WQ="),
- )
+ ]
class DlitzPbkdf2Sha1Test(HandlerCase):
handler = pk2.dlitz_pbkdf2_sha1
- known_correct_hashes = (
+ known_correct_hashes = [
#test vectors from original implementation
('cloadm', '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'),
('gnu', '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'),
@@ -590,11 +590,11 @@ class DlitzPbkdf2Sha1Test(HandlerCase):
('spam', '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'),
(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'),
- )
+ ]
class GrubPbkdf2Sha512Test(HandlerCase):
handler = pk2.grub_pbkdf2_sha512
- known_correct_hashes = (
+ known_correct_hashes = [
#test vectors generated from cmd line tool
#salt=32 bytes
@@ -614,7 +614,7 @@ class GrubPbkdf2Sha512Test(HandlerCase):
'2D3256ECC9F765D84956FC5CA5C4B6FD711AA285F0A04DCF4'
'634083F9A20F4B6F339A52FBD6BED618E527B'),
- )
+ ]
#=========================================================
#PHPass Portable Crypt
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index e444383..ab5826b 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -498,6 +498,10 @@ def xor_bytes(left, right):
#=================================================================================
#alt base64 encoding
#=================================================================================
+_A64_ALTCHARS = b("./")
+_A64_STRIP = b("=\n")
+_A64_PAD1 = b("=")
+_A64_PAD2 = b("==")
def adapted_b64_encode(data):
"""encode using variant of base64
@@ -508,7 +512,7 @@ def adapted_b64_encode(data):
it is primarily used for by passlib's custom pbkdf2 hashes.
"""
- return b64encode(data, "./").strip("=\n")
+ return b64encode(data, _A64_ALTCHARS).strip(_A64_STRIP)
def adapted_b64_decode(data, sixthree="."):
"""decode using variant of base64
@@ -521,13 +525,13 @@ def adapted_b64_decode(data, sixthree="."):
"""
off = len(data) % 4
if off == 0:
- return b64decode(data, "./")
+ return b64decode(data, _A64_ALTCHARS)
elif off == 1:
raise ValueError("invalid bas64 input")
elif off == 2:
- return b64decode(data + "==", "./")
+ return b64decode(data + _A64_PAD2, _A64_ALTCHARS)
else:
- return b64decode(data + "=", "./")
+ return b64decode(data + _A64_PAD1, _A64_ALTCHARS)
#=================================================================================
#randomness
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index b06a9eb..c15deb6 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -366,6 +366,12 @@ class GenericHandler(object):
should return native string type (ascii-bytes under python 2,
unicode under python 3)
"""
+ #NOTE: documenting some non-standardized but common kwd flags
+ # that passlib to_string() method may have
+ #
+ # native=True -- if false, return unicode under py2 -- ignored under py3
+ # withchk=True -- if false, omit checksum portion of hash
+ #
raise NotImplementedError("%s must implement from_string()" % (type(self),))
##def to_config_string(self):