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