summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2017-01-30 13:18:24 -0500
committerEli Collins <elic@assurancetechnologies.com>2017-01-30 13:18:24 -0500
commit1fa1999d529392b0f7c7f8ea7ccf116abc9ae360 (patch)
tree534f86ddf967d5c2e24f6a5e865d3e8a3d374588
parent13d93d9beeedbff4dca296c819116a3a103a376e (diff)
downloadpasslib-1fa1999d529392b0f7c7f8ea7ccf116abc9ae360.tar.gz
bugfix: passlib.hash.cisco_pix & cisco_asa: major overhaul of internal implementation,
fixed a number of edge cases where we were generating the wrong thing. tests ----- * expanded test vectors, and ran them against an ASA 9.6 system. marked out all vectors that were verified on 9.6 system. found that user appending, and various size thresholds, weren't being performed correctly by cisco_pix & cisco_asa -- certain hashes generated prior to this will be "unverifiable" by a Cisco system. * consolidated shared pix & asa tests into common base class. * added test for "spoil_digest" codepath that's been added to cisco_pix _calc_digest() (see below). hashers ------- * cisco_pix & cisco_asa now reject too-large passwords, and silently return False when verifying them; to match Cisco behavior. updated truncate policy flags to match new behavior. * overhaul of _calc_checksum(), to fix identified issues, lay out guesses & reasoning, and added some citations. docs ---- * updated docs to list new algorithm, and conditions where the old code would generate a bad hash. * general cleanup of doc layout for these hashes * updated "Cisco Hashes" section to give better listing of known hash formats.
-rw-r--r--docs/_fragments/asa_verify_callout.rst15
-rw-r--r--docs/history/1.7.rst12
-rw-r--r--docs/lib/passlib.hash.cisco_asa.rst12
-rw-r--r--docs/lib/passlib.hash.cisco_pix.rst129
-rw-r--r--docs/lib/passlib.hash.rst55
-rw-r--r--passlib/handlers/cisco.py224
-rw-r--r--passlib/tests/test_handlers_cisco.py364
7 files changed, 620 insertions, 191 deletions
diff --git a/docs/_fragments/asa_verify_callout.rst b/docs/_fragments/asa_verify_callout.rst
new file mode 100644
index 0000000..46cfdb7
--- /dev/null
+++ b/docs/_fragments/asa_verify_callout.rst
@@ -0,0 +1,15 @@
+.. rst-class:: float-right without-title
+
+.. todo::
+
+ **Caveat Emptor**
+
+ Passlib's implementations of :class:`cisco_pix` and :class:`cisco_asa` both need verification.
+ For those with access to Cisco PIX and ASA systems, verifying Passlib's reference vectors
+ would be a great help (see :issue:`51`). In the mean time, there are no guarantees
+ that passlib correctly replicates the official implementation.
+
+ .. versionchanged:: 1.7.1
+
+ A number of :ref:`bugs <passlib-asa96-bug>` were fixed after expanding
+ the reference vectors, and testing against an ASA 9.6 system.
diff --git a/docs/history/1.7.rst b/docs/history/1.7.rst
index 70d31db..f3dec53 100644
--- a/docs/history/1.7.rst
+++ b/docs/history/1.7.rst
@@ -5,14 +5,20 @@ Passlib 1.7
**1.7.1** (NOT YET RELEASED)
============================
-.. py:currentmodule:: passlib.ifc
-
This release rolls up assorted bug & compatibility fixes since 1.7.0.
Bugfixes
--------
+* .. py:currentmodule:: passlib.hash
+
+ :class:`cisco_asa` and :class:`cisco_pix`: Fixed a number of issues
+ which under :ref:`certain conditions <passlib-asa96-bug>`
+ caused prior releases to generate hashes that were unverifiable
+ on Cisco systems.
+
+* .. py:currentmodule:: passlib.ifc
-* :meth:`PasswordHash.hash` will now warn if passed any settings
+ :meth:`PasswordHash.hash` will now warn if passed any settings
keywords. This usage was deprecated in 1.7.0, but warning wasn't properly enabled.
See :ref:`hash-configuring` for the preferred way to pass settings.
diff --git a/docs/lib/passlib.hash.cisco_asa.rst b/docs/lib/passlib.hash.cisco_asa.rst
index 3b9e757..5875214 100644
--- a/docs/lib/passlib.hash.cisco_asa.rst
+++ b/docs/lib/passlib.hash.cisco_asa.rst
@@ -1,7 +1,7 @@
.. index:: Cisco; ASA hash
==================================================================
-:class:`passlib.hash.cisco_asa` - Cisco ASA hash
+:class:`passlib.hash.cisco_asa` - Cisco ASA MD5 hash
==================================================================
.. include:: ../_fragments/insecure_hash_warning.rst
@@ -10,9 +10,11 @@
.. versionadded:: 1.7
-The :class:`!cisco_asa` handler provides support for the 2005 revision of the older :class:`!cisco_pix` hash.
-The usage, functionality, and format is the same as for :class:`!cisco_pix`,
+.. include:: ../_fragments/asa_verify_callout.rst
-.. seealso::
+The :class:`!cisco_asa` class provides support for Cisco ASA "encrypted" hash format.
+This is a revision of the older :class:`!cisco_pix` hash;
+and the usage and format is the same.
- :doc:`cisco_pix <passlib.hash.cisco_pix>` documentation page.
+**See the** :doc:`cisco_pix <passlib.hash.cisco_pix>` **documentation page**
+for combined details of both these classes.
diff --git a/docs/lib/passlib.hash.cisco_pix.rst b/docs/lib/passlib.hash.cisco_pix.rst
index 5b00b83..f9978f2 100644
--- a/docs/lib/passlib.hash.cisco_pix.rst
+++ b/docs/lib/passlib.hash.cisco_pix.rst
@@ -1,57 +1,57 @@
.. index:: Cisco; PIX hash
==================================================================
-:class:`passlib.hash.cisco_pix` - Cisco PIX hash
+:class:`passlib.hash.cisco_pix` - Cisco PIX MD5 hash
==================================================================
+.. currentmodule:: passlib.hash
+
.. include:: ../_fragments/insecure_hash_warning.rst
.. versionadded:: 1.6
-.. todo::
-
- Passlib currently lack a thorough set of test cases for the :class:`cisco_asa` hash
- For people with access to such a system, verifying passlib's reference vectors
- would be a great help (see :issue:`51`).
- In the mean time, there are no guarantees that its behavior correctly replicates
- the official implementation. *caveat emptor*.
-
-.. warning::
-
- This class does not correctly handle hashes generated by
- Pix/ASA 7.0 (2005) or newer; particularly for passwords 13 characters or more
- (:issue:`51`). A new :class:`!cisco_asa` will be added in Passlib 1.7 to support these hashes.
+Overview
+========
+.. include:: ../_fragments/asa_verify_callout.rst
-.. currentmodule:: passlib.hash
+The :class:`cisco_asa` class implements the "encrypted" password hash algorithm commonly found on Cisco
+ASA systems. The companion :class:`cisco_pix` class
+implements the older variant found on Cisco PIX.
+Aside from internal differences, and slightly different limitations,
+the two hashes have the same format, and in some cases the same output.
-The :class:`cisco_pix` class implements the password hash algorithm commonly found on older Cisco
-PIX firewalls. The :class:`cisco_asa` class implements a newer variant found Cisco ASA 7.0 and
-newer systems. They can be used directly as follows::
+These classes can be used directly to generate or verify a hash for a specific
+user. Specifying the user account name is required for this hash::
- >>> from passlib.hash import cisco_pix as pix
+ >>> from passlib.hash import cisco_asa
>>> # hash password using specified username
- >>> hash = pix.hash("password", user="user")
+ >>> hash = cisco_asa.hash("password", user="user")
>>> hash
'A5XOy94YKDPXCo7U'
>>> # verify correct password
- >>> pix.verify("password", hash, user="user")
+ >>> cisco_asa.verify("password", hash, user="user")
True
+
>>> # verify correct password w/ wrong username
- >>> pm.verify("password", hash, user="other")
+ >>> cisco_asa.verify("password", hash, user="other")
False
+
>>> # verify incorrect password
- >>> pm.verify("letmein", hash, user="user")
+ >>> cisco_asa.verify("letmein", hash, user="user")
False
+The main "enable" password can be hashes / verified just by omitting
+the ``user`` parameter, or setting ``user=""``::
+
>>> # hash password without associated user account
- >>> hash2 = pix.hash("password")
+ >>> hash2 = cisco_asa.hash("password")
>>> hash2
'NuLKvvWGg.x9HEKO'
>>> # verify password without associated user account
- >>> pix.verify("password", hash2)
+ >>> cisco_asa.verify("password", hash2)
True
.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>`
@@ -66,8 +66,8 @@ Interface
.. note::
These hash algorithms have a context-sensitive peculiarity.
- It takes in an optional username, used to salt the hash,
- but with specific restrictions...
+ They take in an optional username to salt the hash,
+ but have specific restrictions...
* The username *must* be provided in order to correctly hash passwords
associated with a user account on the Cisco device.
@@ -80,15 +80,19 @@ Interface
Format & Algorithm
==================
-Cisco PIX / ASA hashes consist of a 12 byte digest, encoded as a 16 character
+Cisco PIX & ASA hashes consist of a 12 byte digest, encoded as a 16 character
:data:`HASH64 <passlib.utils.binary.h64>`-encoded string. An example
-hash (of ``"password"``) is ``"NuLKvvWGg.x9HEKO"``.
+hash (of ``"password"``, with user ``""``) is ``"NuLKvvWGg.x9HEKO"``.
The PIX / ASA digests are calculated as follows:
-1. The password is encoded using an ``ASCII``-compatible encoding
- (all known references are strict 7-bit ascii, and Passlib uses ``UTF-8``
- to provide unicode support).
+1. The password is encoded using ``UTF-8`` (though entering non-ASCII
+ characters is subject to interface-specific issues, and may lead
+ to problems such as double-encoding).
+
+ If the result is greater than 16 bytes (for PIX), or 32 bytes (for ASA),
+ the password is not allowed -- it will be rejected when set,
+ and simplify not verify during authentication.
2. If the hash is associated with a user account,
append the first four bytes of the user account name
@@ -96,14 +100,18 @@ The PIX / ASA digests are calculated as follows:
with a user account (e.g. it's the "enable" password),
this step should be omitted.
+ If the user account is 1-3 bytes, it is repeated until all 4 bytes are filled
+ up (e.g. "usr" becomes "usru").
+
For :class:`!cisco_asa`,
this step is omitted if the password is 28 bytes or more.
-3. The password should be truncated to 16 bytes, or the right side NULL
- padded to 16 bytes, as appropriate.
+3. The password+user string is truncated, or right-padded with NULLs,
+ until it's 16 bytes in size.
For :class:`!cisco_asa`,
- if the password is 13 or more bytes, the truncate/padding size is increased to 32 bytes.
+ if the password+user string is 16 or more bytes,
+ a padding size of 32 is used instead.
4. Run the result of step 3 through MD5.
@@ -112,6 +120,10 @@ The PIX / ASA digests are calculated as follows:
6. Encode the 12-byte result using :data:`HASH64 <passlib.utils.binary.h64>`.
+.. versionchanged:: 1.7.1
+
+ Updated to reflect current understanding of the algorithm.
+
Security Issues
===============
This algorithm is not suitable for *any* use besides manipulating existing
@@ -138,22 +150,44 @@ Cisco PIX hashes, due to the following flaws:
Deviations
==========
-This implementation differs from the standard in one main way:
+This implementation tries to adhere to the canonical Cisco implementation,
+but without an official specification, there may be other unknown deviations.
+The following are known issues:
* Unicode Policy:
- The official Cisco PIX algorithm is primarily used with ``ascii`` passwords,
- how it handles other characters is not known.
+ ASA documentation [#charset]_ indicates it uses UTF-8 encoding,
+ and Passlib does as well. However, some ASA interfaces
+ have issues such as: ASDM may double-encode unicode characters,
+ and SSH connections may drop non-ASCII characters entirely.
+
+* How usernames are added is not entirely pinned down. Under ASA, 3-character
+ usernames have their last character repeated to make a string of length 4.
+ It is currently assumed that a similar repetition would be applied to
+ usernames of 1-2 characters, and that this applies to PIX as well;
+ though neither assumption has been confirmed.
+
+* .. _passlib-asa96-bug:
- In order to provide support for unicode strings,
- Passlib will encode unicode passwords using ``utf-8``
- before running them through this algorithm. If a different
- encoding is desired by an application, the password should be encoded
- before handing it to Passlib.
+ **Passlib 1.7.1 Bugfix**: Prior releases of Passlib had a number of issues
+ with their implementation of the PIX & ASA algorithms. As of 1.7.1,
+ the reference vectors were greatly expanded, and then tested against
+ an ASA 9.6 system. This revealed a number of errors in passlib's implementation,
+ which under the following conditions would create hashes that were
+ unverifiable on a Cisco system:
-* While this implementation agrees with all known references,
- the actual algorithm has not been published by Cisco, so there may be other
- unknown deviations.
+ - PIX and ASA: Usernames containing 1-3 characters were not appended correctly (step 2, above).
+
+ - ASA omits the user entirely (step 2, above) for passwords with >= 28 characters,
+ not >= 27. Non-enable passwords of exactly 27 characters were previous hashed
+ incorrectly.
+
+ - ASA's padding size decision (step 3, above) is made after the user
+ has been appended, not before. This caused prior releases to
+ incorrectly hash non-enable passwords of length 13-15.
+
+ Anyone relying on cisco_asa or cisco_pix should upgrade to Passlib 1.7.1 or newer
+ to avoid these issues.
.. rubric:: Footnotes
@@ -166,3 +200,6 @@ This implementation differs from the standard in one main way:
.. [#] Partial description of ASA algorithm -
`<https://github.com/stekershaw/asa-password-encrypt/blob/master/README.md>`_
+
+.. [#charset] Character set used by ASA 8.4 -
+ `<http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets>`_
diff --git a/docs/lib/passlib.hash.rst b/docs/lib/passlib.hash.rst
index 3211066..94580e8 100644
--- a/docs/lib/passlib.hash.rst
+++ b/docs/lib/passlib.hash.rst
@@ -238,20 +238,65 @@ no identifying markers, identifying them is pretty much context-dependant.
Cisco Hashes
============
-The following hashes are used in various places on Cisco IOS and ASA devices:
+..
+ TODO:
+
+ What was/were IOS types 1, 2, 3, and 6? Don't see many references.
+ Think type 6 is a reversible encryption format ala type 7,
+ per https://supportforums.cisco.com/discussion/11733226/when-use-type-6-encrypted-or-type-7-encrypted
+
+
+**Cisco IOS**
+
+The following hashes are used in various places on Cisco IOS, and
+are usually referred to by a Cisco-assigned "type" code:
+
+.. rst-class:: hidden
.. toctree::
:maxdepth: 1
- passlib.hash.cisco_pix
- passlib.hash.cisco_asa
+ passlib.hash.cisco_type7
+
+* :doc:`passlib.hash.md5_crypt <passlib.hash.md5_crypt>` -- "Type 5" hashes are actually just the standard
+ Unix MD5-Crypt hash, the format is identical.
+
+* :doc:`passlib.hash.cisco_type7 <passlib.hash.cisco_type7>` -- "Type 7" isn't actually a hash,
+ but a reversible encoding designed to obscure passwords from idle view.
+
+* "Type 8" hashes are based on PBKDF2-HMAC-SHA256;
+ but not currently supported by passlib (:issue:`87`).
-* **Cisco "Type 5" hashes** - these are the same as :doc:`md5_crypt <passlib.hash.md5_crypt>`
+* "Type 9" hashes are based on scrypt;
+ but not currently supported by passlib (:issue:`87`).
+
+**Cisco PIX & ASA**
+
+Separately from this, Cisco PIX & ASA firewalls have their own hash formats,
+generally identified by the "format" parameter in the :samp:`username {user} password {hash} {format}` config line
+they occur in. The following are known & handled by passlib:
+
+.. rst-class:: hidden
.. toctree::
:maxdepth: 1
- passlib.hash.cisco_type7
+ passlib.hash.cisco_pix
+ passlib.hash.cisco_asa
+
+* :doc:`passlib.hash.cisco_pix <passlib.hash.cisco_pix>` -- PIX "encrypted" hashes
+ use a simple unsalted MD5-based algorithm.
+
+* :doc:`passlib.hash.cisco_asa <passlib.hash.cisco_asa>` -- ASA "encrypted" hashes
+ use a similar algorithm to PIX, with some minor improvements.
+
+* ASA "nt-encrypted" hashes
+ are the same as :class:`passlib.hash.nthash`,
+ except that they use base64 encoding rather than hexadecimal.
+
+* ASA 9.5 added support for "pbkdf2" hashes
+ (based on PBKDF2-HMAC-SHA512); which aren't currently supported
+ by passlib (:issue:`87`).
.. _other-hashes:
diff --git a/passlib/handlers/cisco.py b/passlib/handlers/cisco.py
index 186c247..e715e1a 100644
--- a/passlib/handlers/cisco.py
+++ b/passlib/handlers/cisco.py
@@ -1,4 +1,6 @@
-"""passlib.handlers.cisco - Cisco password hashes"""
+"""
+passlib.handlers.cisco -- Cisco password hashes
+"""
#=============================================================================
# imports
#=============================================================================
@@ -9,7 +11,7 @@ import logging; log = logging.getLogger(__name__)
from warnings import warn
# site
# pkg
-from passlib.utils import right_pad_string, to_unicode
+from passlib.utils import right_pad_string, to_unicode, repeat_string, to_bytes
from passlib.utils.binary import h64
from passlib.utils.compat import unicode, u, join_byte_values, \
join_byte_elems, iter_byte_values, uascii_to_str
@@ -17,23 +19,37 @@ import passlib.utils.handlers as uh
# local
__all__ = [
"cisco_pix",
+ "cisco_asa",
"cisco_type7",
]
#=============================================================================
+# utils
+#=============================================================================
+
+#: dummy bytes used by spoil_digest var in cisco_pix._calc_checksum()
+_DUMMY_BYTES = b'\xFF' * 32
+
+#=============================================================================
# cisco pix firewall hash
#=============================================================================
-class cisco_pix(uh.TruncateMixin, uh.HasUserContext, uh.StaticHandler):
- """This class implements the password hash used by (older) Cisco PIX firewalls,
+class cisco_pix(uh.HasUserContext, uh.StaticHandler):
+ """
+ This class implements the password hash used by older Cisco PIX firewalls,
and follows the :ref:`password-hash-api`.
It does a single round of hashing, and relies on the username
as the salt.
- The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods
- have the following extra keyword:
+ This class only allows passwords <= 16 bytes, anything larger
+ will result in a :exc:`~passlib.exc.PasswordSizeError` if passed to :meth:`~cisco_pix.hash`,
+ and be silently rejected if passed to :meth:`~cisco_pix.verify`.
+
+ The :meth:`~passlib.ifc.PasswordHash.hash`,
+ :meth:`~passlib.ifc.PasswordHash.genhash`, and
+ :meth:`~passlib.ifc.PasswordHash.verify` methods
+ all support the following extra keyword:
- :type user: str
- :param user:
+ :param str user:
String containing name of user account this password is associated with.
This is *required* in order to correctly hash passwords associated
@@ -44,14 +60,13 @@ class cisco_pix(uh.TruncateMixin, uh.HasUserContext, uh.StaticHandler):
hash passwords which don't have an associated user account
(such as the "enable" password).
- :param bool truncate_error:
- By default, this will silently truncate passwords larger than 16 bytes.
- Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash`
- to raise a :exc:`~passlib.exc.PasswordTruncateError` instead.
+ .. versionadded:: 1.6
- .. versionadded:: 1.7
+ .. versionchanged:: 1.7.1
- .. versionadded:: 1.6
+ Passwords > 16 bytes are now rejected / throw error instead of being silently truncated,
+ to match Cisco behavior. A number of :ref:`bugs <passlib-asa96-bug>` were fixed
+ which caused prior releases to generate unverifiable hashes in certain cases.
"""
#===================================================================
# class attrs
@@ -61,7 +76,13 @@ class cisco_pix(uh.TruncateMixin, uh.HasUserContext, uh.StaticHandler):
# PasswordHash
#--------------------
name = "cisco_pix"
- setting_kwds = ("truncate_error",)
+
+ truncate_size = 16
+
+ # NOTE: these are the default policy for PasswordHash,
+ # but want to set them explicitly for now.
+ truncate_error = True
+ truncate_verify_reject = True
#--------------------
# GenericHandler
@@ -70,63 +91,155 @@ class cisco_pix(uh.TruncateMixin, uh.HasUserContext, uh.StaticHandler):
checksum_chars = uh.HASH64_CHARS
#--------------------
- # TruncateMixin
- #--------------------
- truncate_size = 16
-
- #--------------------
# custom
#--------------------
- #: control flag signalling "cisco_asa" mode
+ #: control flag signalling "cisco_asa" mode, set by cisco_asa class
_is_asa = False
#===================================================================
# methods
#===================================================================
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.
+ """
+ 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
- # XXX: No idea what unicode policy is, but all examples are
- # 7-bit ascii compatible, so using UTF-8.
+ #
+ # 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")
- 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.
+ #
+ # 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 and not (asa and seclen > 27):
+ if user:
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
+ 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:
- padsize = 16
- secret = right_pad_string(secret, padsize)
+ pad_size = 16
+ secret = right_pad_string(secret, pad_size)
+ #
# md5 digest
- hash = md5(secret).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
- hash = join_byte_elems(c for i,c in enumerate(hash) if i & 3 < 3)
+ # 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(hash).decode("ascii")
+ #
+ return h64.encode_bytes(digest).decode("ascii")
+
+ # NOTE: works, but needs UTs.
+ # @classmethod
+ # def same_as_pix(cls, secret, user=""):
+ # """
+ # test whether (secret + user) combination should
+ # have the same hash under PIX and ASA.
+ #
+ # mainly present to help unittests.
+ # """
+ # # see _calc_checksum() above for details of this logic.
+ # size = len(to_bytes(secret, "utf-8"))
+ # if user and size < 28:
+ # size += 4
+ # return size < 17
#===================================================================
# eoc
@@ -140,12 +253,20 @@ class cisco_asa(cisco_pix):
to the older :class:`cisco_pix` class.
For passwords less than 13 characters, this should be identical to :class:`!cisco_pix`,
- but will generate a different hash for anything larger
+ but will generate a different hash for most larger inputs
(See the `Format & Algorithm`_ section for the details).
- Unlike cisco_pix, this will truncate passwords larger than 32 bytes.
+ This class only allows passwords <= 32 bytes, anything larger
+ will result in a :exc:`~passlib.exc.PasswordSizeError` if passed to :meth:`~cisco_asa.hash`,
+ and be silently rejected if passed to :meth:`~cisco_asa.verify`.
.. versionadded:: 1.7
+
+ .. versionchanged:: 1.7.1
+
+ Passwords > 32 bytes are now rejected / throw error instead of being silently truncated,
+ to match Cisco behavior. A number of :ref:`bugs <passlib-asa96-bug>` were fixed
+ which caused prior releases to generate unverifiable hashes in certain cases.
"""
#===================================================================
# class attrs
@@ -174,7 +295,8 @@ class cisco_asa(cisco_pix):
# type 7
#=============================================================================
class cisco_type7(uh.GenericHandler):
- """This class implements the Type 7 password encoding used by Cisco IOS,
+ """
+ This class implements the "Type 7" password encoding used by Cisco IOS,
and follows the :ref:`password-hash-api`.
It has a simple 4-5 bit salt, but is nonetheless a reversible encoding
instead of a real hash.
diff --git a/passlib/tests/test_handlers_cisco.py b/passlib/tests/test_handlers_cisco.py
index 33c7926..b19bacf 100644
--- a/passlib/tests/test_handlers_cisco.py
+++ b/passlib/tests/test_handlers_cisco.py
@@ -21,27 +21,33 @@ __all__ = [
"cisco_type7_test",
]
#=============================================================================
-# cisco pix
+# shared code for cisco PIX & ASA
#=============================================================================
-class cisco_pix_test(UserHandlerMixin, HandlerCase):
- handler = hash.cisco_pix
- requires_user = False
- known_correct_hashes = [
+class _PixAsaSharedTest(UserHandlerMixin, HandlerCase):
+ """
+ class w/ shared info for PIX & ASA tests.
+ """
+ __unittest_skip = True # for TestCase
+ requires_user = False # for UserHandlerMixin
+
+ #: shared list of hashes which should be identical under pix & asa7
+ #: (i.e. combined secret + user < 17 bytes)
+ pix_asa_shared_hashes = [
#
# http://www.perlmonks.org/index.pl?node_id=797623
#
- ("cisco", "2KFQnbNIdI.2KYOU"),
+ (("cisco", ""), "2KFQnbNIdI.2KYOU"), # confirmed ASA 9.6
#
# http://www.hsc.fr/ressources/breves/pix_crack.html.en
#
- ("hsc", "YtT8/k6Np8F1yz2c"),
+ (("hsc", ""), "YtT8/k6Np8F1yz2c"), # confirmed ASA 9.6
#
# www.freerainbowtables.com/phpBB3/viewtopic.php?f=2&t=1441
#
- ("", "8Ry2YjIyt7RRXU24"),
+ (("", ""), "8Ry2YjIyt7RRXU24"), # confirmed ASA 9.6
(("cisco", "john"), "hN7LzeyYjw12FSIU"),
(("cisco", "jack"), "7DrfeZ7cyOj/PslD"),
@@ -61,6 +67,12 @@ class cisco_pix_test(UserHandlerMixin, HandlerCase):
(("phonehome", "rharris"), "zyIIMSYjiPm0L7a6"),
#
+ # http://www.openwall.com/lists/john-users/2010/08/08/3
+ #
+ (("cangetin", ""), "TynyB./ftknE77QP"),
+ (("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"),
+
+ #
# from JTR 1.7.9
#
("test1", "TRPEas6f/aa6JSPL"),
@@ -71,96 +83,286 @@ class cisco_pix_test(UserHandlerMixin, HandlerCase):
("0123456789abcdef", ".7nfVBEIEu4KbF/1"),
#
+ # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html#wp5472
+ #
+ (("1234567890123456", ""), "feCkwUGktTCAgIbD"), # canonical source
+ (("watag00s1am", ""), "jMorNbK0514fadBh"), # canonical source
+
+ #
# custom
#
(("cisco1", "cisco1"), "jmINXNH6p1BxUppp"),
# ensures utf-8 used for unicode
(UPASS_TABLE, 'CaiIvkLMu2TOHXGT'),
+
+ #
+ # passlib reference vectors
+ #
+ # Some of these have been confirmed on various ASA firewalls,
+ # and the exact version is noted next to each hash.
+ # Would like to verify these under more PIX & ASA versions.
+ #
+ # Those without a note are generally an extrapolation,
+ # to ensure the code stays consistent, but for various reasons,
+ # hasn't been verified.
+ #
+ # * One such case is usernames w/ 1 & 2 digits --
+ # ASA (9.6 at least) requires 3+ digits in username.
+ #
+ # The following hashes (below 13 chars) should be identical for PIX/ASA.
+ # Ones which differ are listed separately in the known_correct_hashes
+ # list for the two test classes.
+ #
+
+ # 4 char password
+ (('1234', ''), 'RLPMUQ26KL4blgFN'), # confirmed ASA 9.6
+
+ # 8 char password
+ (('01234567', ''), '0T52THgnYdV1tlOF'), # confirmed ASA 9.6
+ (('01234567', '3'), '.z0dT9Alkdc7EIGS'),
+ (('01234567', '36'), 'CC3Lam53t/mHhoE7'),
+ (('01234567', '365'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6
+ (('01234567', '3333'), '.z0dT9Alkdc7EIGS'), # confirmed ASA 9.6
+ (('01234567', '3636'), 'CC3Lam53t/mHhoE7'), # confirmed ASA 9.6
+ (('01234567', '3653'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6
+ (('01234567', 'adm'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
+ (('01234567', 'adma'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
+ (('01234567', 'admad'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
+ (('01234567', 'user'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6
+ (('01234567', 'user1234'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6
+
+ # 12 char password
+ (('0123456789ab', ''), 'S31BxZOGlAigndcJ'), # confirmed ASA 9.6
+ (('0123456789ab', '36'), 'wFqSX91X5.YaRKsi'),
+ (('0123456789ab', '365'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6
+ (('0123456789ab', '3333'), 'mcXPL/vIZcIxLUQs'), # confirmed ASA 9.6
+ (('0123456789ab', '3636'), 'wFqSX91X5.YaRKsi'), # confirmed ASA 9.6
+ (('0123456789ab', '3653'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6
+ (('0123456789ab', 'user'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6
+ (('0123456789ab', 'user1234'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6
+
+ # NOTE: remaining reference vectors for 13+ char passwords
+ # are split up between cisco_pix & cisco_asa tests.
+
+ # unicode passwords
+ # ASA supposedly uses utf-8 encoding, but entering non-ascii
+ # chars is error-prone, and while UTF-8 appears to be intended,
+ # observed behaviors include:
+ # * ssh cli stripping non-ascii chars entirely
+ # * ASDM web iface double-encoding utf-8 strings
+ ((u("t\xe1ble").encode("utf-8"), 'user'), 'Og8fB4NyF0m5Ed9c'),
+ ((u("t\xe1ble").encode("utf-8").decode("latin-1").encode("utf-8"),
+ 'user'), 'cMvFC2XVBmK/68yB'), # confirmed ASA 9.6 when typed into ASDM
]
+ def test_calc_digest_spoiler(self):
+ """
+ _calc_checksum() -- spoil oversize passwords during verify
+
+ for details, see 'spoil_digest' flag instead that function.
+ this helps cisco_pix/cisco_asa implement their policy of
+ ``.truncate_verify_reject=True``.
+ """
+ def calc(secret, for_hash=False):
+ return self.handler(use_defaults=for_hash)._calc_checksum(secret)
+
+ # short (non-truncated) password
+ short_secret = repeat_string("1234", self.handler.truncate_size)
+ short_hash = calc(short_secret)
+
+ # longer password should have totally different hash,
+ # to prevent verify from matching (i.e. "spoiled").
+ long_secret = short_secret + "X"
+ long_hash = calc(long_secret)
+ self.assertNotEqual(long_hash, short_hash)
+
+ # spoiled hash should depend on whole secret,
+ # so that output isn't predictable
+ alt_long_secret = short_secret + "Y"
+ alt_long_hash = calc(alt_long_secret)
+ self.assertNotEqual(alt_long_hash, short_hash)
+ self.assertNotEqual(alt_long_hash, long_hash)
+
+ # for hash(), should throw error if password too large
+ calc(short_secret, for_hash=True)
+ self.assertRaises(exc.PasswordSizeError, calc, long_secret, for_hash=True)
+ self.assertRaises(exc.PasswordSizeError, calc, alt_long_secret, for_hash=True)
+
#=============================================================================
-# cisco_asa
+# cisco pix
#=============================================================================
-def _get_secret(value):
- """extract secret from secret or (secret, user) tuple"""
- if isinstance(value, tuple):
- return value[0]
- else:
- return value
-
-class cisco_asa_test(UserHandlerMixin, HandlerCase):
+class cisco_pix_test(_PixAsaSharedTest):
+ handler = hash.cisco_pix
+
+ #: known correct pix hashes
+ known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [
+ #
+ # passlib reference vectors (PIX-specific)
+ #
+ # NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors,
+ # and general notes about the 'passlib reference vectors' test set.
+ #
+ # All of the following are PIX-specific, as ASA starts
+ # to use a different padding size at 13 characters.
+ #
+ # TODO: these need confirming w/ an actual PIX system.
+ #
+
+ # 13 char password
+ (('0123456789abc', ''), 'eacOpB7vE7ZDukSF'),
+ (('0123456789abc', '3'), 'ylJTd/qei66WZe3w'),
+ (('0123456789abc', '36'), 'hDx8QRlUhwd6bU8N'),
+ (('0123456789abc', '365'), 'vYOOtnkh1HXcMrM7'),
+ (('0123456789abc', '3333'), 'ylJTd/qei66WZe3w'),
+ (('0123456789abc', '3636'), 'hDx8QRlUhwd6bU8N'),
+ (('0123456789abc', '3653'), 'vYOOtnkh1HXcMrM7'),
+ (('0123456789abc', 'user'), 'f4/.SALxqDo59mfV'),
+ (('0123456789abc', 'user1234'), 'f4/.SALxqDo59mfV'),
+
+ # 14 char password
+ (('0123456789abcd', ''), '6r8888iMxEoPdLp4'),
+ (('0123456789abcd', '3'), 'f5lvmqWYj9gJqkIH'),
+ (('0123456789abcd', '36'), 'OJJ1Khg5HeAYBH1c'),
+ (('0123456789abcd', '365'), 'OJJ1Khg5HeAYBH1c'),
+ (('0123456789abcd', '3333'), 'f5lvmqWYj9gJqkIH'),
+ (('0123456789abcd', '3636'), 'OJJ1Khg5HeAYBH1c'),
+ (('0123456789abcd', '3653'), 'OJJ1Khg5HeAYBH1c'),
+ (('0123456789abcd', 'adm'), 'DbPLCFIkHc2SiyDk'),
+ (('0123456789abcd', 'adma'), 'DbPLCFIkHc2SiyDk'),
+ (('0123456789abcd', 'user'), 'WfO2UiTapPkF/FSn'),
+ (('0123456789abcd', 'user1234'), 'WfO2UiTapPkF/FSn'),
+
+ # 15 char password
+ (('0123456789abcde', ''), 'al1e0XFIugTYLai3'),
+ (('0123456789abcde', '3'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', '36'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', '365'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', '3333'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', '3636'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', '3653'), 'lYbwBu.f82OIApQB'),
+ (('0123456789abcde', 'adm'), 'KgKx1UQvdR/09i9u'),
+ (('0123456789abcde', 'adma'), 'KgKx1UQvdR/09i9u'),
+ (('0123456789abcde', 'user'), 'qLopkenJ4WBqxaZN'),
+ (('0123456789abcde', 'user1234'), 'qLopkenJ4WBqxaZN'),
+
+ # 16 char password
+ (('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', '36'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', '365'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', '3333'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', '3636'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', '3653'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', 'user'), '.7nfVBEIEu4KbF/1'),
+ (('0123456789abcdef', 'user1234'), '.7nfVBEIEu4KbF/1'),
+ ]
+
+
+#=============================================================================
+# cisco asa
+#=============================================================================
+class cisco_asa_test(_PixAsaSharedTest):
handler = hash.cisco_asa
- requires_user = False
- known_correct_hashes = [
- # format: ((secret, user), hash)
-
- #
- # passlib test vectors
- # TODO: these have not been confirmed by an outside source,
- # nor tested against an official implementation.
- # for now, these only confirm we haven't had a regression.
- #
-
- # 8 char password -- should be same as pix
- (('01234567', ''), '0T52THgnYdV1tlOF'),
- (('01234567', '36'), 'oY0Dh6RVC9KFlopL'),
- (('01234567', 'user'), 'PNZ4ycbbZ0jp1.j1'),
- (('01234567', 'user1234'), 'PNZ4ycbbZ0jp1.j1'),
-
- # 12 char password -- should be same as pix
- (('0123456789ab', ''), 'S31BxZOGlAigndcJ'),
- (('0123456789ab', '36'), 'JqCXavOaaaTn9B5y'),
- (('0123456789ab', 'user'), 'f.T4BKdzdNkjxQl7'),
- (('0123456789ab', 'user1234'), 'f.T4BKdzdNkjxQl7'),
-
- # 13 char password -- ASA should switch to larger padding
- (('0123456789abc', ''), 'XGUn8JhVAnJsaJ69'), # e.g: cisco_pix is 'eacOpB7vE7ZDukSF'
- (('0123456789abc', '36'), 'feNbQYEDXynZXMJH'),
- (('0123456789abc', 'user'), '8Q/FZeam5ai1A47p'),
- (('0123456789abc', 'user1234'), '8Q/FZeam5ai1A47p'),
-
- # 16 char password -- verify fencepost
- (('0123456789abcdef', ''), 'YO.dC.tE77bB35aH'),
- (('0123456789abcdef', '36'), 'ekOxFx1Mqt8hL3vJ'),
- (('0123456789abcdef', 'user'), 'IneB.wc9sfRzLPoh'),
- (('0123456789abcdef', 'user1234'), 'IneB.wc9sfRzLPoh'),
-
- # 27 char password -- ASA should still append user
- (('0123456789abcdefqwertyuiopa', ''), '4wp19zS3OCe.2jt5'),
- (('0123456789abcdefqwertyuiopa', '36'), 'GlGggqfEc19br12c'),
- (('0123456789abcdefqwertyuiopa', 'user'), 'zynfWw3UtszxLMgL'),
- (('0123456789abcdefqwertyuiopa', 'user1234'), 'zynfWw3UtszxLMgL'),
-
- # 28 char password -- ASA shouldn't append user anymore
- (('0123456789abcdefqwertyuiopas', ''), 'W6nbOddI0SutTK7m'),
+ known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [
+ #
+ # passlib reference vectors (ASA-specific)
+ #
+ # NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors,
+ # and general notes about the 'passlib reference vectors' test set.
+ #
+
+ # 13 char password
+ # NOTE: past this point, ASA pads to 32 bytes instead of 16
+ # for all cases where user is set (secret + 4 bytes > 16),
+ # but still uses 16 bytes for enable pwds (secret <= 16).
+ # hashes w/ user WON'T match PIX, but "enable" passwords will.
+ (('0123456789abc', ''), 'eacOpB7vE7ZDukSF'), # confirmed ASA 9.6
+ (('0123456789abc', '36'), 'FRV9JG18UBEgX0.O'),
+ (('0123456789abc', '365'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6
+ (('0123456789abc', '3333'), 'NmrkP98nT7RAeKZz'), # confirmed ASA 9.6
+ (('0123456789abc', '3636'), 'FRV9JG18UBEgX0.O'), # confirmed ASA 9.6
+ (('0123456789abc', '3653'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6
+ (('0123456789abc', 'user'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6
+ (('0123456789abc', 'user1234'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6
+
+ # 14 char password
+ (('0123456789abcd', ''), '6r8888iMxEoPdLp4'), # confirmed ASA 9.6
+ (('0123456789abcd', '3'), 'yxGoujXKPduTVaYB'),
+ (('0123456789abcd', '36'), 'W0jckhnhjnr/DiT/'),
+ (('0123456789abcd', '365'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6
+ (('0123456789abcd', '3333'), 'yxGoujXKPduTVaYB'), # confirmed ASA 9.6
+ (('0123456789abcd', '3636'), 'W0jckhnhjnr/DiT/'), # confirmed ASA 9.6
+ (('0123456789abcd', '3653'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6
+ (('0123456789abcd', 'adm'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6
+ (('0123456789abcd', 'adma'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6
+ (('0123456789abcd', 'user'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6
+ (('0123456789abcd', 'user1234'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6
+
+ # 15 char password
+ (('0123456789abcde', ''), 'al1e0XFIugTYLai3'), # confirmed ASA 9.6
+ (('0123456789abcde', '3'), 'nAZrQoHaL.fgrIqt'),
+ (('0123456789abcde', '36'), '2GxIQ6ICE795587X'),
+ (('0123456789abcde', '365'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6
+ (('0123456789abcde', '3333'), 'nAZrQoHaL.fgrIqt'), # confirmed ASA 9.6
+ (('0123456789abcde', '3636'), '2GxIQ6ICE795587X'), # confirmed ASA 9.6
+ (('0123456789abcde', '3653'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6
+ (('0123456789abcde', 'adm'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6
+ (('0123456789abcde', 'adma'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6
+ (('0123456789abcde', 'user'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6
+ (('0123456789abcde', 'user1234'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6
+
+ # 16 char password
+ (('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'), # confirmed ASA 9.6
+ (('0123456789abcdef', '36'), 'GhI8.yFSC5lwoafg'),
+ (('0123456789abcdef', '365'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6
+ (('0123456789abcdef', '3333'), 'Ghdi1IlsswgYzzMH'), # confirmed ASA 9.6
+ (('0123456789abcdef', '3636'), 'GhI8.yFSC5lwoafg'), # confirmed ASA 9.6
+ (('0123456789abcdef', '3653'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6
+ (('0123456789abcdef', 'user'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6
+ (('0123456789abcdef', 'user1234'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6
+
+ # 17 char password
+ # NOTE: past this point, ASA pads to 32 bytes instead of 16
+ # for ALL cases, since secret > 16 bytes even for enable pwds;
+ # and so none of these rest here should match PIX.
+ (('0123456789abcdefq', ''), 'bKshl.EN.X3CVFRQ'), # confirmed ASA 9.6
+ (('0123456789abcdefq', '36'), 'JAeTXHs0n30svlaG'),
+ (('0123456789abcdefq', '365'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6
+ (('0123456789abcdefq', '3333'), 'USEJbxI6.VY4ecBP'), # confirmed ASA 9.6
+ (('0123456789abcdefq', '3636'), 'JAeTXHs0n30svlaG'), # confirmed ASA 9.6
+ (('0123456789abcdefq', '3653'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6
+ (('0123456789abcdefq', 'user'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6
+ (('0123456789abcdefq', 'user1234'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6
+
+ # 27 char password
+ (('0123456789abcdefqwertyuiopa', ''), '4wp19zS3OCe.2jt5'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', '36'), 'PjUoGqWBKPyV9qOe'),
+ (('0123456789abcdefqwertyuiopa', '365'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', '3333'), 'rd/ZMuGTJFIb2BNG'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', '3636'), 'PjUoGqWBKPyV9qOe'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', '3653'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', 'user'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopa', 'user1234'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6
+
+ # 28 char password
+ # NOTE: past this point, ASA stops appending the username AT ALL,
+ # even though there's still room for the first few chars.
+ (('0123456789abcdefqwertyuiopas', ''), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
(('0123456789abcdefqwertyuiopas', '36'), 'W6nbOddI0SutTK7m'),
- (('0123456789abcdefqwertyuiopas', 'user'), 'W6nbOddI0SutTK7m'),
- (('0123456789abcdefqwertyuiopas', 'user1234'), 'W6nbOddI0SutTK7m'),
+ (('0123456789abcdefqwertyuiopas', '365'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopas', 'user'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopas', 'user1234'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
- # 32 char password -- verify fencepost
- (('0123456789abcdefqwertyuiopasdfgh', ''), '5hPT/iC6DnoBxo6a'),
+ # 32 char password
+ # NOTE: this is max size that ASA allows, and throws error for larger
+ (('0123456789abcdefqwertyuiopasdfgh', ''), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
(('0123456789abcdefqwertyuiopasdfgh', '36'), '5hPT/iC6DnoBxo6a'),
- (('0123456789abcdefqwertyuiopasdfgh', 'user'), '5hPT/iC6DnoBxo6a'),
- (('0123456789abcdefqwertyuiopasdfgh', 'user1234'), '5hPT/iC6DnoBxo6a'),
-
- # 33 char password -- ASA should truncate to 32 (should be same as above)
- (('0123456789abcdefqwertyuiopasdfghj', ''), '5hPT/iC6DnoBxo6a'),
- (('0123456789abcdefqwertyuiopasdfghj', '36'), '5hPT/iC6DnoBxo6a'),
- (('0123456789abcdefqwertyuiopasdfghj', 'user'), '5hPT/iC6DnoBxo6a'),
- (('0123456789abcdefqwertyuiopasdfghj', 'user1234'), '5hPT/iC6DnoBxo6a'),
-
- # unicode password -- assumes cisco will use utf-8 encoding
- ((u('t\xe1ble'), ''), 'xQXX755BKYRl0ZpQ'),
- ((u('t\xe1ble'), '36'), 'Q/43xXKmIaKLycSj'),
- ((u('t\xe1ble'), 'user'), 'Og8fB4NyF0m5Ed9c'),
- ((u('t\xe1ble'), 'user1234'), 'Og8fB4NyF0m5Ed9c'),
+ (('0123456789abcdefqwertyuiopasdfgh', '365'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopasdfgh', 'user'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
+ (('0123456789abcdefqwertyuiopasdfgh', 'user1234'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
]
- # append all the cisco_pix hashes w/ password < 13 chars ... those should be the same.
- known_correct_hashes.extend(row for row in cisco_pix_test.known_correct_hashes
- if len(_get_secret(row[0])) < 13)
#=============================================================================
# cisco type 7