Rev 2 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | daniel-mar | 1 | <?php |
2 | |||
3 | // Mastercopy of this file: |
||
4 | // http://www.viathinksoft.de/~daniel-marschall/code/php/marschallHash.phps |
||
5 | |||
6 | function MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) { |
||
7 | |||
8 | // -------------------------------------------------------------------------------- |
||
9 | // MarschallHash: Uncrackable hash with multiple SHA1 iterations in base64 encoding |
||
10 | // This function is pretty slow because of the iterations, but therefore secure |
||
11 | // against offline attacks or rainbowtables. Also, the slowlyness of this hash |
||
12 | // makes the creation of rainbow tables much harder. |
||
13 | // |
||
14 | // (C)Copyright 2011 Daniel Marschall, ViaThinkSoft. All rights reserved. |
||
15 | // www.daniel-marschall.de / www.viathinksoft.de |
||
16 | // |
||
17 | // Notation of the hash name: |
||
18 | // - MHA-1987 (resp. MHA-xxxx where xxxx stands for $iterations) |
||
19 | // - MHAb-1987 is used for the binary output variant |
||
20 | // - MD5MHA-1987 is used if $password is a (unsalted!) md5-hash |
||
21 | // - MD5MHAb-1987 is used for the binary output variant. |
||
22 | // |
||
23 | // Default parameters: |
||
24 | // iteratedSalt = '' --> you should change this value to something |
||
25 | // user-specific (e.g. username) or something random |
||
26 | // iterations = 1987 --> you should ONLY change this value to a lower |
||
27 | // one if you have performance issues |
||
28 | // binary_output = no --> base64 encoding is chosen by default |
||
29 | // |
||
30 | // Format: |
||
31 | // - MHA has the same length as SHA1. |
||
32 | // - MHA in base64 format has a constant length of 27 bytes and is case sensitive. |
||
33 | // - MHA in binary format has a constant length of 20 bytes. |
||
34 | // |
||
35 | // Comparison 1: |
||
36 | // x = '' |
||
37 | // SHA1(x) = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' (Len = 40) |
||
38 | // SHA256(x) = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' (Len = 64) |
||
39 | // MD5(x) = 'd41d8cd98f00b204e9800998ecf8427e' (Len = 32) |
||
40 | // MHA(x) = 'UOLv7DgK5/4S7994FeSWZkHDJoQ' (Len = 27) |
||
41 | // |
||
42 | // Comparison 2: |
||
43 | // x = 'The quick brown fox jumps over the lazy dog' |
||
44 | // SHA1(x) = '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' (Len = 40) |
||
45 | // SHA256(x) = 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592' (Len = 64) |
||
46 | // MD5(x) = '9e107d9d372bb6826bd81d3542a419d6' (Len = 32) |
||
47 | // MHA(x) = 'Bqdd38sigmurBt6kU/0q99GWSnE' (Len = 27) |
||
48 | // |
||
49 | // Mechanism: |
||
50 | // MHA(x, iteratedSalt, iterations) = optionalBase64(rec(iterations, x, iteratedSalt)) |
||
51 | // rec[n, x, iteratedSalt] = binarySHA1(iteratedSalt || rec[n-1, x, iteratedSalt] || iteratedSalt) |
||
52 | // rec[0, x, iteratedSalt] = x |
||
53 | // |
||
54 | // Recommended usage: |
||
55 | // - Use the username as iteratedSalt |
||
56 | // MHA($password, $username) == $database_hash |
||
57 | // - If you want to upgrade your existing user-database, e.g. already hashed with md5(): |
||
58 | // a) Update your database: |
||
59 | // $database_hash_new = MHA($database_hash_old, $username) |
||
60 | // b) Compare with this variant: |
||
61 | // MHA(md5($password), $username) == $database_hash_new |
||
62 | // or |
||
63 | // MD5_MHA($password, $username) == $database_hash_new |
||
64 | // |
||
65 | // Revision: 2011-07-21 (fixed typo in 2013-03-09) |
||
66 | // -------------------------------------------------------------------------------- |
||
67 | |||
68 | if ($iterations < 1) { |
||
69 | trigger_error('at function ' . __FUNCTION__ . ': $iterations has to be greater or equal 1', E_USER_ERROR); |
||
70 | return false; |
||
71 | } |
||
72 | |||
73 | $m = $password; |
||
74 | |||
75 | for ($i=1; $i<=$iterations; $i++) { |
||
76 | $m = sha1($iteratedSalt.$m.$iteratedSalt, true); // SHA1 with binary output |
||
77 | } |
||
78 | |||
79 | if (!$binary_output) { |
||
80 | $m = base64_encode($m); |
||
81 | |||
82 | // Remove single "=" at the end |
||
83 | # $m = str_replace('=', '', $m); |
||
84 | $m = substr($m, 0, 27); |
||
85 | } |
||
86 | |||
87 | return $m; |
||
88 | } |
||
89 | |||
90 | // --- The following functions are useable for database migration --- |
||
91 | |||
92 | function MD5_TO_MD5_MHA($md5_hash, $iteratedSalt='', $iterations=1987, $binary_output=false) { |
||
93 | // Use this function to migrate a (unsalted!) md5 hash into a MD5MHA hash |
||
94 | // |
||
95 | // Actually, this is just an alias of MHA() |
||
96 | |||
97 | return marschallHash($md5_hash, $iteratedSalt, $iterations, $binary_output); |
||
98 | } |
||
99 | |||
100 | function MD5_MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) { |
||
101 | // Use this function if you have a MD5MHA hash instead of a MHA hash |
||
102 | // |
||
103 | // MD5MHA() is equal to MHA(MD5()) where MD5() is unsalted! |
||
104 | |||
105 | return MHA(md5($password), $iteratedSalt, $iterations, $binary_output); |
||
106 | } |
||
107 | |||
108 | function MD5MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) { |
||
109 | // Alias of MD5_MHA() |
||
110 | |||
111 | return MD5_MHA($password, $iteratedSalt, $iterations, $binary_output); |
||
112 | } |
||
113 | |||
114 | function MHA_AddIterations($mha, $iteratedSalt='', $additionalIterations, $binary_output=false) { |
||
115 | // This function converts a MHA with x itertions into a MHA with |
||
116 | // x+additionalIterations iterations, if the iteratedSalt is equal. |
||
117 | // Use this function if you want to upgrade your database to a higher MHA strength. |
||
118 | // |
||
119 | // Example: |
||
120 | // MHA_AddIterations(MHA('test', 'salt', 1987), 'salt', 13) == MHA('test', 'salt', 1987+13); |
||
121 | // |
||
122 | // Of course, you cannot lower the strength of a MHA, so additionalIterations has to be >= 0. |
||
123 | |||
124 | // Is it Base64 input? |
||
125 | # if (strlen($mha) == 28) $mha = base64_decode($mha); |
||
126 | if (strlen($mha) == 27) $mha = base64_decode($mha); |
||
127 | |||
128 | // Is it now binary input? |
||
129 | // (Que) Will there be problems if the input string looks like multibyte? |
||
130 | if (strlen($mha) != 20) { |
||
131 | trigger_error('at function ' . __FUNCTION__ . ': does not seem to be a MHA', E_USER_ERROR); |
||
132 | return false; |
||
133 | } |
||
134 | |||
135 | if ($additionalIterations == 0) return $mha; |
||
136 | |||
137 | if ($additionalIterations < 0) { |
||
138 | trigger_error('at function ' . __FUNCTION__ . ': additionalIterations has to be 0 or higher.', E_USER_ERROR); |
||
139 | return false; |
||
140 | } |
||
141 | |||
142 | return marschallHash($mha, $iteratedSalt, $additionalIterations, $binary_output); |
||
143 | } |
||
144 | |||
145 | ?> |