def to_string(self): ident = self.ident if ident == IDENT_SCRYPT: return "$scrypt$ln=%d,r=%d,p=%d$%s$%s" % ( self.rounds, self.block_size, self.parallelism, bascii_to_str(b64s_encode(self.salt)), bascii_to_str(b64s_encode(self.checksum)), ) else: assert ident == IDENT_7 salt = self.salt try: salt.decode("ascii") except UnicodeDecodeError: raise suppress_cause( NotImplementedError( "scrypt $7$ hashes dont support non-ascii salts")) return bascii_to_str(b"".join([ b"$7$", h64.encode_int6(self.rounds), h64.encode_int30(self.block_size), h64.encode_int30(self.parallelism), self.salt, b"$", h64.encode_bytes(self.checksum) ]))
def _calc_checksum(self, secret): if isinstance(secret, pl_unicode): secret = secret.encode("utf-8") real_rounds = 1 << self.rounds result = hashlib.sha512(self.salt.encode("ascii") + secret).digest() r = 0 while r < real_rounds: result = hashlib.sha512(result + secret).digest() r += 1 return h64.encode_bytes(result).decode("ascii")[:self.checksum_size]
def _calc_checksum(self, secret): # FIXME: can't find definitive policy on how phpass handles non-ascii. if isinstance(secret, unicode): secret = secret.encode("utf-8") real_rounds = 1 << self.rounds result = md5(self.salt.encode("ascii") + secret).digest() r = 0 while r < real_rounds: result = md5(result + secret).digest() r += 1 return h64.encode_bytes(result).decode("ascii")
def _calc_checksum(self, secret): # FIXME: can't find definitive policy on how phpass handles non-ascii. if isinstance(secret, unicode): secret = secret.encode("utf-8") real_rounds = 1<<self.rounds result = md5(self.salt.encode("ascii") + secret).digest() r = 0 while r < real_rounds: result = md5(result + secret).digest() r += 1 return h64.encode_bytes(result).decode("ascii")
def h_scrypt(phrase, rounds, salt): p = 1 r = 8 log2N = rounds + 7 N = 1 << log2N bytesalt = salt.encode("ascii") setting = (b"$7$" + hash64.encode_int6(log2N) + hash64.encode_int30(r) + hash64.encode_int30(p) + bytesalt) binhash = raw_scrypt(phrase, salt=bytesalt, p=p, r=r, n=N, dklen=32) yield (phrase, setting, setting + b'$' + hash64.encode_bytes(binhash))
def _calc_checksum(self, secret): # This function handles both the cisco_pix & cisco_asa formats: # * PIX had a limit of 16 character passwords, and always appended the username. # * ASA 7.0 (2005) increases this limit to 32, and conditionally appends the username. # The two behaviors are controlled based on the _is_asa class-level flag. asa = self._is_asa # XXX: No idea what unicode policy is, but all examples are # 7-bit ascii compatible, so using UTF-8. if isinstance(secret, unicode): secret = secret.encode("utf-8") seclen = len(secret) # check for truncation (during .hash() calls only) if self.use_defaults: self._check_truncate_policy(secret) # PIX/ASA: Per-user accounts use the first 4 chars of the username as the salt, # whereas global "enable" passwords don't have any salt at all. # ASA only: Don't append user if password is 28 or more characters. user = self.user if user and not (asa and seclen > 27): if isinstance(user, unicode): user = user.encode("utf-8") secret += user[:4] # PIX: null-pad or truncate to 16 bytes. # ASA: increase to 32 bytes if password is 13 or more characters. if asa and seclen > 12: padsize = 32 else: padsize = 16 secret = right_pad_string(secret, padsize) # md5 digest hash = md5(secret).digest() # drop every 4th byte hash = join_byte_elems(c for i,c in enumerate(hash) if i & 3 < 3) # encode using Hash64 return h64.encode_bytes(hash).decode("ascii")
def _calc_checksum(self, secret): """ This function implements the "encrypted" hash format used by Cisco PIX & ASA. It's behavior has been confirmed for ASA 9.6, but is presumed correct for PIX & other ASA releases, as it fits with known test vectors, and existing literature. While nearly the same, the PIX & ASA hashes have slight differences, so this function performs differently based on the _is_asa class flag. Noteable changes from PIX to ASA include password size limit increased from 16 -> 32, and other internal changes. """ # select PIX vs or ASA mode asa = self._is_asa # # encode secret # # per ASA 8.4 documentation, # http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets, # it supposedly uses UTF-8 -- though some double-encoding issues have # been observed when trying to actually *set* a non-ascii password # via ASDM, and access via SSH seems to strip 8-bit chars. # if isinstance(secret, unicode): secret = secret.encode("utf-8") # # check if password too large # # Per ASA 9.6 changes listed in # http://www.cisco.com/c/en/us/td/docs/security/asa/roadmap/asa_new_features.html, # prior releases had a maximum limit of 32 characters. # Testing with an ASA 9.6 system bears this out -- # setting 32-char password for a user account, # and logins will fail if any chars are appended. # (ASA 9.6 added new PBKDF2-based hash algorithm, # which supports larger passwords). # # Per PIX documentation # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html, # it would not allow passwords > 16 chars. # # Thus, we unconditionally throw a password size error here, # as nothing valid can come from a larger password. # NOTE: assuming PIX has same behavior, but at 16 char limit. # spoil_digest = None if len(secret) > self.truncate_size: if self.use_defaults: # called from hash() msg = "Password too long (%s allows at most %d bytes)" % ( self.name, self.truncate_size, ) raise uh.exc.PasswordSizeError(self.truncate_size, msg=msg) else: # called from verify() -- # We don't want to throw error, or return early, # as that would let attacker know too much. Instead, we set a # flag to add some dummy data into the md5 digest, so that # output won't match truncated version of secret, or anything # else that's fixed and predictable. spoil_digest = secret + _DUMMY_BYTES # # append user to secret # # Policy appears to be: # # * Nothing appended for enable password (user = "") # # * ASA: If user present, but secret is >= 28 chars, nothing appended. # # * 1-2 byte users not allowed. # DEVIATION: we're letting them through, and repeating their # chars ala 3-char user, to simplify testing. # Could issue warning in the future though. # # * 3 byte user has first char repeated, to pad to 4. # (observed under ASA 9.6, assuming true elsewhere) # # * 4 byte users are used directly. # # * 5+ byte users are truncated to 4 bytes. # user = self.user if user: if isinstance(user, unicode): user = user.encode("utf-8") if not asa or len(secret) < 28: secret += repeat_string(user, 4) # # pad / truncate result to limit # # While PIX always pads to 16 bytes, ASA increases to 32 bytes IFF # secret+user > 16 bytes. This makes PIX & ASA have different results # where secret size in range(13,16), and user is present -- # PIX will truncate to 16, ASA will truncate to 32. # if asa and len(secret) > 16: pad_size = 32 else: pad_size = 16 secret = right_pad_string(secret, pad_size) # # md5 digest # if spoil_digest: # make sure digest won't match truncated version of secret secret += spoil_digest digest = md5(secret).digest() # # drop every 4th byte # NOTE: guessing this was done because it makes output exactly # 16 bytes, which may have been a general 'char password[]' # size limit under PIX # digest = join_byte_elems(c for i, c in enumerate(digest) if (i + 1) & 3) # # encode using Hash64 # return h64.encode_bytes(digest).decode("ascii")
def _calc_checksum(self, secret): """ This function implements the "encrypted" hash format used by Cisco PIX & ASA. It's behavior has been confirmed for ASA 9.6, but is presumed correct for PIX & other ASA releases, as it fits with known test vectors, and existing literature. While nearly the same, the PIX & ASA hashes have slight differences, so this function performs differently based on the _is_asa class flag. Noteable changes from PIX to ASA include password size limit increased from 16 -> 32, and other internal changes. """ # select PIX vs or ASA mode asa = self._is_asa # # encode secret # # per ASA 8.4 documentation, # http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets, # it supposedly uses UTF-8 -- though some double-encoding issues have # been observed when trying to actually *set* a non-ascii password # via ASDM, and access via SSH seems to strip 8-bit chars. # if isinstance(secret, unicode): secret = secret.encode("utf-8") # # check if password too large # # Per ASA 9.6 changes listed in # http://www.cisco.com/c/en/us/td/docs/security/asa/roadmap/asa_new_features.html, # prior releases had a maximum limit of 32 characters. # Testing with an ASA 9.6 system bears this out -- # setting 32-char password for a user account, # and logins will fail if any chars are appended. # (ASA 9.6 added new PBKDF2-based hash algorithm, # which supports larger passwords). # # Per PIX documentation # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html, # it would not allow passwords > 16 chars. # # Thus, we unconditionally throw a password size error here, # as nothing valid can come from a larger password. # NOTE: assuming PIX has same behavior, but at 16 char limit. # spoil_digest = None if len(secret) > self.truncate_size: if self.use_defaults: # called from hash() msg = "Password too long (%s allows at most %d bytes)" % \ (self.name, self.truncate_size) raise uh.exc.PasswordSizeError(self.truncate_size, msg=msg) else: # called from verify() -- # We don't want to throw error, or return early, # as that would let attacker know too much. Instead, we set a # flag to add some dummy data into the md5 digest, so that # output won't match truncated version of secret, or anything # else that's fixed and predictable. spoil_digest = secret + _DUMMY_BYTES # # append user to secret # # Policy appears to be: # # * Nothing appended for enable password (user = "") # # * ASA: If user present, but secret is >= 28 chars, nothing appended. # # * 1-2 byte users not allowed. # DEVIATION: we're letting them through, and repeating their # chars ala 3-char user, to simplify testing. # Could issue warning in the future though. # # * 3 byte user has first char repeated, to pad to 4. # (observed under ASA 9.6, assuming true elsewhere) # # * 4 byte users are used directly. # # * 5+ byte users are truncated to 4 bytes. # user = self.user if user: if isinstance(user, unicode): user = user.encode("utf-8") if not asa or len(secret) < 28: secret += repeat_string(user, 4) # # pad / truncate result to limit # # While PIX always pads to 16 bytes, ASA increases to 32 bytes IFF # secret+user > 16 bytes. This makes PIX & ASA have different results # where secret size in range(13,16), and user is present -- # PIX will truncate to 16, ASA will truncate to 32. # if asa and len(secret) > 16: pad_size = 32 else: pad_size = 16 secret = right_pad_string(secret, pad_size) # # md5 digest # if spoil_digest: # make sure digest won't match truncated version of secret secret += spoil_digest digest = md5(secret).digest() # # drop every 4th byte # NOTE: guessing this was done because it makes output exactly # 16 bytes, which may have been a general 'char password[]' # size limit under PIX # digest = join_byte_elems(c for i, c in enumerate(digest) if (i + 1) & 3) # # encode using Hash64 # return h64.encode_bytes(digest).decode("ascii")