def test_from_source(self): from otp.ai.passlib.totp import TOTP from_source = TOTP.from_source otp = from_source( u('otpauth://totp/Example:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=Example' )) self.assertEqual(otp.key, KEY4_RAW) otp = from_source( 'otpauth://totp/Example:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=Example' ) self.assertEqual(otp.key, KEY4_RAW) otp = from_source(dict(v=1, type='totp', key=KEY4)) self.assertEqual(otp.key, KEY4_RAW) otp = from_source( u('{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}')) self.assertEqual(otp.key, KEY4_RAW) otp = from_source( '{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}') self.assertEqual(otp.key, KEY4_RAW) self.assertIs(from_source(otp), otp) wallet1 = AppWallet() otp1 = TOTP.using(wallet=wallet1).from_source(otp) self.assertIsNot(otp1, otp) self.assertEqual(otp1.to_dict(), otp.to_dict()) otp2 = TOTP.using(wallet=wallet1).from_source(otp1) self.assertIs(otp2, otp1) self.assertRaises(ValueError, from_source, u('foo')) self.assertRaises(ValueError, from_source, 'foo')
def __init__(self, name, wrapped, prefix=u(''), orig_prefix=u(''), lazy=False, doc=None, ident=None): self.name = name if isinstance(prefix, bytes): prefix = prefix.decode('ascii') self.prefix = prefix if isinstance(orig_prefix, bytes): orig_prefix = orig_prefix.decode('ascii') self.orig_prefix = orig_prefix if doc: self.__doc__ = doc if hasattr(wrapped, 'name'): self._set_wrapped(wrapped) else: self._wrapped_name = wrapped if not lazy: self._get_wrapped() if ident is not None: if ident is True: if prefix: ident = prefix else: raise ValueError('no prefix specified') if isinstance(ident, bytes): ident = ident.decode('ascii') if ident[:len(prefix)] != prefix[:len(ident)]: raise ValueError('ident must agree with prefix') self._ident = ident return
def test_10_identify(self): class d1(uh.GenericHandler): @classmethod def from_string(cls, hash): if isinstance(hash, bytes): hash = hash.decode('ascii') if hash == u('a'): return cls(checksum=hash) raise ValueError self.assertRaises(TypeError, d1.identify, None) self.assertRaises(TypeError, d1.identify, 1) self.assertFalse(d1.identify('')) self.assertTrue(d1.identify('a')) self.assertFalse(d1.identify('b')) d1._hash_regex = re.compile(u('@.')) self.assertRaises(TypeError, d1.identify, None) self.assertRaises(TypeError, d1.identify, 1) self.assertTrue(d1.identify('@a')) self.assertFalse(d1.identify('a')) del d1._hash_regex d1.ident = u('!') self.assertRaises(TypeError, d1.identify, None) self.assertRaises(TypeError, d1.identify, 1) self.assertTrue(d1.identify('!a')) self.assertFalse(d1.identify('a')) del d1.ident return
class ldap_salted_sha1(_SaltedBase64DigestHelper): name = 'ldap_salted_sha1' ident = u('{SSHA}') checksum_size = 20 _hash_func = sha1 _hash_regex = re.compile( u('^\\{SSHA\\}(?P<tmp>[+/a-zA-Z0-9]{32,}={0,2})$'))
def test_12_ident(self): h = uh.PrefixWrapper('h2', 'ldap_md5', '{XXX}') self.assertEqual(h.ident, u('{XXX}{MD5}')) self.assertIs(h.ident_values, None) h = uh.PrefixWrapper('h2', 'des_crypt', '{XXX}') self.assertIs(h.ident, None) self.assertIs(h.ident_values, None) h = uh.PrefixWrapper('h1', 'ldap_md5', '{XXX}', '{MD5}') self.assertIs(h.ident, None) self.assertIs(h.ident_values, None) h = uh.PrefixWrapper('h3', 'ldap_md5', '{XXX}', ident='{X') self.assertEqual(h.ident, u('{X')) self.assertIs(h.ident_values, None) h = uh.PrefixWrapper('h3', 'ldap_md5', '{XXX}', ident='{XXX}A') self.assertRaises(ValueError, uh.PrefixWrapper, 'h3', 'ldap_md5', '{XXX}', ident='{XY') self.assertRaises(ValueError, uh.PrefixWrapper, 'h3', 'ldap_md5', '{XXX}', ident='{XXXX') h = uh.PrefixWrapper('h4', 'phpass', '{XXX}') self.assertIs(h.ident, None) self.assertEqual(h.ident_values, (u('{XXX}$P$'), u('{XXX}$H$'))) h = uh.PrefixWrapper('h5', 'des_crypt', '{XXX}', ident=True) self.assertEqual(h.ident, u('{XXX}')) self.assertIs(h.ident_values, None) self.assertRaises(ValueError, uh.PrefixWrapper, 'h6', 'des_crypt', ident=True) with self.assertWarningList('orig_prefix.*may not work correctly'): h = uh.PrefixWrapper('h7', 'phpass', orig_prefix='$', prefix='?') self.assertEqual(h.ident_values, None) self.assertEqual(h.ident, None) return
class ldap_salted_md5(_SaltedBase64DigestHelper): name = 'ldap_salted_md5' ident = u('{SMD5}') checksum_size = 16 _hash_func = md5 _hash_regex = re.compile( u('^\\{SMD5\\}(?P<tmp>[+/a-zA-Z0-9]{27,}={0,2})$'))
def to_string(self): if self.rounds == 5000 and self.implicit_rounds: hash = u('%s%s$%s') % (self.ident, self.salt, self.checksum or u('')) else: hash = u('%srounds=%d$%s$%s') % (self.ident, self.rounds, self.salt, self.checksum or u('')) return uascii_to_str(hash)
def test_lmhash(self): from otp.ai.passlib.win32 import raw_lmhash for secret, hash in [ ('OLDPASSWORD', u('c9b81d939d6fd80cd408e6b105741864')), ('NEWPASSWORD', u('09eeab5aa415d6e4d408e6b105741864')), ('welcome', u('c23413a8a1e7665faad3b435b51404ee')) ]: result = raw_lmhash(secret, hex=True) self.assertEqual(result, hash)
def test_nthash(self): warnings.filterwarnings('ignore', 'nthash\\.raw_nthash\\(\\) is deprecated') from otp.ai.passlib.win32 import raw_nthash for secret, hash in [ ('OLDPASSWORD', u('6677b2c394311355b54f25eec5bfacf5')), ('NEWPASSWORD', u('256781a62031289d3c2c98c14f1efc8c')) ]: result = raw_nthash(secret, hex=True) self.assertEqual(result, hash)
def to_string(self, _withchk=True): ss = u('') if self.bare_salt else u('$') rounds = self.rounds if rounds > 0: hash = u('$md5,rounds=%d$%s%s') % (rounds, self.salt, ss) else: hash = u('$md5$%s%s') % (self.salt, ss) if _withchk: chk = self.checksum hash = u('%s$%s') % (hash, chk) return uascii_to_str(hash)
def from_string(cls, hash): ident, tail = cls._parse_ident(hash) if ident == IDENT_2X: raise ValueError( "crypt_blowfish's buggy '2x' hashes are not currently supported" ) rounds_str, data = tail.split(u('$')) rounds = int(rounds_str) if rounds_str != u('%02d') % (rounds, ): raise uh.exc.MalformedHashError(cls, 'malformed cost field') salt, chk = data[:22], data[22:] return cls(rounds=rounds, salt=salt, checksum=chk or None, ident=ident)
def render_mc3(ident, rounds, salt, checksum, sep=u('$'), rounds_base=10): if rounds is None: rounds = u('') else: if rounds_base == 16: rounds = u('%x') % rounds else: rounds = unicode(rounds) if checksum: parts = [ident, rounds, sep, salt, sep, checksum] else: parts = [ident, rounds, sep, salt] return uascii_to_str(join_unicode(parts))
def test_93_derive_digest(self): hash = self.handler.derive_digest s1 = '\x01\x02\x03' d1 = '\xb2\xfb\xab\x82[tNuPnI\x8aZZ\x19\x87\xcen\xe9\xd3' self.assertEqual(hash(u('\\u2168'), s1, 1000, 'sha-1'), d1) self.assertEqual(hash('\xe2\x85\xa8', s1, 1000, 'SHA-1'), d1) self.assertEqual(hash(u('IX'), s1, 1000, 'sha1'), d1) self.assertEqual(hash('IX', s1, 1000, 'SHA1'), d1) self.assertEqual(hash('IX', s1, 1000, 'md5'), '3\x19\x18\xc0\x1c/\xa8\xbf\xe4\xa3\xc2\x8eM\xe8od') self.assertRaises(ValueError, hash, 'IX', s1, 1000, 'sha-666') self.assertRaises(ValueError, hash, 'IX', s1, 0, 'sha-1') self.assertEqual(hash(u('IX'), s1.decode('latin-1'), 1000, 'sha1'), d1)
def test_b64s_decode(self): from otp.ai.passlib.utils.binary import b64s_decode self.assertEqual(b64s_decode('abc'), hb('69b7')) self.assertEqual(b64s_decode(u('abc')), hb('69b7')) self.assertRaises(ValueError, b64s_decode, u('ab\xff')) self.assertRaises(TypeError, b64s_decode, 'ab\xff') self.assertRaises(TypeError, b64s_decode, 'ab!') self.assertRaises(TypeError, b64s_decode, u('ab!')) self.assertEqual(b64s_decode('abcd'), hb('69b71d')) self.assertRaises(ValueError, b64s_decode, 'abcde') self.assertEqual(b64s_decode('abcdef'), hb('69b71d79')) self.assertEqual(b64s_decode('abcdeQ'), hb('69b71d79')) self.assertEqual(b64s_decode('abcdefg'), hb('69b71d79f8'))
def normalize_token(self_or_cls, token): digits = self_or_cls.digits if isinstance(token, int_types): token = u('%0*d') % (digits, token) else: token = to_unicode(token, param='token') token = _clean_re.sub(u(''), token) if not token.isdigit(): raise MalformedTokenError( 'Token must contain only the digits 0-9') if len(token) != digits: raise MalformedTokenError('Token must have exactly %d digits' % digits) return token
def test_to_bytes(self): from otp.ai.passlib.utils import to_bytes self.assertEqual(to_bytes(u('abc')), 'abc') self.assertEqual(to_bytes(u('\x00\xff')), '\x00\xc3\xbf') self.assertEqual(to_bytes(u('\x00\xff'), 'latin-1'), '\x00\xff') self.assertRaises(ValueError, to_bytes, u('\x00\xff'), 'ascii') self.assertEqual(to_bytes('abc'), 'abc') self.assertEqual(to_bytes('\x00\xff'), '\x00\xff') self.assertEqual(to_bytes('\x00\xc3\xbf'), '\x00\xc3\xbf') self.assertEqual(to_bytes('\x00\xc3\xbf', 'latin-1'), '\x00\xc3\xbf') self.assertEqual(to_bytes('\x00\xc3\xbf', 'latin-1', '', 'utf-8'), '\x00\xff') self.assertRaises(AssertionError, to_bytes, 'abc', None) self.assertRaises(TypeError, to_bytes, None) return
class d1(uh.StaticHandler): name = 'd1' context_kwds = ('flag', ) _hash_prefix = u('_') checksum_chars = u('ab') checksum_size = 1 def __init__(self, flag=False, **kwds): super(d1, self).__init__(**kwds) self.flag = flag def _calc_checksum(self, secret): if self.flag: return u('b') return u('a')
def test_07_encodings(self): self.assertRaises(ValueError, apache.HtpasswdFile, encoding='utf-16') ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding='utf-8', return_unicode=True) self.assertEqual(ht.users(), [u('user\\u00e6')]) with self.assertWarningList('``encoding=None`` is deprecated'): ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding=None) self.assertEqual(ht.users(), ['user\xc3\xa6']) ht = apache.HtpasswdFile.from_string(self.sample_04_latin1, encoding='latin-1', return_unicode=True) self.assertEqual(ht.users(), [u('user\\u00e6')]) return
class bcrypt_sha256(_wrapped_bcrypt): name = 'bcrypt_sha256' ident_values = (IDENT_2A, IDENT_2B) ident_aliases = ( lambda ident_values: dict(item for item in bcrypt.ident_aliases.items() if item[1] in ident_values))(ident_values) default_ident = IDENT_2B prefix = u('$bcrypt-sha256$') _hash_re = re.compile( '\n ^\n [$]bcrypt-sha256\n [$](?P<variant>2[ab])\n ,(?P<rounds>\\d{1,2})\n [$](?P<salt>[^$]{22})\n (?:[$](?P<digest>.{31}))?\n $\n ', re.X) @classmethod def identify(cls, hash): hash = uh.to_unicode_for_identify(hash) if not hash: return False return hash.startswith(cls.prefix) @classmethod def from_string(cls, hash): hash = to_unicode(hash, 'ascii', 'hash') if not hash.startswith(cls.prefix): raise uh.exc.InvalidHashError(cls) m = cls._hash_re.match(hash) if not m: raise uh.exc.MalformedHashError(cls) rounds = m.group('rounds') if rounds.startswith(uh._UZERO) and rounds != uh._UZERO: raise uh.exc.ZeroPaddedRoundsError(cls) return cls(ident=m.group('variant'), rounds=int(rounds), salt=m.group('salt'), checksum=m.group('digest')) _template = u('$bcrypt-sha256$%s,%d$%s$%s') def to_string(self): hash = self._template % (self.ident.strip(_UDOLLAR), self.rounds, self.salt, self.checksum) return uascii_to_str(hash) def _calc_checksum(self, secret): if isinstance(secret, unicode): secret = secret.encode('utf-8') key = b64encode(sha256(secret).digest()) return super(bcrypt_sha256, self)._calc_checksum(key)
def test_to_unicode(self): from otp.ai.passlib.utils import to_unicode self.assertEqual(to_unicode(u('abc')), u('abc')) self.assertEqual(to_unicode(u('\x00\xff')), u('\x00\xff')) self.assertEqual(to_unicode(u('\x00\xff'), 'ascii'), u('\x00\xff')) self.assertEqual(to_unicode('abc'), u('abc')) self.assertEqual(to_unicode('\x00\xc3\xbf'), u('\x00\xff')) self.assertEqual(to_unicode('\x00\xff', 'latin-1'), u('\x00\xff')) self.assertRaises(ValueError, to_unicode, '\x00\xff') self.assertRaises(AssertionError, to_unicode, 'abc', None) self.assertRaises(TypeError, to_unicode, None) return
class SaltedHash(uh.HasSalt, uh.GenericHandler): name = 'salted_test_hash' setting_kwds = ('salt', ) min_salt_size = 2 max_salt_size = 4 checksum_size = 40 salt_chars = checksum_chars = uh.LOWER_HEX_CHARS _hash_regex = re.compile(u('^@salt[0-9a-f]{42,44}$')) @classmethod def from_string(cls, hash): if not cls.identify(hash): raise uh.exc.InvalidHashError(cls) if isinstance(hash, bytes): hash = hash.decode('ascii') return cls(salt=hash[5:-40], checksum=hash[-40:]) def to_string(self): hash = u('@salt%s%s') % (self.salt, self.checksum) return uascii_to_str(hash) def _calc_checksum(self, secret): if isinstance(secret, unicode): secret = secret.encode('utf-8') data = self.salt.encode('ascii') + secret + self.salt.encode('ascii') return str_to_uascii(hashlib.sha1(data).hexdigest())
class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): name = 'grub_pbkdf2_sha512' setting_kwds = ('salt', 'salt_size', 'rounds') ident = u('grub.pbkdf2.sha512.') checksum_size = 64 default_salt_size = 64 max_salt_size = 1024 default_rounds = pbkdf2_sha512.default_rounds min_rounds = 1 max_rounds = 4294967295L rounds_cost = 'linear' @classmethod def from_string(cls, hash): rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u('.'), handler=cls) salt = unhexlify(salt.encode('ascii')) if chk: chk = unhexlify(chk.encode('ascii')) return cls(rounds=rounds, salt=salt, checksum=chk) def to_string(self): salt = hexlify(self.salt).decode('ascii').upper() chk = hexlify(self.checksum).decode('ascii').upper() return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u('.')) def _calc_checksum(self, secret): return pbkdf2_hmac('sha512', secret, self.salt, self.rounds, 64)
class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): name = 'cta_pbkdf2_sha1' setting_kwds = ('salt', 'salt_size', 'rounds') ident = u('$p5k2$') checksum_size = 20 default_salt_size = 16 max_salt_size = 1024 default_rounds = pbkdf2_sha1.default_rounds min_rounds = 1 max_rounds = 4294967295L rounds_cost = 'linear' @classmethod def from_string(cls, hash): rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16, handler=cls) salt = b64decode(salt.encode('ascii'), CTA_ALTCHARS) if chk: chk = b64decode(chk.encode('ascii'), CTA_ALTCHARS) return cls(rounds=rounds, salt=salt, checksum=chk) def to_string(self): salt = b64encode(self.salt, CTA_ALTCHARS).decode('ascii') chk = b64encode(self.checksum, CTA_ALTCHARS).decode('ascii') return uh.render_mc3(self.ident, self.rounds, salt, chk, rounds_base=16) def _calc_checksum(self, secret): return pbkdf2_hmac('sha1', secret, self.salt, self.rounds, 20)
class django_pbkdf2_sha1(django_pbkdf2_sha256): name = 'django_pbkdf2_sha1' django_name = 'pbkdf2_sha1' ident = u('pbkdf2_sha1$') checksum_size = 28 default_rounds = pbkdf2_sha1.default_rounds _digest = 'sha1'
class django_bcrypt_sha256(_wrapped_bcrypt): name = 'django_bcrypt_sha256' django_name = 'bcrypt_sha256' _digest = sha256 django_prefix = u('bcrypt_sha256$') @classmethod def identify(cls, hash): hash = uh.to_unicode_for_identify(hash) if not hash: return False return hash.startswith(cls.django_prefix) @classmethod def from_string(cls, hash): hash = to_unicode(hash, 'ascii', 'hash') if not hash.startswith(cls.django_prefix): raise uh.exc.InvalidHashError(cls) bhash = hash[len(cls.django_prefix):] if not bhash.startswith('$2'): raise uh.exc.MalformedHashError(cls) return super(django_bcrypt_sha256, cls).from_string(bhash) def to_string(self): bhash = super(django_bcrypt_sha256, self).to_string() return uascii_to_str(self.django_prefix) + bhash def _calc_checksum(self, secret): if isinstance(secret, unicode): secret = secret.encode('utf-8') secret = hexlify(self._digest(secret).digest()) return super(django_bcrypt_sha256, self)._calc_checksum(secret)
def test_to_native_str(self): from otp.ai.passlib.utils import to_native_str self.assertEqual(to_native_str(u('abc'), 'ascii'), 'abc') self.assertEqual(to_native_str('abc', 'ascii'), 'abc') if PY3: self.assertEqual(to_native_str(u('\xe0'), 'ascii'), '\xe0') self.assertRaises(UnicodeDecodeError, to_native_str, '\xc3\xa0', 'ascii') else: self.assertRaises(UnicodeEncodeError, to_native_str, u('\xe0'), 'ascii') self.assertEqual(to_native_str('\xc3\xa0', 'ascii'), '\xc3\xa0') self.assertEqual(to_native_str(u('\xe0'), 'latin-1'), '\xe0') self.assertEqual(to_native_str('\xe0', 'latin-1'), '\xe0') self.assertEqual(to_native_str(u('\xe0'), 'utf-8'), '\xe0' if PY3 else '\xc3\xa0') self.assertEqual(to_native_str('\xc3\xa0', 'utf-8'), '\xe0' if PY3 else '\xc3\xa0') self.assertRaises(TypeError, to_native_str, None, 'ascii') return
class md5_crypt(uh.HasManyBackends, _MD5_Common): name = 'md5_crypt' ident = u('$1$') backends = ('os_crypt', 'builtin') @classmethod def _load_backend_os_crypt(cls): if test_crypt('test', '$1$test$pi/xDtU5WFVRqYS6BMU8X/'): cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) return True return False def _calc_checksum_os_crypt(self, secret): config = self.ident + self.salt hash = safe_crypt(secret, config) if hash: return hash[-22:] return self._calc_checksum_builtin(secret) @classmethod def _load_backend_builtin(cls): cls._set_calc_checksum_backend(cls._calc_checksum_builtin) return True def _calc_checksum_builtin(self, secret): return _raw_md5_crypt(secret, self.salt)
def test_md4_update(self): md4 = self.get_md4_const() h = md4('') self.assertEqual(h.hexdigest(), '31d6cfe0d16ae931b73c59d7e0c089c0') h.update('a') self.assertEqual(h.hexdigest(), 'bde52cb31de33e46245e05fbdbd6fb24') h.update('bcdefghijklmnopqrstuvwxyz') self.assertEqual(h.hexdigest(), 'd79e1c308aa5bbcdeea8ed63df412da9') if PY3: h = md4() self.assertRaises(TypeError, h.update, u('a')) self.assertEqual(h.hexdigest(), '31d6cfe0d16ae931b73c59d7e0c089c0') else: h = md4() h.update(u('a')) self.assertEqual(h.hexdigest(), 'bde52cb31de33e46245e05fbdbd6fb24')
def _init_ldap_crypt_handlers(): g = globals() for wname in unix_crypt_schemes: name = 'ldap_' + wname g[name] = uh.PrefixWrapper(name, wname, prefix=u('{CRYPT}'), lazy=True) del g
class oracle11(uh.HasSalt, uh.GenericHandler): name = 'oracle11' setting_kwds = ('salt', ) checksum_size = 40 checksum_chars = uh.UPPER_HEX_CHARS min_salt_size = max_salt_size = 20 salt_chars = uh.UPPER_HEX_CHARS _hash_regex = re.compile( u('^S:(?P<chk>[0-9a-f]{40})(?P<salt>[0-9a-f]{20})$'), re.I) @classmethod def from_string(cls, hash): hash = to_unicode(hash, 'ascii', 'hash') m = cls._hash_regex.match(hash) if not m: raise uh.exc.InvalidHashError(cls) salt, chk = m.group('salt', 'chk') return cls(salt=salt, checksum=chk.upper()) def to_string(self): chk = self.checksum hash = u('S:%s%s') % (chk.upper(), self.salt.upper()) return uascii_to_str(hash) def _calc_checksum(self, secret): if isinstance(secret, unicode): secret = secret.encode('utf-8') chk = sha1(secret + unhexlify(self.salt.encode('ascii'))).hexdigest() return str_to_uascii(chk).upper()