passlib.hash.des_crypt - DES Crypt¶
Danger
This algorithm is dangerously insecure by modern standards. It is trivially broken, and should not be used if at all possible. For new code, see the list of recommended hashes.
This class implements the original DES-based Unix Crypt algorithm. While no longer in active use in most places, it is supported for legacy purposes by many Unix flavors. It can used directly as follows:
from passlib.hash import des_crypt
# generate new salt, hash password
hash = des_crypt.hash("password")
print(hash)
# > 'JQMuyS6H.AGMo'
# verify the password
print(des_crypt.verify("password", hash))
# > True
print(des_crypt.verify("letmein", hash))
# > False
Tip
the generic PasswordHash usage examples
Interface¶
des_crypt
¶
des_crypt(salt=None, **kwds)
This class implements the des-crypt password hash, and follows the :ref:password-hash-api.
It supports a fixed-length salt.
The :meth:~passlib.ifc.PasswordHash.using method accepts the following optional keywords:
Parameters:
-
salt(str, default:None) –Optional salt string. If not specified, one will be autogenerated (this is recommended). If specified, it must be 2 characters, drawn from the regexp range
[./0-9A-Za-z]. -
truncate_error(bool) –By default, des_crypt will silently truncate passwords larger than 8 bytes. Setting
truncate_error=Truewill cause :meth:~passlib.ifc.PasswordHash.hashto raise a :exc:~passlib.exc.PasswordTruncateErrorinstead... versionadded:: 1.7
-
relaxed(bool) –By default, providing an invalid value for one of the other keywords will result in a :exc:
ValueError. Ifrelaxed=True, and the error can be corrected, a :exc:~passlib.exc.PasslibHashWarningwill be issued instead. Correctable errors includesaltstrings that are too long... versionadded:: 1.6
encrypt
classmethod
¶
encrypt(*args, **kwds)
Legacy alias for :meth:hash.
.. deprecated:: 1.7
This method was renamed to :meth:!hash in version 1.7.
This alias will be removed in version 2.0, and should only
be used for compatibility with Passlib 1.3 - 1.6.
parsehash
classmethod
¶
parsehash(hash, checksum=True, sanitize=False)
[experimental method] parse hash into dictionary of settings.
this essentially acts as the inverse of :meth:hash: for most
cases, if hash = cls.hash(secret, **opts), then
cls.parsehash(hash) will return a dict matching the original options
(with the extra keyword checksum).
this method may not work correctly for all hashes, and may not be available on some few. its interface may change in future releases, if it's kept around at all.
Parameters:
-
hash–hash to parse
-
checksum–include checksum keyword? (defaults to True)
-
sanitize–mask data for sensitive fields? (defaults to False)
bitsize
classmethod
¶
bitsize(salt_size=None, **kwds)
[experimental method] return info about bitsizes of hash
default_salt_chars
¶
default_salt_chars()
charset used to generate new salt strings (defaults to salt_chars)
get_backend
classmethod
¶
get_backend()
Return name of currently active backend. if no backend has been loaded, loads and returns name of default backend.
Returns:
-
–
name of active backend
Raises:
-
passlib.exc.MissingBackendError–if no backends are available.
has_backend
classmethod
¶
has_backend(name='any')
Check if support is currently available for specified backend.
Parameters:
-
name–name of backend to check for. can be any string accepted by :meth:
set_backend.
Returns:
-
–
Trueif backend is available.Falseif it's available / can't be loaded.Noneif it's present, but won't load due to a security issue.
Raises:
-
ValueError–if backend name is unknown
set_backend
classmethod
¶
set_backend(name='any', dryrun=False)
Load specified backend.
Parameters:
-
name–name of backend to load, can be any of the following:
-
"any"-- use current backend if one is loaded, otherwise load the first available backend. -
"default"-- use the first available backend. -
any string in :attr:
backends, loads specified backend.
-
-
dryrun–If True, this perform all setup actions except switching over to the new backend. (this flag is used to implement :meth:
has_backend)... versionadded:: 1.7
Raises:
-
ValueError–If backend name is unknown.
-
passlib.exc.MissingBackendError–If specific backend is missing; or in the case of
"any"/"default", if no backends are available. -
passlib.exc.PasslibSecurityError–If
"any"or"default"was specified, but the only backend available has a PasslibSecurityError.
Note
This class will use the first available of two possible backends:
- stdlib
crypt(), if the host OS supports DES-Crypt (most Unix systems). - a pure Python implementation of DES-Crypt built into Passlib.
You can see which backend is in use by calling the get_backend() method.
Format¶
A des-crypt hash string consists of 13 characters, drawn from [./0-9A-Za-z].
The first 2 characters form a hash64 <passlib.utils.binary.h64>-encoded
12 bit integer used as the salt, with the remaining characters
forming a hash64-encoded 64-bit integer checksum.
A des-crypt configuration string is also accepted by this module, consists of only the first 2 characters, corresponding to the salt only.
An example hash (of the string password) is JQMuyS6H.AGMo, where the salt is JQ, and the checksum MuyS6H.AGMo.
Algorithm¶
The checksum is formed by a modified version of the DES cipher in encrypt mode:
-
Given a password string and a salt string.
-
The 2 character salt string is decoded to a 12-bit integer salt value; The salt string uses little-endian
hash64 <passlib.utils.binary.h64>encoding. -
If the password is less than 8 bytes, it's NULL padded at the end to 8 bytes.
-
The lower 7 bits of the first 8 bytes of the password are used to form a 56-bit integer; with the first byte providing the most significant 7 bits, and the 8th byte providing the least significant 7 bits.
The remainder of the password (if any) is ignored.
- 25 repeated rounds of modified DES encryption are performed; starting with a null input block, and using the 56-bit integer from step 4 as the DES key.
The salt is used to mutate the normal DES encrypt operation
by swapping bits {i} and {i}+24 in the DES E-Box output
if and only if bit {i} is set in the salt value.
Thus, if the salt is set to 0, normal DES encryption is performed.
(This was intended to prevent optimized implementations
of regular DES encryption to be useful in attacking this algorithm).
-
The 64-bit result of the last round of step 5 is then lsb-padded with 2 zero bits.
-
The resulting 66-bit integer is encoded in big-endian order using the
hash64-big <passlib.utils.binary.h64big>format.
Security Issues¶
DES-Crypt is no longer considered secure, for a variety of reasons:
-
Its use of the DES stream cipher, which is vulnerable to practical pre-image attacks, and considered broken, as well as having too-small key and block sizes.
-
The 12-bit salt is considered to small to defeat rainbow-table attacks (most modern algorithms provide at least a 48-bit salt).
-
The fact that it only uses the lower 7 bits of the first 8 bytes of the password results in a dangerously small keyspace which needs to be searched.
Deviations¶
This implementation of des-crypt differs from others in a few ways:
- Minimum salt string:
Some implementations of des-crypt permit empty and single-character salt strings. However, behavior in these cases varies wildly; with implementations returning everything from errors to incorrect hashes that never validate. To avoid all this, Passlib will throw an "invalid salt" if the provided salt string is not at least 2 characters.
- Restricted salt string character set:
The underlying algorithm expects salt strings to use the
hash64 <passlib.utils.binary.HASH64_CHARS> character set to encode
a 12-bit integer. Many implementations of des-crypt will
accept a salt containing other characters, but
vary wildly in how they are handled, including errors and implementation-specific value mappings.
To avoid all this, Passlib will throw an "invalid salt" if the salt
string contains any non-standard characters.
- Unicode Policy:
The original des-crypt algorithm was designed for 7-bit us-ascii encoding only
(as evidenced by the fact that it discards the 8th bit of all password bytes).
In order to provide support for unicode strings,
Passlib will encode unicode passwords using utf-8
before running them through des-crypt. If a different
encoding is desired by an application, the password should be encoded
before handing it to Passlib.
References¶
- A java implementation of des-crypt, used as base for Passlib's pure-python implementation, can be found at http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt