def test_safe_os_crypt(self): "test safe_os_crypt() wrapper" if not safe_os_crypt: raise self.skipTest("stdlib crypt module not available") #NOTE: this is assuming EVERY crypt will support des_crypt. # if this fails on some platform, this test will need modifying. #test normal case ok, hash = safe_os_crypt(u'test', u'aa') self.assertTrue(ok) self.assertIsInstance(hash, unicode) self.assertEqual(hash, u'aaqPiZY5xR5l.') #test hash-as-bytes self.assertRaises(TypeError, safe_os_crypt, u'test', b('aa')) #test password as ascii ret = safe_os_crypt(b('test'), u'aa') self.assertEqual(ret, (True, u'aaqPiZY5xR5l.')) #test unicode password w/ high char ret = safe_os_crypt(u'test\u1234', u'aa') self.assertEqual(ret, (True, u'aahWwbrUsKZk.')) #test utf-8 password w/ high char ret = safe_os_crypt(b('test\xe1\x88\xb4'), u'aa') self.assertEqual(ret, (True, u'aahWwbrUsKZk.')) #test latin-1 password ret = safe_os_crypt(b('test\xff'), u'aa') # Py2k # self.assertEqual(ret, (True, u'aaOx.5nbTU/.M'))
def test_05_load(self): "test load()" if gae_env: return self.skipTest( "GAE doesn't offer read/write filesystem access") #setup empty file path = mktemp() set_file(path, "") backdate_file_mtime(path, 5) ha = apache.HtdigestFile(path) self.assertEqual(ha.to_string(), b("")) #make changes, check force=False does nothing ha.update("user1", "realm", "pass1") ha.load(force=False) self.assertEqual(ha.to_string(), b('user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n')) #change file set_file(path, self.sample_01) ha.load(force=False) self.assertEqual(ha.to_string(), self.sample_01) #make changes, check force=True overwrites them ha.update("user5", "realm", "pass5") ha.load() self.assertEqual(ha.to_string(), self.sample_01) #test load w/ no path hb = apache.HtdigestFile() self.assertRaises(RuntimeError, hb.load) self.assertRaises(RuntimeError, hb.load, force=False)
def test_getrandstr(self): "test getrandstr()" def f(*a, **k): return utils.getrandstr(utils.rng, *a, **k) #count 0 self.assertEqual(f('abc', 0), '') #count <0 self.assertRaises(ValueError, f, 'abc', -1) #letters 0 self.assertRaises(ValueError, f, '', 0) #letters 1 self.assertEqual(f('a', 5), 'aaaaa') #letters x = f(u'abc', 16) y = f(u'abc', 16) self.assertIsInstance(x, unicode) self.assertNotEqual(x, y) self.assertEqual(sorted(set(x)), [u'a', u'b', u'c']) #bytes x = f(b('abc'), 16) y = f(b('abc'), 16) self.assertIsInstance(x, bytes) self.assertNotEqual(x, y) #NOTE: decoding this due to py3 bytes self.assertEqual(sorted(set(x.decode("ascii"))), [u'a', u'b', u'c']) #generate_password self.assertEqual(len(utils.generate_password(15)), 15)
def _get_hmac_prf(digest): "helper to return HMAC prf for specific digest" #check if m2crypto is present and supports requested digest if _EVP: try: result = _EVP.hmac(b('x'), b('y'), digest) except ValueError: pass else: #it does. so use M2Crypto's hmac & digest code hmac_const = _EVP.hmac def prf(key, msg): "prf(key,msg)->digest; generated by passlib.utils.pbkdf2.get_prf()" return hmac_const(key, msg, digest) prf.__name__ = "hmac_" + digest digest_size = len(result) return prf, digest_size #fall back to stdlib implementation digest_const = getattr(hashlib, digest, None) if not digest_const: raise ValueError("unknown hash algorithm: %r" % (digest,)) digest_size = digest_const().digest_size hmac_const = hmac.new def prf(key, msg): "prf(key,msg)->digest; generated by passlib.utils.pbkdf2.get_prf()" return hmac_const(key, msg, digest_const).digest() prf.__name__ = "hmac_" + digest return prf, digest_size
def test_getrandstr(self): "test getrandstr()" def f(*a,**k): return utils.getrandstr(utils.rng, *a, **k) #count 0 self.assertEqual(f('abc',0), '') #count <0 self.assertRaises(ValueError, f, 'abc', -1) #letters 0 self.assertRaises(ValueError, f, '', 0) #letters 1 self.assertEqual(f('a',5), 'aaaaa') #letters x = f(u'abc', 16) y = f(u'abc', 16) self.assertIsInstance(x, unicode) self.assertNotEqual(x,y) self.assertEqual(sorted(set(x)), [u'a',u'b',u'c']) #bytes x = f(b('abc'), 16) y = f(b('abc'), 16) self.assertIsInstance(x, bytes) self.assertNotEqual(x,y) #NOTE: decoding this due to py3 bytes self.assertEqual(sorted(set(x.decode("ascii"))), [u'a',u'b',u'c']) #generate_password self.assertEqual(len(utils.generate_password(15)), 15)
def _get_hmac_prf(digest): "helper to return HMAC prf for specific digest" #check if m2crypto is present and supports requested digest if _EVP: try: result = _EVP.hmac(b('x'), b('y'), digest) except ValueError: pass else: #it does. so use M2Crypto's hmac & digest code hmac_const = _EVP.hmac def prf(key, msg): "prf(key,msg)->digest; generated by passlib.utils.pbkdf2.get_prf()" return hmac_const(key, msg, digest) prf.__name__ = "hmac_" + digest digest_size = len(result) return prf, digest_size #fall back to stdlib implementation digest_const = getattr(hashlib, digest, None) if not digest_const: raise ValueError("unknown hash algorithm: %r" % (digest, )) digest_size = digest_const().digest_size hmac_const = hmac.new def prf(key, msg): "prf(key,msg)->digest; generated by passlib.utils.pbkdf2.get_prf()" return hmac_const(key, msg, digest_const).digest() prf.__name__ = "hmac_" + digest return prf, digest_size
def test_05_load(self): "test load()" if gae_env: return self.skipTest("GAE doesn't offer read/write filesystem access") #setup empty file path = mktemp() set_file(path, "") backdate_file_mtime(path, 5) ha = apache.HtdigestFile(path) self.assertEqual(ha.to_string(), b("")) #make changes, check force=False does nothing ha.update("user1", "realm", "pass1") ha.load(force=False) self.assertEqual(ha.to_string(), b('user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n')) #change file set_file(path, self.sample_01) ha.load(force=False) self.assertEqual(ha.to_string(), self.sample_01) #make changes, check force=True overwrites them ha.update("user5", "realm", "pass5") ha.load() self.assertEqual(ha.to_string(), self.sample_01) #test load w/ no path hb = apache.HtdigestFile() self.assertRaises(RuntimeError, hb.load) self.assertRaises(RuntimeError, hb.load, force=False)
def get_prf(name): """lookup pseudo-random family (prf) by name. :arg name: this must be the name of a recognized prf. currently this only recognizes names with the format :samp:`hmac-{digest}`, where :samp:`{digest}` is the name of a hash function such as ``md5``, ``sha256``, etc. this can also be a callable with the signature ``prf(secret, message) -> digest``, in which case it will be returned unchanged. :raises ValueError: if the name is not known :raises TypeError: if the name is not a callable or string :returns: a tuple of :samp:`({func}, {digest_size})`. * :samp:`{func}` is a function implementing the specified prf, and has the signature ``func(secret, message) -> digest``. * :samp:`{digest_size}` is an integer indicating the number of bytes the function returns. usage example:: >>> from passlib.utils.pbkdf2 import get_prf >>> hmac_sha256, dsize = get_prf("hmac-sha256") >>> hmac_sha256 <function hmac_sha256 at 0x1e37c80> >>> dsize 32 >>> digest = hmac_sha256('password', 'message') this function will attempt to return the fastest implementation it can find; if M2Crypto is present, and supports the specified prf, :func:`M2Crypto.EVP.hmac` will be used behind the scenes. """ global _prf_cache if name in _prf_cache: return _prf_cache[name] if isinstance(name, native_str): if name.startswith("hmac-") or name.startswith("hmac_"): retval = _get_hmac_prf(name[5:]) else: raise ValueError("unknown prf algorithm: %r" % (name,)) elif callable(name): #assume it's a callable, use it directly digest_size = len(name(b('x'),b('y'))) retval = (name, digest_size) else: raise TypeError("prf must be string or callable") _prf_cache[name] = retval return retval
def get_prf(name): """lookup pseudo-random family (prf) by name. :arg name: this must be the name of a recognized prf. currently this only recognizes names with the format :samp:`hmac-{digest}`, where :samp:`{digest}` is the name of a hash function such as ``md5``, ``sha256``, etc. this can also be a callable with the signature ``prf(secret, message) -> digest``, in which case it will be returned unchanged. :raises ValueError: if the name is not known :raises TypeError: if the name is not a callable or string :returns: a tuple of :samp:`({func}, {digest_size})`. * :samp:`{func}` is a function implementing the specified prf, and has the signature ``func(secret, message) -> digest``. * :samp:`{digest_size}` is an integer indicating the number of bytes the function returns. usage example:: >>> from passlib.utils.pbkdf2 import get_prf >>> hmac_sha256, dsize = get_prf("hmac-sha256") >>> hmac_sha256 <function hmac_sha256 at 0x1e37c80> >>> dsize 32 >>> digest = hmac_sha256('password', 'message') this function will attempt to return the fastest implementation it can find; if M2Crypto is present, and supports the specified prf, :func:`M2Crypto.EVP.hmac` will be used behind the scenes. """ global _prf_cache if name in _prf_cache: return _prf_cache[name] if isinstance(name, native_str): if name.startswith("hmac-") or name.startswith("hmac_"): retval = _get_hmac_prf(name[5:]) else: raise ValueError("unknown prf algorithm: %r" % (name, )) elif callable(name): #assume it's a callable, use it directly digest_size = len(name(b('x'), b('y'))) retval = (name, digest_size) else: raise TypeError("prf must be string or callable") _prf_cache[name] = retval return retval
def test_24_verify_none(self): "test verify() throws error against hash=None/empty string" #find valid hash so that doesn't mask error self.assertRaises(ValueError, self.do_verify, 'stub', None, __msg__="hash=None:") if self.accepts_empty_hash: self.do_verify("stub", u"") self.do_verify("stub", b("")) else: self.assertRaises(ValueError, self.do_verify, 'stub', u'', __msg__="hash='':") self.assertRaises(ValueError, self.do_verify, 'stub', b(''), __msg__="hash='':")
def test_md4_copy(self): "test md4 copy()" md4 = self.hash h = md4(b('abc')) h2 = h.copy() h2.update(b('def')) self.assertEqual(h2.hexdigest(), '804e7f1c2586e50b49ac65db5b645131') h.update(b('ghi')) self.assertEqual(h.hexdigest(), 'c5225580bfe176f6deeee33dee98732c')
def test_decode_int(self): self.assertEqual(h64.decode_int64(b('...........')), 0) self.assertRaises(ValueError, h64.decode_int12, b('a?')) self.assertRaises(ValueError, h64.decode_int24, b('aaa?')) self.assertRaises(ValueError, h64.decode_int64, b('aaa?aaa?aaa')) self.assertRaises(ValueError, h64.decode_dc_int64, b('aaa?aaa?aaa')) self.assertRaises(TypeError, h64.decode_int12, u'a'*2) self.assertRaises(TypeError, h64.decode_int24, u'a'*4) self.assertRaises(TypeError, h64.decode_int64, u'a'*11) self.assertRaises(TypeError, h64.decode_dc_int64, u'a'*11)
def test_decode_int(self): self.assertEqual(h64.decode_int64(b('...........')), 0) self.assertRaises(ValueError, h64.decode_int12, b('a?')) self.assertRaises(ValueError, h64.decode_int24, b('aaa?')) self.assertRaises(ValueError, h64.decode_int64, b('aaa?aaa?aaa')) self.assertRaises(ValueError, h64.decode_dc_int64, b('aaa?aaa?aaa')) self.assertRaises(TypeError, h64.decode_int12, u'a' * 2) self.assertRaises(TypeError, h64.decode_int24, u'a' * 4) self.assertRaises(TypeError, h64.decode_int64, u'a' * 11) self.assertRaises(TypeError, h64.decode_dc_int64, u'a' * 11)
def test_bytes(self): "test b() helper, bytes and native_str types" # Py2k # self.assertIs(bytes, type('')) self.assertIs(native_str, bytes) # Py3k # #self.assertIs(bytes, type(b'')) #self.assertIs(native_str, unicode) # end Py3k # self.assertIsInstance(b(''), bytes) self.assertIsInstance(b('\x00\xff'), bytes) # Py2k # self.assertEqual(b('\x00\xff'), "\x00\xff")
def test_rfc6070(self): "rfc6070 test vectors" self.assertFunctionResults( pbkdf2.pbkdf2, [ ( hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"), b("password"), b("salt"), 1, 20, ), ( hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), b("password"), b("salt"), 2, 20, ), ( hb("4b007901b765489abead49d926f721d065a429c1"), b("password"), b("salt"), 4096, 20, ), #just runs too long - could enable if ALL option is set ##( ## ## unhexlify("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"), ## "password", "salt", 16777216, 20, ##), ( hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), b("passwordPASSWORDpassword"), b("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, 25, ), ( hb("56fa6aa75548099dcc37d7f03425e0c3"), b("pass\00word"), b("sa\00lt"), 4096, 16, ), ])
def raw_ext_crypt(secret, rounds, salt): "ext_crypt() helper which returns checksum only" #decode salt try: salt_value = h64.decode_int24(salt) except ValueError: #pragma: no cover - always caught by class raise ValueError("invalid salt") #validate secret if b('\x00') in secret: #pragma: no cover - always caught by class #builtin linux crypt doesn't like this, so we don't either #XXX: would make more sense to raise ValueError, but want to be compatible w/ stdlib crypt raise ValueError("secret must be string without null bytes") #convert secret string into an integer key_value = _crypt_secret_to_key(secret) idx = 8 end = len(secret) while idx < end: next = idx+8 key_value = mdes_encrypt_int_block(key_value, key_value) ^ \ _crypt_secret_to_key(secret[idx:next]) idx = next #run data through des using input of 0 result = mdes_encrypt_int_block(key_value, 0, salt_value, rounds) #run h64 encode on result return h64.encode_dc_int64(result)
def identify(cls, hash): if not hash: return False if isinstance(hash, bytes): return hash == b("!") else: return hash == u"!"
def genhash(cls, secret, hash): if not cls.identify(hash): raise ValueError("not a unsalted-example hash") if isinstance(secret, unicode): secret = secret.encode("utf-8") data = b("boblious") + secret return to_hash_str(hashlib.sha1(data).hexdigest())
def test_09_encodings(self): "test encoding parameter" #test bad encodings cause failure in constructor self.assertRaises(ValueError, apache.HtdigestFile, encoding="utf-16") #check users() returns native string by default ht = apache.HtdigestFile._from_string(self.sample_01) self.assertIsInstance(ht.realms()[0], native_str) self.assertIsInstance(ht.users("realm")[0], native_str) #check returns unicode if encoding explicitly set ht = apache.HtdigestFile._from_string(self.sample_01, encoding="utf-8") self.assertIsInstance(ht.realms()[0], unicode) self.assertIsInstance(ht.users(u"realm")[0], unicode) #check returns bytes if encoding explicitly disabled ht = apache.HtdigestFile._from_string(self.sample_01, encoding=None) self.assertIsInstance(ht.realms()[0], bytes) self.assertIsInstance(ht.users(b("realm"))[0], bytes) #check sample utf-8 ht = apache.HtdigestFile._from_string(self.sample_04_utf8, encoding="utf-8") self.assertEqual(ht.realms(), [u"realm\u00e6"]) self.assertEqual(ht.users(u"realm\u00e6"), [u"user\u00e6"]) #check sample latin-1 ht = apache.HtdigestFile._from_string(self.sample_04_latin1, encoding="latin-1") self.assertEqual(ht.realms(), [u"realm\u00e6"]) self.assertEqual(ht.users(u"realm\u00e6"), [u"user\u00e6"])
def test_des_encrypt_block(self): for k,p,c in self.test_des_vectors: k = unhexlify(k) p = unhexlify(p) c = unhexlify(c) result = des.des_encrypt_block(k,p) self.assertEqual(result, c, "key=%r p=%r:" % (k,p)) #test 7 byte key #FIXME: use a better key k,p,c = b('00000000000000'), b('FFFFFFFFFFFFFFFF'), b('355550B2150E2451') k = unhexlify(k) p = unhexlify(p) c = unhexlify(c) result = des.des_encrypt_block(k,p) self.assertEqual(result, c, "key=%r p=%r:" % (k,p))
def test_09_encodings(self): "test encoding parameter" #test bad encodings cause failure in constructor self.assertRaises(ValueError, apache.HtdigestFile, encoding="utf-16") #check users() returns native string by default ht = apache.HtdigestFile._from_string(self.sample_01) self.assertIsInstance(ht.realms()[0], native_str) self.assertIsInstance(ht.users("realm")[0], native_str) #check returns unicode if encoding explicitly set ht = apache.HtdigestFile._from_string(self.sample_01, encoding="utf-8") self.assertIsInstance(ht.realms()[0], unicode) self.assertIsInstance(ht.users(u"realm")[0], unicode) #check returns bytes if encoding explicitly disabled ht = apache.HtdigestFile._from_string(self.sample_01, encoding=None) self.assertIsInstance(ht.realms()[0], bytes) self.assertIsInstance(ht.users(b("realm"))[0], bytes) #check sample utf-8 ht = apache.HtdigestFile._from_string(self.sample_04_utf8, encoding="utf-8") self.assertEqual(ht.realms(), [ u"realm\u00e6" ]) self.assertEqual(ht.users(u"realm\u00e6"), [ u"user\u00e6" ]) #check sample latin-1 ht = apache.HtdigestFile._from_string(self.sample_04_latin1, encoding="latin-1") self.assertEqual(ht.realms(), [ u"realm\u00e6" ]) self.assertEqual(ht.users(u"realm\u00e6"), [ u"user\u00e6" ])
def to_string(self): chk = self.checksum if not chk: #fill in stub checksum chk = b('\x00') * self._info[1] salt = self.salt data = b64encode(salt+chk).decode("ascii") hash = u"{FSHP%d|%d|%d}%s" % (self.variant, len(salt), self.rounds, data) return to_hash_str(hash)
def create_mismatch(self, secret): "return other secret which won't match" #NOTE: this is subclassable mainly for some algorithms #which accept non-strings in secret if isinstance(secret, unicode): return u'x' + secret else: return b('x') + secret
def test_md4_update(self): "test md4 update" md4 = self.hash h = md4(b('')) self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0") #NOTE: under py2, hashlib methods try to encode to ascii, # though shouldn't rely on that. # Py3k # #self.assertRaises(TypeError, h.update, u'x') # end Py3k # h.update(b('a')) self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24") h.update(b('bcdefghijklmnopqrstuvwxyz')) self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
def _calc_checksum_builtin(self, secret): #gotta do something - no official policy since des-crypt predates unicode if isinstance(secret, unicode): secret = secret.encode("utf-8") #forbidding nul chars because linux crypt (and most C implementations) won't accept it either. if b('\x00') in secret: raise ValueError("null char in secret") return raw_crypt(secret, self.salt.encode("ascii")).decode("ascii")
def test_des_encrypt_block(self): for k, p, c in self.test_des_vectors: k = unhexlify(k) p = unhexlify(p) c = unhexlify(c) result = des.des_encrypt_block(k, p) self.assertEqual(result, c, "key=%r p=%r:" % (k, p)) #test 7 byte key #FIXME: use a better key k, p, c = b('00000000000000'), b('FFFFFFFFFFFFFFFF'), b( '355550B2150E2451') k = unhexlify(k) p = unhexlify(p) c = unhexlify(c) result = des.des_encrypt_block(k, p) self.assertEqual(result, c, "key=%r p=%r:" % (k, p))
def test_decode_bytes(self): for result, source in self.encoded_bytes: out = h64.decode_bytes(source) self.assertEqual(out, result) #wrong size (1 % 4) self.assertRaises(ValueError, h64.decode_bytes, b('abcde')) self.assertRaises(TypeError, h64.decode_bytes, u'abcd')
def test_pbkdf1(self): "test pbkdf1" for secret, salt, rounds, klen, hash, correct in [ #http://www.di-mgt.com.au/cryptoKDFs.html (b('password'), hb('78578E5A5D63CB06'), 1000, 16, 'sha1', hb('dc19847e05c64d2faf10ebfb4a3d2a20')), ]: result = pbkdf2.pbkdf1(secret, salt, rounds, klen, hash) self.assertEqual(result, correct)
def test_10_to_string(self): "test to_string()" #check sample ht = apache.HtdigestFile._from_string(self.sample_01) self.assertEqual(ht.to_string(), self.sample_01) #check blank ht = apache.HtdigestFile() self.assertEqual(ht.to_string(), b(""))
def test_08_to_string(self): "test to_string" #check with known sample ht = apache.HtpasswdFile._from_string(self.sample_01) self.assertEqual(ht.to_string(), self.sample_01) #test blank ht = apache.HtpasswdFile() self.assertEqual(ht.to_string(), b(""))
def test_07_realms(self): "test realms() & delete_realm()" ht = apache.HtdigestFile._from_string(self.sample_01) self.assertEqual(ht.delete_realm("x"), 0) self.assertEqual(ht.realms(), ['realm']) self.assertEqual(ht.delete_realm("realm"), 4) self.assertEqual(ht.realms(), []) self.assertEqual(ht.to_string(), b(""))
def test_to_unicode(self): "test to_unicode()" #check unicode inputs self.assertEqual(to_unicode(u'abc'), u'abc') self.assertEqual(to_unicode(u'\x00\xff'), u'\x00\xff') #check unicode input ignores encoding self.assertEqual(to_unicode(u'\x00\xff', None), u'\x00\xff') self.assertEqual(to_unicode(u'\x00\xff', "ascii"), u'\x00\xff') #check bytes input self.assertEqual(to_unicode(b('abc')), u'abc') self.assertEqual(to_unicode(b('\x00\xc3\xbf')), u'\x00\xff') self.assertEqual(to_unicode(b('\x00\xff'), 'latin-1'), u'\x00\xff') self.assertRaises(ValueError, to_unicode, b('\x00\xff')) self.assertRaises(TypeError, to_unicode, b('\x00\xff'), None) #check other self.assertRaises(TypeError, to_unicode, None)
def test_00_static_handler(self): "test StaticHandler helper class" class d1(uh.StaticHandler): name = "d1" context_kwds = ("flag",) @classmethod def genhash(cls, secret, hash, flag=False): if isinstance(hash, bytes): hash = hash.decode("ascii") if hash not in (u"a", u"b"): raise ValueError return to_hash_str(u"b" if flag else u"a") # check default identify method self.assertTrue(d1.identify(u"a")) self.assertTrue(d1.identify(b("a"))) self.assertTrue(d1.identify(u"b")) self.assertFalse(d1.identify(u"c")) self.assertFalse(d1.identify(b("c"))) self.assertFalse(d1.identify(u"")) self.assertFalse(d1.identify(None)) # check default genconfig method self.assertIs(d1.genconfig(), None) d1._stub_config = u"b" self.assertEqual(d1.genconfig(), to_hash_str("b")) # check default verify method self.assertTrue(d1.verify("s", "a")) self.assertTrue(d1.verify("s", u"a")) self.assertFalse(d1.verify("s", "b")) self.assertFalse(d1.verify("s", u"b")) self.assertTrue(d1.verify("s", "b", flag=True)) self.assertRaises(ValueError, d1.verify, "s", "c") # check default encrypt method self.assertEqual(d1.encrypt("s"), to_hash_str("a")) self.assertEqual(d1.encrypt("s"), to_hash_str("a")) self.assertEqual(d1.encrypt("s", flag=True), to_hash_str("b"))
def test_00_static_handler(self): "test StaticHandler helper class" class d1(uh.StaticHandler): name = "d1" context_kwds = ("flag",) @classmethod def genhash(cls, secret, hash, flag=False): if isinstance(hash, bytes): hash = hash.decode("ascii") if hash not in (u'a',u'b'): raise ValueError return to_hash_str(u'b' if flag else u'a') #check default identify method self.assertTrue(d1.identify(u'a')) self.assertTrue(d1.identify(b('a'))) self.assertTrue(d1.identify(u'b')) self.assertFalse(d1.identify(u'c')) self.assertFalse(d1.identify(b('c'))) self.assertFalse(d1.identify(u'')) self.assertFalse(d1.identify(None)) #check default genconfig method self.assertIs(d1.genconfig(), None) d1._stub_config = u'b' self.assertEqual(d1.genconfig(), to_hash_str('b')) #check default verify method self.assertTrue(d1.verify('s','a')) self.assertTrue(d1.verify('s',u'a')) self.assertFalse(d1.verify('s','b')) self.assertFalse(d1.verify('s',u'b')) self.assertTrue(d1.verify('s', 'b', flag=True)) self.assertRaises(ValueError, d1.verify, 's', 'c') #check default encrypt method self.assertEqual(d1.encrypt('s'), to_hash_str('a')) self.assertEqual(d1.encrypt('s'), to_hash_str('a')) self.assertEqual(d1.encrypt('s', flag=True), to_hash_str('b'))
def raw_lmhash(secret, encoding="ascii", hex=False): "encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex" # NOTE: various references say LMHASH uses the OEM codepage of the host # for it's encoding. until a clear reference is found, # as well as a path for getting the encoding, # letting this default to "ascii" to prevent incorrect hashes # from being made w/o user explicitly choosing an encoding. if isinstance(secret, unicode): secret = secret.encode(encoding) ns = secret.upper()[:14] + b("\x00") * (14 - len(secret)) out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block(ns[7:], LM_MAGIC) return hexlify(out).decode("ascii") if hex else out
def test_sha512_string(self): "test alternate digest string (sha512)" self.assertFunctionResults(pbkdf2.pbkdf2, [ # result, secret, salt, rounds, keylen, digest="sha1" #case taken from example in http://grub.enbug.org/Authentication ( hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC6C29E293F0A0"), b("hello"), hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073994D79080136"), 10000, 64, "hmac-sha512" ), ])
def test_sha512_string(self): "test alternate digest string (sha512)" self.assertFunctionResults( pbkdf2.pbkdf2, [ # result, secret, salt, rounds, keylen, digest="sha1" #case taken from example in http://grub.enbug.org/Authentication (hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC6C29E293F0A0" ), b("hello"), hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073994D79080136" ), 10000, 64, "hmac-sha512"), ])
def des_cbc_encrypt(key, value, iv=b('\x00') * 8, pad=b('\x00')): """performs des-cbc encryption, returns only last block. this performs a specific DES-CBC encryption implementation as needed by the Oracle10 hash. it probably won't be useful for other purposes as-is. input value is null-padded to multiple of 8 bytes. :arg key: des key as bytes :arg value: value to encrypt, as bytes. :param iv: optional IV :param pad: optional pad byte :returns: last block of DES-CBC encryption of all ``value``'s byte blocks. """ value += pad * (-len(value) % 8) #null pad to multiple of 8 hash = iv #start things off for offset in xrange(0,len(value),8): chunk = xor_bytes(hash, value[offset:offset+8]) hash = des_encrypt_block(key, chunk) return hash
def _calc_checksum_os_crypt(self, secret): #os_crypt() would raise less useful error null = u'\x00' if isinstance(secret, unicode) else b('\x00') if null in secret: raise ValueError("null char in secret") #NOTE: safe_os_crypt encodes unicode secret -> utf8 #no official policy since des-crypt predates unicode ok, hash = safe_os_crypt(secret, self.salt) if ok: return hash[2:] else: return self._calc_checksum_builtin(secret)
def raw_lmhash(secret, encoding="ascii", hex=False): "encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex" #NOTE: various references say LMHASH uses the OEM codepage of the host # for it's encoding. until a clear reference is found, # as well as a path for getting the encoding, # letting this default to "ascii" to prevent incorrect hashes # from being made w/o user explicitly choosing an encoding. if isinstance(secret, unicode): secret = secret.encode(encoding) ns = secret.upper()[:14] + b("\x00") * (14 - len(secret)) out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block( ns[7:], LM_MAGIC) return hexlify(out).decode("ascii") if hex else out
def test_rfc6070(self): "rfc6070 test vectors" self.assertFunctionResults(pbkdf2.pbkdf2, [ ( hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"), b("password"), b("salt"), 1, 20, ), ( hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), b("password"), b("salt"), 2, 20, ), ( hb("4b007901b765489abead49d926f721d065a429c1"), b("password"), b("salt"), 4096, 20, ), #just runs too long - could enable if ALL option is set ##( ## ## unhexlify("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"), ## "password", "salt", 16777216, 20, ##), ( hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), b("passwordPASSWORDpassword"), b("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, 25, ), ( hb("56fa6aa75548099dcc37d7f03425e0c3"), b("pass\00word"), b("sa\00lt"), 4096, 16, ), ])
def digest(self): #NOTE: backing up state so we can restore it after _process is called, #in case object is updated again (this is only attr altered by this method) orig = list(self._state) #final block: buf + 0x80, # then 0x00 padding until congruent w/ 56 mod 64 bytes # then last 8 bytes = msg length in bits buf = self._buf msglen = self._count * 512 + len(buf) * 8 block = buf + b('\x80') + b('\x00') * ((119-len(buf)) % 64) + \ struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32) if len(block) == 128: self._process(block[:64]) self._process(block[64:]) else: assert len(block) == 64 self._process(block) #render digest & restore un-finalized state out = struct.pack("<4I", *self._state) self._state = orig return out