def test_90_bcrypt_padding(self): """test passlib correctly handles bcrypt padding bits""" self.require_TEST_MODE("full") # # prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25) # were some unused bits were incorrectly set in bcrypt salt strings. # (fixed since 1.5.3) # bcrypt = self.handler corr_desc = ".*incorrectly set padding bits" # # test encrypt() / genconfig() don't generate invalid salts anymore # def check_padding(hash): assert hash.startswith("$2a$") and len(hash) >= 28 self.assertTrue(hash[28] in '.Oeu', "unused bits incorrectly set in hash: %r" % (hash,)) for i in irange(6): check_padding(bcrypt.genconfig()) for i in irange(3): check_padding(bcrypt.encrypt("bob", rounds=bcrypt.min_rounds)) # # test genconfig() corrects invalid salts & issues warning. # with self.assertWarningList(["salt too large", corr_desc]): hash = bcrypt.genconfig(salt="."*21 + "A.", rounds=5, relaxed=True) self.assertEqual(hash, "$2a$05$" + "." * 22) # # test public methods against good & bad hashes # samples = self.known_incorrect_padding for pwd, bad, good in samples: # make sure genhash() corrects bad configs, leaves good unchanged with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.genhash(pwd, bad), good) with self.assertWarningList([]): self.assertEqual(bcrypt.genhash(pwd, good), good) # # and that verify() works good & bad # with self.assertWarningList([corr_desc]): self.assertTrue(bcrypt.verify(pwd, bad)) with self.assertWarningList([]): self.assertTrue(bcrypt.verify(pwd, good)) # # test normhash cleans things up correctly # for pwd, bad, good in samples: with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.normhash(bad), good) with self.assertWarningList([]): self.assertEqual(bcrypt.normhash(good), good) self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc")
def gen(): for i in irange(block_count): digest = prf_func(secret, salt + pack(">L", i+1)) accum = bytes_to_int(digest) for _ in irange(rounds-1): digest = prf_func(secret, digest) accum ^= bytes_to_int(digest) yield int_to_bytes(accum, digest_size)
def _process(self, block): """process 64 byte block""" # unpack block into 16 32-bit ints X = struct.unpack("<16I", block) # clone state orig = self._state state = list(orig) # round 1 - F function - (x&y)|(~x & z) for a,b,c,d,k,s in self._round1: t = (state[a] + F(state[b],state[c],state[d]) + X[k]) & MASK_32 state[a] = ((t<<s) & MASK_32) + (t>>(32-s)) # round 2 - G function for a,b,c,d,k,s in self._round2: t = (state[a] + G(state[b],state[c],state[d]) + X[k] + 0x5a827999) & MASK_32 state[a] = ((t<<s) & MASK_32) + (t>>(32-s)) # round 3 - H function - x ^ y ^ z for a,b,c,d,k,s in self._round3: t = (state[a] + (state[b] ^ state[c] ^ state[d]) + X[k] + 0x6ed9eba1) & MASK_32 state[a] = ((t<<s) & MASK_32) + (t>>(32-s)) # add back into original state for i in irange(4): orig[i] = (orig[i]+state[i]) & MASK_32
def test_04_check_password(self): """test check_password()""" ht = apache.HtpasswdFile.from_string(self.sample_05) self.assertRaises(TypeError, ht.check_password, 1, 'pass9') self.assertTrue(ht.check_password("user9","pass9") is None) # users 1..6 of sample_01 run through all the main hash formats, # to make sure they're recognized. for i in irange(1, 7): i = str(i) try: self.assertTrue(ht.check_password("user"+i, "pass"+i)) self.assertTrue(ht.check_password("user"+i, "pass9") is False) except MissingBackendError: if i == "5": # user5 uses bcrypt, which is apparently not available right now continue raise self.assertRaises(ValueError, ht.check_password, "user:"******"pass") # test that legacy verify() still works with self.assertWarningList(["verify\(\) is deprecated"]*2): self.assertTrue(ht.verify("user1", "pass1")) self.assertFalse(ht.verify("user1", "pass2"))
def _pbkdf2_looper(keyed_hmac, digest, rounds): hexlify = _hexlify accum = int(hexlify(digest), 16) for _ in irange(rounds - 1): digest = keyed_hmac(digest) accum ^= int(hexlify(digest), 16) return int_to_bytes(accum, len(digest))
def pbkdf1(digest, secret, salt, rounds, keylen=None): """pkcs#5 password-based key derivation v1.5 :arg digest: digest name or constructor. :arg secret: secret to use when generating the key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :arg salt: salt string to use when generating key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :param rounds: number of rounds to use to generate key. :arg keylen: number of bytes to generate (if omitted / ``None``, uses digest's native size) :returns: raw :class:`bytes` of generated key .. note:: This algorithm has been deprecated, new code should use PBKDF2. Among other limitations, ``keylen`` cannot be larger than the digest size of the specified hash. """ # resolve digest const, digest_size, block_size = lookup_hash(digest) # validate secret & salt secret = to_bytes(secret, param="secret") salt = to_bytes(salt, param="salt") # validate rounds if not isinstance(rounds, int_types): raise exc.ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise exc.ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") elif keylen > digest_size: raise ValueError("keylength too large for digest: %r > %r" % (keylen, digest_size)) # main pbkdf1 loop block = secret + salt for _ in irange(rounds): block = const(block).digest() return block[:keylen]
def _encode_int(self, value, bits): """encode integer into base64 format :arg value: non-negative integer to encode :arg bits: number of bits to encode :returns: a string of length ``int(ceil(bits/6.0))``. """ assert value >= 0, "caller did not sanitize input" pad = -bits % 6 bits += pad if self.big: itr = irange(bits-6, -6, -6) # shift to add lsb padding. value <<= pad else: itr = irange(0, bits, 6) # padding is msb, so no change needed. return join_byte_elems(imap(self._encode64, ((value>>off) & 0x3f for off in itr)))
def _pbkdf2_looper(digest_size, keyed_hmac, digest, rounds): """ py3-only implementation of pbkdf2 inner loop; uses 'int.from_bytes' + integer XOR """ from_bytes = int.from_bytes BIG = "big" # endianess doesn't matter, just has to be consistent accum = from_bytes(digest, BIG) for _ in irange(rounds - 1): digest = keyed_hmac(digest) accum ^= from_bytes(digest, BIG) return accum.to_bytes(digest_size, BIG)
def __call__(self, returns=None): """ frontend used by genword() / genphrase() to create passwords """ if returns is None: return next(self) elif isinstance(returns, int_types): return [next(self) for _ in irange(returns)] elif returns is iter: return self else: raise exc.ExpectedTypeError(returns, "<None>, int, or <iter>", "returns")
def _calc_checksum_builtin(self, secret): if isinstance(secret, unicode): secret = secret.encode("utf-8") if _BNULL in secret: raise uh.exc.NullPasswordError(self) rounds = self.rounds # NOTE: this seed value is NOT the same as the config string result = (u("%s$sha1$%s") % (self.salt, rounds)).encode("ascii") # NOTE: this algorithm is essentially PBKDF1, modified to use HMAC. keyed_hmac = compile_hmac("sha1", secret) for _ in irange(rounds): result = keyed_hmac(result) return h64.encode_transposed_bytes(result, self._chk_offsets).decode("ascii")
def check_int_pair(self, bits, encoded_pairs): """helper to check encode_intXX & decode_intXX functions""" engine = self.engine encode = getattr(engine, "encode_int%s" % bits) decode = getattr(engine, "decode_int%s" % bits) pad = -bits % 6 chars = (bits + pad) // 6 upper = 1 << bits # test encode func for value, encoded in encoded_pairs: result = encode(value) self.assertIsInstance(result, bytes) self.assertEqual(result, encoded) self.assertRaises(ValueError, encode, -1) self.assertRaises(ValueError, encode, upper) # test decode func for value, encoded in encoded_pairs: self.assertEqual(decode(encoded), value, "encoded %r:" % (encoded, )) m = self.m self.assertRaises(ValueError, decode, m(0) * (chars + 1)) self.assertRaises(ValueError, decode, m(0) * (chars - 1)) self.assertRaises(ValueError, decode, self.bad_byte * chars) self.assertRaises(TypeError, decode, engine.charmap[0]) self.assertRaises(TypeError, decode, None) # do random testing. from lib.passlib.utils import getrandstr for i in irange(100): # generate random value, encode, and then decode value = random.randint(0, upper - 1) encoded = encode(value) self.assertEqual(len(encoded), chars) self.assertEqual(decode(encoded), value) # generate some random encoded data, decode, then encode. encoded = getrandstr(random, engine.bytemap, chars) value = decode(encoded) self.assertGreaterEqual(value, 0, "decode %r out of bounds:" % encoded) self.assertLess(value, upper, "decode %r out of bounds:" % encoded) result = encode(value) if pad: self.assertEqual(result[:-2], encoded[:-2]) else: self.assertEqual(result, encoded)
def render_encipher(write, indent=0): for i in irange(0, 15, 2): write( indent, """\ # Feistel substitution on left word (round %(i)d) r ^= %(left)s ^ p%(i1)d # Feistel substitution on right word (round %(i1)d) l ^= %(right)s ^ p%(i2)d """, i=i, i1=i + 1, i2=i + 2, left=BFSTR, right=BFSTR.replace("l", "r"), )
def test_codec(self): """test encode_bytes/decode_bytes against random data""" engine = self.engine from lib.passlib.utils import getrandbytes, getrandstr saw_zero = False for i in irange(500): # # test raw -> encode() -> decode() -> raw # # generate some random bytes size = random.randint(1 if saw_zero else 0, 12) if not size: saw_zero = True enc_size = (4 * size + 2) // 3 raw = getrandbytes(random, size) # encode them, check invariants encoded = engine.encode_bytes(raw) self.assertEqual(len(encoded), enc_size) # make sure decode returns original result = engine.decode_bytes(encoded) self.assertEqual(result, raw) # # test encoded -> decode() -> encode() -> encoded # # generate some random encoded data if size % 4 == 1: size += random.choice([-1, 1, 2]) raw_size = 3 * size // 4 encoded = getrandstr(random, engine.bytemap, size) # decode them, check invariants raw = engine.decode_bytes(encoded) self.assertEqual(len(raw), raw_size, "encoded %d:" % size) # make sure encode returns original (barring padding bits) result = engine.encode_bytes(raw) if size % 4: self.assertEqual(result[:-1], encoded[:-1]) else: self.assertEqual(result, encoded)
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 irange(0, len(value), 8): chunk = xor_bytes(hash, value[offset:offset + 8]) hash = des_encrypt_block(key, chunk) return hash
def test_04_check_password(self): """test check_password()""" ht = apache.HtdigestFile.from_string(self.sample_01) self.assertRaises(TypeError, ht.check_password, 1, 'realm', 'pass5') self.assertRaises(TypeError, ht.check_password, 'user', 1, 'pass5') self.assertIs(ht.check_password("user5", "realm","pass5"), None) for i in irange(1,5): i = str(i) self.assertTrue(ht.check_password("user"+i, "realm", "pass"+i)) self.assertIs(ht.check_password("user"+i, "realm", "pass5"), False) # default realm self.assertRaises(TypeError, ht.check_password, "user5", "pass5") ht.default_realm = "realm" self.assertTrue(ht.check_password("user1", "pass1")) self.assertIs(ht.check_password("user5", "pass5"), None) # test that legacy verify() still works with self.assertWarningList(["verify\(\) is deprecated"]*2): self.assertTrue(ht.verify("user1", "realm", "pass1")) self.assertFalse(ht.verify("user1", "realm", "pass2")) # invalid user self.assertRaises(ValueError, ht.check_password, "user:"******"realm", "pass")
_stdlib_pbkdf2_hmac(self.name, b"p", b"s", 1) return True except ValueError: # "unsupported hash type" return False #========================================================================= # eoc #========================================================================= #============================================================================= # hmac utils #============================================================================= #: translation tables used by compile_hmac() _TRANS_5C = join_byte_values((x ^ 0x5C) for x in irange(256)) _TRANS_36 = join_byte_values((x ^ 0x36) for x in irange(256)) def compile_hmac(digest, key, multipart=False): """ This function returns an efficient HMAC function, hardcoded with a specific digest & key. It can be used via ``hmac = compile_hmac(digest, key)``. :arg digest: digest name or constructor. :arg key: secret key as :class:`!bytes` or :class:`!unicode` (unicode will be encoded using utf-8). :param multipart: request a multipart constructor instead (see return description).
#: all hex chars HEX_CHARS = u("0123456789abcdefABCDEF") #: upper case hex chars UPPER_HEX_CHARS = u("0123456789ABCDEF") #: lower case hex chars LOWER_HEX_CHARS = u("0123456789abcdef") #------------------------------------------------------------- # byte strings #------------------------------------------------------------- #: special byte string containing all possible byte values #: NOTE: for efficiency, this is treated as singleton by some of the code ALL_BYTE_VALUES = join_byte_values(irange(256)) #: some string constants we reuse B_EMPTY = b'' B_NULL = b'\x00' B_EQUAL = b'=' #============================================================================= # byte translation #============================================================================= #: base list used to compile byte translations _TRANSLATE_SOURCE = list(iter_byte_chars(ALL_BYTE_VALUES)) def compile_byte_translation(mapping, source=None): """
def varlist(name, count): return ", ".join(name + str(x) for x in irange(count))
def __next__(self): words = (self.rng.choice(self.words) for _ in irange(self.length)) return self.sep.join(words)
def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None): """pkcs#5 password-based key derivation v2.0 using HMAC + arbitrary digest. :arg digest: digest name or constructor. :arg secret: passphrase to use to generate key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :arg salt: salt string to use when generating key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :param rounds: number of rounds to use to generate key. :arg keylen: number of bytes to generate. if omitted / ``None``, will use digest's native output size. :returns: raw bytes of generated key .. versionchanged:: 1.7 This function will use the first available of the following backends: * `fastpbk2 <https://pypi.python.org/pypi/fastpbkdf2>`_ * :func:`hashlib.pbkdf2_hmac` (only available in py2 >= 2.7.8, and py3 >= 3.4) * builtin pure-python backend See :data:`passlib.crypto.digest.PBKDF2_BACKENDS` to determine which backend(s) are in use. """ # validate secret & salt secret = to_bytes(secret, param="secret") salt = to_bytes(salt, param="salt") # resolve digest digest_info = lookup_hash(digest) digest_size = digest_info.digest_size # validate rounds if not isinstance(rounds, int_types): raise exc.ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise exc.ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 1: # XXX: could allow keylen=0, but want to be compat w/ stdlib raise ValueError("keylen must be at least 1") # find smallest block count s.t. keylen <= block_count * digest_size; # make sure block count won't overflow (per pbkdf2 spec) # this corresponds to throwing error if keylen > digest_size * MAX_UINT32 # NOTE: stdlib will throw error at lower bound (keylen > MAX_SINT32) # NOTE: have do this before other backends checked, since fastpbkdf2 raises wrong error # (InvocationError, not OverflowError) block_count = (keylen + digest_size - 1) // digest_size if block_count > MAX_UINT32: raise OverflowError("keylen too long for digest") # # check for various high-speed backends # # ~3x faster than pure-python backend # NOTE: have to do this after above guards since fastpbkdf2 lacks bounds checks. if digest_info.supported_by_fastpbkdf2: return _fast_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) # ~1.4x faster than pure-python backend # NOTE: have to do this after fastpbkdf2 since hashlib-ssl is slower, # will support larger number of hashes. if digest_info.supported_by_hashlib_pbkdf2: return _stdlib_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) # # otherwise use our own implementation # # generated keyed hmac keyed_hmac = compile_hmac(digest, secret) # get helper to calculate pbkdf2 inner loop efficiently calc_block = _get_pbkdf2_looper(digest_size) # assemble & return result return join_bytes( calc_block(keyed_hmac, keyed_hmac(salt + _pack_uint32(i)), rounds) for i in irange(1, block_count + 1) )[:keylen]
def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"): """pkcs#5 password-based key derivation v1.5 :arg secret: passphrase to use to generate key :arg salt: salt string to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate (if ``None``, uses digest's native size) :param hash: hash function to use. must be name of a hash recognized by hashlib. :returns: raw bytes of generated key .. note:: This algorithm has been deprecated, new code should use PBKDF2. Among other limitations, ``keylen`` cannot be larger than the digest size of the specified hash. """ # validate secret & salt if not isinstance(secret, bytes): raise ExpectedTypeError(secret, "bytes", "secret") if not isinstance(salt, bytes): raise ExpectedTypeError(salt, "bytes", "salt") # validate rounds if not isinstance(rounds, int_types): raise ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # resolve hash try: hash_const = getattr(hashlib, hash) except AttributeError: # check for ssl hash # NOTE: if hash unknown, new() will throw ValueError, which we'd just # reraise anyways; so instead of checking, we just let it get # thrown during first use, below # TODO: use builtin md4 class if hashlib doesn't have it. def hash_const(msg): return hashlib.new(hash, msg) # prime pbkdf1 loop, get block size block = hash_const(secret + salt).digest() # validate keylen if keylen is None: keylen = len(block) elif not isinstance(keylen, int_types): raise ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") elif keylen > len(block): raise ValueError("keylength too large for digest: %r > %r" % (keylen, len(block))) # main pbkdf1 loop for _ in irange(rounds-1): block = hash_const(block).digest() return block[:keylen]
def _get_pbkdf2_looper(digest_size): """ We want a helper function which performs equivalent of the following:: def helper(keyed_hmac, digest, rounds): accum = digest for _ in irange(rounds - 1): digest = keyed_hmac(digest) accum ^= digest return accum However, no efficient way to implement "bytes ^ bytes" in python. Instead, using approach where we dynamically compile a helper function based on digest size. Instead of a single `accum` var, this helper breaks the digest into a series of integers. It stores these in a series of`accum_<i>` vars, and performs `accum ^= digest` by unpacking digest and perform xor for each "accum_<i> ^= digest_<i>". this keeps everything in locals, avoiding excessive list creation, encoding or decoding, etc. :param digest_size: digest size to compile for, in bytes. (must be multiple of 4). :return: helper function with call signature outlined above. """ # # cache helpers # try: return _looper_cache[digest_size] except KeyError: pass # # figure out most efficient struct format to unpack digest into list of native ints # if _have_64_bit and not digest_size & 0x7: # digest size multiple of 8, on a 64 bit system -- use array of UINT64 count = (digest_size >> 3) fmt = "=%dQ" % count elif not digest_size & 0x3: if _have_64_bit: # digest size multiple of 4, on a 64 bit system -- use array of UINT64 + 1 UINT32 count = (digest_size >> 3) fmt = "=%dQI" % count count += 1 else: # digest size multiple of 4, on a 32 bit system -- use array of UINT32 count = (digest_size >> 2) fmt = "=%dI" % count else: # stopping here, cause no known hashes have digest size that isn't multiple of 4 bytes. # if needed, could go crazy w/ "H" & "B" raise NotImplementedError("unsupported digest size: %d" % digest_size) struct = Struct(fmt) # # build helper source # tdict = dict( digest_size=digest_size, accum_vars=", ".join("acc_%d" % i for i in irange(count)), digest_vars=", ".join("dig_%d" % i for i in irange(count)), ) # head of function source = ( "def helper(keyed_hmac, digest, rounds):\n" " '''pbkdf2 loop helper for digest_size={digest_size}'''\n" " unpack_digest = struct.unpack\n" " {accum_vars} = unpack_digest(digest)\n" " for _ in irange(1, rounds):\n" " digest = keyed_hmac(digest)\n" " {digest_vars} = unpack_digest(digest)\n" ).format(**tdict) # xor digest for i in irange(count): source += " acc_%d ^= dig_%d\n" % (i, i) # return result source += " return struct.pack({accum_vars})\n".format(**tdict) # # compile helper # code = compile(source, "<generated by passlib.crypto.digest._get_pbkdf2_looper()>", "exec") gdict = dict(irange=irange, struct=struct) ldict = dict() eval(code, gdict, ldict) helper = ldict['helper'] if __debug__: helper.__source__ = source # # store in cache # _looper_cache[digest_size] = helper return helper
def test_90_bcrypt_padding(self): """test passlib correctly handles bcrypt padding bits""" self.require_TEST_MODE("full") # # prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25) # were some unused bits were incorrectly set in bcrypt salt strings. # (fixed since 1.5.3) # bcrypt = self.handler corr_desc = ".*incorrectly set padding bits" # # test encrypt() / genconfig() don't generate invalid salts anymore # def check_padding(hash): assert hash.startswith("$2a$") and len(hash) >= 28 self.assertTrue( hash[28] in '.Oeu', "unused bits incorrectly set in hash: %r" % (hash, )) for i in irange(6): check_padding(bcrypt.genconfig()) for i in irange(3): check_padding(bcrypt.encrypt("bob", rounds=bcrypt.min_rounds)) # # test genconfig() corrects invalid salts & issues warning. # with self.assertWarningList(["salt too large", corr_desc]): hash = bcrypt.genconfig(salt="." * 21 + "A.", rounds=5, relaxed=True) self.assertEqual(hash, "$2a$05$" + "." * 22) # # test public methods against good & bad hashes # samples = self.known_incorrect_padding for pwd, bad, good in samples: # make sure genhash() corrects bad configs, leaves good unchanged with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.genhash(pwd, bad), good) with self.assertWarningList([]): self.assertEqual(bcrypt.genhash(pwd, good), good) # # and that verify() works good & bad # with self.assertWarningList([corr_desc]): self.assertTrue(bcrypt.verify(pwd, bad)) with self.assertWarningList([]): self.assertTrue(bcrypt.verify(pwd, good)) # # test normhash cleans things up correctly # for pwd, bad, good in samples: with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.normhash(bad), good) with self.assertWarningList([]): self.assertEqual(bcrypt.normhash(good), good) self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc")
def write_expand_function(write, indent=0): write( indent, """\ def expand(self, key_words): \"""unrolled version of blowfish key expansion\""" ##assert len(key_words) >= 18, "size of key_words must be >= 18" P, S = self.P, self.S S0, S1, S2, S3 = S #============================================================= # integrate key #============================================================= """) for i in irange(18): write(indent + 1, """\ p%(i)d = P[%(i)d] ^ key_words[%(i)d] """, i=i) write( indent + 1, """\ #============================================================= # update P #============================================================= #------------------------------------------------ # update P[0] and P[1] #------------------------------------------------ l, r = p0, 0 """) render_encipher(write, indent + 1) write(indent + 1, """\ p0, p1 = l, r = r ^ p17, l """) for i in irange(2, 18, 2): write(indent + 1, """\ #------------------------------------------------ # update P[%(i)d] and P[%(i1)d] #------------------------------------------------ l ^= p0 """, i=i, i1=i + 1) render_encipher(write, indent + 1) write(indent + 1, """\ p%(i)d, p%(i1)d = l, r = r ^ p17, l """, i=i, i1=i + 1) write( indent + 1, """\ #------------------------------------------------ # save changes to original P array #------------------------------------------------ P[:] = (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17) #============================================================= # update S #============================================================= for box in S: j = 0 while j < 256: l ^= p0 """) render_encipher(write, indent + 3) write( indent + 3, """\ box[j], box[j+1] = l, r = r ^ p17, l j += 2 """)
"The undiscover'd country, from whose bourn\n" "No traveller returns,--puzzles the will,\n" "And makes us rather bear those ills we have\n" "Than fly to others that we know not of?\n" "Thus conscience does make cowards of us all;\n" "And thus the native hue of resolution\n" "Is sicklied o'er with the pale cast of thought;\n" "And enterprises of great pith and moment,\n" "With this regard, their currents turn awry,\n" "And lose the name of action.--Soft you now!\n" "The fair Ophelia!--Nymph, in thy orisons\n" "Be all my sins remember'd.\n\x00" #<- apparently null at end of C string is included (test vector won't pass otherwise) ) # NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below xr = irange(7) _XY_ROUNDS = [ tuple((i,i,i+3) for i in xr), # xrounds 0 tuple((i,i+1,i+4) for i in xr), # xrounds 1 tuple((i,i+8,(i+11)&15) for i in xr), # yrounds 0 tuple((i,(i+9)&15, (i+12)&15) for i in xr), # yrounds 1 ] del xr def raw_sun_md5_crypt(secret, rounds, salt): """given secret & salt, return encoded sun-md5-crypt checksum""" global MAGIC_HAMLET assert isinstance(secret, bytes) assert isinstance(salt, bytes) # validate rounds
"The undiscover'd country, from whose bourn\n" "No traveller returns,--puzzles the will,\n" "And makes us rather bear those ills we have\n" "Than fly to others that we know not of?\n" "Thus conscience does make cowards of us all;\n" "And thus the native hue of resolution\n" "Is sicklied o'er with the pale cast of thought;\n" "And enterprises of great pith and moment,\n" "With this regard, their currents turn awry,\n" "And lose the name of action.--Soft you now!\n" "The fair Ophelia!--Nymph, in thy orisons\n" "Be all my sins remember'd.\n\x00" #<- apparently null at end of C string is included (test vector won't pass otherwise) ) # NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below xr = irange(7) _XY_ROUNDS = [ tuple((i, i, i + 3) for i in xr), # xrounds 0 tuple((i, i + 1, i + 4) for i in xr), # xrounds 1 tuple((i, i + 8, (i + 11) & 15) for i in xr), # yrounds 0 tuple((i, (i + 9) & 15, (i + 12) & 15) for i in xr), # yrounds 1 ] del xr def raw_sun_md5_crypt(secret, rounds, salt): """given secret & salt, return encoded sun-md5-crypt checksum""" global MAGIC_HAMLET assert isinstance(secret, bytes) assert isinstance(salt, bytes)
# else we've done what we can warn("norm_hash_name(): unknown hash: %r" % (orig,), PasslibRuntimeWarning) name2 = name.replace("-", "") row = _nhn_cache[orig] = (name2, name) return row[idx] # TODO: get_hash() func which wraps norm_hash_name(), hashlib.<attr>, and hashlib.new #============================================================================= # general prf lookup #============================================================================= _BNULL = b('\x00') _XY_DIGEST = b(',\x1cb\xe0H\xa5\x82M\xfb>\xd6\x98\xef\x8e\xf9oQ\x85\xa3i') _trans_5C = join_byte_values((x ^ 0x5C) for x in irange(256)) _trans_36 = join_byte_values((x ^ 0x36) for x in irange(256)) def _get_hmac_prf(digest): """helper to return HMAC prf for specific digest""" def tag_wrapper(prf): prf.__name__ = "hmac_" + digest prf.__doc__ = ("hmac_%s(key, msg) -> digest;" " generated by passlib.utils.pbkdf2.get_prf()" % digest) if _EVP and digest == "sha1": # use m2crypto function directly for sha1, since that's its default digest try: result = _EVP.hmac(b('x'),b('y')) except ValueError: # pragma: no cover