diff options
| -rw-r--r-- | passlib/handlers/scram.py | 15 | ||||
| -rw-r--r-- | passlib/tests/test_handlers.py | 27 |
2 files changed, 34 insertions, 8 deletions
diff --git a/passlib/handlers/scram.py b/passlib/handlers/scram.py index 176f57c..a0c03f0 100644 --- a/passlib/handlers/scram.py +++ b/passlib/handlers/scram.py @@ -302,6 +302,8 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): password = saslprep(password).encode("utf-8") if not isinstance(salt, bytes): raise TypeError("salt must be bytes") + if rounds < 1: + raise ValueError("rounds must be >= 1") alg = iana_to_hashlib(norm_digest_name(alg)) return pbkdf2(password, salt, rounds, -1, "hmac-" + alg) @@ -328,7 +330,10 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): raise ValueError("invalid scram hash") # decode salt - salt = adapted_b64_decode(salt_str.encode("ascii")) + try: + salt = adapted_b64_decode(salt_str.encode("ascii")) + except TypeError: + raise ValueError("malformed scram hash") # decode algs/digest list if not chk_str: @@ -340,7 +345,10 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): chkmap = {} for pair in chk_str.split(","): alg, digest = pair.split("=") - chkmap[alg] = adapted_b64_decode(digest.encode("ascii")) + try: + chkmap[alg] = adapted_b64_decode(digest.encode("ascii")) + except TypeError: + raise ValueError("malformed scram hash") else: # comma-separated list of alg names, no digests algs = chk_str @@ -449,7 +457,8 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): self = cls.from_string(hash) chkmap = self.checksum if not chkmap: - return False + raise ValueError("expected %s hash, got %s config string instead" % + (cls.name, cls.name)) # NOTE: to make the verify method efficient, we just calculate hash # of shortest digest by default. apps can pass in "full_verify=True" to diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py index 81e3958..5670339 100644 --- a/passlib/tests/test_handlers.py +++ b/passlib/tests/test_handlers.py @@ -1051,12 +1051,11 @@ class ScramTest(HandlerCase): # non-digit in rounds '$scram$409A$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', -# FIXME: bad chars raise TypeError # bad char in salt -# '$scram$4096$QSXCR.Q6sek8bf9-$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', + '$scram$4096$QSXCR.Q6sek8bf9-$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', # bad char in digest -# '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX3-', + '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX3-', # too many chars in alg '$scram$4096$QSXCR.Q6sek8bf92$shaxxx-190=HZbuOlKbWl.eR8AfIposuKbhX30', @@ -1123,10 +1122,28 @@ class ScramTest(HandlerCase): 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ'), ["sha-1","sha-256","sha-512"]) - # TODO. def test_103_derive_digest(self): "test scram.derive_digest()" - pass + # NOTE: this just does a light test, since derive_digest + # is used by encrypt / verify, and is tested pretty well via those. + + hash = self.handler.derive_digest + + # check various encodings of password work. + s1 = b('\x01\x02\x03') + d1 = b('\xb2\xfb\xab\x82[tNuPnI\x8aZZ\x19\x87\xcen\xe9\xd3') + self.assertEqual(hash(u("\u2168"), s1, 1000, 'sha-1'), d1) + self.assertEqual(hash(b("\xe2\x85\xa8"), s1, 1000, 'SHA-1'), d1) + self.assertEqual(hash(u("IX"), s1, 1000, 'sha1'), d1) + self.assertEqual(hash(b("IX"), s1, 1000, 'SHA1'), d1) + + # check algs + self.assertEqual(hash("IX", s1, 1000, 'md5'), + b('3\x19\x18\xc0\x1c/\xa8\xbf\xe4\xa3\xc2\x8eM\xe8od')) + self.assertRaises(ValueError, hash, "IX", s1, 1000, 'sha-666') + + # check rounds + self.assertRaises(ValueError, hash, "IX", s1, 0, 'sha-1') def test_104_saslprep(self): "test encrypt/verify use saslprep" |
