| 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(n: 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(n: 1)); |
| 90 | } else { |
| 91 | hash1.update(a: password.toByteArray().left(n: 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(n: 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(n: 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 | |