File indexing completed on 2024-04-07 04:36:05

0001 /*
0002   Copyright (C) 2007 Carlo Todeschini - Metarete s.r.l. <info@metarete.it>
0003 
0004   Permission is hereby granted, free of charge, to any person obtaining a copy
0005   of this software and associated documentation files (the "Software"), to deal
0006   in the Software without restriction, including without limitation the rights
0007   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008   copies of the Software, and to permit persons to whom the Software is
0009   furnished to do so, subject to the following conditions:
0010 
0011   The above copyright notice and this permission notice shall be included in
0012   all copies or substantial portions of the Software.
0013 
0014   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
0017   AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
0018   AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0019   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0020 */
0021 
0022 /*
0023   Algorithm inspired by Vladimir Silva's "Secure Java apps on Linux using
0024   MD5 crypt" article
0025   (http://www-128.ibm.com/developerworks/linux/library/l-md5crypt/)
0026 */
0027 
0028 #include <QCoreApplication>
0029 #include <QtCrypto>
0030 #include <QtDebug>
0031 #include <cstdio>
0032 
0033 #ifdef QT_STATICPLUGIN
0034 #include "import_plugins.h"
0035 #endif
0036 
0037 QString to64(long v, int size)
0038 {
0039     // Character set of the encrypted password: A-Za-z0-9./
0040     QString itoa64 = QStringLiteral("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
0041     QString result;
0042 
0043     while (--size >= 0) {
0044         result.append(itoa64.at((int)(v & 0x3f)));
0045         v = v >> 6;
0046     }
0047 
0048     return result;
0049 }
0050 
0051 int byte2unsigned(int byteValue)
0052 {
0053     int integerToReturn;
0054     integerToReturn = (int)byteValue & 0xff;
0055     return integerToReturn;
0056 }
0057 
0058 QString qca_md5crypt(const QCA::SecureArray &password, const QCA::SecureArray &salt)
0059 {
0060     QCA::SecureArray finalState, magic_string = "$1$";
0061 
0062     // The md5crypt algorithm uses two separate hashes
0063     QCA::Hash hash1(QStringLiteral("md5"));
0064     QCA::Hash hash2(QStringLiteral("md5"));
0065 
0066     // MD5 Hash #1: pwd, magic string and salt
0067     hash1.update(password);
0068     hash1.update(magic_string);
0069     hash1.update(salt);
0070 
0071     // MD5 Hash #2: password, salt, password
0072     hash2.update(password);
0073     hash2.update(salt);
0074     hash2.update(password);
0075 
0076     finalState = hash2.final();
0077 
0078     // Two sets of transformations based on the length of the password
0079     for (int i = password.size(); i > 0; i -= 16) {
0080         // Update hash1 from offset value (i > 16 ? 16 : i)
0081         hash1.update(finalState.toByteArray().left(i > 16 ? 16 : i));
0082     }
0083 
0084     // Clear array bits
0085     finalState.fill(0);
0086 
0087     for (int i = password.size(); i != 0; i = i >> 1) {
0088         if ((i & 1) != 0) {
0089             hash1.update(finalState.toByteArray().left(1));
0090         } else {
0091             hash1.update(password.toByteArray().left(1));
0092         }
0093     }
0094 
0095     finalState = hash1.final();
0096 
0097     // Now build a 1000 entry dictionary...
0098     for (int i = 0; i < 1000; i++) {
0099         hash2.clear();
0100 
0101         if ((i & 1) != 0) {
0102             hash2.update(password);
0103         } else {
0104             hash2.update(finalState.toByteArray().left(16));
0105         }
0106 
0107         if ((i % 3) != 0) {
0108             hash2.update(salt);
0109         }
0110 
0111         if ((i % 7) != 0) {
0112             hash2.update(password);
0113         }
0114 
0115         if ((i & 1) != 0) {
0116             hash2.update(finalState.toByteArray().left(16));
0117         } else {
0118             hash2.update(password);
0119         }
0120 
0121         finalState = hash2.final();
0122     }
0123 
0124     // Create an output string
0125     // Salt is part of the encoded password ($1$<string>$)
0126     QString encodedString;
0127 
0128     encodedString.append(QString::fromLatin1(magic_string.toByteArray()));
0129     encodedString.append(QString::fromLatin1(salt.toByteArray()));
0130     encodedString.append(QStringLiteral("$"));
0131 
0132     long l;
0133 
0134     l = (byte2unsigned(finalState.toByteArray().at(0)) << 16 | (byte2unsigned(finalState.toByteArray().at(6))) << 8 |
0135          byte2unsigned(finalState.toByteArray().at(12)));
0136     encodedString.append(to64(l, 4));
0137 
0138     l = (byte2unsigned(finalState.toByteArray().at(1)) << 16 | (byte2unsigned(finalState.toByteArray().at(7))) << 8 |
0139          byte2unsigned(finalState.toByteArray().at(13)));
0140     encodedString.append(to64(l, 4));
0141 
0142     l = (byte2unsigned(finalState.toByteArray().at(2)) << 16 | (byte2unsigned(finalState.toByteArray().at(8))) << 8 |
0143          byte2unsigned(finalState.toByteArray().at(14)));
0144     encodedString.append(to64(l, 4));
0145 
0146     l = (byte2unsigned(finalState.toByteArray().at(3)) << 16 | (byte2unsigned(finalState.toByteArray().at(9))) << 8 |
0147          byte2unsigned(finalState.toByteArray().at(15)));
0148     encodedString.append(to64(l, 4));
0149 
0150     l = (byte2unsigned(finalState.toByteArray().at(4)) << 16 | (byte2unsigned(finalState.toByteArray().at(10))) << 8 |
0151          byte2unsigned(finalState.toByteArray().at(5)));
0152     encodedString.append(to64(l, 4));
0153 
0154     l = byte2unsigned(finalState.toByteArray().at(11));
0155     encodedString.append(to64(l, 2));
0156 
0157     return encodedString;
0158 }
0159 
0160 int main(int argc, char **argv)
0161 {
0162     // the Initializer object sets things up, and
0163     // also does cleanup when it goes out of scope
0164     QCA::Initializer init;
0165 
0166     QCoreApplication app(argc, argv);
0167 
0168     QCA::SecureArray password, salt;
0169 
0170     if (argc < 3) {
0171         printf("Usage: %s password salt (salt without $1$)\n", argv[0]);
0172         return 1;
0173     }
0174 
0175     password.append(argv[1]);
0176 
0177     salt.append(argv[2]);
0178 
0179     // must always check that an algorithm is supported before using it
0180     if (!QCA::isSupported("md5"))
0181         printf("MD5 hash not supported!\n");
0182     else {
0183         QString result = qca_md5crypt(password, salt);
0184 
0185         printf("md5crypt     [ %s , %s ] = '%s'\n", password.data(), salt.data(), qPrintable(result));
0186 
0187         // this is equivalent if you have GNU libc 2.0
0188         // printf( "GNU md5crypt [ %s , %s ] = '%s'\n",  password.data(), salt.data(), crypt( password.data(), (
0189         // "$1$"+salt ).data() ) );
0190     }
0191 
0192     return 0;
0193 }