Example #1
0
 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)
         ]))
Example #2
0
 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]
Example #3
0
 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")
Example #4
0
 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")
Example #5
0
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))
Example #6
0
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))
Example #7
0
    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")
Example #8
0
    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")
Example #9
0
    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")