This algorithm was developed by Alec Muffett  for Solaris, as a replacement for the aging des_crypt. It was introduced in Solaris 9u2. While based on the MD5 message digest, it has very little at all in common with the md5_crypt algorithm. It supports 32 bit variable rounds and an 8 character salt.
password hash usage – for examples of how to use this class via the common hash interface.
The original Solaris implementation has some hash encoding quirks which may not be properly accounted for in Passlib. Until more user feedback and sample hashes have been gathered, caveat emptor.
This class implements the Sun-MD5-Crypt password hash, and follows the Password Hash Interface.
It supports a variable-length salt, and a variable number of rounds.
An example hash (of passwd) is $md5,rounds=5000$GUBv0xjJ$$mSwgIswdjlTY0YxV7HBVm0. A sun-md5-crypt hash string has the format $md5,rounds=rounds$salt$$checksum, where:
An alternate format, $md5$salt$$checksum is used when the rounds value is 0.
There also exists some hashes which have only a single $ between the salt and the checksum; these have a slightly different checksum calculation (see Bare Salt Issue for details).
Solaris seems to deviate from the Modular Crypt Format in that it considers , to indicate the end of the identifier in addition to $.
The algorithm used is based around the MD5 message digest and the “Muffett Coin Toss” algorithm.
an initial MD5 digest is created from the concatentation of the password, and the configuration string (using the format $md5,rounds=rounds$salt$, or $md5$salt$ if rounds is 0).
(See Bare Salt Issue for details about an issue affecting this step)
The final digest is then encoded into hash64 format using the same transposed byte order that md5_crypt uses, and returned.
The Muffet Coin Toss algorithm is as follows: Given the current round number, and a 16 byte MD5 digest, it returns a 0 or 1, using the following formula:
All references below to a specific bit of the digest should be interpreted mod 128. All references below to a specific byte of the digest should be interpreted mod 16.
A 8-bit integer X is generated from the following formula: for each i in 0..7 inclusive:
- let A be the i‘th byte of the digest, as an 8-bit int.
- let B be the i+3‘rd byte of the digest, as an 8-bit int.
- let R be A shifted right by B % 5 bits.
- let V be the R‘th byte of the digest.
- if the A % 8‘th bit of B is 1, divide V by 2.
- use the V‘th bit of the digest as the i‘th bit of X.
Another 8-bit integer, Y, is generated exactly the same manner as X, except that:
- A is the i+8‘th byte of the digest,
- B is the i+11‘th byte of the digest.
if bit round of the digest is 1, X is divided by 2.
if bit round+64 of the digest is 1, Y is divided by 2.
the final result is X‘th bit of the digest XORed against Y‘th bit of the digest.
According to the only existing documentation of this algorithm , it’s hashes were supposed to have the format $md5$salt$checksum, and include only the bare string $md5$salt in the salt digest step (see step 2, above).
However, almost all hashes encountered in production environments have the format $md5$salt$$checksum (note the double $$). Unfortunately, it is not merely a cosmetic difference: hashes of this format incorporate the first $ after the salt within the salt digest step, so the resulting checksum is different.
The documentation hints that this stems from a bug within the production implementation’s parser. This bug causes the implementation to return $$-format hashes when passed a configuration string that ends with $. It returns the intended original format & checksum only if there is at least one letter after the $, e.g. $md5$salt$x.
Passlib attempts to accomodate both formats using the special bare_salt keyword. It is set to True to indicate a configuration or hash string which contains only a single $, and does not incorporate it into the hash calculation. The $$ hash is encountered more often in production since it seems the Solaris salt generator always appends a $; because of this bare_salt=False was chosen as the default, so that hashes will be generated which by default conform to what users are used to.
Passlib’s implementation of Sun-MD5-Crypt deliberately deviates from the official implementation in the following ways:
The underlying algorithm takes in a password specified as a series of non-null bytes, and does not specify what encoding should be used; though a us-ascii compatible encoding is implied by all known reference hashes.
In order to provide support for unicode strings, Passlib will encode unicode passwords using utf-8 before running them through sun-md5-crypt. If a different encoding is desired by an application, the password should be encoded before handing it to Passlib.
The underlying scheme implicitly allows rounds to have zero padding (e.g. $md5,rounds=001$abc$), and also allows 0 rounds to be specified two ways ($md5$abc$ and $md5,rounds=0$abc$). Allowing either of these would result in multiple possible checksums for the same password & salt. To prevent ambiguity, Passlib will throw a ValueError if the rounds value is zero-padded, or specified explicitly as 0 (e.g. $md5,rounds=0$abc$).
Given the lack of documentation, lack of test vectors, and known bugs which accompany the original Solaris implementation, Passlib may not accurately be able to generate and verify all hashes encountered in a Solaris environment. Issues of concern include:
|||(1, 2) Overview of & motivations for the algorithm - http://dropsafe.crypticide.com/article/1389|
|||The source of Hamlet’s speech, used byte-for-byte as the constant data - http://www.ibiblio.org/pub/docs/books/gutenberg/etext98/2ws2610.txt|