summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2016-02-10 14:27:53 -0500
committerEli Collins <elic@assurancetechnologies.com>2016-02-10 14:27:53 -0500
commit7b598fe89d604ef91efd4f71253ffb9ab0aae7c5 (patch)
tree1f732aa6364a37e3e18e5d0788eda208f95219d1
parent590aef9ab3d872830ca27d95833e78c737fccd2d (diff)
downloadpasslib-7b598fe89d604ef91efd4f71253ffb9ab0aae7c5.tar.gz
relocated many of the crypto routes inside passlib.utils,
and moved them to a separate passlib.crypto subpackage. along with this move, made a few api cleanups: * unified all code that's looking up hashes to use new passlib.crypto.lookup_hash() wrapper, which takes care of hash name normalization, loading fallback implementations, and alg metadata inspection, all hidden behind a memoized function. * deprecated pbkdf2() in favor of pbkdf2_hmac() -- only real use, and new signature matches stdlib function. additionally, this version is a bit faster, due to some assumptions that can be made due to the PRF always being HMAC based. * added compile_hmac() helper which does an even more efficient job of pre-compiling a keyed HMAC function; this helped speed up pbkdf2 a bit more.
-rw-r--r--CHANGES23
-rw-r--r--LICENSE2
-rw-r--r--admin/benchmarks.py8
-rw-r--r--docs/conf.py4
-rw-r--r--docs/lib/passlib.crypto.des.rst26
-rw-r--r--docs/lib/passlib.crypto.digest.rst39
-rw-r--r--docs/lib/passlib.hash.fshp.rst2
-rw-r--r--docs/lib/passlib.hash.grub_pbkdf2_sha512.rst2
-rw-r--r--docs/lib/passlib.hash.msdcc2.rst2
-rw-r--r--docs/lib/passlib.hash.nthash.rst4
-rw-r--r--docs/lib/passlib.hash.pbkdf2_digest.rst2
-rw-r--r--docs/lib/passlib.hash.scram.rst2
-rw-r--r--docs/lib/passlib.utils.des.rst13
-rw-r--r--docs/lib/passlib.utils.md4.rst26
-rw-r--r--docs/lib/passlib.utils.pbkdf2.rst19
-rw-r--r--docs/lib/passlib.utils.rst2
-rw-r--r--docs/password_hash_api.rst2
-rw-r--r--passlib/context.py2
-rw-r--r--passlib/crypto/__init__.py1
-rw-r--r--passlib/crypto/_md4.py244
-rw-r--r--passlib/crypto/des.py848
-rw-r--r--passlib/crypto/digest.py646
-rw-r--r--passlib/exc.py11
-rw-r--r--passlib/handlers/des_crypt.py2
-rw-r--r--passlib/handlers/digests.py24
-rw-r--r--passlib/handlers/django.py12
-rw-r--r--passlib/handlers/fshp.py4
-rw-r--r--passlib/handlers/oracle.py2
-rw-r--r--passlib/handlers/pbkdf2.py40
-rw-r--r--passlib/handlers/scram.py17
-rw-r--r--passlib/handlers/sha1_crypt.py4
-rw-r--r--passlib/handlers/windows.py9
-rw-r--r--passlib/tests/test_context.py2
-rw-r--r--passlib/tests/test_crypto_builtin_md4.py160
-rw-r--r--passlib/tests/test_crypto_des.py192
-rw-r--r--passlib/tests/test_crypto_digest.py416
-rw-r--r--passlib/tests/test_handlers.py4
-rw-r--r--passlib/tests/test_totp.py23
-rw-r--r--passlib/tests/test_utils_crypto.py652
-rw-r--r--passlib/tests/test_utils_md4.py41
-rw-r--r--passlib/tests/test_utils_pbkdf2.py321
-rw-r--r--passlib/totp.py20
-rw-r--r--passlib/utils/__init__.py4
-rw-r--r--passlib/utils/compat/__init__.py4
-rw-r--r--passlib/utils/des.py857
-rw-r--r--passlib/utils/md4.py263
-rw-r--r--passlib/utils/pbkdf2.py486
-rw-r--r--passlib/win32.py2
-rw-r--r--setup.py1
49 files changed, 3167 insertions, 2325 deletions
diff --git a/CHANGES b/CHANGES
index 6b5005a..7dbc549 100644
--- a/CHANGES
+++ b/CHANGES
@@ -19,15 +19,10 @@ Release History
* Finish :mod:`passlib.pwd` -- need to finalize API and document it better.
- * Would strongly like to also add pbkdf2 & bcrypt variants which have support for an external
- "pepper" to further increase security.
-
* Internal cleanups:
* Check for additions to Django's hashers as of Django 1.7
- * Potentially reorg the utils module (particularly the crypto bits), it's getting messy.
-
* Finish the :meth:`PasswordHash.using` and :meth:`PasswordHash.needs_update` refactoring,
aimed at eliminating any need for the internal :class:`passlib.context._CryptRecord` class.
@@ -56,7 +51,7 @@ Major Changes
* Added :class:`~passlib.hash.cisco_asa` which provides (tentative)
support for Cisco ASA 7.0 and newer hashes.
-* The :func:`~passlib.utils.pbkdf2.pbkdf2` function and all PBKDF2-based
+* The :func:`~passlib.crypto.digest.pbkdf2_hmac` function and all PBKDF2-based
hashes have been sped up by ~20% compared to Passlib 1.6.
* Passlib will now detect and work around the fatal concurrency bug
@@ -70,9 +65,13 @@ Minor Changes
* The shared :class:`!PasswordHash` unittests now check all hash handlers for
basic thread-safety (motivated by the pybcrypt 0.2 concurrency bug).
-* The internal support module :mod:`passlib.utils.pbkdf2` has gained
- to new helpers: :func:`~passlib.utils.pbkdf2.get_hash_info`
- and :func:`~passlib.utils.pbkdf2.get_keyed_prf`.
+* Most of passlib's internal cryptography helpers have been moved from
+ :mod:`!passlib.utils` to :mod:`passlib.crypto`, and the APIs refactored.
+ This allowed unification of various hash management routines,
+ some speed ups to the HMAC and PBKDF2 primitives, and opens up the architecture
+ to support more optional backend libraries.
+
+ Compatibility wrappers will be kept in place at the old location until Passlib 2.0.
Deprecations
------------
@@ -99,6 +98,10 @@ Internal Changes
Backwards Incompatibilities
---------------------------
+
+* :func:`passlib.utils.pbkdf2.pbkdf2` no longer supports custom PRF callables.
+ this was an unused feature, and prevented some useful optimizations.
+
* The :ref:`min_verify_time <context-min-verify-time-option>` keyword
that was deprecated in release 1.6, is now completely ignored.
It was never very useful, and now complicates the internal code needlessly.
@@ -673,7 +676,7 @@ Internal Changes
----------------
* Added more handler utility functions to reduce code duplication.
- * Expanded kdf helpers in :mod:`passlib.utils.pbkdf2`.
+ * Expanded kdf helpers in :mod:`!passlib.utils.pbkdf2`.
* Removed deprecated parts of :mod:`passlib.utils.handlers`.
* Various minor changes to
:class:`passlib.utils.handlers.HasManyBackends`;
diff --git a/LICENSE b/LICENSE
index 26d718b..87792f1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -60,7 +60,7 @@ which is available under the following license::
DES
---
-The source file ``passlib/utils/des.py`` contains code derived from
+The source file ``passlib/crypto/des.py`` contains code derived from
`UnixCrypt.java <http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_,
a pure-java implementation of the historic unix-crypt password hash algorithm.
It is available under the following license::
diff --git a/admin/benchmarks.py b/admin/benchmarks.py
index 1a01951..3a980c0 100644
--- a/admin/benchmarks.py
+++ b/admin/benchmarks.py
@@ -283,17 +283,17 @@ def test_sha1_crypt():
#=============================================================================
@benchmark.constructor()
def test_pbkdf2_sha1():
- from passlib.utils.pbkdf2 import pbkdf2
+ from passlib.crypto.digest import pbkdf2_hmac
def helper():
- result = hexlify(pbkdf2("abracadabra", "open sesame", 10240, 20, "hmac-sha1"))
+ result = hexlify(pbkdf2_hmac("sha1", "abracadabra", "open sesame", 10240, 20))
assert result == 'e45ce658e79b16107a418ad4634836f5f0601ad1', result
return helper
@benchmark.constructor()
def test_pbkdf2_sha256():
- from passlib.utils.pbkdf2 import pbkdf2
+ from passlib.crypto.digest import pbkdf2_hmac
def helper():
- result = hexlify(pbkdf2("abracadabra", "open sesame", 10240, 32, "hmac-sha256"))
+ result = hexlify(pbkdf2_hmac("sha256", "abracadabra", "open sesame", 10240, 32))
assert result == 'fadef97054306c93c55213cd57111d6c0791735dcdde8ac32f9f934b49c5af1e', result
return helper
diff --git a/docs/conf.py b/docs/conf.py
index 9ddc460..fbda296 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -33,10 +33,6 @@ options = os.environ.get("PASSLIB_DOCS", "").split(",")
# (https://bitbucket.org/ecollins/cloud_sptheme)
import cloud_sptheme as csp
-# hack to make autodoc generate documentation from the correct class...
-import passlib.utils.md4 as md4_mod
-md4_mod.md4 = md4_mod._builtin_md4
-
#=============================================================================
# General configuration
#=============================================================================
diff --git a/docs/lib/passlib.crypto.des.rst b/docs/lib/passlib.crypto.des.rst
new file mode 100644
index 0000000..89ad0bd
--- /dev/null
+++ b/docs/lib/passlib.crypto.des.rst
@@ -0,0 +1,26 @@
+==============================================
+:mod:`passlib.crypto.des` - DES routines
+==============================================
+
+.. module:: passlib.crypto.des
+ :synopsis: routines for performing DES encryption
+
+.. versionchanged:: 1.7
+
+ This module was relocated from :mod:`!passlib.utils.des`;
+ the old location will be removed in Passlib 2.0.
+
+.. warning::
+
+ NIST has declared DES to be "inadequate" for cryptographic purposes.
+ These routines, and the password hashes based on them,
+ should not be used in new applications.
+
+This module contains routines for encrypting blocks of data using the DES algorithm.
+Note that these functions do not support multi-block operation or decryption,
+since they are designed primarily for use in password hash algorithms
+(such as :class:`~passlib.hash.des_crypt` and :class:`~passlib.hash.bsdi_crypt`).
+
+.. autofunction:: expand_des_key
+.. autofunction:: des_encrypt_block
+.. autofunction:: des_encrypt_int_block
diff --git a/docs/lib/passlib.crypto.digest.rst b/docs/lib/passlib.crypto.digest.rst
new file mode 100644
index 0000000..fda5e80
--- /dev/null
+++ b/docs/lib/passlib.crypto.digest.rst
@@ -0,0 +1,39 @@
+=============================================================
+:mod:`passlib.crypto.digest` - Hash & Related Helpers
+=============================================================
+
+.. module:: passlib.crypto.digest
+ :synopsis: Internal cryptographic helpers
+
+.. versionadded:: 1.7
+
+This module provides various cryptographic support functions used by Passlib
+to implement the various password hashes it provides, as well as paper over
+some VM & version incompatibilities.
+
+Hash Functions
+==============
+.. autofunction:: norm_hash_name
+.. autofunction:: lookup_hash
+
+.. autoclass:: HashInfo()
+
+.. note::
+
+ :func:`!lookup_hash` supports all hashes available directly in :mod:`hashlib`,
+ as well as offered through :func:`hashlib.new`.
+ It will also fallback to passlib's builtin MD4 implementation if one is not natively available.
+
+HMAC Functions
+==============
+
+.. autofunction:: compile_hmac
+
+PKCS#5 Key Derivation Functions
+===============================
+.. autofunction:: pbkdf1
+.. autofunction:: pbkdf2_hmac
+
+.. note::
+
+ The details of PBKDF1 and PBKDF2 are specified in :rfc:`2898`.
diff --git a/docs/lib/passlib.hash.fshp.rst b/docs/lib/passlib.hash.fshp.rst
index 28efbde..1ebcc6f 100644
--- a/docs/lib/passlib.hash.fshp.rst
+++ b/docs/lib/passlib.hash.fshp.rst
@@ -77,7 +77,7 @@ A example hash (of ``password``) is:
``da8a6bcfad78c17da993b5940f6524d526d444012cf67ea8f44b7843c7ab2e82``
FSHP is basically just a wrapper around PBKDF1:
-The checksum is calculated using :func:`~passlib.utils.pbkdf2.pbkdf1`,
+The checksum is calculated using :func:`~passlib.crypto.digest.pbkdf1`,
passing in the password, the decoded salt string, the number of
rounds, and hash function specified by the variant identifier.
FSHP has one quirk in that the password is passed in as the pbkdf1 salt,
diff --git a/docs/lib/passlib.hash.grub_pbkdf2_sha512.rst b/docs/lib/passlib.hash.grub_pbkdf2_sha512.rst
index 891f2ab..686f98d 100644
--- a/docs/lib/passlib.hash.grub_pbkdf2_sha512.rst
+++ b/docs/lib/passlib.hash.grub_pbkdf2_sha512.rst
@@ -43,7 +43,7 @@ and :samp:`{checksum}` is the resulting 64-byte derived key, also
encoded in upper-case hexadecimal. It can be identified by the prefix ``grub.pdkdf2.sha512.``.
The algorithm used is the same as :class:`pbkdf2_sha1`: the password is encoded into UTF-8 if not already encoded,
-and passed through :func:`~passlib.utils.pbkdf2.pbkdf2`
+and passed through :func:`~passlib.crypto.digest.pbkdf1`
along with the decoded salt, and the number of rounds.
The result is then encoded into hexadecimal.
diff --git a/docs/lib/passlib.hash.msdcc2.rst b/docs/lib/passlib.hash.msdcc2.rst
index f89edf1..8a100e8 100644
--- a/docs/lib/passlib.hash.msdcc2.rst
+++ b/docs/lib/passlib.hash.msdcc2.rst
@@ -69,7 +69,7 @@ The digest is calculated as follows:
digest from step 2; and the MD4 digest of the result
is calculated (The result of this is identical to the
:class:`~passlib.hash.msdcc` digest).
-5. :func:`PBKDF2-HMAC-SHA1 <passlib.utils.pbkdf2.pbkdf2>` is then invoked,
+5. :func:`PBKDF2-HMAC-SHA1 <passlib.crypto.digest.pbkdf2_hmac>` is then invoked,
using the result of step 4 as the secret, the username from step 3 as
the salt, 10240 rounds, and resulting in a 16 byte digest.
6. The result of step 5 is encoded into hexadecimal;
diff --git a/docs/lib/passlib.hash.nthash.rst b/docs/lib/passlib.hash.nthash.rst
index eda18b0..92cea05 100644
--- a/docs/lib/passlib.hash.nthash.rst
+++ b/docs/lib/passlib.hash.nthash.rst
@@ -8,7 +8,7 @@
.. warning::
- This scheme is very weak, the :mod:`~passlib.utils.md4` digest
+ This scheme is very weak, the MD4 digest
it is based on has been severely compromised for many years.
It should be used for compatibility with existing systems;
**do not use** in new code.
@@ -45,7 +45,7 @@ A nthash consists of 32 hexadecimal digits, which encode the digest.
An example hash (of ``password``) is ``8846f7eaee8fb117ad06bdd830b7586c``.
The digest is calculated by encoding the secret using ``UTF-16-LE``,
-taking the :mod:`~passlib.utils.md4` digest, and then encoding
+taking the MD4 digest, and then encoding
that as hexadecimal.
FreeBSD Variant
diff --git a/docs/lib/passlib.hash.pbkdf2_digest.rst b/docs/lib/passlib.hash.pbkdf2_digest.rst
index c829edf..9a2ec95 100644
--- a/docs/lib/passlib.hash.pbkdf2_digest.rst
+++ b/docs/lib/passlib.hash.pbkdf2_digest.rst
@@ -96,7 +96,7 @@ follow the same format, :samp:`$pbkdf2-{digest}${rounds}${salt}${checksum}`.
The algorithm used by all of these schemes is deliberately identical and simple:
The password is encoded into UTF-8 if not already encoded,
-and run through :func:`~passlib.utils.pbkdf2.pbkdf2`
+and run through :func:`~passlib.crypto.digest.pbkdf2_hmac`
along with the decoded salt, the number of rounds,
and a prf built from HMAC + the respective message digest.
The result is then encoded using :func:`~passlib.utils.ab64_encode`.
diff --git a/docs/lib/passlib.hash.scram.rst b/docs/lib/passlib.hash.scram.rst
index 3adc8c4..bcd98d4 100644
--- a/docs/lib/passlib.hash.scram.rst
+++ b/docs/lib/passlib.hash.scram.rst
@@ -17,7 +17,7 @@ SCRAM.
To accomplish this, Passlib provides the following
:ref:`modular-crypt-format`-compatible password hash scheme which uses the
``$scram$`` identifier. This format encodes a salt, rounds settings, and one
-or more :func:`~passlib.utils.pbkdf2.pbkdf2` digests... one digest for each
+or more :func:`~passlib.crypto.digest.pbkdf2_hmac` digests... one digest for each
of the hash algorithms the server wishes to support over SCRAM.
Since this format is PBKDF2-based, it has equivalent security to
diff --git a/docs/lib/passlib.utils.des.rst b/docs/lib/passlib.utils.des.rst
index 67fef1e..cb40e17 100644
--- a/docs/lib/passlib.utils.des.rst
+++ b/docs/lib/passlib.utils.des.rst
@@ -1,15 +1,14 @@
-=============================================
-:mod:`passlib.utils.des` - DES routines
-=============================================
+====================================================
+:mod:`passlib.utils.des` - DES routines [deprecated]
+====================================================
.. module:: passlib.utils.des
:synopsis: routines for performing DES encryption
.. warning::
-
- NIST has declared DES to be "inadequate" for cryptographic purposes.
- These routines, and the password hashes based on them,
- should not be used in new applications.
+ This module is deprecated as of Passlib 1.7:
+ It has been relocated to :mod:`passlib.crypto.des`;
+ and the aliases here will be removed in Passlib 2.0.
This module contains routines for encrypting blocks of data using the DES algorithm.
Note that these functions do not support multi-block operation or decryption,
diff --git a/docs/lib/passlib.utils.md4.rst b/docs/lib/passlib.utils.md4.rst
deleted file mode 100644
index 87125b7..0000000
--- a/docs/lib/passlib.utils.md4.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-========================================================
-:mod:`passlib.utils.md4` - MD4 message digest algorithm
-========================================================
-
-.. module:: passlib.utils.md4
- :synopsis: MD4 message digest algorithm
-
-.. warning::
-
- This digest is considered **VERY INSECURE**,
- and not suitable for any new cryptographic activities.
- Trivial-cost real-world attacks exist for all
- password algorithms, stream ciphers, etc, that have
- been based on MD4.
- Do not use this hash or derived schemes unless you *really* have to.
-
-This module implements the MD4 hash algorithm in pure python,
-based on the `rfc 1320 <http://www.faqs.org/rfcs/rfc1320.html>`_ specification of MD4.
-
-.. autoclass:: md4
-
-.. note::
-
- If MD4 support is detected in :mod:`!hashlib`, the :class:`!md4` class in this module
- will be replaced by a function wrapping :mod:`!hashlib`'s implementation,
- which should be faster, but otherwise behave exactly the same.
diff --git a/docs/lib/passlib.utils.pbkdf2.rst b/docs/lib/passlib.utils.pbkdf2.rst
index 7fa9f98..cfc503a 100644
--- a/docs/lib/passlib.utils.pbkdf2.rst
+++ b/docs/lib/passlib.utils.pbkdf2.rst
@@ -1,10 +1,17 @@
-=============================================================
-:mod:`passlib.utils.pbkdf2` - PBKDF2 key derivation algorithm
-=============================================================
+==========================================================================
+:mod:`passlib.utils.pbkdf2` - PBKDF2 key derivation algorithm [deprecated]
+==========================================================================
.. module:: passlib.utils.pbkdf2
:synopsis: PBKDF2 and related key derivation algorithms
+.. warning::
+
+ This module has been deprecated as of Passlib 1.7,
+ and will be removed in Passlib 2.0.
+ The functions in this module have been replaced by equivalent
+ (but not identical) functions in the :mod:`passlib.crypto` module.
+
This module provides a couple of key derivation functions,
as well as supporting utilities.
Primarily, it offers :func:`pbkdf2`,
@@ -26,11 +33,5 @@ PKCS#5 Key Derivation Functions
Helper Functions
================
.. autofunction:: norm_hash_name
-.. autofunction:: get_hash_info
.. autofunction:: get_prf
-.. autofunction:: get_keyed_prf
-
-..
- given how this module is expanding in scope,
- perhaps it should be renamed "kdf" or "crypto"?
diff --git a/docs/lib/passlib.utils.rst b/docs/lib/passlib.utils.rst
index db2d3f3..5f26dea 100644
--- a/docs/lib/passlib.utils.rst
+++ b/docs/lib/passlib.utils.rst
@@ -184,6 +184,8 @@ There are also a few sub modules which provide additional utility functions:
passlib.utils.handlers
passlib.utils.des
passlib.utils.pbkdf2
+ passlib.crypto.digest
+ passlib.crypto.des
..
passlib.utils.compat
diff --git a/docs/password_hash_api.rst b/docs/password_hash_api.rst
index 8c04965..67e2a0b 100644
--- a/docs/password_hash_api.rst
+++ b/docs/password_hash_api.rst
@@ -722,7 +722,7 @@ and the following attributes should be defined:
Choosing the right rounds value
===============================
-For hash algorithms with a variable time-cost,
+For hash algorithms with a variable time-cost,
Passlib's :attr:`~PasswordHash.default_rounds` values attempt to be secure enough for
the average [#avgsys]_ system. But the "right" value for a given hash
is dependant on the server, its cpu, its expected load, and its users.
diff --git a/passlib/context.py b/passlib/context.py
index 3c05e62..86a69b4 100644
--- a/passlib/context.py
+++ b/passlib/context.py
@@ -1014,7 +1014,7 @@ class _CryptConfig(object):
#===================================================================
def _init_records(self):
# NOTE: this step handles final validation of settings,
- # checking for violatiions against handler's internal invariants.
+ # checking for violations against handler's internal invariants.
# this is why we create all the records now,
# so CryptContext throws error immediately rather than later.
self._record_lists = {}
diff --git a/passlib/crypto/__init__.py b/passlib/crypto/__init__.py
new file mode 100644
index 0000000..89f5484
--- /dev/null
+++ b/passlib/crypto/__init__.py
@@ -0,0 +1 @@
+"""passlib.crypto -- package containing cryptographic primitives used by passlib"""
diff --git a/passlib/crypto/_md4.py b/passlib/crypto/_md4.py
new file mode 100644
index 0000000..bdc211f
--- /dev/null
+++ b/passlib/crypto/_md4.py
@@ -0,0 +1,244 @@
+"""
+passlib.crypto._md4 -- fallback implementation of MD4
+
+Helper implementing insecure and obsolete md4 algorithm.
+used for NTHASH format, which is also insecure and broken,
+since it's just md4(password).
+
+Implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html
+
+.. note::
+
+ This shouldn't be imported directly, it's merely used conditionally
+ by ``passlib.crypto.lookup_hash()`` when a native implementation can't be found.
+"""
+
+#=============================================================================
+# imports
+#=============================================================================
+# core
+from binascii import hexlify
+import struct
+# site
+from passlib.utils.compat import bascii_to_str, irange, PY3
+# local
+__all__ = ["md4"]
+
+#=============================================================================
+# utils
+#=============================================================================
+def F(x,y,z):
+ return (x&y) | ((~x) & z)
+
+def G(x,y,z):
+ return (x&y) | (x&z) | (y&z)
+
+##def H(x,y,z):
+## return x ^ y ^ z
+
+MASK_32 = 2**32-1
+
+#=============================================================================
+# main class
+#=============================================================================
+class md4(object):
+ """pep-247 compatible implementation of MD4 hash algorithm
+
+ .. attribute:: digest_size
+
+ size of md4 digest in bytes (16 bytes)
+
+ .. method:: update
+
+ update digest by appending additional content
+
+ .. method:: copy
+
+ create clone of digest object, including current state
+
+ .. method:: digest
+
+ return bytes representing md4 digest of current content
+
+ .. method:: hexdigest
+
+ return hexadecimal version of digest
+ """
+ # FIXME: make this follow hash object PEP better.
+ # FIXME: this isn't threadsafe
+
+ name = "md4"
+ digest_size = digestsize = 16
+ block_size = 64
+
+ _count = 0 # number of 64-byte blocks processed so far (not including _buf)
+ _state = None # list of [a,b,c,d] 32 bit ints used as internal register
+ _buf = None # data processed in 64 byte blocks, this holds leftover from last update
+
+ def __init__(self, content=None):
+ self._count = 0
+ self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
+ self._buf = b''
+ if content:
+ self.update(content)
+
+ # round 1 table - [abcd k s]
+ _round1 = [
+ [0,1,2,3, 0,3],
+ [3,0,1,2, 1,7],
+ [2,3,0,1, 2,11],
+ [1,2,3,0, 3,19],
+
+ [0,1,2,3, 4,3],
+ [3,0,1,2, 5,7],
+ [2,3,0,1, 6,11],
+ [1,2,3,0, 7,19],
+
+ [0,1,2,3, 8,3],
+ [3,0,1,2, 9,7],
+ [2,3,0,1, 10,11],
+ [1,2,3,0, 11,19],
+
+ [0,1,2,3, 12,3],
+ [3,0,1,2, 13,7],
+ [2,3,0,1, 14,11],
+ [1,2,3,0, 15,19],
+ ]
+
+ # round 2 table - [abcd k s]
+ _round2 = [
+ [0,1,2,3, 0,3],
+ [3,0,1,2, 4,5],
+ [2,3,0,1, 8,9],
+ [1,2,3,0, 12,13],
+
+ [0,1,2,3, 1,3],
+ [3,0,1,2, 5,5],
+ [2,3,0,1, 9,9],
+ [1,2,3,0, 13,13],
+
+ [0,1,2,3, 2,3],
+ [3,0,1,2, 6,5],
+ [2,3,0,1, 10,9],
+ [1,2,3,0, 14,13],
+
+ [0,1,2,3, 3,3],
+ [3,0,1,2, 7,5],
+ [2,3,0,1, 11,9],
+ [1,2,3,0, 15,13],
+ ]
+
+ # round 3 table - [abcd k s]
+ _round3 = [
+ [0,1,2,3, 0,3],
+ [3,0,1,2, 8,9],
+ [2,3,0,1, 4,11],
+ [1,2,3,0, 12,15],
+
+ [0,1,2,3, 2,3],
+ [3,0,1,2, 10,9],
+ [2,3,0,1, 6,11],
+ [1,2,3,0, 14,15],
+
+ [0,1,2,3, 1,3],
+ [3,0,1,2, 9,9],
+ [2,3,0,1, 5,11],
+ [1,2,3,0, 13,15],
+
+ [0,1,2,3, 3,3],
+ [3,0,1,2, 11,9],
+ [2,3,0,1, 7,11],
+ [1,2,3,0, 15,15],
+ ]
+
+ 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 update(self, content):
+ if not isinstance(content, bytes):
+ if PY3:
+ raise TypeError("expected bytes")
+ else:
+ # replicate behavior of hashlib under py2
+ content = content.encode("ascii")
+ buf = self._buf
+ if buf:
+ content = buf + content
+ idx = 0
+ end = len(content)
+ while True:
+ next = idx + 64
+ if next <= end:
+ self._process(content[idx:next])
+ self._count += 1
+ idx = next
+ else:
+ self._buf = content[idx:]
+ return
+
+ def copy(self):
+ other = md4()
+ other._count = self._count
+ other._state = list(self._state)
+ other._buf = self._buf
+ return other
+
+ def digest(self):
+ # NOTE: backing up state so we can restore it after _process is called,
+ # in case object is updated again (this is only attr altered by this method)
+ orig = list(self._state)
+
+ # final block: buf + 0x80,
+ # then 0x00 padding until congruent w/ 56 mod 64 bytes
+ # then last 8 bytes = msg length in bits
+ buf = self._buf
+ msglen = self._count*512 + len(buf)*8
+ block = buf + b'\x80' + b'\x00' * ((119-len(buf)) % 64) + \
+ struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)
+ if len(block) == 128:
+ self._process(block[:64])
+ self._process(block[64:])
+ else:
+ assert len(block) == 64
+ self._process(block)
+
+ # render digest & restore un-finalized state
+ out = struct.pack("<4I", *self._state)
+ self._state = orig
+ return out
+
+ def hexdigest(self):
+ return bascii_to_str(hexlify(self.digest()))
+
+ #===================================================================
+ # eoc
+ #===================================================================
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/crypto/des.py b/passlib/crypto/des.py
new file mode 100644
index 0000000..3f87aef
--- /dev/null
+++ b/passlib/crypto/des.py
@@ -0,0 +1,848 @@
+"""passlib.crypto.des -- DES block encryption routines
+
+History
+=======
+These routines (which have since been drastically modified for python)
+are based on a Java implementation of the des-crypt algorithm,
+found at `<http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_.
+
+The copyright & license for that source is as follows::
+
+ UnixCrypt.java 0.9 96/11/25
+ Copyright (c) 1996 Aki Yoshida. All rights reserved.
+ Permission to use, copy, modify and distribute this software
+ for non-commercial or commercial purposes and without fee is
+ hereby granted provided that this copyright notice appears in
+ all copies.
+
+ ---
+
+ Unix crypt(3C) utility
+ @version 0.9, 11/25/96
+ @author Aki Yoshida
+
+ ---
+
+ modified April 2001
+ by Iris Van den Broeke, Daniel Deville
+
+ ---
+ Unix Crypt.
+ Implements the one way cryptography used by Unix systems for
+ simple password protection.
+ @version $Id: UnixCrypt2.txt,v 1.1.1.1 2005/09/13 22:20:13 christos Exp $
+ @author Greg Wilkins (gregw)
+
+The netbsd des-crypt implementation has some nice notes on how this all works -
+ http://fxr.googlebit.com/source/lib/libcrypt/crypt.c?v=NETBSD-CURRENT
+"""
+
+# TODO: could use an accelerated C version of this module to speed up lmhash,
+# des-crypt, and ext-des-crypt
+
+#=============================================================================
+# imports
+#=============================================================================
+# core
+import struct
+# pkg
+from passlib import exc
+from passlib.utils.compat import join_byte_values, byte_elem_value, \
+ irange, irange, int_types
+# local
+__all__ = [
+ "expand_des_key",
+ "des_encrypt_block",
+]
+
+#=============================================================================
+# constants
+#=============================================================================
+
+# masks/upper limits for various integer sizes
+INT_24_MASK = 0xffffff
+INT_56_MASK = 0xffffffffffffff
+INT_64_MASK = 0xffffffffffffffff
+
+# mask to clear parity bits from 64-bit key
+_KDATA_MASK = 0xfefefefefefefefe
+_KPARITY_MASK = 0x0101010101010101
+
+# mask used to setup key schedule
+_KS_MASK = 0xfcfcfcfcffffffff
+
+#=============================================================================
+# static DES tables
+#=============================================================================
+
+# placeholders filled in by _load_tables()
+PCXROT = IE3264 = SPE = CF6464 = None
+
+def _load_tables():
+ """delay loading tables until they are actually needed"""
+ global PCXROT, IE3264, SPE, CF6464
+
+ #---------------------------------------------------------------
+ # Initial key schedule permutation
+ # PC1ROT - bit reverse, then PC1, then Rotate, then PC2
+ #---------------------------------------------------------------
+ # NOTE: this was reordered from original table to make perm3264 logic simpler
+ PC1ROT=(
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000002000, 0x0000000000002000,
+ 0x0000000000000020, 0x0000000000000020, 0x0000000000002020, 0x0000000000002020,
+ 0x0000000000000400, 0x0000000000000400, 0x0000000000002400, 0x0000000000002400,
+ 0x0000000000000420, 0x0000000000000420, 0x0000000000002420, 0x0000000000002420, ),
+ ( 0x0000000000000000, 0x2000000000000000, 0x0000000400000000, 0x2000000400000000,
+ 0x0000800000000000, 0x2000800000000000, 0x0000800400000000, 0x2000800400000000,
+ 0x0008000000000000, 0x2008000000000000, 0x0008000400000000, 0x2008000400000000,
+ 0x0008800000000000, 0x2008800000000000, 0x0008800400000000, 0x2008800400000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000040, 0x0000000000000040,
+ 0x0000000020000000, 0x0000000020000000, 0x0000000020000040, 0x0000000020000040,
+ 0x0000000000200000, 0x0000000000200000, 0x0000000000200040, 0x0000000000200040,
+ 0x0000000020200000, 0x0000000020200000, 0x0000000020200040, 0x0000000020200040, ),
+ ( 0x0000000000000000, 0x0002000000000000, 0x0800000000000000, 0x0802000000000000,
+ 0x0100000000000000, 0x0102000000000000, 0x0900000000000000, 0x0902000000000000,
+ 0x4000000000000000, 0x4002000000000000, 0x4800000000000000, 0x4802000000000000,
+ 0x4100000000000000, 0x4102000000000000, 0x4900000000000000, 0x4902000000000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000040000, 0x0000000000040000,
+ 0x0000020000000000, 0x0000020000000000, 0x0000020000040000, 0x0000020000040000,
+ 0x0000000000000004, 0x0000000000000004, 0x0000000000040004, 0x0000000000040004,
+ 0x0000020000000004, 0x0000020000000004, 0x0000020000040004, 0x0000020000040004, ),
+ ( 0x0000000000000000, 0x0000400000000000, 0x0200000000000000, 0x0200400000000000,
+ 0x0080000000000000, 0x0080400000000000, 0x0280000000000000, 0x0280400000000000,
+ 0x0000008000000000, 0x0000408000000000, 0x0200008000000000, 0x0200408000000000,
+ 0x0080008000000000, 0x0080408000000000, 0x0280008000000000, 0x0280408000000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000010000000, 0x0000000010000000,
+ 0x0000000000001000, 0x0000000000001000, 0x0000000010001000, 0x0000000010001000,
+ 0x0000000040000000, 0x0000000040000000, 0x0000000050000000, 0x0000000050000000,
+ 0x0000000040001000, 0x0000000040001000, 0x0000000050001000, 0x0000000050001000, ),
+ ( 0x0000000000000000, 0x0000001000000000, 0x0000080000000000, 0x0000081000000000,
+ 0x1000000000000000, 0x1000001000000000, 0x1000080000000000, 0x1000081000000000,
+ 0x0004000000000000, 0x0004001000000000, 0x0004080000000000, 0x0004081000000000,
+ 0x1004000000000000, 0x1004001000000000, 0x1004080000000000, 0x1004081000000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000080, 0x0000000000000080,
+ 0x0000000000080000, 0x0000000000080000, 0x0000000000080080, 0x0000000000080080,
+ 0x0000000000800000, 0x0000000000800000, 0x0000000000800080, 0x0000000000800080,
+ 0x0000000000880000, 0x0000000000880000, 0x0000000000880080, 0x0000000000880080, ),
+ ( 0x0000000000000000, 0x0000000008000000, 0x0000002000000000, 0x0000002008000000,
+ 0x0000100000000000, 0x0000100008000000, 0x0000102000000000, 0x0000102008000000,
+ 0x0000200000000000, 0x0000200008000000, 0x0000202000000000, 0x0000202008000000,
+ 0x0000300000000000, 0x0000300008000000, 0x0000302000000000, 0x0000302008000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000400000, 0x0000000000400000,
+ 0x0000000004000000, 0x0000000004000000, 0x0000000004400000, 0x0000000004400000,
+ 0x0000000000000800, 0x0000000000000800, 0x0000000000400800, 0x0000000000400800,
+ 0x0000000004000800, 0x0000000004000800, 0x0000000004400800, 0x0000000004400800, ),
+ ( 0x0000000000000000, 0x0000000000008000, 0x0040000000000000, 0x0040000000008000,
+ 0x0000004000000000, 0x0000004000008000, 0x0040004000000000, 0x0040004000008000,
+ 0x8000000000000000, 0x8000000000008000, 0x8040000000000000, 0x8040000000008000,
+ 0x8000004000000000, 0x8000004000008000, 0x8040004000000000, 0x8040004000008000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000004000, 0x0000000000004000,
+ 0x0000000000000008, 0x0000000000000008, 0x0000000000004008, 0x0000000000004008,
+ 0x0000000000000010, 0x0000000000000010, 0x0000000000004010, 0x0000000000004010,
+ 0x0000000000000018, 0x0000000000000018, 0x0000000000004018, 0x0000000000004018, ),
+ ( 0x0000000000000000, 0x0000000200000000, 0x0001000000000000, 0x0001000200000000,
+ 0x0400000000000000, 0x0400000200000000, 0x0401000000000000, 0x0401000200000000,
+ 0x0020000000000000, 0x0020000200000000, 0x0021000000000000, 0x0021000200000000,
+ 0x0420000000000000, 0x0420000200000000, 0x0421000000000000, 0x0421000200000000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000010000000000, 0x0000010000000000,
+ 0x0000000100000000, 0x0000000100000000, 0x0000010100000000, 0x0000010100000000,
+ 0x0000000000100000, 0x0000000000100000, 0x0000010000100000, 0x0000010000100000,
+ 0x0000000100100000, 0x0000000100100000, 0x0000010100100000, 0x0000010100100000, ),
+ ( 0x0000000000000000, 0x0000000080000000, 0x0000040000000000, 0x0000040080000000,
+ 0x0010000000000000, 0x0010000080000000, 0x0010040000000000, 0x0010040080000000,
+ 0x0000000800000000, 0x0000000880000000, 0x0000040800000000, 0x0000040880000000,
+ 0x0010000800000000, 0x0010000880000000, 0x0010040800000000, 0x0010040880000000, ),
+ )
+ #---------------------------------------------------------------
+ # Subsequent key schedule rotation permutations
+ # PC2ROT - PC2 inverse, then Rotate, then PC2
+ #---------------------------------------------------------------
+ # NOTE: this was reordered from original table to make perm3264 logic simpler
+ PC2ROTA=(
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, 0x0000000000200000,
+ 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, 0x0000000004000000,
+ 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, ),
+ ( 0x0000000000000000, 0x0000000000000800, 0x0000010000000000, 0x0000010000000800,
+ 0x0000000000002000, 0x0000000000002800, 0x0000010000002000, 0x0000010000002800,
+ 0x0000000010000000, 0x0000000010000800, 0x0000010010000000, 0x0000010010000800,
+ 0x0000000010002000, 0x0000000010002800, 0x0000010010002000, 0x0000010010002800, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000,
+ 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, 0x0000000000800000,
+ 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, ),
+ ( 0x0000000000000000, 0x0000020000000000, 0x0000000080000000, 0x0000020080000000,
+ 0x0000000000400000, 0x0000020000400000, 0x0000000080400000, 0x0000020080400000,
+ 0x0000000008000000, 0x0000020008000000, 0x0000000088000000, 0x0000020088000000,
+ 0x0000000008400000, 0x0000020008400000, 0x0000000088400000, 0x0000020088400000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, 0x0000000000000040,
+ 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000,
+ 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, ),
+ ( 0x0000000000000000, 0x0000000000000010, 0x0000000000000400, 0x0000000000000410,
+ 0x0000000000000080, 0x0000000000000090, 0x0000000000000480, 0x0000000000000490,
+ 0x0000000040000000, 0x0000000040000010, 0x0000000040000400, 0x0000000040000410,
+ 0x0000000040000080, 0x0000000040000090, 0x0000000040000480, 0x0000000040000490, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000,
+ 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, 0x0000000000100000,
+ 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, ),
+ ( 0x0000000000000000, 0x0000000000040000, 0x0000000000000020, 0x0000000000040020,
+ 0x0000000000000004, 0x0000000000040004, 0x0000000000000024, 0x0000000000040024,
+ 0x0000000200000000, 0x0000000200040000, 0x0000000200000020, 0x0000000200040020,
+ 0x0000000200000004, 0x0000000200040004, 0x0000000200000024, 0x0000000200040024, ),
+ ( 0x0000000000000000, 0x0000000000000008, 0x0000000000008000, 0x0000000000008008,
+ 0x0010000000000000, 0x0010000000000008, 0x0010000000008000, 0x0010000000008008,
+ 0x0020000000000000, 0x0020000000000008, 0x0020000000008000, 0x0020000000008008,
+ 0x0030000000000000, 0x0030000000000008, 0x0030000000008000, 0x0030000000008008, ),
+ ( 0x0000000000000000, 0x0000400000000000, 0x0000080000000000, 0x0000480000000000,
+ 0x0000100000000000, 0x0000500000000000, 0x0000180000000000, 0x0000580000000000,
+ 0x4000000000000000, 0x4000400000000000, 0x4000080000000000, 0x4000480000000000,
+ 0x4000100000000000, 0x4000500000000000, 0x4000180000000000, 0x4000580000000000, ),
+ ( 0x0000000000000000, 0x0000000000004000, 0x0000000020000000, 0x0000000020004000,
+ 0x0001000000000000, 0x0001000000004000, 0x0001000020000000, 0x0001000020004000,
+ 0x0200000000000000, 0x0200000000004000, 0x0200000020000000, 0x0200000020004000,
+ 0x0201000000000000, 0x0201000000004000, 0x0201000020000000, 0x0201000020004000, ),
+ ( 0x0000000000000000, 0x1000000000000000, 0x0004000000000000, 0x1004000000000000,
+ 0x0002000000000000, 0x1002000000000000, 0x0006000000000000, 0x1006000000000000,
+ 0x0000000800000000, 0x1000000800000000, 0x0004000800000000, 0x1004000800000000,
+ 0x0002000800000000, 0x1002000800000000, 0x0006000800000000, 0x1006000800000000, ),
+ ( 0x0000000000000000, 0x0040000000000000, 0x2000000000000000, 0x2040000000000000,
+ 0x0000008000000000, 0x0040008000000000, 0x2000008000000000, 0x2040008000000000,
+ 0x0000001000000000, 0x0040001000000000, 0x2000001000000000, 0x2040001000000000,
+ 0x0000009000000000, 0x0040009000000000, 0x2000009000000000, 0x2040009000000000, ),
+ ( 0x0000000000000000, 0x0400000000000000, 0x8000000000000000, 0x8400000000000000,
+ 0x0000002000000000, 0x0400002000000000, 0x8000002000000000, 0x8400002000000000,
+ 0x0100000000000000, 0x0500000000000000, 0x8100000000000000, 0x8500000000000000,
+ 0x0100002000000000, 0x0500002000000000, 0x8100002000000000, 0x8500002000000000, ),
+ ( 0x0000000000000000, 0x0000800000000000, 0x0800000000000000, 0x0800800000000000,
+ 0x0000004000000000, 0x0000804000000000, 0x0800004000000000, 0x0800804000000000,
+ 0x0000000400000000, 0x0000800400000000, 0x0800000400000000, 0x0800800400000000,
+ 0x0000004400000000, 0x0000804400000000, 0x0800004400000000, 0x0800804400000000, ),
+ ( 0x0000000000000000, 0x0080000000000000, 0x0000040000000000, 0x0080040000000000,
+ 0x0008000000000000, 0x0088000000000000, 0x0008040000000000, 0x0088040000000000,
+ 0x0000200000000000, 0x0080200000000000, 0x0000240000000000, 0x0080240000000000,
+ 0x0008200000000000, 0x0088200000000000, 0x0008240000000000, 0x0088240000000000, ),
+ )
+
+ # NOTE: this was reordered from original table to make perm3264 logic simpler
+ PC2ROTB=(
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, 0x0000000000000400,
+ 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000,
+ 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, ),
+ ( 0x0000000000000000, 0x0000000000800000, 0x0000000000004000, 0x0000000000804000,
+ 0x0000000080000000, 0x0000000080800000, 0x0000000080004000, 0x0000000080804000,
+ 0x0000000000040000, 0x0000000000840000, 0x0000000000044000, 0x0000000000844000,
+ 0x0000000080040000, 0x0000000080840000, 0x0000000080044000, 0x0000000080844000, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, 0x0000000000000008,
+ 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, 0x0000000040000000,
+ 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, ),
+ ( 0x0000000000000000, 0x0000000020000000, 0x0000000200000000, 0x0000000220000000,
+ 0x0000000000000080, 0x0000000020000080, 0x0000000200000080, 0x0000000220000080,
+ 0x0000000000100000, 0x0000000020100000, 0x0000000200100000, 0x0000000220100000,
+ 0x0000000000100080, 0x0000000020100080, 0x0000000200100080, 0x0000000220100080, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, 0x0000000000002000,
+ 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, 0x0000020000000000,
+ 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, ),
+ ( 0x0000000000000000, 0x0000000000000800, 0x0000000100000000, 0x0000000100000800,
+ 0x0000000010000000, 0x0000000010000800, 0x0000000110000000, 0x0000000110000800,
+ 0x0000000000000004, 0x0000000000000804, 0x0000000100000004, 0x0000000100000804,
+ 0x0000000010000004, 0x0000000010000804, 0x0000000110000004, 0x0000000110000804, ),
+ ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000,
+ 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, 0x0000000000000010,
+ 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, ),
+ ( 0x0000000000000000, 0x0000000000000040, 0x0000010000000000, 0x0000010000000040,
+ 0x0000000000200000, 0x0000000000200040, 0x0000010000200000, 0x0000010000200040,
+ 0x0000000000008000, 0x0000000000008040, 0x0000010000008000, 0x0000010000008040,
+ 0x0000000000208000, 0x0000000000208040, 0x0000010000208000, 0x0000010000208040, ),
+ ( 0x0000000000000000, 0x0000000004000000, 0x0000000008000000, 0x000000000c000000,
+ 0x0400000000000000, 0x0400000004000000, 0x0400000008000000, 0x040000000c000000,
+ 0x8000000000000000, 0x8000000004000000, 0x8000000008000000, 0x800000000c000000,
+ 0x8400000000000000, 0x8400000004000000, 0x8400000008000000, 0x840000000c000000, ),
+ ( 0x0000000000000000, 0x0002000000000000, 0x0200000000000000, 0x0202000000000000,
+ 0x1000000000000000, 0x1002000000000000, 0x1200000000000000, 0x1202000000000000,
+ 0x0008000000000000, 0x000a000000000000, 0x0208000000000000, 0x020a000000000000,
+ 0x1008000000000000, 0x100a000000000000, 0x1208000000000000, 0x120a000000000000, ),
+ ( 0x0000000000000000, 0x0000000000400000, 0x0000000000000020, 0x0000000000400020,
+ 0x0040000000000000, 0x0040000000400000, 0x0040000000000020, 0x0040000000400020,
+ 0x0800000000000000, 0x0800000000400000, 0x0800000000000020, 0x0800000000400020,
+ 0x0840000000000000, 0x0840000000400000, 0x0840000000000020, 0x0840000000400020, ),
+ ( 0x0000000000000000, 0x0080000000000000, 0x0000008000000000, 0x0080008000000000,
+ 0x2000000000000000, 0x2080000000000000, 0x2000008000000000, 0x2080008000000000,
+ 0x0020000000000000, 0x00a0000000000000, 0x0020008000000000, 0x00a0008000000000,
+ 0x2020000000000000, 0x20a0000000000000, 0x2020008000000000, 0x20a0008000000000, ),
+ ( 0x0000000000000000, 0x0000002000000000, 0x0000040000000000, 0x0000042000000000,
+ 0x4000000000000000, 0x4000002000000000, 0x4000040000000000, 0x4000042000000000,
+ 0x0000400000000000, 0x0000402000000000, 0x0000440000000000, 0x0000442000000000,
+ 0x4000400000000000, 0x4000402000000000, 0x4000440000000000, 0x4000442000000000, ),
+ ( 0x0000000000000000, 0x0000004000000000, 0x0000200000000000, 0x0000204000000000,
+ 0x0000080000000000, 0x0000084000000000, 0x0000280000000000, 0x0000284000000000,
+ 0x0000800000000000, 0x0000804000000000, 0x0000a00000000000, 0x0000a04000000000,
+ 0x0000880000000000, 0x0000884000000000, 0x0000a80000000000, 0x0000a84000000000, ),
+ ( 0x0000000000000000, 0x0000000800000000, 0x0000000400000000, 0x0000000c00000000,
+ 0x0000100000000000, 0x0000100800000000, 0x0000100400000000, 0x0000100c00000000,
+ 0x0010000000000000, 0x0010000800000000, 0x0010000400000000, 0x0010000c00000000,
+ 0x0010100000000000, 0x0010100800000000, 0x0010100400000000, 0x0010100c00000000, ),
+ ( 0x0000000000000000, 0x0100000000000000, 0x0001000000000000, 0x0101000000000000,
+ 0x0000001000000000, 0x0100001000000000, 0x0001001000000000, 0x0101001000000000,
+ 0x0004000000000000, 0x0104000000000000, 0x0005000000000000, 0x0105000000000000,
+ 0x0004001000000000, 0x0104001000000000, 0x0005001000000000, 0x0105001000000000, ),
+ )
+ #---------------------------------------------------------------
+ # PCXROT - PC1ROT, PC2ROTA, PC2ROTB listed in order
+ # of the PC1 rotation schedule, as used by des_setkey
+ #---------------------------------------------------------------
+ ##ROTATES = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1)
+ ##PCXROT = (
+ ## PC1ROT, PC2ROTA, PC2ROTB, PC2ROTB,
+ ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTB,
+ ## PC2ROTA, PC2ROTB, PC2ROTB, PC2ROTB,
+ ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTA,
+ ## )
+
+ # NOTE: modified PCXROT to contain entrys broken into pairs,
+ # to help generate them in format best used by encoder.
+ PCXROT = (
+ (PC1ROT, PC2ROTA), (PC2ROTB, PC2ROTB),
+ (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTB),
+ (PC2ROTA, PC2ROTB), (PC2ROTB, PC2ROTB),
+ (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTA),
+ )
+
+ #---------------------------------------------------------------
+ # Bit reverse, intial permupation, expantion
+ # Initial permutation/expansion table
+ #---------------------------------------------------------------
+ # NOTE: this was reordered from original table to make perm3264 logic simpler
+ IE3264=(
+ ( 0x0000000000000000, 0x0000000000800800, 0x0000000000008008, 0x0000000000808808,
+ 0x0000008008000000, 0x0000008008800800, 0x0000008008008008, 0x0000008008808808,
+ 0x0000000080080000, 0x0000000080880800, 0x0000000080088008, 0x0000000080888808,
+ 0x0000008088080000, 0x0000008088880800, 0x0000008088088008, 0x0000008088888808, ),
+ ( 0x0000000000000000, 0x0080080000000000, 0x0000800800000000, 0x0080880800000000,
+ 0x0800000000000080, 0x0880080000000080, 0x0800800800000080, 0x0880880800000080,
+ 0x8008000000000000, 0x8088080000000000, 0x8008800800000000, 0x8088880800000000,
+ 0x8808000000000080, 0x8888080000000080, 0x8808800800000080, 0x8888880800000080, ),
+ ( 0x0000000000000000, 0x0000000000001000, 0x0000000000000010, 0x0000000000001010,
+ 0x0000000010000000, 0x0000000010001000, 0x0000000010000010, 0x0000000010001010,
+ 0x0000000000100000, 0x0000000000101000, 0x0000000000100010, 0x0000000000101010,
+ 0x0000000010100000, 0x0000000010101000, 0x0000000010100010, 0x0000000010101010, ),
+ ( 0x0000000000000000, 0x0000100000000000, 0x0000001000000000, 0x0000101000000000,
+ 0x1000000000000000, 0x1000100000000000, 0x1000001000000000, 0x1000101000000000,
+ 0x0010000000000000, 0x0010100000000000, 0x0010001000000000, 0x0010101000000000,
+ 0x1010000000000000, 0x1010100000000000, 0x1010001000000000, 0x1010101000000000, ),
+ ( 0x0000000000000000, 0x0000000000002000, 0x0000000000000020, 0x0000000000002020,
+ 0x0000000020000000, 0x0000000020002000, 0x0000000020000020, 0x0000000020002020,
+ 0x0000000000200000, 0x0000000000202000, 0x0000000000200020, 0x0000000000202020,
+ 0x0000000020200000, 0x0000000020202000, 0x0000000020200020, 0x0000000020202020, ),
+ ( 0x0000000000000000, 0x0000200000000000, 0x0000002000000000, 0x0000202000000000,
+ 0x2000000000000000, 0x2000200000000000, 0x2000002000000000, 0x2000202000000000,
+ 0x0020000000000000, 0x0020200000000000, 0x0020002000000000, 0x0020202000000000,
+ 0x2020000000000000, 0x2020200000000000, 0x2020002000000000, 0x2020202000000000, ),
+ ( 0x0000000000000000, 0x0000000000004004, 0x0400000000000040, 0x0400000000004044,
+ 0x0000000040040000, 0x0000000040044004, 0x0400000040040040, 0x0400000040044044,
+ 0x0000000000400400, 0x0000000000404404, 0x0400000000400440, 0x0400000000404444,
+ 0x0000000040440400, 0x0000000040444404, 0x0400000040440440, 0x0400000040444444, ),
+ ( 0x0000000000000000, 0x0000400400000000, 0x0000004004000000, 0x0000404404000000,
+ 0x4004000000000000, 0x4004400400000000, 0x4004004004000000, 0x4004404404000000,
+ 0x0040040000000000, 0x0040440400000000, 0x0040044004000000, 0x0040444404000000,
+ 0x4044040000000000, 0x4044440400000000, 0x4044044004000000, 0x4044444404000000, ),
+ )
+
+ #---------------------------------------------------------------
+ # Table that combines the S, P, and E operations.
+ #---------------------------------------------------------------
+ SPE=(
+ ( 0x0080088008200000, 0x0000008008000000, 0x0000000000200020, 0x0080088008200020,
+ 0x0000000000200000, 0x0080088008000020, 0x0000008008000020, 0x0000000000200020,
+ 0x0080088008000020, 0x0080088008200000, 0x0000008008200000, 0x0080080000000020,
+ 0x0080080000200020, 0x0000000000200000, 0x0000000000000000, 0x0000008008000020,
+ 0x0000008008000000, 0x0000000000000020, 0x0080080000200000, 0x0080088008000000,
+ 0x0080088008200020, 0x0000008008200000, 0x0080080000000020, 0x0080080000200000,
+ 0x0000000000000020, 0x0080080000000000, 0x0080088008000000, 0x0000008008200020,
+ 0x0080080000000000, 0x0080080000200020, 0x0000008008200020, 0x0000000000000000,
+ 0x0000000000000000, 0x0080088008200020, 0x0080080000200000, 0x0000008008000020,
+ 0x0080088008200000, 0x0000008008000000, 0x0080080000000020, 0x0080080000200000,
+ 0x0000008008200020, 0x0080080000000000, 0x0080088008000000, 0x0000000000200020,
+ 0x0080088008000020, 0x0000000000000020, 0x0000000000200020, 0x0000008008200000,
+ 0x0080088008200020, 0x0080088008000000, 0x0000008008200000, 0x0080080000200020,
+ 0x0000000000200000, 0x0080080000000020, 0x0000008008000020, 0x0000000000000000,
+ 0x0000008008000000, 0x0000000000200000, 0x0080080000200020, 0x0080088008200000,
+ 0x0000000000000020, 0x0000008008200020, 0x0080080000000000, 0x0080088008000020, ),
+ ( 0x1000800810004004, 0x0000000000000000, 0x0000800810000000, 0x0000000010004004,
+ 0x1000000000004004, 0x1000800800000000, 0x0000800800004004, 0x0000800810000000,
+ 0x0000800800000000, 0x1000000010004004, 0x1000000000000000, 0x0000800800004004,
+ 0x1000000010000000, 0x0000800810004004, 0x0000000010004004, 0x1000000000000000,
+ 0x0000000010000000, 0x1000800800004004, 0x1000000010004004, 0x0000800800000000,
+ 0x1000800810000000, 0x0000000000004004, 0x0000000000000000, 0x1000000010000000,
+ 0x1000800800004004, 0x1000800810000000, 0x0000800810004004, 0x1000000000004004,
+ 0x0000000000004004, 0x0000000010000000, 0x1000800800000000, 0x1000800810004004,
+ 0x1000000010000000, 0x0000800810004004, 0x0000800800004004, 0x1000800810000000,
+ 0x1000800810004004, 0x1000000010000000, 0x1000000000004004, 0x0000000000000000,
+ 0x0000000000004004, 0x1000800800000000, 0x0000000010000000, 0x1000000010004004,
+ 0x0000800800000000, 0x0000000000004004, 0x1000800810000000, 0x1000800800004004,
+ 0x0000800810004004, 0x0000800800000000, 0x0000000000000000, 0x1000000000004004,
+ 0x1000000000000000, 0x1000800810004004, 0x0000800810000000, 0x0000000010004004,
+ 0x1000000010004004, 0x0000000010000000, 0x1000800800000000, 0x0000800800004004,
+ 0x1000800800004004, 0x1000000000000000, 0x0000000010004004, 0x0000800810000000, ),
+ ( 0x0000000000400410, 0x0010004004400400, 0x0010000000000000, 0x0010000000400410,
+ 0x0000004004000010, 0x0000000000400400, 0x0010000000400410, 0x0010004004000000,
+ 0x0010000000400400, 0x0000004004000000, 0x0000004004400400, 0x0000000000000010,
+ 0x0010004004400410, 0x0010000000000010, 0x0000000000000010, 0x0000004004400410,
+ 0x0000000000000000, 0x0000004004000010, 0x0010004004400400, 0x0010000000000000,
+ 0x0010000000000010, 0x0010004004400410, 0x0000004004000000, 0x0000000000400410,
+ 0x0000004004400410, 0x0010000000400400, 0x0010004004000010, 0x0000004004400400,
+ 0x0010004004000000, 0x0000000000000000, 0x0000000000400400, 0x0010004004000010,
+ 0x0010004004400400, 0x0010000000000000, 0x0000000000000010, 0x0000004004000000,
+ 0x0010000000000010, 0x0000004004000010, 0x0000004004400400, 0x0010000000400410,
+ 0x0000000000000000, 0x0010004004400400, 0x0010004004000000, 0x0000004004400410,
+ 0x0000004004000010, 0x0000000000400400, 0x0010004004400410, 0x0000000000000010,
+ 0x0010004004000010, 0x0000000000400410, 0x0000000000400400, 0x0010004004400410,
+ 0x0000004004000000, 0x0010000000400400, 0x0010000000400410, 0x0010004004000000,
+ 0x0010000000400400, 0x0000000000000000, 0x0000004004400410, 0x0010000000000010,
+ 0x0000000000400410, 0x0010004004000010, 0x0010000000000000, 0x0000004004400400, ),
+ ( 0x0800100040040080, 0x0000100000001000, 0x0800000000000080, 0x0800100040041080,
+ 0x0000000000000000, 0x0000000040041000, 0x0800100000001080, 0x0800000040040080,
+ 0x0000100040041000, 0x0800000000001080, 0x0000000000001000, 0x0800100000000080,
+ 0x0800000000001080, 0x0800100040040080, 0x0000000040040000, 0x0000000000001000,
+ 0x0800000040041080, 0x0000100040040000, 0x0000100000000000, 0x0800000000000080,
+ 0x0000100040040000, 0x0800100000001080, 0x0000000040041000, 0x0000100000000000,
+ 0x0800100000000080, 0x0000000000000000, 0x0800000040040080, 0x0000100040041000,
+ 0x0000100000001000, 0x0800000040041080, 0x0800100040041080, 0x0000000040040000,
+ 0x0800000040041080, 0x0800100000000080, 0x0000000040040000, 0x0800000000001080,
+ 0x0000100040040000, 0x0000100000001000, 0x0800000000000080, 0x0000000040041000,
+ 0x0800100000001080, 0x0000000000000000, 0x0000100000000000, 0x0800000040040080,
+ 0x0000000000000000, 0x0800000040041080, 0x0000100040041000, 0x0000100000000000,
+ 0x0000000000001000, 0x0800100040041080, 0x0800100040040080, 0x0000000040040000,
+ 0x0800100040041080, 0x0800000000000080, 0x0000100000001000, 0x0800100040040080,
+ 0x0800000040040080, 0x0000100040040000, 0x0000000040041000, 0x0800100000001080,
+ 0x0800100000000080, 0x0000000000001000, 0x0800000000001080, 0x0000100040041000, ),
+ ( 0x0000000000800800, 0x0000001000000000, 0x0040040000000000, 0x2040041000800800,
+ 0x2000001000800800, 0x0040040000800800, 0x2040041000000000, 0x0000001000800800,
+ 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040041000000000,
+ 0x2040040000800800, 0x2000001000800800, 0x0040041000800800, 0x0000000000000000,
+ 0x0040041000000000, 0x0000000000800800, 0x2000001000000000, 0x2040040000000000,
+ 0x0040040000800800, 0x2040041000000000, 0x0000000000000000, 0x2000000000800800,
+ 0x2000000000000000, 0x2040040000800800, 0x2040041000800800, 0x2000001000000000,
+ 0x0000001000800800, 0x0040040000000000, 0x2040040000000000, 0x0040041000800800,
+ 0x0040041000800800, 0x2040040000800800, 0x2000001000000000, 0x0000001000800800,
+ 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040040000800800,
+ 0x0000000000800800, 0x0040041000000000, 0x2040041000800800, 0x0000000000000000,
+ 0x2040041000000000, 0x0000000000800800, 0x0040040000000000, 0x2000001000000000,
+ 0x2040040000800800, 0x0040040000000000, 0x0000000000000000, 0x2040041000800800,
+ 0x2000001000800800, 0x0040041000800800, 0x2040040000000000, 0x0000001000000000,
+ 0x0040041000000000, 0x2000001000800800, 0x0040040000800800, 0x2040040000000000,
+ 0x2000000000000000, 0x2040041000000000, 0x0000001000800800, 0x2000000000800800, ),
+ ( 0x4004000000008008, 0x4004000020000000, 0x0000000000000000, 0x0000200020008008,
+ 0x4004000020000000, 0x0000200000000000, 0x4004200000008008, 0x0000000020000000,
+ 0x4004200000000000, 0x4004200020008008, 0x0000200020000000, 0x0000000000008008,
+ 0x0000200000008008, 0x4004000000008008, 0x0000000020008008, 0x4004200020000000,
+ 0x0000000020000000, 0x4004200000008008, 0x4004000020008008, 0x0000000000000000,
+ 0x0000200000000000, 0x4004000000000000, 0x0000200020008008, 0x4004000020008008,
+ 0x4004200020008008, 0x0000000020008008, 0x0000000000008008, 0x4004200000000000,
+ 0x4004000000000000, 0x0000200020000000, 0x4004200020000000, 0x0000200000008008,
+ 0x4004200000000000, 0x0000000000008008, 0x0000200000008008, 0x4004200020000000,
+ 0x0000200020008008, 0x4004000020000000, 0x0000000000000000, 0x0000200000008008,
+ 0x0000000000008008, 0x0000200000000000, 0x4004000020008008, 0x0000000020000000,
+ 0x4004000020000000, 0x4004200020008008, 0x0000200020000000, 0x4004000000000000,
+ 0x4004200020008008, 0x0000200020000000, 0x0000000020000000, 0x4004200000008008,
+ 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, 0x0000000000000000,
+ 0x0000200000000000, 0x4004000000008008, 0x4004200000008008, 0x0000200020008008,
+ 0x0000000020008008, 0x4004200000000000, 0x4004000000000000, 0x4004000020008008, ),
+ ( 0x0000400400000000, 0x0020000000000000, 0x0020000000100000, 0x0400000000100040,
+ 0x0420400400100040, 0x0400400400000040, 0x0020400400000000, 0x0000000000000000,
+ 0x0000000000100000, 0x0420000000100040, 0x0420000000000040, 0x0000400400100000,
+ 0x0400000000000040, 0x0020400400100000, 0x0000400400100000, 0x0420000000000040,
+ 0x0420000000100040, 0x0000400400000000, 0x0400400400000040, 0x0420400400100040,
+ 0x0000000000000000, 0x0020000000100000, 0x0400000000100040, 0x0020400400000000,
+ 0x0400400400100040, 0x0420400400000040, 0x0020400400100000, 0x0400000000000040,
+ 0x0420400400000040, 0x0400400400100040, 0x0020000000000000, 0x0000000000100000,
+ 0x0420400400000040, 0x0000400400100000, 0x0400400400100040, 0x0420000000000040,
+ 0x0000400400000000, 0x0020000000000000, 0x0000000000100000, 0x0400400400100040,
+ 0x0420000000100040, 0x0420400400000040, 0x0020400400000000, 0x0000000000000000,
+ 0x0020000000000000, 0x0400000000100040, 0x0400000000000040, 0x0020000000100000,
+ 0x0000000000000000, 0x0420000000100040, 0x0020000000100000, 0x0020400400000000,
+ 0x0420000000000040, 0x0000400400000000, 0x0420400400100040, 0x0000000000100000,
+ 0x0020400400100000, 0x0400000000000040, 0x0400400400000040, 0x0420400400100040,
+ 0x0400000000100040, 0x0020400400100000, 0x0000400400100000, 0x0400400400000040, ),
+ ( 0x8008000080082000, 0x0000002080082000, 0x8008002000000000, 0x0000000000000000,
+ 0x0000002000002000, 0x8008000080080000, 0x0000000080082000, 0x8008002080082000,
+ 0x8008000000000000, 0x0000000000002000, 0x0000002080080000, 0x8008002000000000,
+ 0x8008002080080000, 0x8008002000002000, 0x8008000000002000, 0x0000000080082000,
+ 0x0000002000000000, 0x8008002080080000, 0x8008000080080000, 0x0000002000002000,
+ 0x8008002080082000, 0x8008000000002000, 0x0000000000000000, 0x0000002080080000,
+ 0x0000000000002000, 0x0000000080080000, 0x8008002000002000, 0x8008000080082000,
+ 0x0000000080080000, 0x0000002000000000, 0x0000002080082000, 0x8008000000000000,
+ 0x0000000080080000, 0x0000002000000000, 0x8008000000002000, 0x8008002080082000,
+ 0x8008002000000000, 0x0000000000002000, 0x0000000000000000, 0x0000002080080000,
+ 0x8008000080082000, 0x8008002000002000, 0x0000002000002000, 0x8008000080080000,
+ 0x0000002080082000, 0x8008000000000000, 0x8008000080080000, 0x0000002000002000,
+ 0x8008002080082000, 0x0000000080080000, 0x0000000080082000, 0x8008000000002000,
+ 0x0000002080080000, 0x8008002000000000, 0x8008002000002000, 0x0000000080082000,
+ 0x8008000000000000, 0x0000002080082000, 0x8008002080080000, 0x0000000000000000,
+ 0x0000000000002000, 0x8008000080082000, 0x0000002000000000, 0x8008002080080000, ),
+ )
+
+ #---------------------------------------------------------------
+ # compressed/interleaved => final permutation table
+ # Compression, final permutation, bit reverse
+ #---------------------------------------------------------------
+ # NOTE: this was reordered from original table to make perm6464 logic simpler
+ CF6464=(
+ ( 0x0000000000000000, 0x0000002000000000, 0x0000200000000000, 0x0000202000000000,
+ 0x0020000000000000, 0x0020002000000000, 0x0020200000000000, 0x0020202000000000,
+ 0x2000000000000000, 0x2000002000000000, 0x2000200000000000, 0x2000202000000000,
+ 0x2020000000000000, 0x2020002000000000, 0x2020200000000000, 0x2020202000000000, ),
+ ( 0x0000000000000000, 0x0000000200000000, 0x0000020000000000, 0x0000020200000000,
+ 0x0002000000000000, 0x0002000200000000, 0x0002020000000000, 0x0002020200000000,
+ 0x0200000000000000, 0x0200000200000000, 0x0200020000000000, 0x0200020200000000,
+ 0x0202000000000000, 0x0202000200000000, 0x0202020000000000, 0x0202020200000000, ),
+ ( 0x0000000000000000, 0x0000000000000020, 0x0000000000002000, 0x0000000000002020,
+ 0x0000000000200000, 0x0000000000200020, 0x0000000000202000, 0x0000000000202020,
+ 0x0000000020000000, 0x0000000020000020, 0x0000000020002000, 0x0000000020002020,
+ 0x0000000020200000, 0x0000000020200020, 0x0000000020202000, 0x0000000020202020, ),
+ ( 0x0000000000000000, 0x0000000000000002, 0x0000000000000200, 0x0000000000000202,
+ 0x0000000000020000, 0x0000000000020002, 0x0000000000020200, 0x0000000000020202,
+ 0x0000000002000000, 0x0000000002000002, 0x0000000002000200, 0x0000000002000202,
+ 0x0000000002020000, 0x0000000002020002, 0x0000000002020200, 0x0000000002020202, ),
+ ( 0x0000000000000000, 0x0000008000000000, 0x0000800000000000, 0x0000808000000000,
+ 0x0080000000000000, 0x0080008000000000, 0x0080800000000000, 0x0080808000000000,
+ 0x8000000000000000, 0x8000008000000000, 0x8000800000000000, 0x8000808000000000,
+ 0x8080000000000000, 0x8080008000000000, 0x8080800000000000, 0x8080808000000000, ),
+ ( 0x0000000000000000, 0x0000000800000000, 0x0000080000000000, 0x0000080800000000,
+ 0x0008000000000000, 0x0008000800000000, 0x0008080000000000, 0x0008080800000000,
+ 0x0800000000000000, 0x0800000800000000, 0x0800080000000000, 0x0800080800000000,
+ 0x0808000000000000, 0x0808000800000000, 0x0808080000000000, 0x0808080800000000, ),
+ ( 0x0000000000000000, 0x0000000000000080, 0x0000000000008000, 0x0000000000008080,
+ 0x0000000000800000, 0x0000000000800080, 0x0000000000808000, 0x0000000000808080,
+ 0x0000000080000000, 0x0000000080000080, 0x0000000080008000, 0x0000000080008080,
+ 0x0000000080800000, 0x0000000080800080, 0x0000000080808000, 0x0000000080808080, ),
+ ( 0x0000000000000000, 0x0000000000000008, 0x0000000000000800, 0x0000000000000808,
+ 0x0000000000080000, 0x0000000000080008, 0x0000000000080800, 0x0000000000080808,
+ 0x0000000008000000, 0x0000000008000008, 0x0000000008000800, 0x0000000008000808,
+ 0x0000000008080000, 0x0000000008080008, 0x0000000008080800, 0x0000000008080808, ),
+ ( 0x0000000000000000, 0x0000001000000000, 0x0000100000000000, 0x0000101000000000,
+ 0x0010000000000000, 0x0010001000000000, 0x0010100000000000, 0x0010101000000000,
+ 0x1000000000000000, 0x1000001000000000, 0x1000100000000000, 0x1000101000000000,
+ 0x1010000000000000, 0x1010001000000000, 0x1010100000000000, 0x1010101000000000, ),
+ ( 0x0000000000000000, 0x0000000100000000, 0x0000010000000000, 0x0000010100000000,
+ 0x0001000000000000, 0x0001000100000000, 0x0001010000000000, 0x0001010100000000,
+ 0x0100000000000000, 0x0100000100000000, 0x0100010000000000, 0x0100010100000000,
+ 0x0101000000000000, 0x0101000100000000, 0x0101010000000000, 0x0101010100000000, ),
+ ( 0x0000000000000000, 0x0000000000000010, 0x0000000000001000, 0x0000000000001010,
+ 0x0000000000100000, 0x0000000000100010, 0x0000000000101000, 0x0000000000101010,
+ 0x0000000010000000, 0x0000000010000010, 0x0000000010001000, 0x0000000010001010,
+ 0x0000000010100000, 0x0000000010100010, 0x0000000010101000, 0x0000000010101010, ),
+ ( 0x0000000000000000, 0x0000000000000001, 0x0000000000000100, 0x0000000000000101,
+ 0x0000000000010000, 0x0000000000010001, 0x0000000000010100, 0x0000000000010101,
+ 0x0000000001000000, 0x0000000001000001, 0x0000000001000100, 0x0000000001000101,
+ 0x0000000001010000, 0x0000000001010001, 0x0000000001010100, 0x0000000001010101, ),
+ ( 0x0000000000000000, 0x0000004000000000, 0x0000400000000000, 0x0000404000000000,
+ 0x0040000000000000, 0x0040004000000000, 0x0040400000000000, 0x0040404000000000,
+ 0x4000000000000000, 0x4000004000000000, 0x4000400000000000, 0x4000404000000000,
+ 0x4040000000000000, 0x4040004000000000, 0x4040400000000000, 0x4040404000000000, ),
+ ( 0x0000000000000000, 0x0000000400000000, 0x0000040000000000, 0x0000040400000000,
+ 0x0004000000000000, 0x0004000400000000, 0x0004040000000000, 0x0004040400000000,
+ 0x0400000000000000, 0x0400000400000000, 0x0400040000000000, 0x0400040400000000,
+ 0x0404000000000000, 0x0404000400000000, 0x0404040000000000, 0x0404040400000000, ),
+ ( 0x0000000000000000, 0x0000000000000040, 0x0000000000004000, 0x0000000000004040,
+ 0x0000000000400000, 0x0000000000400040, 0x0000000000404000, 0x0000000000404040,
+ 0x0000000040000000, 0x0000000040000040, 0x0000000040004000, 0x0000000040004040,
+ 0x0000000040400000, 0x0000000040400040, 0x0000000040404000, 0x0000000040404040, ),
+ ( 0x0000000000000000, 0x0000000000000004, 0x0000000000000400, 0x0000000000000404,
+ 0x0000000000040000, 0x0000000000040004, 0x0000000000040400, 0x0000000000040404,
+ 0x0000000004000000, 0x0000000004000004, 0x0000000004000400, 0x0000000004000404,
+ 0x0000000004040000, 0x0000000004040004, 0x0000000004040400, 0x0000000004040404, ),
+ )
+ #===================================================================
+ # eof _load_tables()
+ #===================================================================
+
+#=============================================================================
+# support
+#=============================================================================
+
+def _permute(c, p):
+ """Returns the permutation of the given 32-bit or 64-bit code with
+ the specified permutation table."""
+ # NOTE: only difference between 32 & 64 bit permutations
+ # is that len(p)==8 for 32 bit, and len(p)==16 for 64 bit.
+ out = 0
+ for r in p:
+ out |= r[c&0xf]
+ c >>= 4
+ return out
+
+#=============================================================================
+# packing & unpacking
+#=============================================================================
+# FIXME: more properly named _uint8_struct...
+_uint64_struct = struct.Struct(">Q")
+
+def _pack64(value):
+ return _uint64_struct.pack(value)
+
+def _unpack64(value):
+ return _uint64_struct.unpack(value)[0]
+
+def _pack56(value):
+ return _uint64_struct.pack(value)[1:]
+
+def _unpack56(value):
+ return _uint64_struct.unpack(b'\x00' + value)[0]
+
+#=============================================================================
+# 56->64 key manipulation
+#=============================================================================
+
+##def expand_7bit(value):
+## "expand 7-bit integer => 7-bits + 1 odd-parity bit"
+## # parity calc adapted from 32-bit even parity alg found at
+## # http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
+## assert 0 <= value < 0x80, "value out of range"
+## return (value<<1) | (0x9669 >> ((value ^ (value >> 4)) & 0xf)) & 1
+
+_EXPAND_ITER = irange(49,-7,-7)
+
+def expand_des_key(key):
+ """convert DES from 7 bytes to 8 bytes (by inserting empty parity bits)"""
+ if isinstance(key, bytes):
+ if len(key) != 7:
+ raise ValueError("key must be 7 bytes in size")
+ elif isinstance(key, int_types):
+ if key < 0 or key > INT_56_MASK:
+ raise ValueError("key must be 56-bit non-negative integer")
+ return _unpack64(expand_des_key(_pack56(key)))
+ else:
+ raise exc.ExpectedTypeError(key, "bytes or int", "key")
+ key = _unpack56(key)
+ # NOTE: the following would insert correctly-valued parity bits in each key,
+ # but the parity bit would just be ignored in des_encrypt_block(),
+ # so not bothering to use it.
+ # XXX: could make parity-restoring optionally available via flag
+ ##return join_byte_values(expand_7bit((key >> shift) & 0x7f)
+ ## for shift in _EXPAND_ITER)
+ return join_byte_values(((key>>shift) & 0x7f)<<1 for shift in _EXPAND_ITER)
+
+def shrink_des_key(key):
+ """convert DES key from 8 bytes to 7 bytes (by discarding the parity bits)"""
+ if isinstance(key, bytes):
+ if len(key) != 8:
+ raise ValueError("key must be 8 bytes in size")
+ return _pack56(shrink_des_key(_unpack64(key)))
+ elif isinstance(key, int_types):
+ if key < 0 or key > INT_64_MASK:
+ raise ValueError("key must be 64-bit non-negative integer")
+ else:
+ raise exc.ExpectedTypeError(key, "bytes or int", "key")
+ key >>= 1
+ result = 0
+ offset = 0
+ while offset < 56:
+ result |= (key & 0x7f)<<offset
+ key >>= 8
+ offset += 7
+ assert not (result & ~INT_64_MASK)
+ return result
+
+#=============================================================================
+# des encryption
+#=============================================================================
+def des_encrypt_block(key, input, salt=0, rounds=1):
+ """encrypt single block of data using DES, operates on 8-byte strings.
+
+ :arg key:
+ DES key as 7 byte string, or 8 byte string with parity bits
+ (parity bit values are ignored).
+
+ :arg input:
+ plaintext block to encrypt, as 8 byte string.
+
+ :arg salt:
+ Optional 24-bit integer used to mutate the base DES algorithm in a
+ manner specific to :class:`~passlib.hash.des_crypt` and its variants.
+ The default value ``0`` provides the normal (unsalted) DES behavior.
+ The salt functions as follows:
+ if the ``i``'th bit of ``salt`` is set,
+ bits ``i`` and ``i+24`` are swapped in the DES E-box output.
+
+ :arg rounds:
+ Optional number of rounds of to apply the DES key schedule.
+ the default (``rounds=1``) provides the normal DES behavior,
+ but :class:`~passlib.hash.des_crypt` and its variants use
+ alternate rounds values.
+
+ :raises TypeError: if any of the provided args are of the wrong type.
+ :raises ValueError:
+ if any of the input blocks are the wrong size,
+ or the salt/rounds values are out of range.
+
+ :returns:
+ resulting 8-byte ciphertext block.
+ """
+ # validate & unpack key
+ if isinstance(key, bytes):
+ if len(key) == 7:
+ key = expand_des_key(key)
+ elif len(key) != 8:
+ raise ValueError("key must be 7 or 8 bytes")
+ key = _unpack64(key)
+ else:
+ raise exc.ExpectedTypeError(key, "bytes", "key")
+
+ # validate & unpack input
+ if isinstance(input, bytes):
+ if len(input) != 8:
+ raise ValueError("input block must be 8 bytes")
+ input = _unpack64(input)
+ else:
+ raise exc.ExpectedTypeError(input, "bytes", "input")
+
+ # hand things off to other func
+ result = des_encrypt_int_block(key, input, salt, rounds)
+
+ # repack result
+ return _pack64(result)
+
+def des_encrypt_int_block(key, input, salt=0, rounds=1):
+ """encrypt single block of data using DES, operates on 64-bit integers.
+
+ this function is essentially the same as :func:`des_encrypt_block`,
+ except that it operates on integers, and will NOT automatically
+ expand 56-bit keys if provided (since there's no way to detect them).
+
+ :arg key:
+ DES key as 64-bit integer (the parity bits are ignored).
+
+ :arg input:
+ input block as 64-bit integer
+
+ :arg salt:
+ optional 24-bit integer used to mutate the base DES algorithm.
+ defaults to ``0`` (no mutation applied).
+
+ :arg rounds:
+ optional number of rounds of to apply the DES key schedule.
+ defaults to ``1``.
+
+ :raises TypeError: if any of the provided args are of the wrong type.
+ :raises ValueError:
+ if any of the input blocks are the wrong size,
+ or the salt/rounds values are out of range.
+
+ :returns:
+ resulting ciphertext as 64-bit integer.
+ """
+ #---------------------------------------------------------------
+ # input validation
+ #---------------------------------------------------------------
+
+ # validate salt, rounds
+ if rounds < 1:
+ raise ValueError("rounds must be positive integer")
+ if salt < 0 or salt > INT_24_MASK:
+ raise ValueError("salt must be 24-bit non-negative integer")
+
+ # validate & unpack key
+ if not isinstance(key, int_types):
+ raise exc.ExpectedTypeError(key, "int", "key")
+ elif key < 0 or key > INT_64_MASK:
+ raise ValueError("key must be 64-bit non-negative integer")
+
+ # validate & unpack input
+ if not isinstance(input, int_types):
+ raise exc.ExpectedTypeError(input, "int", "input")
+ elif input < 0 or input > INT_64_MASK:
+ raise ValueError("input must be 64-bit non-negative integer")
+
+ #---------------------------------------------------------------
+ # DES setup
+ #---------------------------------------------------------------
+ # load tables if not already done
+ global SPE, PCXROT, IE3264, CF6464
+ if PCXROT is None:
+ _load_tables()
+
+ # load SPE into local vars to speed things up and remove an array access call
+ SPE0, SPE1, SPE2, SPE3, SPE4, SPE5, SPE6, SPE7 = SPE
+
+ # NOTE: parity bits are ignored completely
+ # (UTs do fuzz testing to ensure this)
+
+ # generate key schedule
+ # NOTE: generation was modified to output two elements at a time,
+ # so that per-round loop could do two passes at once.
+ def _iter_key_schedule(ks_odd):
+ """given 64-bit key, iterates over the 8 (even,odd) key schedule pairs"""
+ for p_even, p_odd in PCXROT:
+ ks_even = _permute(ks_odd, p_even)
+ ks_odd = _permute(ks_even, p_odd)
+ yield ks_even & _KS_MASK, ks_odd & _KS_MASK
+ ks_list = list(_iter_key_schedule(key))
+
+ # expand 24 bit salt -> 32 bit per des_crypt & bsdi_crypt
+ salt = (
+ ((salt & 0x00003f) << 26) |
+ ((salt & 0x000fc0) << 12) |
+ ((salt & 0x03f000) >> 2) |
+ ((salt & 0xfc0000) >> 16)
+ )
+
+ # init L & R
+ if input == 0:
+ L = R = 0
+ else:
+ L = ((input >> 31) & 0xaaaaaaaa) | (input & 0x55555555)
+ L = _permute(L, IE3264)
+
+ R = ((input >> 32) & 0xaaaaaaaa) | ((input >> 1) & 0x55555555)
+ R = _permute(R, IE3264)
+
+ #---------------------------------------------------------------
+ # main DES loop - run for specified number of rounds
+ #---------------------------------------------------------------
+ while rounds:
+ rounds -= 1
+
+ # run over each part of the schedule, 2 parts at a time
+ for ks_even, ks_odd in ks_list:
+ k = ((R>>32) ^ R) & salt # use the salt to flip specific bits
+ B = (k<<32) ^ k ^ R ^ ks_even
+
+ L ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^
+ SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^
+ SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^
+ SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f])
+
+ k = ((L>>32) ^ L) & salt # use the salt to flip specific bits
+ B = (k<<32) ^ k ^ L ^ ks_odd
+
+ R ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^
+ SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^
+ SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^
+ SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f])
+
+ # swap L and R
+ L, R = R, L
+
+ #---------------------------------------------------------------
+ # return final result
+ #---------------------------------------------------------------
+ C = (
+ ((L>>3) & 0x0f0f0f0f00000000)
+ |
+ ((L<<33) & 0xf0f0f0f000000000)
+ |
+ ((R>>35) & 0x000000000f0f0f0f)
+ |
+ ((R<<1) & 0x00000000f0f0f0f0)
+ )
+ return _permute(C, CF6464)
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/crypto/digest.py b/passlib/crypto/digest.py
new file mode 100644
index 0000000..7685537
--- /dev/null
+++ b/passlib/crypto/digest.py
@@ -0,0 +1,646 @@
+"""passlib.crypto.digest -- crytographic helpers used by the password hashes in passlib
+
+.. versionadded:: 1.7
+"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import division
+# core
+import hashlib
+import logging; log = logging.getLogger(__name__)
+import re
+from struct import Struct
+from warnings import warn
+# site
+try:
+ from M2Crypto.EVP import pbkdf2 as _m2crypto_pbkdf2_hmac_sha1
+except ImportError:
+ _m2crypto_pbkdf2_hmac_sha1 = None
+# pkg
+from passlib import exc
+from passlib.utils import join_bytes, to_native_str, bytes_to_int, int_to_bytes, \
+ join_byte_values, to_bytes, SequenceMixin
+from passlib.utils.compat import irange, int_types, unicode_or_bytes_types
+# local
+__all__ = [
+ # hash utils
+ "lookup_hash",
+ "HashInfo",
+ "norm_hash_name",
+
+ # hmac utils
+ "compile_hmac",
+
+ # kdfs
+ "pbkdf1",
+ "pbkdf2_hmac",
+]
+
+#=============================================================================
+# generic constants
+#=============================================================================
+
+#: max 32-bit value
+MAX_UINT4 = (1<<32)-1
+
+#: max 64-bit value
+MAX_UINT8 = (1<<64)-1
+
+#=============================================================================
+# hash utils
+#=============================================================================
+
+#: list of known hash names, used by lookup_hash()'s _norm_hash_name() helper
+_known_hash_names = [
+ # format: (hashlib/ssl name, iana name or standin, other known aliases ...)
+
+ # hashes with official IANA-assigned names
+ # (as of 2012-03 - http://www.iana.org/assignments/hash-function-text-names)
+ ("md2", "md2"),
+ ("md5", "md5"),
+ ("sha1", "sha-1"),
+ ("sha224", "sha-224", "sha2-224"),
+ ("sha256", "sha-256", "sha2-256"),
+ ("sha384", "sha-384", "sha2-384"),
+ ("sha512", "sha-512", "sha2-512"),
+
+ # TODO: add sha3 to this table.
+
+ # hashlib/ssl-supported hashes without official IANA names,
+ # (hopefully-) compatible stand-ins have been chosen.
+ ("md4", "md4"),
+ ("sha", "sha-0", "sha0"),
+ ("ripemd", "ripemd"),
+ ("ripemd160", "ripemd-160"),
+]
+
+#: cache of hash info instances used by lookup_hash()
+_hash_info_cache = {}
+
+def _get_hash_aliases(name):
+ """
+ internal helper used by :func:`lookup_hash` --
+ normalize arbitrary hash name to hashlib format.
+ if name not recognized, returns dummy record and issues a warning.
+
+ :arg name:
+ unnormalized name
+
+ :returns:
+ tuple with 2+ elements: ``(hashlib_name, iana_name|None, ... 0+ aliases)``.
+ """
+
+ # normalize input
+ orig = name
+ if not isinstance(name, str):
+ name = to_native_str(name, 'utf-8', 'hash name')
+ name = re.sub("[_ /]", "-", name.strip().lower())
+ if name.startswith("scram-"): # helper for SCRAM protocol (see passlib.handlers.scram)
+ name = name[6:]
+ if name.endswith("-plus"):
+ name = name[:-5]
+
+ # look through standard names and known aliases
+ def check_table(name):
+ for row in _known_hash_names:
+ if name in row:
+ return row
+ result = check_table(name)
+ if result:
+ return result
+
+ # try to clean name up some more
+ m = re.match("(?i)^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$", name)
+ if m:
+ # roughly follows "SHA2-256" style format, normalize representation,
+ # and checked table.
+ iana_name, rev, size = m.group("name", "rev", "size")
+ if rev:
+ iana_name += rev
+ hashlib_name = iana_name
+ if size:
+ iana_name += "-" + size
+ if rev:
+ hashlib_name += "_"
+ hashlib_name += size
+ result = check_table(iana_name)
+ if result:
+ return result
+
+ # not found in table, but roughly recognize format. use names we built up as fallback.
+ log.info("normalizing unrecognized hash name %r => %r / %r",
+ orig, hashlib_name, iana_name)
+
+ else:
+ # just can't make sense of it. return something
+ iana_name = name
+ hashlib_name = name.replace("-", "_")
+ log.warning("normalizing unrecognized hash name and format %r => %r / %r",
+ orig, hashlib_name, iana_name)
+
+ return hashlib_name, iana_name
+
+
+def _get_hash_const(name):
+ """
+ internal helper used by :func:`lookup_hash` --
+ lookup hash constructor by name
+
+ :arg name:
+ name (normalized to hashlib format, e.g. ``"sha256"``)
+
+ :returns:
+ hash constructor, e.g. ``hashlib.sha256()``;
+ or None if hash can't be located.
+ """
+ # check hashlib.<attr> for an efficient constructor
+ if not name.startswith("_") and name not in ("new", "algorithms"):
+ try:
+ return getattr(hashlib, name)
+ except AttributeError:
+ pass
+
+ # check hashlib.new() in case SSL supports the digest
+ new_ssl_hash = hashlib.new
+ try:
+ # new() should throw ValueError if alg is unknown
+ new_ssl_hash(name, b"")
+ except ValueError:
+ pass
+ else:
+ # create wrapper function
+ # XXX: is there a faster way to wrap this?
+ def const(msg=b""):
+ return new_ssl_hash(name, msg)
+ const.__name__ = name
+ const.__module__ = "hashlib"
+ const.__doc__ = ("wrapper for hashlib.new(%r),\n"
+ "generated by passlib.crypto.digest.lookup_hash()") % name
+ return const
+
+ # use builtin md4 as fallback when not supported by hashlib
+ if name == "md4":
+ from passlib.crypto._md4 import md4
+ return md4
+
+ # XXX: any other modules / registries we should check?
+ # TODO: add pysha3 support.
+
+ return None
+
+def lookup_hash(digest, return_unknown=False):
+ """
+ Returns a :class:`HashInfo` record containing information about a given hash function.
+ Can be used to look up a hash constructor by name, normalize hash name representation, etc.
+
+ :arg digest:
+ This can be a digest constructor (e.g. :func:`hashlib.sha256`),
+ a string containing a :mod:`!hashlib` digest name (e.g. ``"sha256"``),
+ an IANA-assigned hash name. Case is ignored, underscores are converted to hyphens,
+ and various other cleanups are made.
+
+ :param return_unknown:
+ By default, this function will throw a :exc:`~passlib.exc.KnownHashError` if no hash constructor
+ can be found. However, if this flag is False, it will instead return a dummy record
+ without a constructor function. This is mainly used by :func:`norm_hash_name`.
+
+ Multiple calls made with the same input, or made with inputs that reference the same hash,
+ should all return the same :class:`!lookup_hash` instance.
+
+ :returns:
+ :class:`HashInfo` instance for specified digest.
+ """
+ # check for cached entry
+ cache = _hash_info_cache
+ try:
+ return cache[digest]
+ except (KeyError, TypeError):
+ # NOTE: TypeError is to catch 'TypeError: unhashable type' (e.g. HashInfo)
+ pass
+
+ # resolve ``digest`` to ``const`` & ``name_record``
+ cache_by_name = True
+ if isinstance(digest, unicode_or_bytes_types):
+ # normalize name
+ name_list = _get_hash_aliases(digest)
+ name = name_list[0]
+ assert name
+
+ # if name wasn't normalized to hashlib format,
+ # get info for normalized name and reuse it.
+ if name != digest:
+ info = lookup_hash(name, return_unknown=return_unknown)
+ if info.const is None:
+ # pass through dummy record
+ assert return_unknown
+ return info
+ cache[digest] = info
+ return info
+
+ # else look up constructor
+ const = _get_hash_const(name)
+ if const is None:
+ if return_unknown:
+ # return a dummy record (but don't cache it, so normal lookup still returns error)
+ return HashInfo(None, name_list)
+ else:
+ raise exc.UnknownHashError(name)
+
+ elif isinstance(digest, HashInfo):
+ # handle border case where HashInfo is passed in.
+ return digest
+
+ elif callable(digest):
+ # try to lookup digest based on it's self-reported name
+ # (which we trust to be the canonical "hashlib" name)
+ const = digest
+ name_list = _get_hash_aliases(const().name)
+ name = name_list[0]
+ other_const = _get_hash_const(name)
+ if other_const is None:
+ # this is probably a third-party digest we don't know about,
+ # so just pass it on through, and register reverse lookup for it's name.
+ pass
+
+ elif other_const is const:
+ # if we got back same constructor, this is just a known stdlib constructor,
+ # which was passed in before we had cached it by name. proceed normally.
+ pass
+
+ else:
+ # if we got back different object, then ``const`` is something else
+ # (such as a mock object), in which case we want to skip caching it by name,
+ # as that would conflict with real hash.
+ cache_by_name = False
+
+ else:
+ raise exc.ExpectedTypeError(digest, "digest name or constructor", "digest")
+
+ # create new instance
+ info = HashInfo(const, name_list)
+
+ # populate cache
+ cache[const] = info
+ if cache_by_name:
+ for name in name_list:
+ if name: # (skips iana name if it's empty)
+ assert cache.get(name) in [None, info], "%r already in cache" % name
+ cache[name] = info
+ return info
+
+#: UT helper for clearing internal cache
+lookup_hash.clear_cache = _hash_info_cache.clear
+
+
+def norm_hash_name(name, format="hashlib"):
+ """Normalize hash function name (convenience wrapper for :func:`lookup_hash`).
+
+ :arg name:
+ Original hash function name.
+
+ This name can be a Python :mod:`~hashlib` digest name,
+ a SCRAM mechanism name, IANA assigned hash name, etc.
+ Case is ignored, and underscores are converted to hyphens.
+
+ :param format:
+ Naming convention to normalize to.
+ Possible values are:
+
+ * ``"hashlib"`` (the default) - normalizes name to be compatible
+ with Python's :mod:`!hashlib`.
+
+ * ``"iana"`` - normalizes name to IANA-assigned hash function name.
+ for hashes which IANA hasn't assigned a name for, issues a warning,
+ and then uses a heuristic to give a "best guess".
+
+ :returns:
+ Hash name, returned as native :class:`!str`.
+ """
+ info = lookup_hash(name, return_unknown=True)
+ if not info.const:
+ warn("norm_hash_name(): unknown hash: %r" % (name,), exc.PasslibRuntimeWarning)
+ if format == "hashlib":
+ return info.name
+ elif format == "iana":
+ return info.iana_name
+ else:
+ raise ValueError("unknown format: %r" % (format,))
+
+
+class HashInfo(SequenceMixin):
+ """
+ Record containing information about a given hash algorithm, as returned :func:`lookup_hash`.
+
+ This class exposes the following attributes:
+
+ .. autoattribute:: const
+ .. autoattribute:: digest_size
+ .. autoattribute:: block_size
+ .. autoattribute:: name
+ .. autoattribute:: iana_name
+ .. autoattribute:: aliases
+
+ Also acts as a sequence of ``(const, digest_size, block_size)``.
+ """
+ #=========================================================================
+ # instance attrs
+ #=========================================================================
+
+ #: Canonical (hashlib-compatible) name (e.g. ``"sha256"``).
+ name = None
+
+ #: IANA assigned name (e.g. ``"sha-256"``), may be ``None`` if unknown.
+ iana_name = None
+
+ #: Tuple of other known aliases (may be empty)
+ aliases = ()
+
+ #: Hash constructor function (e.g. :func:`hashlib.sha256`)
+ const = None
+
+ #: Hash's digest size
+ digest_size = None
+
+ #: Hash's block size
+ block_size = None
+
+ def __init__(self, const, names):
+ """
+ initialize new instance.
+ :arg const:
+ hash constructor
+ :arg names:
+ list of 2+ names. should be list of ``(name, iana_name, ... 0+ aliases)``.
+ names must be lower-case. only iana name may be None.
+ """
+ self.name = names[0]
+ self.iana_name = names[1]
+ self.aliases = names[2:]
+
+ self.const = const
+ if const is None:
+ return
+
+ hash = const()
+ self.digest_size = hash.digest_size
+ self.block_size = hash.block_size
+
+ # do sanity check on digest size
+ if len(hash.digest()) != hash.digest_size:
+ raise RuntimeError("%r constructor failed sanity check" % self.name)
+
+ # do sanity check on name.
+ if hash.name != self.name:
+ warn("inconsistent digest name: %r resolved to %r, which reports name as %r" %
+ (self.name, const, hash.name), exc.PasslibRuntimeWarning)
+
+ #=========================================================================
+ # methods
+ #=========================================================================
+ def __repr__(self):
+ return "<lookup_hash(%r): digest_size=%r block_size=%r)" % \
+ (self.name, self.digest_size, self.block_size)
+
+ def _as_tuple(self):
+ return self.const, self.digest_size, self.block_size
+
+ #=========================================================================
+ # 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).
+
+ :returns:
+ By default, the returned function has the signature ``hmac(msg) -> digest output``.
+
+ However, if ``multipart=True``, the returned function has the signature
+ ``hmac() -> update, finalize``, where ``update(msg)`` may be called multiple times,
+ and ``finalize() -> digest_output`` may be repeatedly called at any point to
+ calculate the HMAC digest so far.
+
+ The returned object will also have a ``digest_info`` attribute, containing
+ a :class:`lookup_hash` instance for the specified digest.
+
+ This function exists, and has the weird signature it does, in order to squeeze as
+ provide as much efficiency as possible, by omitting much of the setup cost
+ and features of the stdlib :mod:`hmac` module.
+ """
+ # all the following was adapted from stdlib's hmac module
+
+ # resolve digest (cached)
+ digest_info = lookup_hash(digest)
+ const, digest_size, block_size = digest_info
+ assert block_size >= 16, "block size too small"
+
+ # prepare key
+ if not isinstance(key, bytes):
+ key = to_bytes(key, param="key")
+ klen = len(key)
+ if klen > block_size:
+ key = const(key).digest()
+ klen = digest_size
+ if klen < block_size:
+ key += b'\x00' * (block_size - klen)
+
+ # create pre-initialized hash constructors
+ _inner_copy = const(key.translate(_TRANS_36)).copy
+ _outer_copy = const(key.translate(_TRANS_5C)).copy
+
+ if multipart:
+ # create multi-part function
+ # NOTE: this is slightly slower than the single-shot version,
+ # and should only be used if needed.
+ def hmac():
+ """generated by compile_hmac(multipart=True)"""
+ inner = _inner_copy()
+ def finalize():
+ outer = _outer_copy()
+ outer.update(inner.digest())
+ return outer.digest()
+ return inner.update, finalize
+ else:
+
+ # single-shot function
+ def hmac(msg):
+ """generated by compile_hmac()"""
+ inner = _inner_copy()
+ inner.update(msg)
+ outer = _outer_copy()
+ outer.update(inner.digest())
+ return outer.digest()
+
+ # add info attr
+ hmac.digest_info = digest_info
+ return hmac
+
+#=============================================================================
+# pbkdf1
+#=============================================================================
+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]
+
+#=============================================================================
+# pbkdf2
+#=============================================================================
+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
+ """
+ # validate secret & salt
+ secret = to_bytes(secret, param="secret")
+ salt = to_bytes(salt, param="salt")
+
+ # TODO: check for hashlib.pbkdf2_hmac() helper, new in 2.7.8 / 3.4.
+ # it may be SSL or pure-python. should time pure-python and see if we should use it.
+ # SSL version will *definitely* be faster.
+
+ # 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")
+
+ # generated keyed hmac
+ keyed_hmac = compile_hmac(digest, secret)
+ digest_size = keyed_hmac.digest_info.digest_size
+
+ # 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")
+
+ # m2crypto's pbkdf2-hmac-sha1 is faster than ours, so use it if available.
+ # NOTE: as of 2012-4-4, m2crypto has buffer overflow issue which frequently
+ # causes segfaults if keylen > 32 (EVP_MAX_KEY_LENGTH).
+ # therefore we're avoiding m2crypto for large keys until that's fixed.
+ # (https://bugzilla.osafoundation.org/show_bug.cgi?id=13052)
+ if digest == "sha1" and _m2crypto_pbkdf2_hmac_sha1 and keylen < 32:
+ return _m2crypto_pbkdf2_hmac_sha1(secret, salt, rounds, keylen)
+
+ # find smallest block count s.t. keylen <= block_count * digest_size
+ block_count = (keylen + digest_size - 1) // digest_size
+ if block_count >= MAX_UINT4:
+ raise ValueError("keylen too long for digest")
+
+ # build up result from blocks
+ # TODO: consider some alternatives to speed up loop:
+ # int.to_bytes / int.from_bytes for py3
+ # struct Q/L for digest sizes <= 32/64
+ # C-accelerated xor_bytes helper, if available (e.g. PyCrypto's Crypto.Util.strxor.strxor)
+ pack_uint4 = Struct(">L").pack
+ def gen():
+ for i in irange(block_count):
+ digest = keyed_hmac(salt + pack_uint4(i+1))
+ accum = bytes_to_int(digest)
+ # --- begin speed-critical loop ---
+ # NOTE: currently converting digests to integers since that XORs faster.
+ for _ in irange(rounds-1):
+ digest = keyed_hmac(digest)
+ accum ^= bytes_to_int(digest)
+ # --- end speed critical loop ---
+ yield int_to_bytes(accum, digest_size)
+ return join_bytes(gen())[:keylen]
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/exc.py b/passlib/exc.py
index c972597..35ca816 100644
--- a/passlib/exc.py
+++ b/passlib/exc.py
@@ -63,6 +63,17 @@ class TokenReuseError(ValueError):
self.expire_time = kwds.pop("expire_time", None)
ValueError.__init__(self, *args, **kwds)
+
+class UnknownHashError(ValueError):
+ """Error raised by :class:`~passlib.crypto.lookup_hash` if hash name is not recognized.
+ This exception derives from :exc:`!ValueError`.
+
+ .. versionadded:: 1.7
+ """
+ def __init__(self, name):
+ self.name = name
+ ValueError.__init__(self, "unknown hash algorithm: %r" % name)
+
#=============================================================================
# warnings
#=============================================================================
diff --git a/passlib/handlers/des_crypt.py b/passlib/handlers/des_crypt.py
index d4e3991..1194e54 100644
--- a/passlib/handlers/des_crypt.py
+++ b/passlib/handlers/des_crypt.py
@@ -10,7 +10,7 @@ from warnings import warn
# pkg
from passlib.utils import h64, h64big, safe_crypt, test_crypt, to_unicode
from passlib.utils.compat import byte_elem_value, u, uascii_to_str, unicode
-from passlib.utils.des import des_encrypt_int_block
+from passlib.crypto.des import des_encrypt_int_block
import passlib.utils.handlers as uh
# local
__all__ = [
diff --git a/passlib/handlers/digests.py b/passlib/handlers/digests.py
index 39ef4b4..f33aabf 100644
--- a/passlib/handlers/digests.py
+++ b/passlib/handlers/digests.py
@@ -11,7 +11,7 @@ import logging; log = logging.getLogger(__name__)
from passlib.utils import to_native_str, to_bytes, render_bytes, consteq
from passlib.utils.compat import unicode, str_to_uascii
import passlib.utils.handlers as uh
-from passlib.utils.md4 import md4
+from passlib.crypto.digest import lookup_hash
# local
__all__ = [
"create_hex_hash",
@@ -50,30 +50,30 @@ class HexDigestHash(uh.StaticHandler):
# eoc
#===================================================================
-def create_hex_hash(hash, digest_name, module=__name__):
+def create_hex_hash(digest, module=__name__):
# NOTE: could set digest_name=hash.name for cpython, but not for some other platforms.
- h = hash()
- name = "hex_" + digest_name
+ info = lookup_hash(digest)
+ name = "hex_" + info.name
return type(name, (HexDigestHash,), dict(
name=name,
__module__=module, # so ABCMeta won't clobber it
- _hash_func=staticmethod(hash), # sometimes it's a function, sometimes not. so wrap it.
- checksum_size=h.digest_size*2,
+ _hash_func=staticmethod(info.const), # sometimes it's a function, sometimes not. so wrap it.
+ checksum_size=info.digest_size*2,
__doc__="""This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`.
It supports no optional or contextual keywords.
-""" % (digest_name,)
+""" % (info.name,)
))
#=============================================================================
# predefined handlers
#=============================================================================
-hex_md4 = create_hex_hash(md4, "md4")
-hex_md5 = create_hex_hash(hashlib.md5, "md5")
+hex_md4 = create_hex_hash("md4")
+hex_md5 = create_hex_hash("md5")
hex_md5.django_name = "unsalted_md5"
-hex_sha1 = create_hex_hash(hashlib.sha1, "sha1")
-hex_sha256 = create_hex_hash(hashlib.sha256, "sha256")
-hex_sha512 = create_hex_hash(hashlib.sha512, "sha512")
+hex_sha1 = create_hex_hash("sha1")
+hex_sha256 = create_hex_hash("sha256")
+hex_sha512 = create_hex_hash("sha512")
#=============================================================================
# htdigest
diff --git a/passlib/handlers/django.py b/passlib/handlers/django.py
index d6972a7..8ed7925 100644
--- a/passlib/handlers/django.py
+++ b/passlib/handlers/django.py
@@ -12,7 +12,7 @@ import logging; log = logging.getLogger(__name__)
from passlib.hash import bcrypt, pbkdf2_sha1, pbkdf2_sha256
from passlib.utils import to_unicode, classproperty
from passlib.utils.compat import str_to_uascii, uascii_to_str, unicode, u
-from passlib.utils.pbkdf2 import pbkdf2
+from passlib.crypto.digest import pbkdf2_hmac
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -282,13 +282,11 @@ class django_pbkdf2_sha256(DjangoVariableHash):
checksum_chars = uh.PADDED_BASE64_CHARS
checksum_size = 44 # 32 bytes -> base64
default_rounds = pbkdf2_sha256.default_rounds # NOTE: django 1.6 uses 12000
- _prf = "hmac-sha256"
+ _digest = "sha256"
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- hash = pbkdf2(secret, self.salt.encode("ascii"), self.rounds,
- keylen=None, prf=self._prf)
+ # NOTE: secret & salt will be encoded using UTF-8 by pbkdf2_hmac()
+ hash = pbkdf2_hmac(self._digest, secret, self.salt, self.rounds)
return b64encode(hash).rstrip().decode("ascii")
class django_pbkdf2_sha1(django_pbkdf2_sha256):
@@ -332,7 +330,7 @@ class django_pbkdf2_sha1(django_pbkdf2_sha256):
ident = u('pbkdf2_sha1$')
checksum_size = 28 # 20 bytes -> base64
default_rounds = pbkdf2_sha1.default_rounds # NOTE: django 1.6 uses 12000
- _prf = "hmac-sha1"
+ _digest = "sha1"
#=============================================================================
# other
diff --git a/passlib/handlers/fshp.py b/passlib/handlers/fshp.py
index 5d47518..0c082f1 100644
--- a/passlib/handlers/fshp.py
+++ b/passlib/handlers/fshp.py
@@ -14,7 +14,7 @@ from passlib.utils import to_unicode
import passlib.utils.handlers as uh
from passlib.utils.compat import bascii_to_str, iteritems, u,\
unicode
-from passlib.utils.pbkdf2 import pbkdf1
+from passlib.crypto.digest import pbkdf1
# local
__all__ = [
'fshp',
@@ -189,11 +189,11 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
# this has only a minimal impact on security,
# but it is worth noting this deviation.
return pbkdf1(
+ digest=self.checksum_alg,
secret=self.salt,
salt=secret,
rounds=self.rounds,
keylen=self.checksum_size,
- hash=self.checksum_alg,
)
#===================================================================
diff --git a/passlib/handlers/oracle.py b/passlib/handlers/oracle.py
index 252c159..250731b 100644
--- a/passlib/handlers/oracle.py
+++ b/passlib/handlers/oracle.py
@@ -12,7 +12,7 @@ import logging; log = logging.getLogger(__name__)
from passlib.utils import to_unicode, xor_bytes
from passlib.utils.compat import irange, u, \
uascii_to_str, unicode, str_to_uascii
-from passlib.utils.des import des_encrypt_block
+from passlib.crypto.des import des_encrypt_block
import passlib.utils.handlers as uh
# local
__all__ = [
diff --git a/passlib/handlers/pbkdf2.py b/passlib/handlers/pbkdf2.py
index c14c24b..3832562 100644
--- a/passlib/handlers/pbkdf2.py
+++ b/passlib/handlers/pbkdf2.py
@@ -10,7 +10,7 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import ab64_decode, ab64_encode, to_unicode
from passlib.utils.compat import str_to_bascii, u, uascii_to_str, unicode
-from passlib.utils.pbkdf2 import pbkdf2
+from passlib.crypto.digest import pbkdf2_hmac
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -47,7 +47,7 @@ class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gen
rounds_cost = "linear"
#--this class--
- _prf = None # subclass specified prf identifier
+ _digest = None # name of subclass-specified hash
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
# the underlying pbkdf2 specifies no bounds for either.
@@ -77,26 +77,24 @@ class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gen
return uh.render_mc3(self.ident, self.rounds, salt, chk)
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- return pbkdf2(secret, self.salt, self.rounds, self.checksum_size, self._prf)
+ # NOTE: pbkdf2_hmac() will encode secret & salt using UTF8
+ return pbkdf2_hmac(self._digest, secret, self.salt, self.rounds, self.checksum_size)
def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=__name__):
"""create new Pbkdf2DigestHandler subclass for a specific hash"""
name = 'pbkdf2_' + hash_name
if ident is None:
ident = u("$pbkdf2-%s$") % (hash_name,)
- prf = "hmac-%s" % (hash_name,)
base = Pbkdf2DigestHandler
return type(name, (base,), dict(
__module__=module, # so ABCMeta won't clobber it.
name=name,
ident=ident,
- _prf = prf,
+ _digest = hash_name,
default_rounds=rounds,
checksum_size=digest_size,
encoded_checksum_size=(digest_size*4+2)//3,
- __doc__="""This class implements a generic ``PBKDF2-%(prf)s``-based password hash, and follows the :ref:`password-hash-api`.
+ __doc__="""This class implements a generic ``PBKDF2-HMAC-%(digest)s``-based password hash, and follows the :ref:`password-hash-api`.
It supports a variable-length salt, and a variable number of rounds.
@@ -111,7 +109,7 @@ def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=
:type salt_size: int
:param salt_size:
Optional number of bytes to use when autogenerating new salts.
- Defaults to 16 bytes, but can be any value between 0 and 1024.
+ Defaults to %(dsc)d bytes, but can be any value between 0 and 1024.
:type rounds: int
:param rounds:
@@ -127,7 +125,7 @@ def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=
that are too small or too large, and ``salt`` strings that are too long.
.. versionadded:: 1.6
- """ % dict(prf=prf.upper(), dsc=base.default_salt_size, dr=rounds)
+ """ % dict(digest=hash_name.upper(), dsc=base.default_salt_size, dr=rounds)
))
#------------------------------------------------------------------------
@@ -237,9 +235,8 @@ class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Generic
# backend
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- return pbkdf2(secret, self.salt, self.rounds, 20, "hmac-sha1")
+ # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
+ return pbkdf2_hmac("sha1", secret, self.salt, self.rounds, 20)
#===================================================================
# eoc
@@ -337,10 +334,9 @@ class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
# backend
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- salt = str_to_bascii(self.to_string(withchk=False))
- result = pbkdf2(secret, salt, self.rounds, 24, "hmac-sha1")
+ # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
+ salt = self.to_string(withchk=False)
+ result = pbkdf2_hmac("sha1", secret, salt, self.rounds, 24)
return ab64_encode(result).decode("ascii")
#===================================================================
@@ -401,10 +397,9 @@ class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler)
def _calc_checksum(self, secret):
# TODO: find out what crowd's policy is re: unicode
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
# crowd seems to use a fixed number of rounds.
- return pbkdf2(secret, self.salt, 10000, 32, "hmac-sha1")
+ # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
+ return pbkdf2_hmac("sha1", secret, self.salt, 10000, 32)
#=============================================================================
# grub
@@ -479,9 +474,8 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
def _calc_checksum(self, secret):
# TODO: find out what grub's policy is re: unicode
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- return pbkdf2(secret, self.salt, self.rounds, 64, "hmac-sha512")
+ # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
+ return pbkdf2_hmac("sha512", secret, self.salt, self.rounds, 64)
#=============================================================================
# eof
diff --git a/passlib/handlers/scram.py b/passlib/handlers/scram.py
index a4ba68a..49672bd 100644
--- a/passlib/handlers/scram.py
+++ b/passlib/handlers/scram.py
@@ -9,7 +9,7 @@ import logging; log = logging.getLogger(__name__)
from passlib.utils import ab64_decode, ab64_encode, consteq, saslprep, \
to_native_str, splitcomma
from passlib.utils.compat import bascii_to_str, iteritems, u, native_string_types
-from passlib.utils.pbkdf2 import pbkdf2, norm_hash_name
+from passlib.crypto.digest import pbkdf2_hmac, norm_hash_name
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -134,7 +134,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
:arg alg:
Name of digest algorithm (e.g. ``"sha-1"``) requested by client.
- This value is run through :func:`~passlib.utils.pbkdf2.norm_hash_name`,
+ This value is run through :func:`~passlib.crypto.digest.norm_hash_name`,
so it is case-insensitive, and can be the raw SCRAM
mechanism name (e.g. ``"SCRAM-SHA-1"``), the IANA name,
or the hashlib name.
@@ -172,8 +172,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
:param format:
This changes the naming convention used by the
returned algorithm names. By default the names
- are IANA-compatible; see :func:`~passlib.utils.pbkdf2.norm_hash_name`
- for possible values.
+ are IANA-compatible; possible values are ``"iana"`` or ``"hashlib"``.
:returns:
Returns a list of digest algorithms; e.g. ``["sha-1"]``
@@ -212,13 +211,9 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
"""
if isinstance(password, bytes):
password = password.decode("utf-8")
- password = saslprep(password).encode("utf-8")
- if not isinstance(salt, bytes):
- raise TypeError("salt must be bytes")
- if rounds < 1:
- raise ValueError("rounds must be >= 1")
- alg = norm_hash_name(alg, "hashlib")
- return pbkdf2(password, salt, rounds, None, "hmac-" + alg)
+ # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8,
+ # and handle normalizing alg name.
+ return pbkdf2_hmac(alg, saslprep(password), salt, rounds)
#===================================================================
# serialization
diff --git a/passlib/handlers/sha1_crypt.py b/passlib/handlers/sha1_crypt.py
index 0717d66..243bd99 100644
--- a/passlib/handlers/sha1_crypt.py
+++ b/passlib/handlers/sha1_crypt.py
@@ -11,7 +11,7 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import h64, safe_crypt, test_crypt
from passlib.utils.compat import u, unicode, irange
-from passlib.utils.pbkdf2 import get_keyed_prf
+from passlib.crypto.digest import compile_hmac
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -131,7 +131,7 @@ class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
# 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 = get_keyed_prf("hmac-sha1", secret)[0]
+ 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")
diff --git a/passlib/handlers/windows.py b/passlib/handlers/windows.py
index ef246eb..e7e0c6a 100644
--- a/passlib/handlers/windows.py
+++ b/passlib/handlers/windows.py
@@ -10,7 +10,8 @@ from warnings import warn
# pkg
from passlib.utils import to_unicode, right_pad_string
from passlib.utils.compat import unicode
-from passlib.utils.md4 import md4
+from passlib.crypto.digest import lookup_hash
+md4 = lookup_hash("md4").const
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -82,7 +83,7 @@ class lmhash(uh.HasEncodingContext, uh.StaticHandler):
# some nice empircal data re: different encodings is at...
# http://www.openwall.com/lists/john-dev/2011/08/01/2
# http://www.freerainbowtables.com/phpBB3/viewtopic.php?t=387&p=12163
- from passlib.utils.des import des_encrypt_block
+ from passlib.crypto.des import des_encrypt_block
MAGIC = cls._magic
if isinstance(secret, unicode):
# perform uppercasing while we're still unicode,
@@ -298,11 +299,11 @@ class msdcc2(uh.HasUserContext, uh.StaticHandler):
:returns: returns string of raw bytes
"""
- from passlib.utils.pbkdf2 import pbkdf2
+ from passlib.crypto.digest import pbkdf2_hmac
secret = to_unicode(secret, "utf-8", param="secret").encode("utf-16-le")
user = to_unicode(user, "utf-8", param="user").lower().encode("utf-16-le")
tmp = md4(md4(secret).digest() + user).digest()
- return pbkdf2(tmp, user, 10240, 16, 'hmac-sha1')
+ return pbkdf2_hmac("sha1", tmp, user, 10240, 16)
#=============================================================================
# eof
diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py
index b6c649f..a061384 100644
--- a/passlib/tests/test_context.py
+++ b/passlib/tests/test_context.py
@@ -465,7 +465,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(parse(admin__all__vary_rounds=0.1), result)
self.assertEqual(parse(**{"admin.all.vary_rounds":0.1}), result)
- # settings not allowed if not in hash.settings_kwds
+ # settings not allowed if not in hash.setting_kwds
ctx = CryptContext(["phpass", "md5_crypt"], phpass__ident="P")
self.assertRaises(KeyError, ctx.copy, md5_crypt__ident="P")
diff --git a/passlib/tests/test_crypto_builtin_md4.py b/passlib/tests/test_crypto_builtin_md4.py
new file mode 100644
index 0000000..0aca1eb
--- /dev/null
+++ b/passlib/tests/test_crypto_builtin_md4.py
@@ -0,0 +1,160 @@
+"""passlib.tests -- unittests for passlib.crypto._md4"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement, division
+# core
+from binascii import hexlify
+import hashlib
+# site
+# pkg
+# module
+from passlib.utils.compat import bascii_to_str, PY3, u
+from passlib.crypto.digest import lookup_hash
+from passlib.tests.utils import TestCase, skipUnless
+# local
+__all__ = [
+ "_Common_MD4_Test",
+ "MD4_Builtin_Test",
+ "MD4_SSL_Test",
+]
+#=============================================================================
+# test pure-python MD4 implementation
+#=============================================================================
+class _Common_MD4_Test(TestCase):
+ """common code for testing md4 backends"""
+
+ vectors = [
+ # input -> hex digest
+ # test vectors from http://www.faqs.org/rfcs/rfc1320.html - A.5
+ (b"", "31d6cfe0d16ae931b73c59d7e0c089c0"),
+ (b"a", "bde52cb31de33e46245e05fbdbd6fb24"),
+ (b"abc", "a448017aaf21d8525fc10ae87aa6729d"),
+ (b"message digest", "d9130a8164549fe818874806e1c7014b"),
+ (b"abcdefghijklmnopqrstuvwxyz", "d79e1c308aa5bbcdeea8ed63df412da9"),
+ (b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "043f8582f241db351ce627e153e7f0e4"),
+ (b"12345678901234567890123456789012345678901234567890123456789012345678901234567890", "e33b4ddc9c38f2199c3e7b164fcc0536"),
+ ]
+
+ def get_md4_const(self):
+ """
+ get md4 constructor --
+ overridden by subclasses to use alternate backends.
+ """
+ return lookup_hash("md4").const
+
+ def test_attrs(self):
+ """informational attributes"""
+ h = self.get_md4_const()()
+ self.assertEqual(h.name, "md4")
+ self.assertEqual(h.digest_size, 16)
+ self.assertEqual(h.block_size, 64)
+
+ def test_md4_update(self):
+ """update() method"""
+ md4 = self.get_md4_const()
+ h = md4(b'')
+ self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
+
+ h.update(b'a')
+ self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24")
+
+ h.update(b'bcdefghijklmnopqrstuvwxyz')
+ self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
+
+ if PY3:
+ # reject unicode, hash should return digest of b''
+ h = md4()
+ self.assertRaises(TypeError, h.update, u('a'))
+ self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
+ else:
+ # coerce unicode to ascii, hash should return digest of b'a'
+ h = md4()
+ h.update(u('a'))
+ self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24")
+
+ def test_md4_hexdigest(self):
+ """hexdigest() method"""
+ md4 = self.get_md4_const()
+ for input, hex in self.vectors:
+ out = md4(input).hexdigest()
+ self.assertEqual(out, hex)
+
+ def test_md4_digest(self):
+ """digest() method"""
+ md4 = self.get_md4_const()
+ for input, hex in self.vectors:
+ out = bascii_to_str(hexlify(md4(input).digest()))
+ self.assertEqual(out, hex)
+
+ def test_md4_copy(self):
+ """copy() method"""
+ md4 = self.get_md4_const()
+ h = md4(b'abc')
+
+ h2 = h.copy()
+ h2.update(b'def')
+ self.assertEqual(h2.hexdigest(), '804e7f1c2586e50b49ac65db5b645131')
+
+ h.update(b'ghi')
+ self.assertEqual(h.hexdigest(), 'c5225580bfe176f6deeee33dee98732c')
+
+
+#------------------------------------------------------------------------
+# create subclasses to test various backends
+#------------------------------------------------------------------------
+
+def has_native_md4(): # pragma: no cover -- runtime detection
+ """
+ check if hashlib natively supports md4.
+ """
+ try:
+ hashlib.new("md4")
+ return True
+ except ValueError:
+ # not supported - ssl probably missing (e.g. ironpython)
+ return False
+
+
+@skipUnless(has_native_md4(), "hashlib lacks ssl/md4 support")
+class MD4_SSL_Test(_Common_MD4_Test):
+ descriptionPrefix = "hashlib.new('md4')"
+
+ # NOTE: we trust ssl got md4 implementation right,
+ # this is more to test our test is correct :)
+
+ def setUp(self):
+ super(MD4_SSL_Test, self).setUp()
+
+ # make sure we're using right constructor.
+ self.assertEqual(self.get_md4_const().__module__, "hashlib")
+
+
+class MD4_Builtin_Test(_Common_MD4_Test):
+ descriptionPrefix = "passlib.crypto._md4.md4()"
+
+ def setUp(self):
+ super(MD4_Builtin_Test, self).setUp()
+
+ if has_native_md4():
+
+ # Temporarily make lookup_hash() use builtin pure-python implementation,
+ # by monkeypatching hashlib.new() to ensure we fall back to passlib's md4 class.
+ orig = hashlib.new
+ def wrapper(name, *args):
+ if name == "md4":
+ raise ValueError("md4 disabled for testing")
+ return orig(name, *args)
+ self.patchAttr(hashlib, "new", wrapper)
+
+ # flush cache before & after test, since we're mucking with it.
+ lookup_hash.clear_cache()
+ self.addCleanup(lookup_hash.clear_cache)
+
+ # make sure we're using right constructor.
+ self.assertEqual(self.get_md4_const().__module__, "passlib.crypto._md4")
+
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/tests/test_crypto_des.py b/passlib/tests/test_crypto_des.py
new file mode 100644
index 0000000..3203c24
--- /dev/null
+++ b/passlib/tests/test_crypto_des.py
@@ -0,0 +1,192 @@
+"""passlib.tests -- unittests for passlib.crypto.des"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement, division
+# core
+# site
+# pkg
+# module
+from passlib.utils import rng, getrandbytes
+from passlib.tests.utils import TestCase
+
+#=============================================================================
+# test DES routines
+#=============================================================================
+class DesTest(TestCase):
+ descriptionPrefix = "passlib.crypto.des"
+
+ # test vectors taken from http://www.skepticfiles.org/faq/testdes.htm
+ des_test_vectors = [
+ # key, plaintext, ciphertext
+ (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7),
+ (0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7359B2163E4EDC58),
+ (0x3000000000000000, 0x1000000000000001, 0x958E6E627A05557B),
+ (0x1111111111111111, 0x1111111111111111, 0xF40379AB9E0EC533),
+ (0x0123456789ABCDEF, 0x1111111111111111, 0x17668DFC7292532D),
+ (0x1111111111111111, 0x0123456789ABCDEF, 0x8A5AE1F81AB8F2DD),
+ (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7),
+ (0xFEDCBA9876543210, 0x0123456789ABCDEF, 0xED39D950FA74BCC4),
+ (0x7CA110454A1A6E57, 0x01A1D6D039776742, 0x690F5B0D9A26939B),
+ (0x0131D9619DC1376E, 0x5CD54CA83DEF57DA, 0x7A389D10354BD271),
+ (0x07A1133E4A0B2686, 0x0248D43806F67172, 0x868EBB51CAB4599A),
+ (0x3849674C2602319E, 0x51454B582DDF440A, 0x7178876E01F19B2A),
+ (0x04B915BA43FEB5B6, 0x42FD443059577FA2, 0xAF37FB421F8C4095),
+ (0x0113B970FD34F2CE, 0x059B5E0851CF143A, 0x86A560F10EC6D85B),
+ (0x0170F175468FB5E6, 0x0756D8E0774761D2, 0x0CD3DA020021DC09),
+ (0x43297FAD38E373FE, 0x762514B829BF486A, 0xEA676B2CB7DB2B7A),
+ (0x07A7137045DA2A16, 0x3BDD119049372802, 0xDFD64A815CAF1A0F),
+ (0x04689104C2FD3B2F, 0x26955F6835AF609A, 0x5C513C9C4886C088),
+ (0x37D06BB516CB7546, 0x164D5E404F275232, 0x0A2AEEAE3FF4AB77),
+ (0x1F08260D1AC2465E, 0x6B056E18759F5CCA, 0xEF1BF03E5DFA575A),
+ (0x584023641ABA6176, 0x004BD6EF09176062, 0x88BF0DB6D70DEE56),
+ (0x025816164629B007, 0x480D39006EE762F2, 0xA1F9915541020B56),
+ (0x49793EBC79B3258F, 0x437540C8698F3CFA, 0x6FBF1CAFCFFD0556),
+ (0x4FB05E1515AB73A7, 0x072D43A077075292, 0x2F22E49BAB7CA1AC),
+ (0x49E95D6D4CA229BF, 0x02FE55778117F12A, 0x5A6B612CC26CCE4A),
+ (0x018310DC409B26D6, 0x1D9D5C5018F728C2, 0x5F4C038ED12B2E41),
+ (0x1C587F1C13924FEF, 0x305532286D6F295A, 0x63FAC0D034D9F793),
+ (0x0101010101010101, 0x0123456789ABCDEF, 0x617B3A0CE8F07100),
+ (0x1F1F1F1F0E0E0E0E, 0x0123456789ABCDEF, 0xDB958605F8C8C606),
+ (0xE0FEE0FEF1FEF1FE, 0x0123456789ABCDEF, 0xEDBFD1C66C29CCC7),
+ (0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0x355550B2150E2451),
+ (0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0xCAAAAF4DEAF1DBAE),
+ (0x0123456789ABCDEF, 0x0000000000000000, 0xD5D44FF720683D0D),
+ (0xFEDCBA9876543210, 0xFFFFFFFFFFFFFFFF, 0x2A2BB008DF97C2F2),
+ ]
+
+ def test_01_expand(self):
+ """expand_des_key()"""
+ from passlib.crypto.des import expand_des_key, shrink_des_key, \
+ _KDATA_MASK, INT_56_MASK
+
+ # make sure test vectors are preserved (sans parity bits)
+ # uses ints, bytes are tested under # 02
+ for key1, _, _ in self.des_test_vectors:
+ key2 = shrink_des_key(key1)
+ key3 = expand_des_key(key2)
+ # NOTE: this assumes expand_des_key() sets parity bits to 0
+ self.assertEqual(key3, key1 & _KDATA_MASK)
+
+ # type checks
+ self.assertRaises(TypeError, expand_des_key, 1.0)
+
+ # too large
+ self.assertRaises(ValueError, expand_des_key, INT_56_MASK+1)
+ self.assertRaises(ValueError, expand_des_key, b"\x00"*8)
+
+ # too small
+ self.assertRaises(ValueError, expand_des_key, -1)
+ self.assertRaises(ValueError, expand_des_key, b"\x00"*6)
+
+ def test_02_shrink(self):
+ """shrink_des_key()"""
+ from passlib.crypto.des import expand_des_key, shrink_des_key, INT_64_MASK
+
+ # make sure reverse works for some random keys
+ # uses bytes, ints are tested under # 01
+ for i in range(20):
+ key1 = getrandbytes(rng, 7)
+ key2 = expand_des_key(key1)
+ key3 = shrink_des_key(key2)
+ self.assertEqual(key3, key1)
+
+ # type checks
+ self.assertRaises(TypeError, shrink_des_key, 1.0)
+
+ # too large
+ self.assertRaises(ValueError, shrink_des_key, INT_64_MASK+1)
+ self.assertRaises(ValueError, shrink_des_key, b"\x00"*9)
+
+ # too small
+ self.assertRaises(ValueError, shrink_des_key, -1)
+ self.assertRaises(ValueError, shrink_des_key, b"\x00"*7)
+
+ def _random_parity(self, key):
+ """randomize parity bits"""
+ from passlib.crypto.des import _KDATA_MASK, _KPARITY_MASK, INT_64_MASK
+ from passlib.utils import rng
+ return (key & _KDATA_MASK) | (rng.randint(0,INT_64_MASK) & _KPARITY_MASK)
+
+ def test_03_encrypt_bytes(self):
+ """des_encrypt_block()"""
+ from passlib.crypto.des import (des_encrypt_block, shrink_des_key,
+ _pack64, _unpack64)
+
+ # run through test vectors
+ for key, plaintext, correct in self.des_test_vectors:
+ # convert to bytes
+ key = _pack64(key)
+ plaintext = _pack64(plaintext)
+ correct = _pack64(correct)
+
+ # test 64-bit key
+ result = des_encrypt_block(key, plaintext)
+ self.assertEqual(result, correct, "key=%r plaintext=%r:" %
+ (key, plaintext))
+
+ # test 56-bit version
+ key2 = shrink_des_key(key)
+ result = des_encrypt_block(key2, plaintext)
+ self.assertEqual(result, correct, "key=%r shrink(key)=%r plaintext=%r:" %
+ (key, key2, plaintext))
+
+ # test with random parity bits
+ for _ in range(20):
+ key3 = _pack64(self._random_parity(_unpack64(key)))
+ result = des_encrypt_block(key3, plaintext)
+ self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" %
+ (key, key3, plaintext))
+
+ # check invalid keys
+ stub = b'\x00' * 8
+ self.assertRaises(TypeError, des_encrypt_block, 0, stub)
+ self.assertRaises(ValueError, des_encrypt_block, b'\x00'*6, stub)
+
+ # check invalid input
+ self.assertRaises(TypeError, des_encrypt_block, stub, 0)
+ self.assertRaises(ValueError, des_encrypt_block, stub, b'\x00'*7)
+
+ # check invalid salts
+ self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=-1)
+ self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=1<<24)
+
+ # check invalid rounds
+ self.assertRaises(ValueError, des_encrypt_block, stub, stub, 0, rounds=0)
+
+ def test_04_encrypt_ints(self):
+ """des_encrypt_int_block()"""
+ from passlib.crypto.des import des_encrypt_int_block
+
+ # run through test vectors
+ for key, plaintext, correct in self.des_test_vectors:
+ # test 64-bit key
+ result = des_encrypt_int_block(key, plaintext)
+ self.assertEqual(result, correct, "key=%r plaintext=%r:" %
+ (key, plaintext))
+
+ # test with random parity bits
+ for _ in range(20):
+ key3 = self._random_parity(key)
+ result = des_encrypt_int_block(key3, plaintext)
+ self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" %
+ (key, key3, plaintext))
+
+ # check invalid keys
+ self.assertRaises(TypeError, des_encrypt_int_block, b'\x00', 0)
+ self.assertRaises(ValueError, des_encrypt_int_block, -1, 0)
+
+ # check invalid input
+ self.assertRaises(TypeError, des_encrypt_int_block, 0, b'\x00')
+ self.assertRaises(ValueError, des_encrypt_int_block, 0, -1)
+
+ # check invalid salts
+ self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=-1)
+ self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=1<<24)
+
+ # check invalid rounds
+ self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, 0, rounds=0)
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/tests/test_crypto_digest.py b/passlib/tests/test_crypto_digest.py
new file mode 100644
index 0000000..4a179f0
--- /dev/null
+++ b/passlib/tests/test_crypto_digest.py
@@ -0,0 +1,416 @@
+"""tests for passlib.utils.(des|pbkdf2|md4)"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement, division
+# core
+from binascii import hexlify, unhexlify
+import hashlib
+import warnings
+# site
+# pkg
+# module
+from passlib.utils.compat import PY3, u, JYTHON
+from passlib.tests.utils import TestCase, TEST_MODE, skipUnless
+
+#=============================================================================
+# support
+#=============================================================================
+def hb(source):
+ """
+ helper for represent byte strings in hex.
+
+ usage: ``hb("deadbeef23")``
+ """
+ if PY3:
+ source = source.encode("ascii")
+ return unhexlify(source)
+
+#=============================================================================
+# test assorted crypto helpers
+#=============================================================================
+class HashInfoTest(TestCase):
+ """test various crypto functions"""
+ descriptionPrefix = "passlib.crypto.digest"
+
+ #: list of formats norm_hash_name() should support
+ norm_hash_formats = ["hashlib", "iana"]
+
+ #: test cases for norm_hash_name()
+ #: each row contains (iana name, hashlib name, ... 0+ unnormalized names)
+ norm_hash_samples = [
+ # real hashes
+ ("md5", "md5", "SCRAM-MD5-PLUS", "MD-5"),
+ ("sha1", "sha-1", "SCRAM-SHA-1", "SHA1"),
+ ("sha256", "sha-256", "SHA_256", "sha2-256"),
+ ("ripemd", "ripemd", "SCRAM-RIPEMD", "RIPEMD"),
+ ("ripemd160", "ripemd-160", "SCRAM-RIPEMD-160", "RIPEmd160"),
+
+ # fake hashes (to check if fallback normalization behaves sanely)
+ ("sha4_256", "sha4-256", "SHA4-256", "SHA-4-256"),
+ ("test128", "test-128", "TEST128"),
+ ("test2", "test2", "TEST-2"),
+ ("test3_128", "test3-128", "TEST-3-128"),
+ ]
+
+ def test_norm_hash_name(self):
+ """norm_hash_name()"""
+ from itertools import chain
+ from passlib.crypto.digest import norm_hash_name, _known_hash_names
+
+ # snapshot warning state, ignore unknown hash warnings
+ ctx = warnings.catch_warnings()
+ ctx.__enter__()
+ self.addCleanup(ctx.__exit__)
+ warnings.filterwarnings("ignore", '.*unknown hash')
+
+ # test string types
+ self.assertEqual(norm_hash_name(u("MD4")), "md4")
+ self.assertEqual(norm_hash_name(b"MD4"), "md4")
+ self.assertRaises(TypeError, norm_hash_name, None)
+
+ # test selected results
+ for row in chain(_known_hash_names, self.norm_hash_samples):
+ for idx, format in enumerate(self.norm_hash_formats):
+ correct = row[idx]
+ for value in row:
+ result = norm_hash_name(value, format)
+ self.assertEqual(result, correct,
+ "name=%r, format=%r:" % (value,
+ format))
+
+ def test_lookup_hash_ctor(self):
+ """lookup_hash() -- constructor"""
+ from passlib.crypto.digest import lookup_hash
+
+ # invalid/unknown names should be rejected
+ self.assertRaises(ValueError, lookup_hash, "new")
+ self.assertRaises(ValueError, lookup_hash, "__name__")
+ self.assertRaises(ValueError, lookup_hash, "sha4")
+
+ # 1. should return hashlib builtin if found
+ self.assertEqual(lookup_hash("md5"), (hashlib.md5, 16, 64))
+
+ # 2. should return wrapper around hashlib.new() if found
+ try:
+ hashlib.new("sha")
+ has_sha = True
+ except ValueError:
+ has_sha = False
+ if has_sha:
+ record = lookup_hash("sha")
+ const = record[0]
+ self.assertEqual(record, (const, 20, 64))
+ self.assertEqual(hexlify(const(b"abc").digest()),
+ b"0164b8a914cd2a5e74c4f7ff082c4d97f1edf880")
+
+ else:
+ self.assertRaises(ValueError, lookup_hash, "sha")
+
+ # 3. should fall back to builtin md4
+ try:
+ hashlib.new("md4")
+ has_md4 = True
+ except ValueError:
+ has_md4 = False
+ record = lookup_hash("md4")
+ const = record[0]
+ if not has_md4:
+ from passlib.crypto._md4 import md4
+ self.assertIs(const, md4)
+ self.assertEqual(record, (const, 16, 64))
+ self.assertEqual(hexlify(const(b"abc").digest()),
+ b"a448017aaf21d8525fc10ae87aa6729d")
+
+ # 4. unknown names should be rejected
+ self.assertRaises(ValueError, lookup_hash, "xxx256")
+
+ # should memoize records
+ self.assertIs(lookup_hash("md5"), lookup_hash("md5"))
+
+ def test_lookup_hash_metadata(self):
+ """lookup_hash() -- metadata"""
+
+ from passlib.crypto.digest import lookup_hash
+
+ # quick test of metadata using known reference - sha256
+ info = lookup_hash("sha256")
+ self.assertEqual(info.name, "sha256")
+ self.assertEqual(info.iana_name, "sha-256")
+ self.assertEqual(info.block_size, 64)
+ self.assertEqual(info.digest_size, 32)
+ self.assertIs(lookup_hash("SHA2-256"), info)
+
+ # quick test of metadata using known reference - md5
+ info = lookup_hash("md5")
+ self.assertEqual(info.name, "md5")
+ self.assertEqual(info.iana_name, "md5")
+ self.assertEqual(info.block_size, 64)
+ self.assertEqual(info.digest_size, 16)
+
+ def test_lookup_hash_alt_types(self):
+ """lookup_hash() -- alternate types"""
+
+ from passlib.crypto.digest import lookup_hash
+
+ info = lookup_hash("sha256")
+ self.assertIs(lookup_hash(info), info)
+ self.assertIs(lookup_hash(info.const), info)
+
+ self.assertRaises(TypeError, lookup_hash, 123)
+
+ # TODO: write full test of compile_hmac() -- currently relying on pbkdf2_hmac() tests
+
+#=============================================================================
+# test PBKDF1 support
+#=============================================================================
+class Pbkdf1_Test(TestCase):
+ """test kdf helpers"""
+ descriptionPrefix = "passlib.crypto.digest.pbkdf1"
+
+ pbkdf1_tests = [
+ # (password, salt, rounds, keylen, hash, result)
+
+ #
+ # from http://www.di-mgt.com.au/cryptoKDFs.html
+ #
+ (b'password', hb('78578E5A5D63CB06'), 1000, 16, 'sha1', hb('dc19847e05c64d2faf10ebfb4a3d2a20')),
+
+ #
+ # custom
+ #
+ (b'password', b'salt', 1000, 0, 'md5', b''),
+ (b'password', b'salt', 1000, 1, 'md5', hb('84')),
+ (b'password', b'salt', 1000, 8, 'md5', hb('8475c6a8531a5d27')),
+ (b'password', b'salt', 1000, 16, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
+ (b'password', b'salt', 1000, None, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
+ (b'password', b'salt', 1000, None, 'sha1', hb('4a8fd48e426ed081b535be5769892fa396293efb')),
+ ]
+ if not JYTHON: # FIXME: find out why not jython, or reenable this.
+ pbkdf1_tests.append(
+ (b'password', b'salt', 1000, None, 'md4', hb('f7f2e91100a8f96190f2dd177cb26453'))
+ )
+
+ def test_known(self):
+ """test reference vectors"""
+ from passlib.crypto.digest import pbkdf1
+ for secret, salt, rounds, keylen, digest, correct in self.pbkdf1_tests:
+ result = pbkdf1(digest, secret, salt, rounds, keylen)
+ self.assertEqual(result, correct)
+
+ def test_border(self):
+ """test border cases"""
+ from passlib.crypto.digest import pbkdf1
+ def helper(secret=b'secret', salt=b'salt', rounds=1, keylen=1, hash='md5'):
+ return pbkdf1(hash, secret, salt, rounds, keylen)
+ helper()
+
+ # salt/secret wrong type
+ self.assertRaises(TypeError, helper, secret=1)
+ self.assertRaises(TypeError, helper, salt=1)
+
+ # non-existent hashes
+ self.assertRaises(ValueError, helper, hash='missing')
+
+ # rounds < 1 and wrong type
+ self.assertRaises(ValueError, helper, rounds=0)
+ self.assertRaises(TypeError, helper, rounds='1')
+
+ # keylen < 0, keylen > block_size, and wrong type
+ self.assertRaises(ValueError, helper, keylen=-1)
+ self.assertRaises(ValueError, helper, keylen=17, hash='md5')
+ self.assertRaises(TypeError, helper, keylen='1')
+
+#=============================================================================
+# test PBKDF2-HMAC support
+#=============================================================================
+
+# import the test subject
+from passlib.crypto.digest import pbkdf2_hmac
+
+class _Common_Pbkdf2_Test(TestCase):
+ """test pbkdf2() support"""
+
+ pbkdf2_test_vectors = [
+ # (result, secret, salt, rounds, keylen, digest="sha1")
+
+ #
+ # from rfc 3962
+ #
+
+ # test case 1 / 128 bit
+ (
+ hb("cdedb5281bb2f801565a1122b2563515"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 1, 16
+ ),
+
+ # test case 2 / 128 bit
+ (
+ hb("01dbee7f4a9e243e988b62c73cda935d"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 2, 16
+ ),
+
+ # test case 2 / 256 bit
+ (
+ hb("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 2, 32
+ ),
+
+ # test case 3 / 256 bit
+ (
+ hb("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 1200, 32
+ ),
+
+ # test case 4 / 256 bit
+ (
+ hb("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"),
+ b"password", b'\x12\x34\x56\x78\x78\x56\x34\x12', 5, 32
+ ),
+
+ # test case 5 / 256 bit
+ (
+ hb("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"),
+ b"X"*64, b"pass phrase equals block size", 1200, 32
+ ),
+
+ # test case 6 / 256 bit
+ (
+ hb("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"),
+ b"X"*65, b"pass phrase exceeds block size", 1200, 32
+ ),
+
+ #
+ # from rfc 6070
+ #
+ (
+ hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"),
+ b"password", b"salt", 1, 20,
+ ),
+
+ (
+ hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"),
+ b"password", b"salt", 2, 20,
+ ),
+
+ (
+ hb("4b007901b765489abead49d926f721d065a429c1"),
+ b"password", b"salt", 4096, 20,
+ ),
+
+ # just runs too long - could enable if ALL option is set
+ ##(
+ ##
+ ## hb("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"),
+ ## "password", "salt", 16777216, 20,
+ ##),
+
+ (
+ hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"),
+ b"passwordPASSWORDpassword",
+ b"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ 4096, 25,
+ ),
+
+ (
+ hb("56fa6aa75548099dcc37d7f03425e0c3"),
+ b"pass\00word", b"sa\00lt", 4096, 16,
+ ),
+
+ #
+ # from example in http://grub.enbug.org/Authentication
+ #
+ (
+ hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED"
+ "97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC"
+ "6C29E293F0A0"),
+ b"hello",
+ hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71"
+ "784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073"
+ "994D79080136"),
+ 10000, 64, "sha512"
+ ),
+
+ #
+ # custom tests
+ #
+ (
+ hb('e248fb6b13365146f8ac6307cc222812'),
+ b"secret", b"salt", 10, 16, "sha1",
+ ),
+ (
+ hb('e248fb6b13365146f8ac6307cc2228127872da6d'),
+ b"secret", b"salt", 10, None, "sha1",
+ ),
+
+ ]
+
+ def test_known(self):
+ """test reference vectors"""
+ for row in self.pbkdf2_test_vectors:
+ correct, secret, salt, rounds, keylen = row[:5]
+ digest = row[5] if len(row) == 6 else "sha1"
+ result = pbkdf2_hmac(digest, secret, salt, rounds, keylen)
+ self.assertEqual(result, correct)
+
+ def test_border(self):
+ """test border cases"""
+ def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, digest="sha1"):
+ return pbkdf2_hmac(digest, secret, salt, rounds, keylen)
+ helper()
+
+ # invalid rounds
+ self.assertRaises(ValueError, helper, rounds=0)
+ self.assertRaises(TypeError, helper, rounds='x')
+
+ # invalid keylen
+ helper(keylen=0)
+ self.assertRaises(ValueError, helper, keylen=-1)
+ self.assertRaises(ValueError, helper, keylen=20*(2**32-1)+1)
+ self.assertRaises(TypeError, helper, keylen='x')
+
+ # invalid secret/salt type
+ self.assertRaises(TypeError, helper, salt=5)
+ self.assertRaises(TypeError, helper, secret=5)
+
+ # invalid hash
+ self.assertRaises(ValueError, helper, digest='foo')
+ self.assertRaises(TypeError, helper, digest=5)
+
+ def test_default_keylen(self):
+ """test keylen==None"""
+ def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, digest="sha1"):
+ return pbkdf2_hmac(digest, secret, salt, rounds, keylen)
+ self.assertEqual(len(helper(digest='sha1')), 20)
+ self.assertEqual(len(helper(digest='sha256')), 32)
+
+#------------------------------------------------------------------------
+# create subclasses to test with- and without- m2crypto
+#------------------------------------------------------------------------
+
+def has_m2crypto():
+ try:
+ import M2Crypto
+ return True
+ except ImportError:
+ return False
+
+@skipUnless(has_m2crypto(), "M2Crypto not found")
+class Pbkdf2_M2Crypto_Test(_Common_Pbkdf2_Test):
+ descriptionPrefix = "passlib.crypto.digest.pbkdf2_hmac() <m2crypto backend>"
+
+@skipUnless(TEST_MODE("full") or not has_m2crypto(), "skipped under current test mode")
+class Pbkdf2_Builtin_Test(_Common_Pbkdf2_Test):
+ descriptionPrefix = "passlib.crypto.digest.pbkdf2_hmac() <builtin backend>"
+
+ def setUp(self):
+ super(Pbkdf2_Builtin_Test, self).setUp()
+ # make sure m2crypto support is disabled, to force pure-python backend
+ import passlib.crypto.digest as mod
+ self.addCleanup(setattr, mod, "_m2crypto_pbkdf2_hmac_sha1",
+ mod._m2crypto_pbkdf2_hmac_sha1)
+ mod._m2crypto_pbkdf2_hmac_sha1 = None
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py
index 7992f82..348411c 100644
--- a/passlib/tests/test_handlers.py
+++ b/passlib/tests/test_handlers.py
@@ -1760,8 +1760,8 @@ class scram_test(HandlerCase):
# check rounds
self.assertRaises(ValueError, hash, "IX", s1, 0, 'sha-1')
- # bad types
- self.assertRaises(TypeError, hash, "IX", u('\x01'), 1000, 'md5')
+ # unicode salts accepted as of passlib 1.7 (previous caused TypeError)
+ self.assertEqual(hash(u("IX"), s1.decode("latin-1"), 1000, 'sha1'), d1)
def test_94_saslprep(self):
"""test encrypt/verify use saslprep"""
diff --git a/passlib/tests/test_totp.py b/passlib/tests/test_totp.py
index 0b8647d..41fba5a 100644
--- a/passlib/tests/test_totp.py
+++ b/passlib/tests/test_totp.py
@@ -15,7 +15,6 @@ import time as _time
# pkg
from passlib import exc
from passlib.utils import to_bytes, to_unicode
-from passlib.utils.pbkdf2 import _clear_caches
from passlib.utils.compat import unicode, u
from passlib.tests.utils import TestCase
# local
@@ -267,7 +266,8 @@ class _BaseOTPTest(TestCase):
super(_BaseOTPTest, self).setUp()
# clear norm_hash_name() cache so 'unknown hash' warnings get emitted each time
- _clear_caches()
+ from passlib.crypto.digest import lookup_hash
+ lookup_hash.clear_cache()
#=============================================================================
# subclass utils
@@ -382,10 +382,7 @@ class _BaseOTPTest(TestCase):
self.assertEqual(OTP(KEY1, alg="SHA256").alg, "sha256")
# invalid alg
- with self.assertWarningList([
- dict(category=exc.PasslibRuntimeWarning, message_re="unknown hash.*SHA-333")
- ]):
- self.assertRaises(ValueError, OTP, KEY1, alg="SHA-333")
+ self.assertRaises(ValueError, OTP, KEY1, alg="SHA-333")
def test_ctor_w_digits(self):
"""constructor -- 'digits' parameter"""
@@ -1265,9 +1262,8 @@ class TotpTest(_BaseOTPTest):
self.assertEqual(otp.alg, "sha256")
# unknown alg
- with self.assertWarningList([exc.PasslibRuntimeWarning]):
- self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?"
- "secret=JBSWY3DPEHPK3PXP&algorithm=SHA333")
+ self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?"
+ "secret=JBSWY3DPEHPK3PXP&algorithm=SHA333")
#--------------------------------------------------------------------------------
# digit param
@@ -1962,12 +1958,9 @@ class HotpTest(_BaseOTPTest):
self.assertEqual(otp.alg, "sha256")
# unknown alg
- with self.assertWarningList([
- dict(category=exc.PasslibRuntimeWarning, message_re="unknown hash.*SHA333")
- ]):
- self.assertRaises(ValueError, from_uri, "otpauth://hotp/Example:alice@google.com?"
- "secret=JBSWY3DPEHPK3PXP&counter=123"
- "&algorithm=SHA333")
+ self.assertRaises(ValueError, from_uri, "otpauth://hotp/Example:alice@google.com?"
+ "secret=JBSWY3DPEHPK3PXP&counter=123"
+ "&algorithm=SHA333")
#--------------------------------------------------------------------------------
# digit param
diff --git a/passlib/tests/test_utils_crypto.py b/passlib/tests/test_utils_crypto.py
deleted file mode 100644
index 4c909f4..0000000
--- a/passlib/tests/test_utils_crypto.py
+++ /dev/null
@@ -1,652 +0,0 @@
-"""tests for passlib.utils.(des|pbkdf2|md4)"""
-#=============================================================================
-# imports
-#=============================================================================
-from __future__ import with_statement
-# core
-from binascii import hexlify, unhexlify
-import hashlib
-import warnings
-# site
-try:
- import M2Crypto
-except ImportError:
- M2Crypto = None
-# pkg
-# module
-from passlib.utils.compat import bascii_to_str, PY3, u, JYTHON
-from passlib.tests.utils import TestCase, TEST_MODE, skipUnless
-
-#=============================================================================
-# support
-#=============================================================================
-def hb(source):
- """
- helper for represent byte strings in hex.
-
- usage: ``hb("deadbeef23")``
- """
- if PY3:
- source = source.encode("ascii")
- return unhexlify(source)
-
-#=============================================================================
-# test assorted crypto helpers
-#=============================================================================
-class CryptoTest(TestCase):
- """test various crypto functions"""
-
- ndn_formats = ["hashlib", "iana"]
- ndn_values = [
- # (iana name, hashlib name, ... other unnormalized names)
- ("md5", "md5", "SCRAM-MD5-PLUS", "MD-5"),
- ("sha1", "sha-1", "SCRAM-SHA-1", "SHA1"),
- ("sha256", "sha-256", "SHA_256", "sha2-256"),
- ("ripemd", "ripemd", "SCRAM-RIPEMD", "RIPEMD"),
- ("ripemd160", "ripemd-160",
- "SCRAM-RIPEMD-160", "RIPEmd160"),
- ("test128", "test-128", "TEST128"),
- ("test2", "test2", "TEST-2"),
- ("test3128", "test3-128", "TEST-3-128"),
- ]
-
- def test_norm_hash_name(self):
- """test norm_hash_name()"""
- from itertools import chain
- from passlib.utils.pbkdf2 import norm_hash_name, _nhn_hash_names
-
- # test formats
- for format in self.ndn_formats:
- norm_hash_name("md4", format)
- self.assertRaises(ValueError, norm_hash_name, "md4", None)
- self.assertRaises(ValueError, norm_hash_name, "md4", "fake")
-
- # test types
- self.assertEqual(norm_hash_name(u("MD4")), "md4")
- self.assertEqual(norm_hash_name(b"MD4"), "md4")
- self.assertRaises(TypeError, norm_hash_name, None)
-
- # test selected results
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", '.*unknown hash')
- for row in chain(_nhn_hash_names, self.ndn_values):
- for idx, format in enumerate(self.ndn_formats):
- correct = row[idx]
- for value in row:
- result = norm_hash_name(value, format)
- self.assertEqual(result, correct,
- "name=%r, format=%r:" % (value,
- format))
-
- def test_get_hash_info(self):
- """test get_hash_info()"""
- import hashlib
- from passlib.utils.pbkdf2 import get_hash_info
-
- # invalid names should be rejected
- self.assertRaises(ValueError, get_hash_info, "new")
- self.assertRaises(ValueError, get_hash_info, "__name__")
-
- # 1. should return hashlib builtin if found
- self.assertEqual(get_hash_info("md5"), (hashlib.md5, 16, 64))
-
- # 2. should return wrapper around hashlib.new() if found
- try:
- hashlib.new("sha")
- has_sha = True
- except ValueError:
- has_sha = False
- if has_sha:
- record = get_hash_info("sha")
- const = record[0]
- self.assertEqual(record, (const, 20, 64))
- self.assertEqual(hexlify(const(b"abc").digest()),
- b"0164b8a914cd2a5e74c4f7ff082c4d97f1edf880")
-
- else:
- self.assertRaises(ValueError, get_hash_info, "sha")
-
- # 3. should fall back to builtin md4
- try:
- hashlib.new("md4")
- has_md4 = True
- except ValueError:
- has_md4 = False
- record = get_hash_info("md4")
- const = record[0]
- if not has_md4:
- from passlib.utils.md4 import md4
- self.assertIs(const, md4)
- self.assertEqual(record, (const, 16, 64))
- self.assertEqual(hexlify(const(b"abc").digest()),
- b"a448017aaf21d8525fc10ae87aa6729d")
-
- # 4. unknown names should be rejected
- self.assertRaises(ValueError, get_hash_info, "xxx256")
-
- # should memoize records
- self.assertIs(get_hash_info("md5"), get_hash_info("md5"))
-
- # TODO: write full test of get_prf()
- # TODO: write full test of get_keyed_prf() -- currently relying on pbkdf2() tests
-
-#=============================================================================
-# test DES routines
-#=============================================================================
-class DesTest(TestCase):
- descriptionPrefix = "DES"
-
- # test vectors taken from http://www.skepticfiles.org/faq/testdes.htm
- des_test_vectors = [
- # key, plaintext, ciphertext
- (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7),
- (0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7359B2163E4EDC58),
- (0x3000000000000000, 0x1000000000000001, 0x958E6E627A05557B),
- (0x1111111111111111, 0x1111111111111111, 0xF40379AB9E0EC533),
- (0x0123456789ABCDEF, 0x1111111111111111, 0x17668DFC7292532D),
- (0x1111111111111111, 0x0123456789ABCDEF, 0x8A5AE1F81AB8F2DD),
- (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7),
- (0xFEDCBA9876543210, 0x0123456789ABCDEF, 0xED39D950FA74BCC4),
- (0x7CA110454A1A6E57, 0x01A1D6D039776742, 0x690F5B0D9A26939B),
- (0x0131D9619DC1376E, 0x5CD54CA83DEF57DA, 0x7A389D10354BD271),
- (0x07A1133E4A0B2686, 0x0248D43806F67172, 0x868EBB51CAB4599A),
- (0x3849674C2602319E, 0x51454B582DDF440A, 0x7178876E01F19B2A),
- (0x04B915BA43FEB5B6, 0x42FD443059577FA2, 0xAF37FB421F8C4095),
- (0x0113B970FD34F2CE, 0x059B5E0851CF143A, 0x86A560F10EC6D85B),
- (0x0170F175468FB5E6, 0x0756D8E0774761D2, 0x0CD3DA020021DC09),
- (0x43297FAD38E373FE, 0x762514B829BF486A, 0xEA676B2CB7DB2B7A),
- (0x07A7137045DA2A16, 0x3BDD119049372802, 0xDFD64A815CAF1A0F),
- (0x04689104C2FD3B2F, 0x26955F6835AF609A, 0x5C513C9C4886C088),
- (0x37D06BB516CB7546, 0x164D5E404F275232, 0x0A2AEEAE3FF4AB77),
- (0x1F08260D1AC2465E, 0x6B056E18759F5CCA, 0xEF1BF03E5DFA575A),
- (0x584023641ABA6176, 0x004BD6EF09176062, 0x88BF0DB6D70DEE56),
- (0x025816164629B007, 0x480D39006EE762F2, 0xA1F9915541020B56),
- (0x49793EBC79B3258F, 0x437540C8698F3CFA, 0x6FBF1CAFCFFD0556),
- (0x4FB05E1515AB73A7, 0x072D43A077075292, 0x2F22E49BAB7CA1AC),
- (0x49E95D6D4CA229BF, 0x02FE55778117F12A, 0x5A6B612CC26CCE4A),
- (0x018310DC409B26D6, 0x1D9D5C5018F728C2, 0x5F4C038ED12B2E41),
- (0x1C587F1C13924FEF, 0x305532286D6F295A, 0x63FAC0D034D9F793),
- (0x0101010101010101, 0x0123456789ABCDEF, 0x617B3A0CE8F07100),
- (0x1F1F1F1F0E0E0E0E, 0x0123456789ABCDEF, 0xDB958605F8C8C606),
- (0xE0FEE0FEF1FEF1FE, 0x0123456789ABCDEF, 0xEDBFD1C66C29CCC7),
- (0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0x355550B2150E2451),
- (0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0xCAAAAF4DEAF1DBAE),
- (0x0123456789ABCDEF, 0x0000000000000000, 0xD5D44FF720683D0D),
- (0xFEDCBA9876543210, 0xFFFFFFFFFFFFFFFF, 0x2A2BB008DF97C2F2),
- ]
-
- def test_01_expand(self):
- """test expand_des_key()"""
- from passlib.utils.des import expand_des_key, shrink_des_key, \
- _KDATA_MASK, INT_56_MASK
-
- # make sure test vectors are preserved (sans parity bits)
- # uses ints, bytes are tested under # 02
- for key1, _, _ in self.des_test_vectors:
- key2 = shrink_des_key(key1)
- key3 = expand_des_key(key2)
- # NOTE: this assumes expand_des_key() sets parity bits to 0
- self.assertEqual(key3, key1 & _KDATA_MASK)
-
- # type checks
- self.assertRaises(TypeError, expand_des_key, 1.0)
-
- # too large
- self.assertRaises(ValueError, expand_des_key, INT_56_MASK+1)
- self.assertRaises(ValueError, expand_des_key, b"\x00"*8)
-
- # too small
- self.assertRaises(ValueError, expand_des_key, -1)
- self.assertRaises(ValueError, expand_des_key, b"\x00"*6)
-
- def test_02_shrink(self):
- """test shrink_des_key()"""
- from passlib.utils.des import expand_des_key, shrink_des_key, \
- INT_64_MASK
- from passlib.utils import random, getrandbytes
-
- # make sure reverse works for some random keys
- # uses bytes, ints are tested under # 01
- for i in range(20):
- key1 = getrandbytes(random, 7)
- key2 = expand_des_key(key1)
- key3 = shrink_des_key(key2)
- self.assertEqual(key3, key1)
-
- # type checks
- self.assertRaises(TypeError, shrink_des_key, 1.0)
-
- # too large
- self.assertRaises(ValueError, shrink_des_key, INT_64_MASK+1)
- self.assertRaises(ValueError, shrink_des_key, b"\x00"*9)
-
- # too small
- self.assertRaises(ValueError, shrink_des_key, -1)
- self.assertRaises(ValueError, shrink_des_key, b"\x00"*7)
-
- def _random_parity(self, key):
- """randomize parity bits"""
- from passlib.utils.des import _KDATA_MASK, _KPARITY_MASK, INT_64_MASK
- from passlib.utils import rng
- return (key & _KDATA_MASK) | (rng.randint(0,INT_64_MASK) & _KPARITY_MASK)
-
- def test_03_encrypt_bytes(self):
- """test des_encrypt_block()"""
- from passlib.utils.des import (des_encrypt_block, shrink_des_key,
- _pack64, _unpack64)
-
- # run through test vectors
- for key, plaintext, correct in self.des_test_vectors:
- # convert to bytes
- key = _pack64(key)
- plaintext = _pack64(plaintext)
- correct = _pack64(correct)
-
- # test 64-bit key
- result = des_encrypt_block(key, plaintext)
- self.assertEqual(result, correct, "key=%r plaintext=%r:" %
- (key, plaintext))
-
- # test 56-bit version
- key2 = shrink_des_key(key)
- result = des_encrypt_block(key2, plaintext)
- self.assertEqual(result, correct, "key=%r shrink(key)=%r plaintext=%r:" %
- (key, key2, plaintext))
-
- # test with random parity bits
- for _ in range(20):
- key3 = _pack64(self._random_parity(_unpack64(key)))
- result = des_encrypt_block(key3, plaintext)
- self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" %
- (key, key3, plaintext))
-
- # check invalid keys
- stub = b'\x00' * 8
- self.assertRaises(TypeError, des_encrypt_block, 0, stub)
- self.assertRaises(ValueError, des_encrypt_block, b'\x00'*6, stub)
-
- # check invalid input
- self.assertRaises(TypeError, des_encrypt_block, stub, 0)
- self.assertRaises(ValueError, des_encrypt_block, stub, b'\x00'*7)
-
- # check invalid salts
- self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=-1)
- self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=1<<24)
-
- # check invalid rounds
- self.assertRaises(ValueError, des_encrypt_block, stub, stub, 0, rounds=0)
-
- def test_04_encrypt_ints(self):
- """test des_encrypt_int_block()"""
- from passlib.utils.des import des_encrypt_int_block
-
- # run through test vectors
- for key, plaintext, correct in self.des_test_vectors:
- # test 64-bit key
- result = des_encrypt_int_block(key, plaintext)
- self.assertEqual(result, correct, "key=%r plaintext=%r:" %
- (key, plaintext))
-
- # test with random parity bits
- for _ in range(20):
- key3 = self._random_parity(key)
- result = des_encrypt_int_block(key3, plaintext)
- self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" %
- (key, key3, plaintext))
-
- # check invalid keys
- self.assertRaises(TypeError, des_encrypt_int_block, b'\x00', 0)
- self.assertRaises(ValueError, des_encrypt_int_block, -1, 0)
-
- # check invalid input
- self.assertRaises(TypeError, des_encrypt_int_block, 0, b'\x00')
- self.assertRaises(ValueError, des_encrypt_int_block, 0, -1)
-
- # check invalid salts
- self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=-1)
- self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=1<<24)
-
- # check invalid rounds
- self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, 0, rounds=0)
-
-#=============================================================================
-# test pure-python MD4 implementation
-#=============================================================================
-from passlib.utils.md4 import _has_native_md4
-has_native_md4 = _has_native_md4()
-
-class _MD4_Test(TestCase):
- _disable_native = False
-
- def setUp(self):
- super(_MD4_Test, self).setUp()
- import passlib.utils.md4 as mod
- if has_native_md4 and self._disable_native:
- self.addCleanup(setattr, mod, "md4", mod.md4)
- mod.md4 = mod._builtin_md4
-
- vectors = [
- # input -> hex digest
- # test vectors from http://www.faqs.org/rfcs/rfc1320.html - A.5
- (b"", "31d6cfe0d16ae931b73c59d7e0c089c0"),
- (b"a", "bde52cb31de33e46245e05fbdbd6fb24"),
- (b"abc", "a448017aaf21d8525fc10ae87aa6729d"),
- (b"message digest", "d9130a8164549fe818874806e1c7014b"),
- (b"abcdefghijklmnopqrstuvwxyz", "d79e1c308aa5bbcdeea8ed63df412da9"),
- (b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "043f8582f241db351ce627e153e7f0e4"),
- (b"12345678901234567890123456789012345678901234567890123456789012345678901234567890", "e33b4ddc9c38f2199c3e7b164fcc0536"),
- ]
-
- def test_md4_update(self):
- """test md4 update"""
- from passlib.utils.md4 import md4
- h = md4(b'')
- self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
-
- # NOTE: under py2, hashlib methods try to encode to ascii,
- # though shouldn't rely on that.
- if PY3 or self._disable_native:
- self.assertRaises(TypeError, h.update, u('x'))
-
- h.update(b'a')
- self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24")
-
- h.update(b'bcdefghijklmnopqrstuvwxyz')
- self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
-
- def test_md4_hexdigest(self):
- """test md4 hexdigest()"""
- from passlib.utils.md4 import md4
- for input, hex in self.vectors:
- out = md4(input).hexdigest()
- self.assertEqual(out, hex)
-
- def test_md4_digest(self):
- """test md4 digest()"""
- from passlib.utils.md4 import md4
- for input, hex in self.vectors:
- out = bascii_to_str(hexlify(md4(input).digest()))
- self.assertEqual(out, hex)
-
- def test_md4_copy(self):
- """test md4 copy()"""
- from passlib.utils.md4 import md4
- h = md4(b'abc')
-
- h2 = h.copy()
- h2.update(b'def')
- self.assertEqual(h2.hexdigest(), '804e7f1c2586e50b49ac65db5b645131')
-
- h.update(b'ghi')
- self.assertEqual(h.hexdigest(), 'c5225580bfe176f6deeee33dee98732c')
-
-# create subclasses to test with and without native backend
-@skipUnless(has_native_md4, "hashlib lacks ssl support")
-class MD4_SSL_Test(_MD4_Test):
- descriptionPrefix = "MD4 (ssl version)"
-
-@skipUnless(TEST_MODE("full") or not has_native_md4, "skipped under current test mode")
-class MD4_Builtin_Test(_MD4_Test):
- descriptionPrefix = "MD4 (builtin version)"
- _disable_native = True
-
-#=============================================================================
-# test PBKDF1 support
-#=============================================================================
-class Pbkdf1_Test(TestCase):
- """test kdf helpers"""
- descriptionPrefix = "pbkdf1"
-
- pbkdf1_tests = [
- # (password, salt, rounds, keylen, hash, result)
-
- #
- # from http://www.di-mgt.com.au/cryptoKDFs.html
- #
- (b'password', hb('78578E5A5D63CB06'), 1000, 16, 'sha1', hb('dc19847e05c64d2faf10ebfb4a3d2a20')),
-
- #
- # custom
- #
- (b'password', b'salt', 1000, 0, 'md5', b''),
- (b'password', b'salt', 1000, 1, 'md5', hb('84')),
- (b'password', b'salt', 1000, 8, 'md5', hb('8475c6a8531a5d27')),
- (b'password', b'salt', 1000, 16, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
- (b'password', b'salt', 1000, None, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
- (b'password', b'salt', 1000, None, 'sha1', hb('4a8fd48e426ed081b535be5769892fa396293efb')),
- ]
- if not JYTHON:
- pbkdf1_tests.append(
- (b'password', b'salt', 1000, None, 'md4', hb('f7f2e91100a8f96190f2dd177cb26453'))
- )
-
- def test_known(self):
- """test reference vectors"""
- from passlib.utils.pbkdf2 import pbkdf1
- for secret, salt, rounds, keylen, digest, correct in self.pbkdf1_tests:
- result = pbkdf1(secret, salt, rounds, keylen, digest)
- self.assertEqual(result, correct)
-
- def test_border(self):
- """test border cases"""
- from passlib.utils.pbkdf2 import pbkdf1
- def helper(secret=b'secret', salt=b'salt', rounds=1, keylen=1, hash='md5'):
- return pbkdf1(secret, salt, rounds, keylen, hash)
- helper()
-
- # salt/secret wrong type
- self.assertRaises(TypeError, helper, secret=1)
- self.assertRaises(TypeError, helper, salt=1)
-
- # non-existent hashes
- self.assertRaises(ValueError, helper, hash='missing')
-
- # rounds < 1 and wrong type
- self.assertRaises(ValueError, helper, rounds=0)
- self.assertRaises(TypeError, helper, rounds='1')
-
- # keylen < 0, keylen > block_size, and wrong type
- self.assertRaises(ValueError, helper, keylen=-1)
- self.assertRaises(ValueError, helper, keylen=17, hash='md5')
- self.assertRaises(TypeError, helper, keylen='1')
-
-#=============================================================================
-# test PBKDF2 support
-#=============================================================================
-class _Pbkdf2_Test(TestCase):
- """test pbkdf2() support"""
-
- def setUp(self):
- super(_Pbkdf2_Test, self).setUp()
- # flush cached prf functions, since we're screwing with their backend.
- from passlib.utils.pbkdf2 import _clear_caches
- _clear_caches()
- self.addCleanup(_clear_caches)
-
- pbkdf2_test_vectors = [
- # (result, secret, salt, rounds, keylen, prf="sha1")
-
- #
- # from rfc 3962
- #
-
- # test case 1 / 128 bit
- (
- hb("cdedb5281bb2f801565a1122b2563515"),
- b"password", b"ATHENA.MIT.EDUraeburn", 1, 16
- ),
-
- # test case 2 / 128 bit
- (
- hb("01dbee7f4a9e243e988b62c73cda935d"),
- b"password", b"ATHENA.MIT.EDUraeburn", 2, 16
- ),
-
- # test case 2 / 256 bit
- (
- hb("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"),
- b"password", b"ATHENA.MIT.EDUraeburn", 2, 32
- ),
-
- # test case 3 / 256 bit
- (
- hb("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"),
- b"password", b"ATHENA.MIT.EDUraeburn", 1200, 32
- ),
-
- # test case 4 / 256 bit
- (
- hb("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"),
- b"password", b'\x12\x34\x56\x78\x78\x56\x34\x12', 5, 32
- ),
-
- # test case 5 / 256 bit
- (
- hb("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"),
- b"X"*64, b"pass phrase equals block size", 1200, 32
- ),
-
- # test case 6 / 256 bit
- (
- hb("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"),
- b"X"*65, b"pass phrase exceeds block size", 1200, 32
- ),
-
- #
- # from rfc 6070
- #
- (
- hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"),
- b"password", b"salt", 1, 20,
- ),
-
- (
- hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"),
- b"password", b"salt", 2, 20,
- ),
-
- (
- hb("4b007901b765489abead49d926f721d065a429c1"),
- b"password", b"salt", 4096, 20,
- ),
-
- # just runs too long - could enable if ALL option is set
- ##(
- ##
- ## unhexlify("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"),
- ## "password", "salt", 16777216, 20,
- ##),
-
- (
- hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"),
- b"passwordPASSWORDpassword",
- b"saltSALTsaltSALTsaltSALTsaltSALTsalt",
- 4096, 25,
- ),
-
- (
- hb("56fa6aa75548099dcc37d7f03425e0c3"),
- b"pass\00word", b"sa\00lt", 4096, 16,
- ),
-
- #
- # from example in http://grub.enbug.org/Authentication
- #
- (
- hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED"
- "97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC"
- "6C29E293F0A0"),
- b"hello",
- hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71"
- "784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073"
- "994D79080136"),
- 10000, 64, "hmac-sha512"
- ),
-
- #
- # custom
- #
- (
- hb('e248fb6b13365146f8ac6307cc222812'),
- b"secret", b"salt", 10, 16, "hmac-sha1",
- ),
- (
- hb('e248fb6b13365146f8ac6307cc2228127872da6d'),
- b"secret", b"salt", 10, None, "hmac-sha1",
- ),
-
- ]
-
- def test_known(self):
- """test reference vectors"""
- from passlib.utils.pbkdf2 import pbkdf2
- for row in self.pbkdf2_test_vectors:
- correct, secret, salt, rounds, keylen = row[:5]
- prf = row[5] if len(row) == 6 else "hmac-sha1"
- result = pbkdf2(secret, salt, rounds, keylen, prf)
- self.assertEqual(result, correct)
-
- def test_border(self):
- """test border cases"""
- from passlib.utils.pbkdf2 import pbkdf2
- def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"):
- return pbkdf2(secret, salt, rounds, keylen, prf)
- helper()
-
- # invalid rounds
- self.assertRaises(ValueError, helper, rounds=0)
- self.assertRaises(TypeError, helper, rounds='x')
-
- # invalid keylen
- helper(keylen=0)
- self.assertRaises(ValueError, helper, keylen=-1)
- self.assertRaises(ValueError, helper, keylen=20*(2**32-1)+1)
- self.assertRaises(TypeError, helper, keylen='x')
-
- # invalid secret/salt type
- self.assertRaises(TypeError, helper, salt=5)
- self.assertRaises(TypeError, helper, secret=5)
-
- # invalid hash
- self.assertRaises(ValueError, helper, prf='hmac-foo')
- self.assertRaises(ValueError, helper, prf='foo')
- self.assertRaises(TypeError, helper, prf=5)
-
- def test_default_keylen(self):
- """test keylen==None"""
- from passlib.utils.pbkdf2 import pbkdf2
- def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"):
- return pbkdf2(secret, salt, rounds, keylen, prf)
- self.assertEqual(len(helper(prf='hmac-sha1')), 20)
- self.assertEqual(len(helper(prf='hmac-sha256')), 32)
-
- def test_custom_prf(self):
- """test custom prf function"""
- from passlib.utils.pbkdf2 import pbkdf2
- def prf(key, msg):
- return hashlib.md5(key+msg+b'fooey').digest()
- result = pbkdf2(b'secret', b'salt', 1000, 20, prf)
- self.assertEqual(result, hb('5fe7ce9f7e379d3f65cbc66ba8aa6440474a6849'))
-
-#------------------------------------------------------------------------
-# create subclasses to test with- and without- m2crypto
-#------------------------------------------------------------------------
-@skipUnless(M2Crypto, "M2Crypto not found")
-class Pbkdf2_M2Crypto_Test(_Pbkdf2_Test):
- descriptionPrefix = "pbkdf2 (m2crypto backend)"
-
-@skipUnless(TEST_MODE("full") or not M2Crypto, "skipped under current test mode")
-class Pbkdf2_Builtin_Test(_Pbkdf2_Test):
- descriptionPrefix = "pbkdf2 (builtin backend)"
-
- def setUp(self):
- super(Pbkdf2_Builtin_Test, self).setUp()
- # disable m2crypto support, and force pure-python backend
- if M2Crypto:
- import passlib.utils.pbkdf2 as mod
- self.addCleanup(setattr, mod, "_EVP", mod._EVP)
- mod._EVP = None
-
-#=============================================================================
-# eof
-#=============================================================================
diff --git a/passlib/tests/test_utils_md4.py b/passlib/tests/test_utils_md4.py
new file mode 100644
index 0000000..5d824a1
--- /dev/null
+++ b/passlib/tests/test_utils_md4.py
@@ -0,0 +1,41 @@
+"""
+passlib.tests -- tests for passlib.utils.md4
+
+.. warning::
+
+ This module & it's functions have been deprecated, and superceded
+ by the functions in passlib.crypto. This file is being maintained
+ until the deprecated functions are removed, and is only present prevent
+ historical regressions up to that point. New and more thorough testing
+ is being done by the replacement tests in ``test_utils_crypto_builtin_md4``.
+"""
+#=============================================================================
+# imports
+#=============================================================================
+# core
+import warnings
+# site
+# pkg
+# module
+from passlib.tests.test_crypto_builtin_md4 import _Common_MD4_Test
+# local
+__all__ = [
+ "Legacy_MD4_Test",
+]
+#=============================================================================
+# test pure-python MD4 implementation
+#=============================================================================
+class Legacy_MD4_Test(_Common_MD4_Test):
+ descriptionPrefix = "passlib.utils.md4.md4()"
+
+ def setUp(self):
+ super(Legacy_MD4_Test, self).setUp()
+ warnings.filterwarnings("ignore", ".*passlib.utils.md4.*deprecated", DeprecationWarning)
+
+ def get_md4_const(self):
+ from passlib.utils.md4 import md4
+ return md4
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/tests/test_utils_pbkdf2.py b/passlib/tests/test_utils_pbkdf2.py
new file mode 100644
index 0000000..dbdb98f
--- /dev/null
+++ b/passlib/tests/test_utils_pbkdf2.py
@@ -0,0 +1,321 @@
+"""
+passlib.tests -- tests for passlib.utils.pbkdf2
+
+.. warning::
+
+ This module & it's functions have been deprecated, and superceded
+ by the functions in passlib.crypto. This file is being maintained
+ until the deprecated functions are removed, and is only present prevent
+ historical regressions up to that point. New and more thorough testing
+ is being done by the replacement tests in ``test_utils_crypto.py``.
+"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement
+# core
+import hashlib
+import warnings
+# site
+# pkg
+# module
+from passlib.utils.compat import u, JYTHON
+from passlib.tests.utils import TestCase
+from passlib.tests.test_crypto_digest import hb
+
+#=============================================================================
+# test assorted crypto helpers
+#=============================================================================
+class UtilsTest(TestCase):
+ """test various utils functions"""
+ descriptionPrefix = "passlib.utils.pbkdf2"
+
+ ndn_formats = ["hashlib", "iana"]
+ ndn_values = [
+ # (iana name, hashlib name, ... other unnormalized names)
+ ("md5", "md5", "SCRAM-MD5-PLUS", "MD-5"),
+ ("sha1", "sha-1", "SCRAM-SHA-1", "SHA1"),
+ ("sha256", "sha-256", "SHA_256", "sha2-256"),
+ ("ripemd", "ripemd", "SCRAM-RIPEMD", "RIPEMD"),
+ ("ripemd160", "ripemd-160",
+ "SCRAM-RIPEMD-160", "RIPEmd160"),
+ ("test128", "test-128", "TEST128"),
+ ("test2", "test2", "TEST-2"),
+ ("test3_128", "test3-128", "TEST-3-128"),
+ ]
+
+ def setUp(self):
+ super(UtilsTest, self).setUp()
+ warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
+
+ def test_norm_hash_name(self):
+ """norm_hash_name()"""
+ from itertools import chain
+ from passlib.utils.pbkdf2 import norm_hash_name
+ from passlib.crypto.digest import _known_hash_names
+
+ # test formats
+ for format in self.ndn_formats:
+ norm_hash_name("md4", format)
+ self.assertRaises(ValueError, norm_hash_name, "md4", None)
+ self.assertRaises(ValueError, norm_hash_name, "md4", "fake")
+
+ # test types
+ self.assertEqual(norm_hash_name(u("MD4")), "md4")
+ self.assertEqual(norm_hash_name(b"MD4"), "md4")
+ self.assertRaises(TypeError, norm_hash_name, None)
+
+ # test selected results
+ with warnings.catch_warnings():
+ warnings.filterwarnings("ignore", '.*unknown hash')
+ for row in chain(_known_hash_names, self.ndn_values):
+ for idx, format in enumerate(self.ndn_formats):
+ correct = row[idx]
+ for value in row:
+ result = norm_hash_name(value, format)
+ self.assertEqual(result, correct,
+ "name=%r, format=%r:" % (value,
+ format))
+
+#=============================================================================
+# test PBKDF1 support
+#=============================================================================
+class Pbkdf1_Test(TestCase):
+ """test kdf helpers"""
+ descriptionPrefix = "passlib.utils.pbkdf2.pbkdf1()"
+
+ pbkdf1_tests = [
+ # (password, salt, rounds, keylen, hash, result)
+
+ #
+ # from http://www.di-mgt.com.au/cryptoKDFs.html
+ #
+ (b'password', hb('78578E5A5D63CB06'), 1000, 16, 'sha1', hb('dc19847e05c64d2faf10ebfb4a3d2a20')),
+
+ #
+ # custom
+ #
+ (b'password', b'salt', 1000, 0, 'md5', b''),
+ (b'password', b'salt', 1000, 1, 'md5', hb('84')),
+ (b'password', b'salt', 1000, 8, 'md5', hb('8475c6a8531a5d27')),
+ (b'password', b'salt', 1000, 16, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
+ (b'password', b'salt', 1000, None, 'md5', hb('8475c6a8531a5d27e386cd496457812c')),
+ (b'password', b'salt', 1000, None, 'sha1', hb('4a8fd48e426ed081b535be5769892fa396293efb')),
+ ]
+ if not JYTHON:
+ pbkdf1_tests.append(
+ (b'password', b'salt', 1000, None, 'md4', hb('f7f2e91100a8f96190f2dd177cb26453'))
+ )
+
+ def setUp(self):
+ super(Pbkdf1_Test, self).setUp()
+ warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
+
+ def test_known(self):
+ """test reference vectors"""
+ from passlib.utils.pbkdf2 import pbkdf1
+ for secret, salt, rounds, keylen, digest, correct in self.pbkdf1_tests:
+ result = pbkdf1(secret, salt, rounds, keylen, digest)
+ self.assertEqual(result, correct)
+
+ def test_border(self):
+ """test border cases"""
+ from passlib.utils.pbkdf2 import pbkdf1
+ def helper(secret=b'secret', salt=b'salt', rounds=1, keylen=1, hash='md5'):
+ return pbkdf1(secret, salt, rounds, keylen, hash)
+ helper()
+
+ # salt/secret wrong type
+ self.assertRaises(TypeError, helper, secret=1)
+ self.assertRaises(TypeError, helper, salt=1)
+
+ # non-existent hashes
+ self.assertRaises(ValueError, helper, hash='missing')
+
+ # rounds < 1 and wrong type
+ self.assertRaises(ValueError, helper, rounds=0)
+ self.assertRaises(TypeError, helper, rounds='1')
+
+ # keylen < 0, keylen > block_size, and wrong type
+ self.assertRaises(ValueError, helper, keylen=-1)
+ self.assertRaises(ValueError, helper, keylen=17, hash='md5')
+ self.assertRaises(TypeError, helper, keylen='1')
+
+#=============================================================================
+# test PBKDF2 support
+#=============================================================================
+class Pbkdf2_Test(TestCase):
+ """test pbkdf2() support"""
+ descriptionPrefix = "passlib.utils.pbkdf2.pbkdf2()"
+
+ pbkdf2_test_vectors = [
+ # (result, secret, salt, rounds, keylen, prf="sha1")
+
+ #
+ # from rfc 3962
+ #
+
+ # test case 1 / 128 bit
+ (
+ hb("cdedb5281bb2f801565a1122b2563515"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 1, 16
+ ),
+
+ # test case 2 / 128 bit
+ (
+ hb("01dbee7f4a9e243e988b62c73cda935d"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 2, 16
+ ),
+
+ # test case 2 / 256 bit
+ (
+ hb("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 2, 32
+ ),
+
+ # test case 3 / 256 bit
+ (
+ hb("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"),
+ b"password", b"ATHENA.MIT.EDUraeburn", 1200, 32
+ ),
+
+ # test case 4 / 256 bit
+ (
+ hb("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"),
+ b"password", b'\x12\x34\x56\x78\x78\x56\x34\x12', 5, 32
+ ),
+
+ # test case 5 / 256 bit
+ (
+ hb("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"),
+ b"X"*64, b"pass phrase equals block size", 1200, 32
+ ),
+
+ # test case 6 / 256 bit
+ (
+ hb("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"),
+ b"X"*65, b"pass phrase exceeds block size", 1200, 32
+ ),
+
+ #
+ # from rfc 6070
+ #
+ (
+ hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"),
+ b"password", b"salt", 1, 20,
+ ),
+
+ (
+ hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"),
+ b"password", b"salt", 2, 20,
+ ),
+
+ (
+ hb("4b007901b765489abead49d926f721d065a429c1"),
+ b"password", b"salt", 4096, 20,
+ ),
+
+ # just runs too long - could enable if ALL option is set
+ ##(
+ ##
+ ## unhexlify("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"),
+ ## "password", "salt", 16777216, 20,
+ ##),
+
+ (
+ hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"),
+ b"passwordPASSWORDpassword",
+ b"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ 4096, 25,
+ ),
+
+ (
+ hb("56fa6aa75548099dcc37d7f03425e0c3"),
+ b"pass\00word", b"sa\00lt", 4096, 16,
+ ),
+
+ #
+ # from example in http://grub.enbug.org/Authentication
+ #
+ (
+ hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED"
+ "97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC"
+ "6C29E293F0A0"),
+ b"hello",
+ hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71"
+ "784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073"
+ "994D79080136"),
+ 10000, 64, "hmac-sha512"
+ ),
+
+ #
+ # custom
+ #
+ (
+ hb('e248fb6b13365146f8ac6307cc222812'),
+ b"secret", b"salt", 10, 16, "hmac-sha1",
+ ),
+ (
+ hb('e248fb6b13365146f8ac6307cc2228127872da6d'),
+ b"secret", b"salt", 10, None, "hmac-sha1",
+ ),
+
+ ]
+
+ def setUp(self):
+ super(Pbkdf2_Test, self).setUp()
+ warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
+
+ def test_known(self):
+ """test reference vectors"""
+ from passlib.utils.pbkdf2 import pbkdf2
+ for row in self.pbkdf2_test_vectors:
+ correct, secret, salt, rounds, keylen = row[:5]
+ prf = row[5] if len(row) == 6 else "hmac-sha1"
+ result = pbkdf2(secret, salt, rounds, keylen, prf)
+ self.assertEqual(result, correct)
+
+ def test_border(self):
+ """test border cases"""
+ from passlib.utils.pbkdf2 import pbkdf2
+ def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"):
+ return pbkdf2(secret, salt, rounds, keylen, prf)
+ helper()
+
+ # invalid rounds
+ self.assertRaises(ValueError, helper, rounds=0)
+ self.assertRaises(TypeError, helper, rounds='x')
+
+ # invalid keylen
+ helper(keylen=0)
+ self.assertRaises(ValueError, helper, keylen=-1)
+ self.assertRaises(ValueError, helper, keylen=20*(2**32-1)+1)
+ self.assertRaises(TypeError, helper, keylen='x')
+
+ # invalid secret/salt type
+ self.assertRaises(TypeError, helper, salt=5)
+ self.assertRaises(TypeError, helper, secret=5)
+
+ # invalid hash
+ self.assertRaises(ValueError, helper, prf='hmac-foo')
+ self.assertRaises(NotImplementedError, helper, prf='foo')
+ self.assertRaises(TypeError, helper, prf=5)
+
+ def test_default_keylen(self):
+ """test keylen==None"""
+ from passlib.utils.pbkdf2 import pbkdf2
+ def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"):
+ return pbkdf2(secret, salt, rounds, keylen, prf)
+ self.assertEqual(len(helper(prf='hmac-sha1')), 20)
+ self.assertEqual(len(helper(prf='hmac-sha256')), 32)
+
+ def test_custom_prf(self):
+ """test custom prf function"""
+ from passlib.utils.pbkdf2 import pbkdf2
+ def prf(key, msg):
+ return hashlib.md5(key+msg+b'fooey').digest()
+ self.assertRaises(NotImplementedError, pbkdf2, b'secret', b'salt', 1000, 20, prf)
+
+#=============================================================================
+# eof
+#=============================================================================
diff --git a/passlib/totp.py b/passlib/totp.py
index 4f1639f..3074e25 100644
--- a/passlib/totp.py
+++ b/passlib/totp.py
@@ -24,7 +24,7 @@ from passlib.utils import (to_unicode, to_bytes, consteq, memoized_property,
getrandbytes, rng, xor_bytes, SequenceMixin)
from passlib.utils.compat import (u, unicode, bascii_to_str, int_types, num_types,
irange, byte_elem_value, UnicodeIO, PY26)
-from passlib.utils.pbkdf2 import get_prf, norm_hash_name, pbkdf2
+from passlib.crypto.digest import lookup_hash, compile_hmac, pbkdf2_hmac
# local
__all__ = [
# frontend classes
@@ -160,8 +160,7 @@ def _raw_encrypt_key(key, password, salt, cost):
# (all without needing 'password')
assert isinstance(key, bytes)
password = to_bytes(password, param="password")
- data = pbkdf2(password, salt=salt, rounds=(1<<cost),
- keylen=len(key), prf="hmac-sha256")
+ data = pbkdf2_hmac("sha256", password, salt=salt, rounds=(1<<cost), keylen=len(key))
return xor_bytes(key, data)
def encrypt_key(key, password, cost=None):
@@ -537,9 +536,9 @@ class BaseOTP(object):
self.dirty = dirty
# validate & normalize alg
- self.alg = norm_hash_name(alg or self.alg)
- # XXX: could use get_keyed_prf() instead
- digest_size = get_prf("hmac-" + self.alg)[1]
+ info = lookup_hash(alg or self.alg)
+ self.alg = info.name
+ digest_size = info.digest_size
if digest_size < 4:
raise RuntimeError("%r hash digest too small" % alg)
@@ -676,10 +675,6 @@ class BaseOTP(object):
# token helpers
#=============================================================================
- @memoized_property
- def _prf_info(self):
- return get_prf("hmac-" + self.alg)
-
def _generate(self, counter):
"""
implementation of lowlevel HOTP generation algorithm,
@@ -689,9 +684,10 @@ class BaseOTP(object):
:returns: token as unicode string
"""
# generate digest
- prf, digest_size = self._prf_info
assert isinstance(counter, int_types), "counter must be integer"
- digest = prf(self.key, struct.pack(">Q", counter))
+ keyed_hmac = compile_hmac(self.alg, self.key)
+ digest = keyed_hmac(struct.pack(">Q", counter))
+ digest_size = keyed_hmac.digest_info.digest_size
assert len(digest) == digest_size, "digest_size: sanity check failed"
# derive 31-bit token value
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index 1f703b4..3bfa08c 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -134,7 +134,7 @@ class classproperty(object):
return self.im_func
def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
- replacement=None, _is_method=False):
+ replacement=None, _is_method=False, func_module=None):
"""decorator to deprecate a function.
:arg msg: optional msg, default chosen if omitted
@@ -157,7 +157,7 @@ def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
msg += "."
def build(func):
opts = dict(
- mod=func.__module__,
+ mod=func_module or func.__module__,
name=func.__name__,
deprecated=deprecated,
removed=removed,
diff --git a/passlib/utils/compat/__init__.py b/passlib/utils/compat/__init__.py
index 51c5b72..e4cd07e 100644
--- a/passlib/utils/compat/__init__.py
+++ b/passlib/utils/compat/__init__.py
@@ -93,7 +93,7 @@ if PY3:
assert isinstance(s, str)
return s
- unicode_or_bytes_types = (unicode, bytes)
+ unicode_or_bytes_types = (str, bytes)
native_string_types = (unicode,)
else:
@@ -365,7 +365,7 @@ class _LazyOverlayModule(ModuleType):
that are only needed by certain password hashes,
yet allow them to be imported from a single location.
- used by :mod:`passlib.utils`, :mod:`passlib.utils.crypto`,
+ used by :mod:`passlib.utils`, :mod:`passlib.crypto`,
and :mod:`passlib.utils.compat`.
"""
diff --git a/passlib/utils/des.py b/passlib/utils/des.py
index 1a7985a..2eb556b 100644
--- a/passlib/utils/des.py
+++ b/passlib/utils/des.py
@@ -1,857 +1,44 @@
-"""passlib.utils.des -- DES block encryption routines
-
-History
-=======
-These routines (which have since been drastically modified for python)
-are based on a Java implementation of the des-crypt algorithm,
-found at `<http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_.
-
-The copyright & license for that source is as follows::
-
- UnixCrypt.java 0.9 96/11/25
- Copyright (c) 1996 Aki Yoshida. All rights reserved.
- Permission to use, copy, modify and distribute this software
- for non-commercial or commercial purposes and without fee is
- hereby granted provided that this copyright notice appears in
- all copies.
-
- ---
-
- Unix crypt(3C) utility
- @version 0.9, 11/25/96
- @author Aki Yoshida
-
- ---
-
- modified April 2001
- by Iris Van den Broeke, Daniel Deville
-
- ---
- Unix Crypt.
- Implements the one way cryptography used by Unix systems for
- simple password protection.
- @version $Id: UnixCrypt2.txt,v 1.1.1.1 2005/09/13 22:20:13 christos Exp $
- @author Greg Wilkins (gregw)
-
-The netbsd des-crypt implementation has some nice notes on how this all works -
- http://fxr.googlebit.com/source/lib/libcrypt/crypt.c?v=NETBSD-CURRENT
"""
+passlib.utils.des - DEPRECATED LOCATION, WILL BE REMOVED IN 2.0
-# TODO: could use an accelerated C version of this module to speed up lmhash,
-# des-crypt, and ext-des-crypt
-
-#=============================================================================
-# imports
-#=============================================================================
-# core
-import struct
-# pkg
-from passlib import exc
-from passlib.utils.compat import join_byte_values, byte_elem_value, \
- irange, irange, int_types
-from passlib.utils import deprecated_function
-# local
-__all__ = [
- "expand_des_key",
- "des_encrypt_block",
- "mdes_encrypt_int_block",
-]
-
-#=============================================================================
-# constants
-#=============================================================================
-
-# masks/upper limits for various integer sizes
-INT_24_MASK = 0xffffff
-INT_56_MASK = 0xffffffffffffff
-INT_64_MASK = 0xffffffffffffffff
-
-# mask to clear parity bits from 64-bit key
-_KDATA_MASK = 0xfefefefefefefefe
-_KPARITY_MASK = 0x0101010101010101
-
-# mask used to setup key schedule
-_KS_MASK = 0xfcfcfcfcffffffff
-
-#=============================================================================
-# static DES tables
-#=============================================================================
-
-# placeholders filled in by _load_tables()
-PCXROT = IE3264 = SPE = CF6464 = None
-
-def _load_tables():
- """delay loading tables until they are actually needed"""
- global PCXROT, IE3264, SPE, CF6464
-
- #---------------------------------------------------------------
- # Initial key schedule permutation
- # PC1ROT - bit reverse, then PC1, then Rotate, then PC2
- #---------------------------------------------------------------
- # NOTE: this was reordered from original table to make perm3264 logic simpler
- PC1ROT=(
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000002000, 0x0000000000002000,
- 0x0000000000000020, 0x0000000000000020, 0x0000000000002020, 0x0000000000002020,
- 0x0000000000000400, 0x0000000000000400, 0x0000000000002400, 0x0000000000002400,
- 0x0000000000000420, 0x0000000000000420, 0x0000000000002420, 0x0000000000002420, ),
- ( 0x0000000000000000, 0x2000000000000000, 0x0000000400000000, 0x2000000400000000,
- 0x0000800000000000, 0x2000800000000000, 0x0000800400000000, 0x2000800400000000,
- 0x0008000000000000, 0x2008000000000000, 0x0008000400000000, 0x2008000400000000,
- 0x0008800000000000, 0x2008800000000000, 0x0008800400000000, 0x2008800400000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000040, 0x0000000000000040,
- 0x0000000020000000, 0x0000000020000000, 0x0000000020000040, 0x0000000020000040,
- 0x0000000000200000, 0x0000000000200000, 0x0000000000200040, 0x0000000000200040,
- 0x0000000020200000, 0x0000000020200000, 0x0000000020200040, 0x0000000020200040, ),
- ( 0x0000000000000000, 0x0002000000000000, 0x0800000000000000, 0x0802000000000000,
- 0x0100000000000000, 0x0102000000000000, 0x0900000000000000, 0x0902000000000000,
- 0x4000000000000000, 0x4002000000000000, 0x4800000000000000, 0x4802000000000000,
- 0x4100000000000000, 0x4102000000000000, 0x4900000000000000, 0x4902000000000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000040000, 0x0000000000040000,
- 0x0000020000000000, 0x0000020000000000, 0x0000020000040000, 0x0000020000040000,
- 0x0000000000000004, 0x0000000000000004, 0x0000000000040004, 0x0000000000040004,
- 0x0000020000000004, 0x0000020000000004, 0x0000020000040004, 0x0000020000040004, ),
- ( 0x0000000000000000, 0x0000400000000000, 0x0200000000000000, 0x0200400000000000,
- 0x0080000000000000, 0x0080400000000000, 0x0280000000000000, 0x0280400000000000,
- 0x0000008000000000, 0x0000408000000000, 0x0200008000000000, 0x0200408000000000,
- 0x0080008000000000, 0x0080408000000000, 0x0280008000000000, 0x0280408000000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000010000000, 0x0000000010000000,
- 0x0000000000001000, 0x0000000000001000, 0x0000000010001000, 0x0000000010001000,
- 0x0000000040000000, 0x0000000040000000, 0x0000000050000000, 0x0000000050000000,
- 0x0000000040001000, 0x0000000040001000, 0x0000000050001000, 0x0000000050001000, ),
- ( 0x0000000000000000, 0x0000001000000000, 0x0000080000000000, 0x0000081000000000,
- 0x1000000000000000, 0x1000001000000000, 0x1000080000000000, 0x1000081000000000,
- 0x0004000000000000, 0x0004001000000000, 0x0004080000000000, 0x0004081000000000,
- 0x1004000000000000, 0x1004001000000000, 0x1004080000000000, 0x1004081000000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000080, 0x0000000000000080,
- 0x0000000000080000, 0x0000000000080000, 0x0000000000080080, 0x0000000000080080,
- 0x0000000000800000, 0x0000000000800000, 0x0000000000800080, 0x0000000000800080,
- 0x0000000000880000, 0x0000000000880000, 0x0000000000880080, 0x0000000000880080, ),
- ( 0x0000000000000000, 0x0000000008000000, 0x0000002000000000, 0x0000002008000000,
- 0x0000100000000000, 0x0000100008000000, 0x0000102000000000, 0x0000102008000000,
- 0x0000200000000000, 0x0000200008000000, 0x0000202000000000, 0x0000202008000000,
- 0x0000300000000000, 0x0000300008000000, 0x0000302000000000, 0x0000302008000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000400000, 0x0000000000400000,
- 0x0000000004000000, 0x0000000004000000, 0x0000000004400000, 0x0000000004400000,
- 0x0000000000000800, 0x0000000000000800, 0x0000000000400800, 0x0000000000400800,
- 0x0000000004000800, 0x0000000004000800, 0x0000000004400800, 0x0000000004400800, ),
- ( 0x0000000000000000, 0x0000000000008000, 0x0040000000000000, 0x0040000000008000,
- 0x0000004000000000, 0x0000004000008000, 0x0040004000000000, 0x0040004000008000,
- 0x8000000000000000, 0x8000000000008000, 0x8040000000000000, 0x8040000000008000,
- 0x8000004000000000, 0x8000004000008000, 0x8040004000000000, 0x8040004000008000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000004000, 0x0000000000004000,
- 0x0000000000000008, 0x0000000000000008, 0x0000000000004008, 0x0000000000004008,
- 0x0000000000000010, 0x0000000000000010, 0x0000000000004010, 0x0000000000004010,
- 0x0000000000000018, 0x0000000000000018, 0x0000000000004018, 0x0000000000004018, ),
- ( 0x0000000000000000, 0x0000000200000000, 0x0001000000000000, 0x0001000200000000,
- 0x0400000000000000, 0x0400000200000000, 0x0401000000000000, 0x0401000200000000,
- 0x0020000000000000, 0x0020000200000000, 0x0021000000000000, 0x0021000200000000,
- 0x0420000000000000, 0x0420000200000000, 0x0421000000000000, 0x0421000200000000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000010000000000, 0x0000010000000000,
- 0x0000000100000000, 0x0000000100000000, 0x0000010100000000, 0x0000010100000000,
- 0x0000000000100000, 0x0000000000100000, 0x0000010000100000, 0x0000010000100000,
- 0x0000000100100000, 0x0000000100100000, 0x0000010100100000, 0x0000010100100000, ),
- ( 0x0000000000000000, 0x0000000080000000, 0x0000040000000000, 0x0000040080000000,
- 0x0010000000000000, 0x0010000080000000, 0x0010040000000000, 0x0010040080000000,
- 0x0000000800000000, 0x0000000880000000, 0x0000040800000000, 0x0000040880000000,
- 0x0010000800000000, 0x0010000880000000, 0x0010040800000000, 0x0010040880000000, ),
- )
- #---------------------------------------------------------------
- # Subsequent key schedule rotation permutations
- # PC2ROT - PC2 inverse, then Rotate, then PC2
- #---------------------------------------------------------------
- # NOTE: this was reordered from original table to make perm3264 logic simpler
- PC2ROTA=(
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, 0x0000000000200000,
- 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, 0x0000000004000000,
- 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, ),
- ( 0x0000000000000000, 0x0000000000000800, 0x0000010000000000, 0x0000010000000800,
- 0x0000000000002000, 0x0000000000002800, 0x0000010000002000, 0x0000010000002800,
- 0x0000000010000000, 0x0000000010000800, 0x0000010010000000, 0x0000010010000800,
- 0x0000000010002000, 0x0000000010002800, 0x0000010010002000, 0x0000010010002800, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000,
- 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, 0x0000000000800000,
- 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, ),
- ( 0x0000000000000000, 0x0000020000000000, 0x0000000080000000, 0x0000020080000000,
- 0x0000000000400000, 0x0000020000400000, 0x0000000080400000, 0x0000020080400000,
- 0x0000000008000000, 0x0000020008000000, 0x0000000088000000, 0x0000020088000000,
- 0x0000000008400000, 0x0000020008400000, 0x0000000088400000, 0x0000020088400000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, 0x0000000000000040,
- 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000,
- 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, ),
- ( 0x0000000000000000, 0x0000000000000010, 0x0000000000000400, 0x0000000000000410,
- 0x0000000000000080, 0x0000000000000090, 0x0000000000000480, 0x0000000000000490,
- 0x0000000040000000, 0x0000000040000010, 0x0000000040000400, 0x0000000040000410,
- 0x0000000040000080, 0x0000000040000090, 0x0000000040000480, 0x0000000040000490, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000,
- 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, 0x0000000000100000,
- 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, ),
- ( 0x0000000000000000, 0x0000000000040000, 0x0000000000000020, 0x0000000000040020,
- 0x0000000000000004, 0x0000000000040004, 0x0000000000000024, 0x0000000000040024,
- 0x0000000200000000, 0x0000000200040000, 0x0000000200000020, 0x0000000200040020,
- 0x0000000200000004, 0x0000000200040004, 0x0000000200000024, 0x0000000200040024, ),
- ( 0x0000000000000000, 0x0000000000000008, 0x0000000000008000, 0x0000000000008008,
- 0x0010000000000000, 0x0010000000000008, 0x0010000000008000, 0x0010000000008008,
- 0x0020000000000000, 0x0020000000000008, 0x0020000000008000, 0x0020000000008008,
- 0x0030000000000000, 0x0030000000000008, 0x0030000000008000, 0x0030000000008008, ),
- ( 0x0000000000000000, 0x0000400000000000, 0x0000080000000000, 0x0000480000000000,
- 0x0000100000000000, 0x0000500000000000, 0x0000180000000000, 0x0000580000000000,
- 0x4000000000000000, 0x4000400000000000, 0x4000080000000000, 0x4000480000000000,
- 0x4000100000000000, 0x4000500000000000, 0x4000180000000000, 0x4000580000000000, ),
- ( 0x0000000000000000, 0x0000000000004000, 0x0000000020000000, 0x0000000020004000,
- 0x0001000000000000, 0x0001000000004000, 0x0001000020000000, 0x0001000020004000,
- 0x0200000000000000, 0x0200000000004000, 0x0200000020000000, 0x0200000020004000,
- 0x0201000000000000, 0x0201000000004000, 0x0201000020000000, 0x0201000020004000, ),
- ( 0x0000000000000000, 0x1000000000000000, 0x0004000000000000, 0x1004000000000000,
- 0x0002000000000000, 0x1002000000000000, 0x0006000000000000, 0x1006000000000000,
- 0x0000000800000000, 0x1000000800000000, 0x0004000800000000, 0x1004000800000000,
- 0x0002000800000000, 0x1002000800000000, 0x0006000800000000, 0x1006000800000000, ),
- ( 0x0000000000000000, 0x0040000000000000, 0x2000000000000000, 0x2040000000000000,
- 0x0000008000000000, 0x0040008000000000, 0x2000008000000000, 0x2040008000000000,
- 0x0000001000000000, 0x0040001000000000, 0x2000001000000000, 0x2040001000000000,
- 0x0000009000000000, 0x0040009000000000, 0x2000009000000000, 0x2040009000000000, ),
- ( 0x0000000000000000, 0x0400000000000000, 0x8000000000000000, 0x8400000000000000,
- 0x0000002000000000, 0x0400002000000000, 0x8000002000000000, 0x8400002000000000,
- 0x0100000000000000, 0x0500000000000000, 0x8100000000000000, 0x8500000000000000,
- 0x0100002000000000, 0x0500002000000000, 0x8100002000000000, 0x8500002000000000, ),
- ( 0x0000000000000000, 0x0000800000000000, 0x0800000000000000, 0x0800800000000000,
- 0x0000004000000000, 0x0000804000000000, 0x0800004000000000, 0x0800804000000000,
- 0x0000000400000000, 0x0000800400000000, 0x0800000400000000, 0x0800800400000000,
- 0x0000004400000000, 0x0000804400000000, 0x0800004400000000, 0x0800804400000000, ),
- ( 0x0000000000000000, 0x0080000000000000, 0x0000040000000000, 0x0080040000000000,
- 0x0008000000000000, 0x0088000000000000, 0x0008040000000000, 0x0088040000000000,
- 0x0000200000000000, 0x0080200000000000, 0x0000240000000000, 0x0080240000000000,
- 0x0008200000000000, 0x0088200000000000, 0x0008240000000000, 0x0088240000000000, ),
- )
-
- # NOTE: this was reordered from original table to make perm3264 logic simpler
- PC2ROTB=(
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, 0x0000000000000400,
- 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000,
- 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, ),
- ( 0x0000000000000000, 0x0000000000800000, 0x0000000000004000, 0x0000000000804000,
- 0x0000000080000000, 0x0000000080800000, 0x0000000080004000, 0x0000000080804000,
- 0x0000000000040000, 0x0000000000840000, 0x0000000000044000, 0x0000000000844000,
- 0x0000000080040000, 0x0000000080840000, 0x0000000080044000, 0x0000000080844000, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, 0x0000000000000008,
- 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, 0x0000000040000000,
- 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, ),
- ( 0x0000000000000000, 0x0000000020000000, 0x0000000200000000, 0x0000000220000000,
- 0x0000000000000080, 0x0000000020000080, 0x0000000200000080, 0x0000000220000080,
- 0x0000000000100000, 0x0000000020100000, 0x0000000200100000, 0x0000000220100000,
- 0x0000000000100080, 0x0000000020100080, 0x0000000200100080, 0x0000000220100080, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, 0x0000000000002000,
- 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, 0x0000020000000000,
- 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, ),
- ( 0x0000000000000000, 0x0000000000000800, 0x0000000100000000, 0x0000000100000800,
- 0x0000000010000000, 0x0000000010000800, 0x0000000110000000, 0x0000000110000800,
- 0x0000000000000004, 0x0000000000000804, 0x0000000100000004, 0x0000000100000804,
- 0x0000000010000004, 0x0000000010000804, 0x0000000110000004, 0x0000000110000804, ),
- ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
- 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000,
- 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, 0x0000000000000010,
- 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, ),
- ( 0x0000000000000000, 0x0000000000000040, 0x0000010000000000, 0x0000010000000040,
- 0x0000000000200000, 0x0000000000200040, 0x0000010000200000, 0x0000010000200040,
- 0x0000000000008000, 0x0000000000008040, 0x0000010000008000, 0x0000010000008040,
- 0x0000000000208000, 0x0000000000208040, 0x0000010000208000, 0x0000010000208040, ),
- ( 0x0000000000000000, 0x0000000004000000, 0x0000000008000000, 0x000000000c000000,
- 0x0400000000000000, 0x0400000004000000, 0x0400000008000000, 0x040000000c000000,
- 0x8000000000000000, 0x8000000004000000, 0x8000000008000000, 0x800000000c000000,
- 0x8400000000000000, 0x8400000004000000, 0x8400000008000000, 0x840000000c000000, ),
- ( 0x0000000000000000, 0x0002000000000000, 0x0200000000000000, 0x0202000000000000,
- 0x1000000000000000, 0x1002000000000000, 0x1200000000000000, 0x1202000000000000,
- 0x0008000000000000, 0x000a000000000000, 0x0208000000000000, 0x020a000000000000,
- 0x1008000000000000, 0x100a000000000000, 0x1208000000000000, 0x120a000000000000, ),
- ( 0x0000000000000000, 0x0000000000400000, 0x0000000000000020, 0x0000000000400020,
- 0x0040000000000000, 0x0040000000400000, 0x0040000000000020, 0x0040000000400020,
- 0x0800000000000000, 0x0800000000400000, 0x0800000000000020, 0x0800000000400020,
- 0x0840000000000000, 0x0840000000400000, 0x0840000000000020, 0x0840000000400020, ),
- ( 0x0000000000000000, 0x0080000000000000, 0x0000008000000000, 0x0080008000000000,
- 0x2000000000000000, 0x2080000000000000, 0x2000008000000000, 0x2080008000000000,
- 0x0020000000000000, 0x00a0000000000000, 0x0020008000000000, 0x00a0008000000000,
- 0x2020000000000000, 0x20a0000000000000, 0x2020008000000000, 0x20a0008000000000, ),
- ( 0x0000000000000000, 0x0000002000000000, 0x0000040000000000, 0x0000042000000000,
- 0x4000000000000000, 0x4000002000000000, 0x4000040000000000, 0x4000042000000000,
- 0x0000400000000000, 0x0000402000000000, 0x0000440000000000, 0x0000442000000000,
- 0x4000400000000000, 0x4000402000000000, 0x4000440000000000, 0x4000442000000000, ),
- ( 0x0000000000000000, 0x0000004000000000, 0x0000200000000000, 0x0000204000000000,
- 0x0000080000000000, 0x0000084000000000, 0x0000280000000000, 0x0000284000000000,
- 0x0000800000000000, 0x0000804000000000, 0x0000a00000000000, 0x0000a04000000000,
- 0x0000880000000000, 0x0000884000000000, 0x0000a80000000000, 0x0000a84000000000, ),
- ( 0x0000000000000000, 0x0000000800000000, 0x0000000400000000, 0x0000000c00000000,
- 0x0000100000000000, 0x0000100800000000, 0x0000100400000000, 0x0000100c00000000,
- 0x0010000000000000, 0x0010000800000000, 0x0010000400000000, 0x0010000c00000000,
- 0x0010100000000000, 0x0010100800000000, 0x0010100400000000, 0x0010100c00000000, ),
- ( 0x0000000000000000, 0x0100000000000000, 0x0001000000000000, 0x0101000000000000,
- 0x0000001000000000, 0x0100001000000000, 0x0001001000000000, 0x0101001000000000,
- 0x0004000000000000, 0x0104000000000000, 0x0005000000000000, 0x0105000000000000,
- 0x0004001000000000, 0x0104001000000000, 0x0005001000000000, 0x0105001000000000, ),
- )
- #---------------------------------------------------------------
- # PCXROT - PC1ROT, PC2ROTA, PC2ROTB listed in order
- # of the PC1 rotation schedule, as used by des_setkey
- #---------------------------------------------------------------
- ##ROTATES = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1)
- ##PCXROT = (
- ## PC1ROT, PC2ROTA, PC2ROTB, PC2ROTB,
- ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTB,
- ## PC2ROTA, PC2ROTB, PC2ROTB, PC2ROTB,
- ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTA,
- ## )
-
- # NOTE: modified PCXROT to contain entrys broken into pairs,
- # to help generate them in format best used by encoder.
- PCXROT = (
- (PC1ROT, PC2ROTA), (PC2ROTB, PC2ROTB),
- (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTB),
- (PC2ROTA, PC2ROTB), (PC2ROTB, PC2ROTB),
- (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTA),
- )
-
- #---------------------------------------------------------------
- # Bit reverse, intial permupation, expantion
- # Initial permutation/expansion table
- #---------------------------------------------------------------
- # NOTE: this was reordered from original table to make perm3264 logic simpler
- IE3264=(
- ( 0x0000000000000000, 0x0000000000800800, 0x0000000000008008, 0x0000000000808808,
- 0x0000008008000000, 0x0000008008800800, 0x0000008008008008, 0x0000008008808808,
- 0x0000000080080000, 0x0000000080880800, 0x0000000080088008, 0x0000000080888808,
- 0x0000008088080000, 0x0000008088880800, 0x0000008088088008, 0x0000008088888808, ),
- ( 0x0000000000000000, 0x0080080000000000, 0x0000800800000000, 0x0080880800000000,
- 0x0800000000000080, 0x0880080000000080, 0x0800800800000080, 0x0880880800000080,
- 0x8008000000000000, 0x8088080000000000, 0x8008800800000000, 0x8088880800000000,
- 0x8808000000000080, 0x8888080000000080, 0x8808800800000080, 0x8888880800000080, ),
- ( 0x0000000000000000, 0x0000000000001000, 0x0000000000000010, 0x0000000000001010,
- 0x0000000010000000, 0x0000000010001000, 0x0000000010000010, 0x0000000010001010,
- 0x0000000000100000, 0x0000000000101000, 0x0000000000100010, 0x0000000000101010,
- 0x0000000010100000, 0x0000000010101000, 0x0000000010100010, 0x0000000010101010, ),
- ( 0x0000000000000000, 0x0000100000000000, 0x0000001000000000, 0x0000101000000000,
- 0x1000000000000000, 0x1000100000000000, 0x1000001000000000, 0x1000101000000000,
- 0x0010000000000000, 0x0010100000000000, 0x0010001000000000, 0x0010101000000000,
- 0x1010000000000000, 0x1010100000000000, 0x1010001000000000, 0x1010101000000000, ),
- ( 0x0000000000000000, 0x0000000000002000, 0x0000000000000020, 0x0000000000002020,
- 0x0000000020000000, 0x0000000020002000, 0x0000000020000020, 0x0000000020002020,
- 0x0000000000200000, 0x0000000000202000, 0x0000000000200020, 0x0000000000202020,
- 0x0000000020200000, 0x0000000020202000, 0x0000000020200020, 0x0000000020202020, ),
- ( 0x0000000000000000, 0x0000200000000000, 0x0000002000000000, 0x0000202000000000,
- 0x2000000000000000, 0x2000200000000000, 0x2000002000000000, 0x2000202000000000,
- 0x0020000000000000, 0x0020200000000000, 0x0020002000000000, 0x0020202000000000,
- 0x2020000000000000, 0x2020200000000000, 0x2020002000000000, 0x2020202000000000, ),
- ( 0x0000000000000000, 0x0000000000004004, 0x0400000000000040, 0x0400000000004044,
- 0x0000000040040000, 0x0000000040044004, 0x0400000040040040, 0x0400000040044044,
- 0x0000000000400400, 0x0000000000404404, 0x0400000000400440, 0x0400000000404444,
- 0x0000000040440400, 0x0000000040444404, 0x0400000040440440, 0x0400000040444444, ),
- ( 0x0000000000000000, 0x0000400400000000, 0x0000004004000000, 0x0000404404000000,
- 0x4004000000000000, 0x4004400400000000, 0x4004004004000000, 0x4004404404000000,
- 0x0040040000000000, 0x0040440400000000, 0x0040044004000000, 0x0040444404000000,
- 0x4044040000000000, 0x4044440400000000, 0x4044044004000000, 0x4044444404000000, ),
- )
-
- #---------------------------------------------------------------
- # Table that combines the S, P, and E operations.
- #---------------------------------------------------------------
- SPE=(
- ( 0x0080088008200000, 0x0000008008000000, 0x0000000000200020, 0x0080088008200020,
- 0x0000000000200000, 0x0080088008000020, 0x0000008008000020, 0x0000000000200020,
- 0x0080088008000020, 0x0080088008200000, 0x0000008008200000, 0x0080080000000020,
- 0x0080080000200020, 0x0000000000200000, 0x0000000000000000, 0x0000008008000020,
- 0x0000008008000000, 0x0000000000000020, 0x0080080000200000, 0x0080088008000000,
- 0x0080088008200020, 0x0000008008200000, 0x0080080000000020, 0x0080080000200000,
- 0x0000000000000020, 0x0080080000000000, 0x0080088008000000, 0x0000008008200020,
- 0x0080080000000000, 0x0080080000200020, 0x0000008008200020, 0x0000000000000000,
- 0x0000000000000000, 0x0080088008200020, 0x0080080000200000, 0x0000008008000020,
- 0x0080088008200000, 0x0000008008000000, 0x0080080000000020, 0x0080080000200000,
- 0x0000008008200020, 0x0080080000000000, 0x0080088008000000, 0x0000000000200020,
- 0x0080088008000020, 0x0000000000000020, 0x0000000000200020, 0x0000008008200000,
- 0x0080088008200020, 0x0080088008000000, 0x0000008008200000, 0x0080080000200020,
- 0x0000000000200000, 0x0080080000000020, 0x0000008008000020, 0x0000000000000000,
- 0x0000008008000000, 0x0000000000200000, 0x0080080000200020, 0x0080088008200000,
- 0x0000000000000020, 0x0000008008200020, 0x0080080000000000, 0x0080088008000020, ),
- ( 0x1000800810004004, 0x0000000000000000, 0x0000800810000000, 0x0000000010004004,
- 0x1000000000004004, 0x1000800800000000, 0x0000800800004004, 0x0000800810000000,
- 0x0000800800000000, 0x1000000010004004, 0x1000000000000000, 0x0000800800004004,
- 0x1000000010000000, 0x0000800810004004, 0x0000000010004004, 0x1000000000000000,
- 0x0000000010000000, 0x1000800800004004, 0x1000000010004004, 0x0000800800000000,
- 0x1000800810000000, 0x0000000000004004, 0x0000000000000000, 0x1000000010000000,
- 0x1000800800004004, 0x1000800810000000, 0x0000800810004004, 0x1000000000004004,
- 0x0000000000004004, 0x0000000010000000, 0x1000800800000000, 0x1000800810004004,
- 0x1000000010000000, 0x0000800810004004, 0x0000800800004004, 0x1000800810000000,
- 0x1000800810004004, 0x1000000010000000, 0x1000000000004004, 0x0000000000000000,
- 0x0000000000004004, 0x1000800800000000, 0x0000000010000000, 0x1000000010004004,
- 0x0000800800000000, 0x0000000000004004, 0x1000800810000000, 0x1000800800004004,
- 0x0000800810004004, 0x0000800800000000, 0x0000000000000000, 0x1000000000004004,
- 0x1000000000000000, 0x1000800810004004, 0x0000800810000000, 0x0000000010004004,
- 0x1000000010004004, 0x0000000010000000, 0x1000800800000000, 0x0000800800004004,
- 0x1000800800004004, 0x1000000000000000, 0x0000000010004004, 0x0000800810000000, ),
- ( 0x0000000000400410, 0x0010004004400400, 0x0010000000000000, 0x0010000000400410,
- 0x0000004004000010, 0x0000000000400400, 0x0010000000400410, 0x0010004004000000,
- 0x0010000000400400, 0x0000004004000000, 0x0000004004400400, 0x0000000000000010,
- 0x0010004004400410, 0x0010000000000010, 0x0000000000000010, 0x0000004004400410,
- 0x0000000000000000, 0x0000004004000010, 0x0010004004400400, 0x0010000000000000,
- 0x0010000000000010, 0x0010004004400410, 0x0000004004000000, 0x0000000000400410,
- 0x0000004004400410, 0x0010000000400400, 0x0010004004000010, 0x0000004004400400,
- 0x0010004004000000, 0x0000000000000000, 0x0000000000400400, 0x0010004004000010,
- 0x0010004004400400, 0x0010000000000000, 0x0000000000000010, 0x0000004004000000,
- 0x0010000000000010, 0x0000004004000010, 0x0000004004400400, 0x0010000000400410,
- 0x0000000000000000, 0x0010004004400400, 0x0010004004000000, 0x0000004004400410,
- 0x0000004004000010, 0x0000000000400400, 0x0010004004400410, 0x0000000000000010,
- 0x0010004004000010, 0x0000000000400410, 0x0000000000400400, 0x0010004004400410,
- 0x0000004004000000, 0x0010000000400400, 0x0010000000400410, 0x0010004004000000,
- 0x0010000000400400, 0x0000000000000000, 0x0000004004400410, 0x0010000000000010,
- 0x0000000000400410, 0x0010004004000010, 0x0010000000000000, 0x0000004004400400, ),
- ( 0x0800100040040080, 0x0000100000001000, 0x0800000000000080, 0x0800100040041080,
- 0x0000000000000000, 0x0000000040041000, 0x0800100000001080, 0x0800000040040080,
- 0x0000100040041000, 0x0800000000001080, 0x0000000000001000, 0x0800100000000080,
- 0x0800000000001080, 0x0800100040040080, 0x0000000040040000, 0x0000000000001000,
- 0x0800000040041080, 0x0000100040040000, 0x0000100000000000, 0x0800000000000080,
- 0x0000100040040000, 0x0800100000001080, 0x0000000040041000, 0x0000100000000000,
- 0x0800100000000080, 0x0000000000000000, 0x0800000040040080, 0x0000100040041000,
- 0x0000100000001000, 0x0800000040041080, 0x0800100040041080, 0x0000000040040000,
- 0x0800000040041080, 0x0800100000000080, 0x0000000040040000, 0x0800000000001080,
- 0x0000100040040000, 0x0000100000001000, 0x0800000000000080, 0x0000000040041000,
- 0x0800100000001080, 0x0000000000000000, 0x0000100000000000, 0x0800000040040080,
- 0x0000000000000000, 0x0800000040041080, 0x0000100040041000, 0x0000100000000000,
- 0x0000000000001000, 0x0800100040041080, 0x0800100040040080, 0x0000000040040000,
- 0x0800100040041080, 0x0800000000000080, 0x0000100000001000, 0x0800100040040080,
- 0x0800000040040080, 0x0000100040040000, 0x0000000040041000, 0x0800100000001080,
- 0x0800100000000080, 0x0000000000001000, 0x0800000000001080, 0x0000100040041000, ),
- ( 0x0000000000800800, 0x0000001000000000, 0x0040040000000000, 0x2040041000800800,
- 0x2000001000800800, 0x0040040000800800, 0x2040041000000000, 0x0000001000800800,
- 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040041000000000,
- 0x2040040000800800, 0x2000001000800800, 0x0040041000800800, 0x0000000000000000,
- 0x0040041000000000, 0x0000000000800800, 0x2000001000000000, 0x2040040000000000,
- 0x0040040000800800, 0x2040041000000000, 0x0000000000000000, 0x2000000000800800,
- 0x2000000000000000, 0x2040040000800800, 0x2040041000800800, 0x2000001000000000,
- 0x0000001000800800, 0x0040040000000000, 0x2040040000000000, 0x0040041000800800,
- 0x0040041000800800, 0x2040040000800800, 0x2000001000000000, 0x0000001000800800,
- 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040040000800800,
- 0x0000000000800800, 0x0040041000000000, 0x2040041000800800, 0x0000000000000000,
- 0x2040041000000000, 0x0000000000800800, 0x0040040000000000, 0x2000001000000000,
- 0x2040040000800800, 0x0040040000000000, 0x0000000000000000, 0x2040041000800800,
- 0x2000001000800800, 0x0040041000800800, 0x2040040000000000, 0x0000001000000000,
- 0x0040041000000000, 0x2000001000800800, 0x0040040000800800, 0x2040040000000000,
- 0x2000000000000000, 0x2040041000000000, 0x0000001000800800, 0x2000000000800800, ),
- ( 0x4004000000008008, 0x4004000020000000, 0x0000000000000000, 0x0000200020008008,
- 0x4004000020000000, 0x0000200000000000, 0x4004200000008008, 0x0000000020000000,
- 0x4004200000000000, 0x4004200020008008, 0x0000200020000000, 0x0000000000008008,
- 0x0000200000008008, 0x4004000000008008, 0x0000000020008008, 0x4004200020000000,
- 0x0000000020000000, 0x4004200000008008, 0x4004000020008008, 0x0000000000000000,
- 0x0000200000000000, 0x4004000000000000, 0x0000200020008008, 0x4004000020008008,
- 0x4004200020008008, 0x0000000020008008, 0x0000000000008008, 0x4004200000000000,
- 0x4004000000000000, 0x0000200020000000, 0x4004200020000000, 0x0000200000008008,
- 0x4004200000000000, 0x0000000000008008, 0x0000200000008008, 0x4004200020000000,
- 0x0000200020008008, 0x4004000020000000, 0x0000000000000000, 0x0000200000008008,
- 0x0000000000008008, 0x0000200000000000, 0x4004000020008008, 0x0000000020000000,
- 0x4004000020000000, 0x4004200020008008, 0x0000200020000000, 0x4004000000000000,
- 0x4004200020008008, 0x0000200020000000, 0x0000000020000000, 0x4004200000008008,
- 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, 0x0000000000000000,
- 0x0000200000000000, 0x4004000000008008, 0x4004200000008008, 0x0000200020008008,
- 0x0000000020008008, 0x4004200000000000, 0x4004000000000000, 0x4004000020008008, ),
- ( 0x0000400400000000, 0x0020000000000000, 0x0020000000100000, 0x0400000000100040,
- 0x0420400400100040, 0x0400400400000040, 0x0020400400000000, 0x0000000000000000,
- 0x0000000000100000, 0x0420000000100040, 0x0420000000000040, 0x0000400400100000,
- 0x0400000000000040, 0x0020400400100000, 0x0000400400100000, 0x0420000000000040,
- 0x0420000000100040, 0x0000400400000000, 0x0400400400000040, 0x0420400400100040,
- 0x0000000000000000, 0x0020000000100000, 0x0400000000100040, 0x0020400400000000,
- 0x0400400400100040, 0x0420400400000040, 0x0020400400100000, 0x0400000000000040,
- 0x0420400400000040, 0x0400400400100040, 0x0020000000000000, 0x0000000000100000,
- 0x0420400400000040, 0x0000400400100000, 0x0400400400100040, 0x0420000000000040,
- 0x0000400400000000, 0x0020000000000000, 0x0000000000100000, 0x0400400400100040,
- 0x0420000000100040, 0x0420400400000040, 0x0020400400000000, 0x0000000000000000,
- 0x0020000000000000, 0x0400000000100040, 0x0400000000000040, 0x0020000000100000,
- 0x0000000000000000, 0x0420000000100040, 0x0020000000100000, 0x0020400400000000,
- 0x0420000000000040, 0x0000400400000000, 0x0420400400100040, 0x0000000000100000,
- 0x0020400400100000, 0x0400000000000040, 0x0400400400000040, 0x0420400400100040,
- 0x0400000000100040, 0x0020400400100000, 0x0000400400100000, 0x0400400400000040, ),
- ( 0x8008000080082000, 0x0000002080082000, 0x8008002000000000, 0x0000000000000000,
- 0x0000002000002000, 0x8008000080080000, 0x0000000080082000, 0x8008002080082000,
- 0x8008000000000000, 0x0000000000002000, 0x0000002080080000, 0x8008002000000000,
- 0x8008002080080000, 0x8008002000002000, 0x8008000000002000, 0x0000000080082000,
- 0x0000002000000000, 0x8008002080080000, 0x8008000080080000, 0x0000002000002000,
- 0x8008002080082000, 0x8008000000002000, 0x0000000000000000, 0x0000002080080000,
- 0x0000000000002000, 0x0000000080080000, 0x8008002000002000, 0x8008000080082000,
- 0x0000000080080000, 0x0000002000000000, 0x0000002080082000, 0x8008000000000000,
- 0x0000000080080000, 0x0000002000000000, 0x8008000000002000, 0x8008002080082000,
- 0x8008002000000000, 0x0000000000002000, 0x0000000000000000, 0x0000002080080000,
- 0x8008000080082000, 0x8008002000002000, 0x0000002000002000, 0x8008000080080000,
- 0x0000002080082000, 0x8008000000000000, 0x8008000080080000, 0x0000002000002000,
- 0x8008002080082000, 0x0000000080080000, 0x0000000080082000, 0x8008000000002000,
- 0x0000002080080000, 0x8008002000000000, 0x8008002000002000, 0x0000000080082000,
- 0x8008000000000000, 0x0000002080082000, 0x8008002080080000, 0x0000000000000000,
- 0x0000000000002000, 0x8008000080082000, 0x0000002000000000, 0x8008002080080000, ),
- )
-
- #---------------------------------------------------------------
- # compressed/interleaved => final permutation table
- # Compression, final permutation, bit reverse
- #---------------------------------------------------------------
- # NOTE: this was reordered from original table to make perm6464 logic simpler
- CF6464=(
- ( 0x0000000000000000, 0x0000002000000000, 0x0000200000000000, 0x0000202000000000,
- 0x0020000000000000, 0x0020002000000000, 0x0020200000000000, 0x0020202000000000,
- 0x2000000000000000, 0x2000002000000000, 0x2000200000000000, 0x2000202000000000,
- 0x2020000000000000, 0x2020002000000000, 0x2020200000000000, 0x2020202000000000, ),
- ( 0x0000000000000000, 0x0000000200000000, 0x0000020000000000, 0x0000020200000000,
- 0x0002000000000000, 0x0002000200000000, 0x0002020000000000, 0x0002020200000000,
- 0x0200000000000000, 0x0200000200000000, 0x0200020000000000, 0x0200020200000000,
- 0x0202000000000000, 0x0202000200000000, 0x0202020000000000, 0x0202020200000000, ),
- ( 0x0000000000000000, 0x0000000000000020, 0x0000000000002000, 0x0000000000002020,
- 0x0000000000200000, 0x0000000000200020, 0x0000000000202000, 0x0000000000202020,
- 0x0000000020000000, 0x0000000020000020, 0x0000000020002000, 0x0000000020002020,
- 0x0000000020200000, 0x0000000020200020, 0x0000000020202000, 0x0000000020202020, ),
- ( 0x0000000000000000, 0x0000000000000002, 0x0000000000000200, 0x0000000000000202,
- 0x0000000000020000, 0x0000000000020002, 0x0000000000020200, 0x0000000000020202,
- 0x0000000002000000, 0x0000000002000002, 0x0000000002000200, 0x0000000002000202,
- 0x0000000002020000, 0x0000000002020002, 0x0000000002020200, 0x0000000002020202, ),
- ( 0x0000000000000000, 0x0000008000000000, 0x0000800000000000, 0x0000808000000000,
- 0x0080000000000000, 0x0080008000000000, 0x0080800000000000, 0x0080808000000000,
- 0x8000000000000000, 0x8000008000000000, 0x8000800000000000, 0x8000808000000000,
- 0x8080000000000000, 0x8080008000000000, 0x8080800000000000, 0x8080808000000000, ),
- ( 0x0000000000000000, 0x0000000800000000, 0x0000080000000000, 0x0000080800000000,
- 0x0008000000000000, 0x0008000800000000, 0x0008080000000000, 0x0008080800000000,
- 0x0800000000000000, 0x0800000800000000, 0x0800080000000000, 0x0800080800000000,
- 0x0808000000000000, 0x0808000800000000, 0x0808080000000000, 0x0808080800000000, ),
- ( 0x0000000000000000, 0x0000000000000080, 0x0000000000008000, 0x0000000000008080,
- 0x0000000000800000, 0x0000000000800080, 0x0000000000808000, 0x0000000000808080,
- 0x0000000080000000, 0x0000000080000080, 0x0000000080008000, 0x0000000080008080,
- 0x0000000080800000, 0x0000000080800080, 0x0000000080808000, 0x0000000080808080, ),
- ( 0x0000000000000000, 0x0000000000000008, 0x0000000000000800, 0x0000000000000808,
- 0x0000000000080000, 0x0000000000080008, 0x0000000000080800, 0x0000000000080808,
- 0x0000000008000000, 0x0000000008000008, 0x0000000008000800, 0x0000000008000808,
- 0x0000000008080000, 0x0000000008080008, 0x0000000008080800, 0x0000000008080808, ),
- ( 0x0000000000000000, 0x0000001000000000, 0x0000100000000000, 0x0000101000000000,
- 0x0010000000000000, 0x0010001000000000, 0x0010100000000000, 0x0010101000000000,
- 0x1000000000000000, 0x1000001000000000, 0x1000100000000000, 0x1000101000000000,
- 0x1010000000000000, 0x1010001000000000, 0x1010100000000000, 0x1010101000000000, ),
- ( 0x0000000000000000, 0x0000000100000000, 0x0000010000000000, 0x0000010100000000,
- 0x0001000000000000, 0x0001000100000000, 0x0001010000000000, 0x0001010100000000,
- 0x0100000000000000, 0x0100000100000000, 0x0100010000000000, 0x0100010100000000,
- 0x0101000000000000, 0x0101000100000000, 0x0101010000000000, 0x0101010100000000, ),
- ( 0x0000000000000000, 0x0000000000000010, 0x0000000000001000, 0x0000000000001010,
- 0x0000000000100000, 0x0000000000100010, 0x0000000000101000, 0x0000000000101010,
- 0x0000000010000000, 0x0000000010000010, 0x0000000010001000, 0x0000000010001010,
- 0x0000000010100000, 0x0000000010100010, 0x0000000010101000, 0x0000000010101010, ),
- ( 0x0000000000000000, 0x0000000000000001, 0x0000000000000100, 0x0000000000000101,
- 0x0000000000010000, 0x0000000000010001, 0x0000000000010100, 0x0000000000010101,
- 0x0000000001000000, 0x0000000001000001, 0x0000000001000100, 0x0000000001000101,
- 0x0000000001010000, 0x0000000001010001, 0x0000000001010100, 0x0000000001010101, ),
- ( 0x0000000000000000, 0x0000004000000000, 0x0000400000000000, 0x0000404000000000,
- 0x0040000000000000, 0x0040004000000000, 0x0040400000000000, 0x0040404000000000,
- 0x4000000000000000, 0x4000004000000000, 0x4000400000000000, 0x4000404000000000,
- 0x4040000000000000, 0x4040004000000000, 0x4040400000000000, 0x4040404000000000, ),
- ( 0x0000000000000000, 0x0000000400000000, 0x0000040000000000, 0x0000040400000000,
- 0x0004000000000000, 0x0004000400000000, 0x0004040000000000, 0x0004040400000000,
- 0x0400000000000000, 0x0400000400000000, 0x0400040000000000, 0x0400040400000000,
- 0x0404000000000000, 0x0404000400000000, 0x0404040000000000, 0x0404040400000000, ),
- ( 0x0000000000000000, 0x0000000000000040, 0x0000000000004000, 0x0000000000004040,
- 0x0000000000400000, 0x0000000000400040, 0x0000000000404000, 0x0000000000404040,
- 0x0000000040000000, 0x0000000040000040, 0x0000000040004000, 0x0000000040004040,
- 0x0000000040400000, 0x0000000040400040, 0x0000000040404000, 0x0000000040404040, ),
- ( 0x0000000000000000, 0x0000000000000004, 0x0000000000000400, 0x0000000000000404,
- 0x0000000000040000, 0x0000000000040004, 0x0000000000040400, 0x0000000000040404,
- 0x0000000004000000, 0x0000000004000004, 0x0000000004000400, 0x0000000004000404,
- 0x0000000004040000, 0x0000000004040004, 0x0000000004040400, 0x0000000004040404, ),
- )
- #===================================================================
- # eof _load_tables()
- #===================================================================
-
-#=============================================================================
-# support
-#=============================================================================
-
-def _permute(c, p):
- """Returns the permutation of the given 32-bit or 64-bit code with
- the specified permutation table."""
- # NOTE: only difference between 32 & 64 bit permutations
- # is that len(p)==8 for 32 bit, and len(p)==16 for 64 bit.
- out = 0
- for r in p:
- out |= r[c&0xf]
- c >>= 4
- return out
-
+This has been moved to :mod:`passlib.crypto.des`.
+"""
#=============================================================================
-# packing & unpacking
+# import from new location
#=============================================================================
-_uint64_struct = struct.Struct(">Q")
-
-_BNULL = b'\x00'
-
-def _pack64(value):
- return _uint64_struct.pack(value)
-
-def _unpack64(value):
- return _uint64_struct.unpack(value)[0]
-
-def _pack56(value):
- return _uint64_struct.pack(value)[1:]
-
-def _unpack56(value):
- return _uint64_struct.unpack(_BNULL+value)[0]
+from warnings import warn
+warn("the 'passlib.utils.des' module has been relocated to 'passlib.crypto.des' "
+ "as of passlib 1.7, and the old location will be removed in passlib 2.0",
+ DeprecationWarning)
#=============================================================================
-# 56->64 key manipulation
+# relocated functions
#=============================================================================
+from passlib.utils import deprecated_function
+from passlib.crypto.des import expand_des_key, des_encrypt_block, des_encrypt_int_block
-##def expand_7bit(value):
-## "expand 7-bit integer => 7-bits + 1 odd-parity bit"
-## # parity calc adapted from 32-bit even parity alg found at
-## # http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
-## assert 0 <= value < 0x80, "value out of range"
-## return (value<<1) | (0x9669 >> ((value ^ (value >> 4)) & 0xf)) & 1
-
-_EXPAND_ITER = irange(49,-7,-7)
+expand_des_key = deprecated_function(deprecated="1.7", removed="1.8",
+ replacement="passlib.crypto.des.expand_des_key")(expand_des_key)
-def expand_des_key(key):
- """convert DES from 7 bytes to 8 bytes (by inserting empty parity bits)"""
- if isinstance(key, bytes):
- if len(key) != 7:
- raise ValueError("key must be 7 bytes in size")
- elif isinstance(key, int_types):
- if key < 0 or key > INT_56_MASK:
- raise ValueError("key must be 56-bit non-negative integer")
- return _unpack64(expand_des_key(_pack56(key)))
- else:
- raise exc.ExpectedTypeError(key, "bytes or int", "key")
- key = _unpack56(key)
- # NOTE: the following would insert correctly-valued parity bits in each key,
- # but the parity bit would just be ignored in des_encrypt_block(),
- # so not bothering to use it.
- ##return join_byte_values(expand_7bit((key >> shift) & 0x7f)
- ## for shift in _EXPAND_ITER)
- return join_byte_values(((key>>shift) & 0x7f)<<1 for shift in _EXPAND_ITER)
+des_encrypt_block = deprecated_function(deprecated="1.7", removed="1.8",
+ replacement="passlib.crypto.des.des_encrypt_block")(des_encrypt_block)
-def shrink_des_key(key):
- """convert DES key from 8 bytes to 7 bytes (by discarding the parity bits)"""
- if isinstance(key, bytes):
- if len(key) != 8:
- raise ValueError("key must be 8 bytes in size")
- return _pack56(shrink_des_key(_unpack64(key)))
- elif isinstance(key, int_types):
- if key < 0 or key > INT_64_MASK:
- raise ValueError("key must be 64-bit non-negative integer")
- else:
- raise exc.ExpectedTypeError(key, "bytes or int", "key")
- key >>= 1
- result = 0
- offset = 0
- while offset < 56:
- result |= (key & 0x7f)<<offset
- key >>= 8
- offset += 7
- assert not (result & ~INT_64_MASK)
- return result
+des_encrypt_int_block = deprecated_function(deprecated="1.7", removed="1.8",
+ replacement="passlib.crypto.des.des_encrypt_int_block")(des_encrypt_int_block)
#=============================================================================
-# des encryption
+# deprecated functions -- not carried over to passlib.crypto.des
#=============================================================================
-def des_encrypt_block(key, input, salt=0, rounds=1):
- """encrypt single block of data using DES, operates on 8-byte strings.
-
- :arg key:
- DES key as 7 byte string, or 8 byte string with parity bits
- (parity bit values are ignored).
-
- :arg input:
- plaintext block to encrypt, as 8 byte string.
-
- :arg salt:
- Optional 24-bit integer used to mutate the base DES algorithm in a
- manner specific to :class:`~passlib.hash.des_crypt` and its variants.
- The default value ``0`` provides the normal (unsalted) DES behavior.
- The salt functions as follows:
- if the ``i``'th bit of ``salt`` is set,
- bits ``i`` and ``i+24`` are swapped in the DES E-box output.
-
- :arg rounds:
- Optional number of rounds of to apply the DES key schedule.
- the default (``rounds=1``) provides the normal DES behavior,
- but :class:`~passlib.hash.des_crypt` and its variants use
- alternate rounds values.
-
- :raises TypeError: if any of the provided args are of the wrong type.
- :raises ValueError:
- if any of the input blocks are the wrong size,
- or the salt/rounds values are out of range.
-
- :returns:
- resulting 8-byte ciphertext block.
- """
- # validate & unpack key
- if isinstance(key, bytes):
- if len(key) == 7:
- key = expand_des_key(key)
- elif len(key) != 8:
- raise ValueError("key must be 7 or 8 bytes")
- key = _unpack64(key)
- else:
- raise exc.ExpectedTypeError(key, "bytes", "key")
-
- # validate & unpack input
- if isinstance(input, bytes):
- if len(input) != 8:
- raise ValueError("input block must be 8 bytes")
- input = _unpack64(input)
- else:
- raise exc.ExpectedTypeError(input, "bytes", "input")
-
- # hand things off to other func
- result = des_encrypt_int_block(key, input, salt, rounds)
-
- # repack result
- return _pack64(result)
-
-def des_encrypt_int_block(key, input, salt=0, rounds=1):
- """encrypt single block of data using DES, operates on 64-bit integers.
-
- this function is essentially the same as :func:`des_encrypt_block`,
- except that it operates on integers, and will NOT automatically
- expand 56-bit keys if provided (since there's no way to detect them).
-
- :arg key:
- DES key as 64-bit integer (the parity bits are ignored).
-
- :arg input:
- input block as 64-bit integer
-
- :arg salt:
- optional 24-bit integer used to mutate the base DES algorithm.
- defaults to ``0`` (no mutation applied).
-
- :arg rounds:
- optional number of rounds of to apply the DES key schedule.
- defaults to ``1``.
-
- :raises TypeError: if any of the provided args are of the wrong type.
- :raises ValueError:
- if any of the input blocks are the wrong size,
- or the salt/rounds values are out of range.
-
- :returns:
- resulting ciphertext as 64-bit integer.
- """
- #---------------------------------------------------------------
- # input validation
- #---------------------------------------------------------------
-
- # validate salt, rounds
- if rounds < 1:
- raise ValueError("rounds must be positive integer")
- if salt < 0 or salt > INT_24_MASK:
- raise ValueError("salt must be 24-bit non-negative integer")
-
- # validate & unpack key
- if not isinstance(key, int_types):
- raise exc.ExpectedTypeError(key, "int", "key")
- elif key < 0 or key > INT_64_MASK:
- raise ValueError("key must be 64-bit non-negative integer")
-
- # validate & unpack input
- if not isinstance(input, int_types):
- raise exc.ExpectedTypeError(input, "int", "input")
- elif input < 0 or input > INT_64_MASK:
- raise ValueError("input must be 64-bit non-negative integer")
-
- #---------------------------------------------------------------
- # DES setup
- #---------------------------------------------------------------
- # load tables if not already done
- global SPE, PCXROT, IE3264, CF6464
- if PCXROT is None:
- _load_tables()
-
- # load SPE into local vars to speed things up and remove an array access call
- SPE0, SPE1, SPE2, SPE3, SPE4, SPE5, SPE6, SPE7 = SPE
-
- # NOTE: parity bits are ignored completely
- # (UTs do fuzz testing to ensure this)
-
- # generate key schedule
- # NOTE: generation was modified to output two elements at a time,
- # so that per-round loop could do two passes at once.
- def _iter_key_schedule(ks_odd):
- """given 64-bit key, iterates over the 8 (even,odd) key schedule pairs"""
- for p_even, p_odd in PCXROT:
- ks_even = _permute(ks_odd, p_even)
- ks_odd = _permute(ks_even, p_odd)
- yield ks_even & _KS_MASK, ks_odd & _KS_MASK
- ks_list = list(_iter_key_schedule(key))
-
- # expand 24 bit salt -> 32 bit per des_crypt & bsdi_crypt
- salt = (
- ((salt & 0x00003f) << 26) |
- ((salt & 0x000fc0) << 12) |
- ((salt & 0x03f000) >> 2) |
- ((salt & 0xfc0000) >> 16)
- )
-
- # init L & R
- if input == 0:
- L = R = 0
- else:
- L = ((input >> 31) & 0xaaaaaaaa) | (input & 0x55555555)
- L = _permute(L, IE3264)
-
- R = ((input >> 32) & 0xaaaaaaaa) | ((input >> 1) & 0x55555555)
- R = _permute(R, IE3264)
-
- #---------------------------------------------------------------
- # main DES loop - run for specified number of rounds
- #---------------------------------------------------------------
- while rounds:
- rounds -= 1
-
- # run over each part of the schedule, 2 parts at a time
- for ks_even, ks_odd in ks_list:
- k = ((R>>32) ^ R) & salt # use the salt to flip specific bits
- B = (k<<32) ^ k ^ R ^ ks_even
-
- L ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^
- SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^
- SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^
- SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f])
-
- k = ((L>>32) ^ L) & salt # use the salt to flip specific bits
- B = (k<<32) ^ k ^ L ^ ks_odd
-
- R ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^
- SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^
- SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^
- SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f])
-
- # swap L and R
- L, R = R, L
-
- #---------------------------------------------------------------
- # return final result
- #---------------------------------------------------------------
- C = (
- ((L>>3) & 0x0f0f0f0f00000000)
- |
- ((L<<33) & 0xf0f0f0f000000000)
- |
- ((R>>35) & 0x000000000f0f0f0f)
- |
- ((R<<1) & 0x00000000f0f0f0f0)
- )
- return _permute(C, CF6464)
+import struct
+_unpack_uint8 = struct.Struct(">Q").unpack
@deprecated_function(deprecated="1.6", removed="1.8",
- replacement="des_encrypt_int_block()")
+ replacement="passlib.crypto.des.des_encrypt_int_block()")
def mdes_encrypt_int_block(key, input, salt=0, rounds=1): # pragma: no cover -- deprecated & unused
if isinstance(key, bytes):
if len(key) == 7:
key = expand_des_key(key)
- key = _unpack64(key)
+ key = _unpack_uint8(key)[0]
return des_encrypt_int_block(key, input, salt, rounds)
#=============================================================================
diff --git a/passlib/utils/md4.py b/passlib/utils/md4.py
index 7b34f17..ef3f7a5 100644
--- a/passlib/utils/md4.py
+++ b/passlib/utils/md4.py
@@ -1,261 +1,28 @@
"""
-helper implementing insecure and obsolete md4 algorithm.
-used for NTHASH format, which is also insecure and broken,
-since it's just md4(password)
-
-implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html
+passlib.utils.md4 - DEPRECATED MODULE, WILL BE REMOVED IN 2.0
+MD4 should now be looked up through ``passlib.crypto.digest.lookup_hash("md4").const``,
+which provides unified handling stdlib implementation (if present).
"""
-
#=============================================================================
-# imports
+# issue deprecation warning for module
#=============================================================================
-# core
-from binascii import hexlify
-import struct
from warnings import warn
-# site
-from passlib.utils.compat import bascii_to_str, irange, PY3
-# local
-__all__ = [ "md4" ]
-#=============================================================================
-# utils
-#=============================================================================
-def F(x,y,z):
- return (x&y) | ((~x) & z)
-
-def G(x,y,z):
- return (x&y) | (x&z) | (y&z)
-
-##def H(x,y,z):
-## return x ^ y ^ z
-
-MASK_32 = 2**32-1
-
-#=============================================================================
-# main class
-#=============================================================================
-class md4(object):
- """pep-247 compatible implementation of MD4 hash algorithm
-
- .. attribute:: digest_size
-
- size of md4 digest in bytes (16 bytes)
-
- .. method:: update
-
- update digest by appending additional content
-
- .. method:: copy
-
- create clone of digest object, including current state
-
- .. method:: digest
-
- return bytes representing md4 digest of current content
-
- .. method:: hexdigest
-
- return hexadecimal version of digest
- """
- # FIXME: make this follow hash object PEP better.
- # FIXME: this isn't threadsafe
- # XXX: should we monkeypatch ourselves into hashlib for general use? probably wouldn't be nice.
-
- name = "md4"
- digest_size = digestsize = 16
-
- _count = 0 # number of 64-byte blocks processed so far (not including _buf)
- _state = None # list of [a,b,c,d] 32 bit ints used as internal register
- _buf = None # data processed in 64 byte blocks, this holds leftover from last update
-
- def __init__(self, content=None):
- self._count = 0
- self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
- self._buf = b''
- if content:
- self.update(content)
-
- # round 1 table - [abcd k s]
- _round1 = [
- [0,1,2,3, 0,3],
- [3,0,1,2, 1,7],
- [2,3,0,1, 2,11],
- [1,2,3,0, 3,19],
-
- [0,1,2,3, 4,3],
- [3,0,1,2, 5,7],
- [2,3,0,1, 6,11],
- [1,2,3,0, 7,19],
-
- [0,1,2,3, 8,3],
- [3,0,1,2, 9,7],
- [2,3,0,1, 10,11],
- [1,2,3,0, 11,19],
-
- [0,1,2,3, 12,3],
- [3,0,1,2, 13,7],
- [2,3,0,1, 14,11],
- [1,2,3,0, 15,19],
- ]
-
- # round 2 table - [abcd k s]
- _round2 = [
- [0,1,2,3, 0,3],
- [3,0,1,2, 4,5],
- [2,3,0,1, 8,9],
- [1,2,3,0, 12,13],
-
- [0,1,2,3, 1,3],
- [3,0,1,2, 5,5],
- [2,3,0,1, 9,9],
- [1,2,3,0, 13,13],
-
- [0,1,2,3, 2,3],
- [3,0,1,2, 6,5],
- [2,3,0,1, 10,9],
- [1,2,3,0, 14,13],
-
- [0,1,2,3, 3,3],
- [3,0,1,2, 7,5],
- [2,3,0,1, 11,9],
- [1,2,3,0, 15,13],
- ]
-
- # round 3 table - [abcd k s]
- _round3 = [
- [0,1,2,3, 0,3],
- [3,0,1,2, 8,9],
- [2,3,0,1, 4,11],
- [1,2,3,0, 12,15],
-
- [0,1,2,3, 2,3],
- [3,0,1,2, 10,9],
- [2,3,0,1, 6,11],
- [1,2,3,0, 14,15],
-
- [0,1,2,3, 1,3],
- [3,0,1,2, 9,9],
- [2,3,0,1, 5,11],
- [1,2,3,0, 13,15],
-
- [0,1,2,3, 3,3],
- [3,0,1,2, 11,9],
- [2,3,0,1, 7,11],
- [1,2,3,0, 15,15],
- ]
-
- 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 update(self, content):
- if not isinstance(content, bytes):
- raise TypeError("expected bytes")
- buf = self._buf
- if buf:
- content = buf + content
- idx = 0
- end = len(content)
- while True:
- next = idx + 64
- if next <= end:
- self._process(content[idx:next])
- self._count += 1
- idx = next
- else:
- self._buf = content[idx:]
- return
-
- def copy(self):
- other = _builtin_md4()
- other._count = self._count
- other._state = list(self._state)
- other._buf = self._buf
- return other
-
- def digest(self):
- # NOTE: backing up state so we can restore it after _process is called,
- # in case object is updated again (this is only attr altered by this method)
- orig = list(self._state)
-
- # final block: buf + 0x80,
- # then 0x00 padding until congruent w/ 56 mod 64 bytes
- # then last 8 bytes = msg length in bits
- buf = self._buf
- msglen = self._count*512 + len(buf)*8
- block = buf + b'\x80' + b'\x00' * ((119-len(buf)) % 64) + \
- struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)
- if len(block) == 128:
- self._process(block[:64])
- self._process(block[64:])
- else:
- assert len(block) == 64
- self._process(block)
-
- # render digest & restore un-finalized state
- out = struct.pack("<4I", *self._state)
- self._state = orig
- return out
-
- def hexdigest(self):
- return bascii_to_str(hexlify(self.digest()))
-
- #===================================================================
- # eoc
- #===================================================================
-
-# keep ref around for unittest, 'md4' usually replaced by ssl wrapper, below.
-_builtin_md4 = md4
+warn("the module 'passlib.utils.md4' is deprecated as of Passlib 1.7, "
+ "and will be removed in Passlib 2.0, please use "
+ "'lookup_hash(\"md4\").const()' from 'passlib.crypto' instead",
+ DeprecationWarning)
#=============================================================================
-# check if hashlib provides accelarated md4
+# backwards compat exports
#=============================================================================
-import hashlib
-
-def _has_native_md4(): # pragma: no cover -- runtime detection
- try:
- h = hashlib.new("md4")
- except ValueError:
- # not supported - ssl probably missing (e.g. ironpython)
- return False
- result = h.hexdigest()
- if result == '31d6cfe0d16ae931b73c59d7e0c089c0':
- return True
- # anything else and we should alert user
- from passlib.exc import PasslibRuntimeWarning
- warn("native md4 support disabled, sanity check failed!", PasslibRuntimeWarning)
- return False
+__all__ = ["md4"]
-if _has_native_md4():
- # overwrite md4 class w/ hashlib wrapper
- def md4(content=None):
- """wrapper for hashlib.new('md4')"""
- return hashlib.new('md4', content or b'')
+# this should use hashlib version if available,
+# and fall back to builtin version.
+from passlib.crypto.digest import lookup_hash
+md4 = lookup_hash("md4").const
+del lookup_hash
#=============================================================================
# eof
diff --git a/passlib/utils/pbkdf2.py b/passlib/utils/pbkdf2.py
index 74dbc0c..2e08a09 100644
--- a/passlib/utils/pbkdf2.py
+++ b/passlib/utils/pbkdf2.py
@@ -6,293 +6,54 @@ maybe rename to "kdf" since it's getting more key derivation functions added.
#=============================================================================
# imports
#=============================================================================
+from __future__ import division
# core
-import hashlib
import logging; log = logging.getLogger(__name__)
-import re
-from struct import pack
-from warnings import warn
# site
-try:
- import M2Crypto.EVP as _EVP
-except ImportError:
- _EVP = None
-#_EVP = None
# pkg
-from passlib.exc import PasslibRuntimeWarning, ExpectedTypeError
-from passlib.utils import join_bytes, to_native_str, bytes_to_int, int_to_bytes, join_byte_values
-from passlib.utils.compat import BytesIO, irange, int_types
+from passlib.exc import ExpectedTypeError
+from passlib.utils import deprecated_function
+from passlib.utils.compat import native_string_types
+from passlib.crypto.digest import norm_hash_name, lookup_hash, pbkdf1 as _pbkdf1, pbkdf2_hmac, compile_hmac
# local
__all__ = [
# hash utils
"norm_hash_name",
- "get_hash_info",
# prf utils
"get_prf",
- "get_keyed_prf",
# kdfs
"pbkdf1",
"pbkdf2",
]
-def _clear_caches():
- """unittest helper -- clears get_hash_info() / get_prf() caches"""
- _ghi_cache.clear()
- _prf_cache.clear()
- _nhn_cache.clear()
-
#=============================================================================
-# hash helpers
+# issue deprecation warning for module
#=============================================================================
+from warnings import warn
-# indexes into _nhn_hash_names
-_nhn_formats = dict(hashlib=0, iana=1)
-
-# known hash names
-_nhn_hash_names = [
- # format: (hashlib/ssl name, iana name or standin, other known aliases ...)
-
- # hashes with official IANA-assigned names
- # (as of 2012-03 - http://www.iana.org/assignments/hash-function-text-names)
- ("md2", "md2"),
- ("md5", "md5"),
- ("sha1", "sha-1"),
- ("sha224", "sha-224", "sha2-224"),
- ("sha256", "sha-256", "sha2-256"),
- ("sha384", "sha-384", "sha2-384"),
- ("sha512", "sha-512", "sha2-512"),
-
- # hashlib/ssl-supported hashes without official IANA names,
- # hopefully compatible stand-ins have been chosen.
- ("md4", "md4"),
- ("sha", "sha-0", "sha0"),
- ("ripemd", "ripemd"),
- ("ripemd160", "ripemd-160"),
-]
-
-# cache for norm_hash_name()
-_nhn_cache = {}
-
-def norm_hash_name(name, format="hashlib"):
- """Normalize hash function name
-
- :arg name:
- Original hash function name.
-
- This name can be a Python :mod:`~hashlib` digest name,
- a SCRAM mechanism name, IANA assigned hash name, etc.
- Case is ignored, and underscores are converted to hyphens.
-
- :param format:
- Naming convention to normalize to.
- Possible values are:
-
- * ``"hashlib"`` (the default) - normalizes name to be compatible
- with Python's :mod:`!hashlib`.
-
- * ``"iana"`` - normalizes name to IANA-assigned hash function name.
- for hashes which IANA hasn't assigned a name for, issues a warning,
- and then uses a heuristic to give a "best guess".
-
- :returns:
- Hash name, returned as native :class:`!str`.
-
- .. versionadded:: 1.6
- """
- # check cache
- try:
- idx = _nhn_formats[format]
- except KeyError:
- raise ValueError("unknown format: %r" % (format,))
- try:
- return _nhn_cache[name][idx]
- except KeyError:
- pass
- orig = name
-
- # normalize input
- if not isinstance(name, str):
- name = to_native_str(name, 'utf-8', 'hash name')
- name = re.sub("[_ /]", "-", name.strip().lower())
- if name.startswith("scram-"):
- name = name[6:]
- if name.endswith("-plus"):
- name = name[:-5]
-
- # look through standard names and known aliases
- def check_table(name):
- for row in _nhn_hash_names:
- if name in row:
- _nhn_cache[orig] = row
- return row[idx]
- result = check_table(name)
- if result:
- return result
-
- # try to clean name up, and recheck table
- m = re.match("^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$", name)
- if m:
- name, rev, size = m.group("name", "rev", "size")
- if rev:
- name += rev
- if size:
- name += "-" + size
- result = check_table(name)
- if result:
- return result
-
- # 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]
-
-def _get_hash_const(name):
- """internal helper used by :func:`get_hash_info`"""
- # typecheck
- if not isinstance(name, str):
- raise TypeError("expected digest name")
-
- # abort on bad values, and on hashlib attrs which aren't hashes
- if name.startswith("_") or name in ("new", "algorithms"):
- return None
-
- # first, check hashlib.<attr> for an efficient constructor
- try:
- return getattr(hashlib, name)
- except AttributeError:
- pass
-
- # second, check hashlib.new() in case SSL supports the digest
- try:
- # new() should throw ValueError if alg is unknown
- tmp = hashlib.new(name, b"")
- except ValueError:
- pass
- else:
- # create wrapper function
- # XXX: find a better way to just return hash constructor
- def const(msg=b""):
- return hashlib.new(name, msg)
- const.__name__ = "new(%r)" % name
- const.__module__ = "hashlib"
- const.__doc__ = "wrapper for %s hash constructor" % name
- return const
-
- # third, use md4 fallback if needed
- if name == "md4":
- from passlib.utils.md4 import md4
- return md4
-
- # finally, give up!
- return None
-
-# cache for get_hash_const() lookups
-_ghi_cache = {}
-
-def get_hash_info(name):
- """Lookup hash constructor & stats by name.
-
- This is a thin wrapper around :mod:`hashlib`. It looks up a hash by name,
- and returns a hash constructor, along with the digest & block sizes.
- Calls to this method are cached, making it a lot faster
- than looking up a hash and then creating a temporary instance
- in order to read the digest size. Additionally, this function includes
- workarounds and fallbacks for various VM-specific issues.
-
- :arg name: hashlib-compatible name of hash function
-
- :raises ValueError: if hash is unknown or unsupported.
+warn("the module 'passlib.utils.pbkdf2' is deprecated as of Passlib 1.7, "
+ "and will be removed in Passlib 2.0, please use 'passlib.crypto' instead",
+ DeprecationWarning)
- :returns: `(hash_constructor, digest_size, block_size)`
+#=============================================================================
+# hash helpers
+#=============================================================================
- .. versionadded:: 1.7
- """
- try:
- return _ghi_cache[name]
- except KeyError:
- pass
- const = _get_hash_const(name)
- if not const:
- raise ValueError("unknown hash algorithm: %r" % name)
- tmp = const()
- if len(tmp.digest()) != tmp.digest_size:
- raise RuntimeError("%r constructor failed sanity check" % name)
- record = _ghi_cache[name] = (const, tmp.digest_size, tmp.block_size)
- return record
+norm_hash_name = deprecated_function(deprecated="1.7", removed="1.8", func_module=__name__,
+ replacement="passlib.crypto.digest.norm_hash_name")(norm_hash_name)
#=============================================================================
# prf lookup
#=============================================================================
-_BNULL = b'\x00'
-
-# sanity check
-_TEST_HMAC_SHA1 = b',\x1cb\xe0H\xa5\x82M\xfb>\xd6\x98\xef\x8e\xf9oQ\x85\xa3i'
-
-# xlat tables used by hmac routines
-_TRANS_5C = join_byte_values((x ^ 0x5C) for x in irange(256))
-_TRANS_36 = join_byte_values((x ^ 0x36) for x in irange(256))
+#: cache mapping prf name/func -> (func, digest_size)
+_prf_cache = {}
-# prefixes for recognizing hmac-{digest} prf names
+#: list of accepted prefixes
_HMAC_PREFIXES = ("hmac_", "hmac-")
-#------------------------------------------------------------------------
-# general prf lookup
-#------------------------------------------------------------------------
-def _get_hmac_prf(digest):
- """helper for get_prf() -- returns HMAC-based prf for specified digest"""
- # helpers
- def tag_wrapper(prf):
- """helper to document generated wrappers"""
- prf.__name__ = "hmac_" + digest
- prf.__doc__ = ("hmac_%s(key, msg) -> digest;"
- " generated by passlib.utils.pbkdf2.get_prf()" %
- digest)
-
- # use m2crypto if it's present and supports requested digest
- if _EVP:
- # use m2crypto function directly for sha1, since that's its default digest
- if digest == "sha1":
- if _EVP.hmac(b'x', b'y') != _TEST_HMAC_SHA1:
- # don't expect to ever get here, but just in case
- raise RuntimeError("M2Crypto.EVP.hmac() failed sanity check")
- return _EVP.hmac, 20
-
- # else check if it supports given digest as an option
- try:
- result = _EVP.hmac(b'x', b'y', digest)
- except ValueError:
- pass
- else:
- const = _EVP.hmac
- def prf(key, msg):
- return const(key, msg, digest)
- digest_size = len(result)
- tag_wrapper(prf)
- return prf, digest_size
-
- # fall back to hashlib-based implementation --
- # this is a simplified version of stdlib's hmac module.
- const, digest_size, block_size = get_hash_info(digest)
- assert block_size >= 16, "unacceptably low block size"
- def prf(key, msg):
- klen = len(key)
- if klen > block_size:
- key = const(key).digest()
- klen = digest_size
- if klen < block_size:
- key += _BNULL * (block_size - klen)
- tmp = const(key.translate(_TRANS_36) + msg).digest()
- return const(key.translate(_TRANS_5C) + tmp).digest()
- tag_wrapper(prf)
- return prf, digest_size
-
-# cache mapping prf name/func -> (func, digest_size)
-_prf_cache = {}
-
def get_prf(name):
"""Lookup pseudo-random family (PRF) by name.
@@ -303,9 +64,7 @@ def get_prf(name):
is the name of a hash function such as
``md5``, ``sha256``, etc.
- This can also be a callable with the signature
- ``prf_func(secret, message) -> digest``,
- in which case it will be returned unchanged.
+ todo: restore text about callables.
:raises ValueError: if the name is not known
:raises TypeError: if the name is not a callable or string
@@ -333,15 +92,22 @@ def get_prf(name):
This function will attempt to return the fastest implementation
it can find. Primarily, if M2Crypto is present, and supports the specified PRF,
:func:`M2Crypto.EVP.hmac` will be used behind the scenes.
+
+ .. deprecated:: 1.7
+
+ This function is deprecated, and will be removed in Passlib 2.0.
+ This only related replacement is :func:`passlib.crypto.digest.compile_hmac`.
"""
global _prf_cache
if name in _prf_cache:
return _prf_cache[name]
- if isinstance(name, str):
- if name.startswith(_HMAC_PREFIXES):
- record = _get_hmac_prf(name[5:])
- else:
+ if isinstance(name, native_string_types):
+ if not name.startswith(_HMAC_PREFIXES):
raise ValueError("unknown prf algorithm: %r" % (name,))
+ digest = lookup_hash(name[5:]).name
+ def hmac(key, msg):
+ return compile_hmac(digest, key)(msg)
+ record = (hmac, hmac.digest_info.digest_size)
elif callable(name):
# assume it's a callable, use it directly
digest_size = len(name(b'x', b'y'))
@@ -351,74 +117,6 @@ def get_prf(name):
_prf_cache[name] = record
return record
-#------------------------------------------------------------------------
-# keyed prf generation
-#------------------------------------------------------------------------
-
-def _get_keyed_hmac_prf(digest, key):
- """get_keyed_prf() helper -- returns efficent hmac() function
- hardcoded with specific digest and key.
- """
- # all the following was adapted from stdlib's hmac module
-
- # resolve digest, get info
- const, digest_size, block_size = get_hash_info(digest)
- assert block_size >= 16, "unacceptably low block size"
-
- # prepare key
- klen = len(key)
- if klen > block_size:
- key = const(key).digest()
- klen = digest_size
- if klen < block_size:
- key += _BNULL * (block_size - klen)
-
- # return optimized hmac function for given key
- inner_proto = const(key.translate(_TRANS_36))
- outer_proto = const(key.translate(_TRANS_5C))
- def kprf(msg):
- inner = inner_proto.copy()
- inner.update(msg)
- outer = outer_proto.copy()
- outer.update(inner.digest())
- return outer.digest()
-
- ##kprf.__name__ = "keyed_%s_hmac" % digest
- ##kprf.__doc__ = "keyed %s-hmac function, " \
- ## "generated by passlib.utils.pbkdf2.get_keyed_prf()" % digest
- return kprf, digest_size
-
-def get_keyed_prf(name, key):
- """Lookup psuedo-random function family by name,
- and return a psuedo-random function bound to a specific key.
-
- :arg name:
- name of psuedorandom family.
- accepts same inputs as :func:`get_prf`.
-
- :arg key:
- key encoded as bytes.
-
- :returns:
- tuple of :samp:`({bound_prf_func}, {digest_size})`,
- where function has signature `bound_prf_func(message) -> digest`.
-
- .. versionadded:: 1.7
- """
- # check for optimized functions (common case)
- if isinstance(name, str) and name.startswith(_HMAC_PREFIXES):
- return _get_keyed_hmac_prf(name[5:], key)
-
- # fallback to making a generic wrapper
- prf, digest_size = get_prf(name)
- def kprf(message):
- return prf(key, message)
-
- ##kprf.__name__ = "keyed_%s" % prf.__name__
- ##kprf.__doc__ = "keyed %s function, " \
- ## "generated by passlib.utils.pbkdf2.get_keyed_prf()" % prf.__name__
- return kprf, digest_size
-
#=============================================================================
# pbkdf1 support
#=============================================================================
@@ -440,115 +138,59 @@ def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"):
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.
+
+ .. deprecated:: 1.7
+
+ This has been relocated to :func:`passlib.crypto.digest.pbkdf1`,
+ and this version will be removed in Passlib 2.0.
+ *Note the call signature has changed.*
"""
- # 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
- const, digest_size, block_size = get_hash_info(hash)
-
- # validate keylen
- if keylen is None:
- keylen = digest_size
- 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 > 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]
+ return _pbkdf1(hash, secret, salt, rounds, keylen)
#=============================================================================
# pbkdf2
#=============================================================================
-
-# NOTE: the pbkdf2 spec does not specify a maximum number of rounds.
-# however, many of the hashes in passlib are currently clamped
-# at the 32-bit limit, just for sanity. Once realistic pbkdf2 rounds
-# start approaching 24 bits or so, this limit will be raised.
-_MAX_BLOCKS = 0xffffffff # 2**32-1
-
def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"):
"""pkcs#5 password-based key derivation v2.0
- :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 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 set to ``None``, will use digest size of selected prf.
+
:param prf:
psuedo-random family to use for key strengthening.
- this can be any string or callable accepted by :func:`get_prf`.
+ this must be a string starting with ``"hmac-"``, followed by the name of a known digest.
this defaults to ``"hmac-sha1"`` (the only prf explicitly listed in
the PBKDF2 specification)
+ .. rst-class:: warning
+
+ .. versionchanged 1.7:
+
+ This argument no longer supports arbitrary PRF callables --
+ These were rarely / never used, and created too many unwanted codepaths.
+
:returns:
raw bytes of generated key
+
+ .. deprecated:: 1.7
+
+ This has been deprecated in favor of :func:`passlib.crypto.digest.pbkdf2_hmac`,
+ and will be removed in Passlib 2.0. *Note the call signature has changed.*
"""
- # 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")
-
- # generated keyed prf helper
- keyed_prf, digest_size = get_keyed_prf(prf, secret)
-
- # validate keylen
- if keylen is None:
- keylen = digest_size
- elif not isinstance(keylen, int_types):
- raise ExpectedTypeError(keylen, "int or None", "keylen")
- elif keylen < 0:
- raise ValueError("keylen must be at least 0")
-
- # m2crypto's pbkdf2-hmac-sha1 is faster than ours, so use it if available.
- # NOTE: as of 2012-4-4, m2crypto has buffer overflow issue which frequently
- # causes segfaults if keylen > 32 (EVP_MAX_KEY_LENGTH).
- # therefore we're avoiding m2crypto for large keys until that's fixed.
- # (https://bugzilla.osafoundation.org/show_bug.cgi?id=13052)
- if prf == "hmac-sha1" and _EVP and keylen < 32:
- return _EVP.pbkdf2(secret, salt, rounds, keylen)
-
- # work out min block count s.t. keylen <= block_count * digest_size
- block_count = (keylen + digest_size - 1) // digest_size
- if block_count >= _MAX_BLOCKS:
- raise ValueError("keylen too long for digest")
-
- # build up result from blocks
- def gen():
- for i in irange(block_count):
- digest = keyed_prf(salt + pack(">L", i+1))
- accum = bytes_to_int(digest)
- # speed-critical loop of pbkdf2
- # NOTE: currently converting digests to integers since that XORs faster.
- for _ in irange(rounds-1):
- digest = keyed_prf(digest)
- accum ^= bytes_to_int(digest)
- yield int_to_bytes(accum, digest_size)
- return join_bytes(gen())[:keylen]
+ if callable(prf) or (isinstance(prf, native_string_types) and not prf.startswith(_HMAC_PREFIXES)):
+ raise NotImplementedError("non-HMAC prfs are not supported as of Passlib 1.7")
+ digest = prf[5:]
+ return pbkdf2_hmac(digest, secret, salt, rounds, keylen)
#=============================================================================
# eof
diff --git a/passlib/win32.py b/passlib/win32.py
index bbdcd57..223dd6c 100644
--- a/passlib/win32.py
+++ b/passlib/win32.py
@@ -35,7 +35,7 @@ from binascii import hexlify
# site
# pkg
from passlib.utils.compat import unicode
-from passlib.utils.des import des_encrypt_block
+from passlib.crypto.des import des_encrypt_block
from passlib.hash import nthash
# local
__all__ = [
diff --git a/setup.py b/setup.py
index 084055a..63313e0 100644
--- a/setup.py
+++ b/setup.py
@@ -132,6 +132,7 @@ setup(
# package info
packages = [
"passlib",
+ "passlib.crypto",
"passlib.ext",
"passlib.ext.django",
"passlib.handlers",