1 | /* |
2 | Copyright (C) 2007 Carlo Todeschini - Metarete s.r.l. <info@metarete.it> |
3 | |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | of this software and associated documentation files (the "Software"), to deal |
6 | in the Software without restriction, including without limitation the rights |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | copies of the Software, and to permit persons to whom the Software is |
9 | furnished to do so, subject to the following conditions: |
10 | |
11 | The above copyright notice and this permission notice shall be included in |
12 | all copies or substantial portions of the Software. |
13 | |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
18 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
20 | */ |
21 | |
22 | /* |
23 | Algorithm inspired by Vladimir Silva's "Secure Java apps on Linux using |
24 | MD5 crypt" article |
25 | (http://www-128.ibm.com/developerworks/linux/library/l-md5crypt/) |
26 | */ |
27 | |
28 | #include <QCoreApplication> |
29 | #include <QtCrypto> |
30 | #include <QtDebug> |
31 | #include <cstdio> |
32 | |
33 | #ifdef QT_STATICPLUGIN |
34 | #include "import_plugins.h" |
35 | #endif |
36 | |
37 | QString to64(long v, int size) |
38 | { |
39 | // Character set of the encrypted password: A-Za-z0-9./ |
40 | QString itoa64 = QStringLiteral("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ); |
41 | QString result; |
42 | |
43 | while (--size >= 0) { |
44 | result.append(c: itoa64.at(i: (int)(v & 0x3f))); |
45 | v = v >> 6; |
46 | } |
47 | |
48 | return result; |
49 | } |
50 | |
51 | int byte2unsigned(int byteValue) |
52 | { |
53 | int integerToReturn; |
54 | integerToReturn = (int)byteValue & 0xff; |
55 | return integerToReturn; |
56 | } |
57 | |
58 | QString qca_md5crypt(const QCA::SecureArray &password, const QCA::SecureArray &salt) |
59 | { |
60 | QCA::SecureArray finalState, magic_string = "$1$" ; |
61 | |
62 | // The md5crypt algorithm uses two separate hashes |
63 | QCA::Hash hash1(QStringLiteral("md5" )); |
64 | QCA::Hash hash2(QStringLiteral("md5" )); |
65 | |
66 | // MD5 Hash #1: pwd, magic string and salt |
67 | hash1.update(a: password); |
68 | hash1.update(a: magic_string); |
69 | hash1.update(a: salt); |
70 | |
71 | // MD5 Hash #2: password, salt, password |
72 | hash2.update(a: password); |
73 | hash2.update(a: salt); |
74 | hash2.update(a: password); |
75 | |
76 | finalState = hash2.final(); |
77 | |
78 | // Two sets of transformations based on the length of the password |
79 | for (int i = password.size(); i > 0; i -= 16) { |
80 | // Update hash1 from offset value (i > 16 ? 16 : i) |
81 | hash1.update(a: finalState.toByteArray().left(len: i > 16 ? 16 : i)); |
82 | } |
83 | |
84 | // Clear array bits |
85 | finalState.fill(fillChar: 0); |
86 | |
87 | for (int i = password.size(); i != 0; i = i >> 1) { |
88 | if ((i & 1) != 0) { |
89 | hash1.update(a: finalState.toByteArray().left(len: 1)); |
90 | } else { |
91 | hash1.update(a: password.toByteArray().left(len: 1)); |
92 | } |
93 | } |
94 | |
95 | finalState = hash1.final(); |
96 | |
97 | // Now build a 1000 entry dictionary... |
98 | for (int i = 0; i < 1000; i++) { |
99 | hash2.clear(); |
100 | |
101 | if ((i & 1) != 0) { |
102 | hash2.update(a: password); |
103 | } else { |
104 | hash2.update(a: finalState.toByteArray().left(len: 16)); |
105 | } |
106 | |
107 | if ((i % 3) != 0) { |
108 | hash2.update(a: salt); |
109 | } |
110 | |
111 | if ((i % 7) != 0) { |
112 | hash2.update(a: password); |
113 | } |
114 | |
115 | if ((i & 1) != 0) { |
116 | hash2.update(a: finalState.toByteArray().left(len: 16)); |
117 | } else { |
118 | hash2.update(a: password); |
119 | } |
120 | |
121 | finalState = hash2.final(); |
122 | } |
123 | |
124 | // Create an output string |
125 | // Salt is part of the encoded password ($1$<string>$) |
126 | QString encodedString; |
127 | |
128 | encodedString.append(s: QString::fromLatin1(ba: magic_string.toByteArray())); |
129 | encodedString.append(s: QString::fromLatin1(ba: salt.toByteArray())); |
130 | encodedString.append(QStringLiteral("$" )); |
131 | |
132 | long l; |
133 | |
134 | l = (byte2unsigned(byteValue: finalState.toByteArray().at(i: 0)) << 16 | (byte2unsigned(byteValue: finalState.toByteArray().at(i: 6))) << 8 | |
135 | byte2unsigned(byteValue: finalState.toByteArray().at(i: 12))); |
136 | encodedString.append(s: to64(v: l, size: 4)); |
137 | |
138 | l = (byte2unsigned(byteValue: finalState.toByteArray().at(i: 1)) << 16 | (byte2unsigned(byteValue: finalState.toByteArray().at(i: 7))) << 8 | |
139 | byte2unsigned(byteValue: finalState.toByteArray().at(i: 13))); |
140 | encodedString.append(s: to64(v: l, size: 4)); |
141 | |
142 | l = (byte2unsigned(byteValue: finalState.toByteArray().at(i: 2)) << 16 | (byte2unsigned(byteValue: finalState.toByteArray().at(i: 8))) << 8 | |
143 | byte2unsigned(byteValue: finalState.toByteArray().at(i: 14))); |
144 | encodedString.append(s: to64(v: l, size: 4)); |
145 | |
146 | l = (byte2unsigned(byteValue: finalState.toByteArray().at(i: 3)) << 16 | (byte2unsigned(byteValue: finalState.toByteArray().at(i: 9))) << 8 | |
147 | byte2unsigned(byteValue: finalState.toByteArray().at(i: 15))); |
148 | encodedString.append(s: to64(v: l, size: 4)); |
149 | |
150 | l = (byte2unsigned(byteValue: finalState.toByteArray().at(i: 4)) << 16 | (byte2unsigned(byteValue: finalState.toByteArray().at(i: 10))) << 8 | |
151 | byte2unsigned(byteValue: finalState.toByteArray().at(i: 5))); |
152 | encodedString.append(s: to64(v: l, size: 4)); |
153 | |
154 | l = byte2unsigned(byteValue: finalState.toByteArray().at(i: 11)); |
155 | encodedString.append(s: to64(v: l, size: 2)); |
156 | |
157 | return encodedString; |
158 | } |
159 | |
160 | int main(int argc, char **argv) |
161 | { |
162 | // the Initializer object sets things up, and |
163 | // also does cleanup when it goes out of scope |
164 | QCA::Initializer init; |
165 | |
166 | QCoreApplication app(argc, argv); |
167 | |
168 | QCA::SecureArray password, salt; |
169 | |
170 | if (argc < 3) { |
171 | printf(format: "Usage: %s password salt (salt without $1$)\n" , argv[0]); |
172 | return 1; |
173 | } |
174 | |
175 | password.append(a: argv[1]); |
176 | |
177 | salt.append(a: argv[2]); |
178 | |
179 | // must always check that an algorithm is supported before using it |
180 | if (!QCA::isSupported(features: "md5" )) |
181 | printf(format: "MD5 hash not supported!\n" ); |
182 | else { |
183 | QString result = qca_md5crypt(password, salt); |
184 | |
185 | printf(format: "md5crypt [ %s , %s ] = '%s'\n" , password.data(), salt.data(), qPrintable(result)); |
186 | |
187 | // this is equivalent if you have GNU libc 2.0 |
188 | // printf( "GNU md5crypt [ %s , %s ] = '%s'\n", password.data(), salt.data(), crypt( password.data(), ( |
189 | // "$1$"+salt ).data() ) ); |
190 | } |
191 | |
192 | return 0; |
193 | } |
194 | |