Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>OK, so maybe this answer is a year late, but I'll give it a shot. In your own answer, you note that <code>crypt()</code> is using the FreeBSD MD5, which also does some interesting transformations on the salt before running the hash, so the result of what I'm about to give you will never quite match up with the results of a call to <code>md5()</code>. That said, the only difference between the output you are seeing and the format you are used to is that the output you are seeing is encoded as follows</p> <pre><code>$1$ # this indicates that it is MD5 Vf/.4.1. # these eight characters are the significant portion of the salt $ # this character is technically part of the salt, but it is ignored CgCo33eb # the last 22 characters are the actual hash iHVuFhpw # they are base64 encoded (to be printable) using crypt's alphabet S.kMI0 # floor(22 * 6 / 8) = 16 (the length in bytes of a raw MD5 hash) </code></pre> <p>To my knowledge, the alphabet used by crypt looks like this:</p> <pre><code>./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz </code></pre> <p>So, with all of this borne in mind, here is how you can convert the 22 character crypt-base64 hash into a 32 character base16 (hexadecimal) hash:</p> <p>First, you need something to convert the base64 (with custom alphabet) into a raw 16-byte MD5 hash.</p> <pre><code>define('CRYPT_ALPHA','./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); /** * Decodes a base64 string based on the alphabet set in constant CRYPT_ALPHA * Uses string functions rather than binary transformations, because said * transformations aren't really much faster in PHP * @params string $str The string to decode * @return string The raw output, which may include unprintable characters */ function base64_decode_ex($str) { // set up the array to feed numerical data using characters as keys $alpha = array_flip(str_split(CRYPT_ALPHA)); // split the input into single-character (6 bit) chunks $bitArray = str_split($str); $decodedStr = ''; foreach ($bitArray as &amp;$bits) { if ($bits == '$') { // $ indicates the end of the string, to stop processing here break; } if (!isset($alpha[$bits])) { // if we encounter a character not in the alphabet return false; // then break execution, the string is invalid } // decbin will only return significant digits, so use sprintf to pad to 6 bits $decodedStr .= sprintf('%06s', decbin($alpha[$bits])); } // there can be up to 6 unused bits at the end of a string, so discard them $decodedStr = substr($decodedStr, 0, strlen($decodedStr) - (strlen($decodedStr) % 8)); $byteArray = str_split($decodedStr, 8); foreach ($byteArray as &amp;$byte) { $byte = chr(bindec($byte)); } return join($byteArray); } </code></pre> <p>Now that you've got the raw data, you'll need a method to convert it to the base-16 format you're expecting, which couldn't be easier.</p> <pre><code>/** * Takes an input in base 256 and encodes it to base 16 using the Hex alphabet * This function will not be commented. For more info: * @see http://php.net/str-split * @see http://php.net/sprintf * * @param string $str The value to convert * @return string The base 16 rendering */ function base16_encode($str) { $byteArray = str_split($str); foreach ($byteArray as &amp;$byte) { $byte = sprintf('%02x', ord($byte)); } return join($byteArray); } </code></pre> <p>Finally, since the output of crypt includes a lot of data we don't need (and, in fact, cannot use) for this process, a short and sweet function to not only tie these two together but to allow for direct input of output from crypt.</p> <pre><code>/** * Takes a 22 byte crypt-base-64 hash and converts it to base 16 * If the input is longer than 22 chars (e.g., the entire output of crypt()), * then this function will strip all but the last 22. Fails if under 22 chars * * @param string $hash The hash to convert * @param string The equivalent base16 hash (therefore a number) */ function md5_b64tob16($hash) { if (strlen($hash) &lt; 22) { return false; } if (strlen($hash) &gt; 22) { $hash = substr($hash,-22); } return base16_encode(base64_decode_ex($hash)); } </code></pre> <p>Given these functions, the base16 representation of your three examples are:</p> <pre><code>3ac3b4145aa7b9387a46dd7c780c1850 6f80dba665e27749ae88f58eaef5fe84 ec5f74086ec3fab34957d3ef0f838154 </code></pre> <p>Of course, it is important to remember that they were always valid, just formatted differently.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. COI'm afraid md5-crypt's output does not just differ from a md5 hash by just base64-encoding with a custom alphabet. Looking at one of the instances of the implementation, http://google.com/codesearch/p?hl=en#eiS4vny31P0/Linux-PAM-0.99.7.0/modules/pam_unix/md5_crypt.c md5-crypt also massages hashes in such a way as to make the encryption computation-intensive (relatively to the computational power available at the time, see the comment about a 60MHz Pentium). The solution you provide does not invert that transformation.
      singulars
    2. COHoly crap, coming back from beyond the grave to downvote me, and furthermore not on the basis of the question? The issue at hand was that the values coming out of the `crypt()` function didn't LOOK like the asker wanted them to -- this was a solution to the asker's issue with the visual representation of the hash, not making the hash produced by `md5()` equitable to the hash produced by `crypt()`. In my humble opinion, this is not what the downvote button is for.
      singulars
    3. COThe base64 encoded versions don't have padding. The hashes are 128 bits long so after stepping through it 6 bit at a time you will have 2 bits remaining. An obvious solution is to use these as LSBs and use the index as usual. The strange thing is that although each of the provided hashes end in 00 bitpairs, the last character of their base64 representation (from the Q) differ. Can you please explain this?
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload