passlib.hash.bsdi_crypt - BSDi Crypt¶
Danger
This algorithm is not considered secure by modern standards. It should only be used when verifying existing hashes, or when interacting with applications that require this format. For new code, see the list of recommended hashes.
This algorithm was developed by BSDi for their BSD/OS distribution.
It's based on passlib.hash.des_crypt, and contains a larger
salt and a variable number of rounds. This algorithm is also
known as "Extended DES Crypt".
It class can be used directly as follows:
from passlib.hash import bsdi_crypt
# generate new salt, hash password
hash = bsdi_crypt.hash("password")
print(hash)
# > '_7C/.Bf/4gZk10RYRs4Y'
# same, but with explict number of rounds
print(bsdi_crypt.using(rounds=10001).hash("password"))
# > '_FQ0.amG/zwCMip7DnBk'
# verify password
print(bsdi_crypt.verify("password", hash))
# > True
print(bsdi_crypt.verify("secret", hash))
# > False
Tip
See the generic PasswordHash usage examples.
Interface¶
bsdi_crypt
¶
bsdi_crypt(rounds=None, **kwds)
This class implements the BSDi-Crypt password hash, and follows the :ref:password-hash-api.
It supports a fixed-length salt, and a variable number of rounds.
The :meth:~passlib.ifc.PasswordHash.using method accepts the following optional keywords:
Parameters:
-
salt(str) –Optional salt string. If not specified, one will be autogenerated (this is recommended). If specified, it must be 4 characters, drawn from the regexp range
[./0-9A-Za-z]. -
rounds(int, default:None) –Optional number of rounds to use. Defaults to 5001, must be between 1 and 16777215, inclusive.
-
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 includeroundsthat are too small or too large, andsaltstrings that are too long... versionadded:: 1.6.. versionchanged:: 1.6 :meth:
hashwill now issue a warning if an even number of rounds is used (see :ref:bsdi-crypt-security-issuesregarding weak DES keys).
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(rounds=None, vary_rounds=0.1, **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 BSDi-Crypt (primarily BSD-derived systems). - a pure Python implementation of BSDi-Crypt built into Passlib.
You can see which backend is in use by calling the get_backend() method.
Format¶
An example hash (of the string password) is _EQ0.jzhSVeUyoSqLupI.
A bsdi_crypt hash string consists of a 20 character string of the form _{rounds}{salt}{checksum}.
All characters except the underscore prefix are drawn from [./0-9A-Za-z].
_- the underscore is used to distinguish this scheme from others, such as des-crypt.{rounds}is the number of rounds, stored as a 4 characterhash64 <passlib.utils.binary.h64>-encoded 24-bit integer (EQ0.in the example).{salt}is the salt, stored as as a 4 character hash64-encoded 24-bit integer (jzhSin the example).{checksum}is the checksum, stored as an 11 character hash64-encoded 64-bit integer (VeUyoSqLupIin the example).
A bsdi_crypt configuration string is also accepted by this module; and has the same format as the hash string, but with the checksum portion omitted.
Algorithm¶
The checksum is formed by a modified version of the DES cipher in encrypt mode:
-
Given a password string, a salt string, and rounds string.
-
The 4 character rounds string is decoded to a 24-bit integer rounds value; The rounds string uses little-endian
hash64 <passlib.utils.binary.h64>encoding. -
The 4 character salt string is decoded to a 24-bit integer salt value; The salt string uses little-endian
hash64 <passlib.utils.binary.h64>encoding. -
The password is NULL-padded on the end to the smallest non-zero multiple of 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. This is the DES key.
-
For each additional block of 8 bytes in the padded password:
a. The current DES key is encrypted using a single round of normal DES, with itself as the input block. b. Step 5 is repeated for the current 8-byte block, and xored against the existing DES key.
-
Repeated rounds of (modified) DES encryption are performed; starting with a null input block, and using the 56-bit integer from step 5/6 as the DES key.
The salt is used to 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.
The number of rounds is controlled by the value decoded in step 2.
-
The 64-bit result of the last round of step 7 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¶
BSDi Crypt should not be considered sufficiently secure, for a number 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 24-bit salt is too 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 each byte of the password restricts the keyspace which needs to be searched.
-
Additionally, even rounds values are slightly weaker still, as they may reveal the hash used one of the weak DES keys 1. This information could theoretically allow an attacker to perform a brute-force attack on a reduced keyspace and against only 1-2 rounds of DES. (This issue is mitigated by the fact that few passwords are both valid and result in a weak key).
This algorithm is none-the-less stronger than des_crypt itself,
since it supports variable rounds, a larger salt size,
and uses all the bytes of the password.
Deviations¶
This implementation of bsdi-crypt differs from others in one way:
- Unicode Policy:
The original bsdi-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 bsdi-crypt. If a different
encoding is desired by an application, the password should be encoded
before handing it to Passlib.
References¶
- Primary source used for description of bsdi-crypt format & algorithm - http://fuse4bsd.creo.hu/localcgi/man-cgi.cgi?crypt+3
- Another source describing algorithm - http://ftp.lava.net/cgi-bin/bsdi-man?proto=1.1&query=crypt&msection=3&apropos=0
-
DES weak keys - https://en.wikipedia.org/wiki/Weak_key#Weak_keys_in_DES ↩