File indexing completed on 2024-05-12 04:58:25

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2013-2014 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
0004 * Copyright (C) 2013-2017 David Rosca <nowrep@gmail.com>
0005 *
0006 * This is based on a work by Saju Pillai <saju.pillai@gmail.com>
0007 *
0008 * This program is free software: you can redistribute it and/or modify
0009 * it under the terms of the GNU General Public License as published by
0010 * the Free Software Foundation, either version 3 of the License, or
0011 * (at your option) any later version.
0012 *
0013 * This program is distributed in the hope that it will be useful,
0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016 * GNU General Public License for more details.
0017 *
0018 * You should have received a copy of the GNU General Public License
0019 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0020 * ============================================================ */
0021 
0022 #include "aesinterface.h"
0023 
0024 #include <openssl/aes.h>
0025 #include <openssl/rand.h>
0026 #include <openssl/sha.h>
0027 
0028 #include <QCryptographicHash>
0029 #include <QByteArray>
0030 #include <QMessageBox>
0031 
0032 //////////////////////////////////////////////
0033 /// Version 1:
0034 /// init(): n=5, EVP_CIPHER=EVP_aes_256_cbc(), EVP_MD=EVP_sha256(), Random IV
0035 /// Encrypted data structure: Version$InitializationVector_base64$EncryptedData_base64
0036 const int AesInterface::VERSION = 1;
0037 
0038 AesInterface::AesInterface(QObject* parent)
0039     : QObject(parent)
0040 {
0041     m_encodeCTX = EVP_CIPHER_CTX_new();
0042     m_decodeCTX = EVP_CIPHER_CTX_new();
0043     EVP_CIPHER_CTX_init(m_encodeCTX);
0044     EVP_CIPHER_CTX_init(m_decodeCTX);
0045 }
0046 
0047 AesInterface::~AesInterface()
0048 {
0049     EVP_CIPHER_CTX_cleanup(m_encodeCTX);
0050     EVP_CIPHER_CTX_cleanup(m_decodeCTX);
0051     EVP_CIPHER_CTX_free(m_encodeCTX);
0052     EVP_CIPHER_CTX_free(m_decodeCTX);
0053 }
0054 
0055 bool AesInterface::isOk() const
0056 {
0057     return m_ok;
0058 }
0059 
0060 // Create an 256 bit 'key' using the supplied password, and creates a random 'iv'.
0061 // saltArray is an array of 8 bytes can be added for taste.
0062 // Fills in the encryption and decryption ctx objects and returns true on success
0063 bool AesInterface::init(int evpMode, const QByteArray &password, const QByteArray &iVector)
0064 {
0065     m_iVector.clear();
0066 
0067     int i;
0068     const int nrounds = 5;
0069     uchar key[EVP_MAX_KEY_LENGTH];
0070 
0071     // Gen "key" for AES 256 CBC mode. A SHA1 digest is used to hash the supplied
0072     // key material. nrounds is the number of times that we hash the material.
0073     // More rounds are more secure but slower.
0074     i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), nullptr, (uchar*)password.data(), password.size(), nrounds, key, nullptr);
0075 
0076     if (i != 32) {
0077         qWarning("Key size is %d bits - should be 256 bits", i * 8);
0078         return false;
0079     }
0080 
0081     int result = 0;
0082     if (evpMode == EVP_PKEY_MO_ENCRYPT) {
0083         m_iVector = createRandomData(EVP_MAX_IV_LENGTH);
0084         result = EVP_EncryptInit_ex(m_encodeCTX, EVP_aes_256_cbc(), NULL, key, (uchar*)m_iVector.constData());
0085     }
0086     else if (evpMode == EVP_PKEY_MO_DECRYPT) {
0087         result = EVP_DecryptInit_ex(m_decodeCTX, EVP_aes_256_cbc(), NULL, key, (uchar*)iVector.constData());
0088     }
0089 
0090     if (result == 0) {
0091         qWarning("EVP is not initialized!");
0092         return false;
0093     }
0094 
0095     return true;
0096 }
0097 
0098 QByteArray AesInterface::encrypt(const QByteArray &plainData, const QByteArray &password)
0099 {
0100     if (!init(EVP_PKEY_MO_ENCRYPT, password)) {
0101         m_ok = false;
0102         return plainData;
0103     }
0104 
0105     // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes
0106     int dataLength = plainData.size();
0107     int cipherlength = dataLength + AES_BLOCK_SIZE;
0108     int finalLength = 0;
0109     auto* ciphertext = (uchar*)malloc(cipherlength);
0110 
0111     // allows reusing of 'm_encodeCTX' for multiple encryption cycles
0112     EVP_EncryptInit_ex(m_encodeCTX, NULL, NULL, NULL, NULL);
0113 
0114     // update ciphertext, c_len is filled with the length of ciphertext generated,
0115     // dataLength is the size of plaintext in bytes
0116     EVP_EncryptUpdate(m_encodeCTX, ciphertext, &cipherlength, (uchar*)plainData.data(), dataLength);
0117 
0118     // update ciphertext with the final remaining bytes
0119     EVP_EncryptFinal_ex(m_encodeCTX, ciphertext + cipherlength, &finalLength);
0120 
0121     dataLength = cipherlength + finalLength;
0122     QByteArray out((char*)ciphertext, dataLength);
0123     out = QByteArray::number(AesInterface::VERSION) + '$' + m_iVector.toBase64() + '$' + out.toBase64();
0124     free(ciphertext);
0125 
0126     m_ok = true;
0127     return out;
0128 }
0129 
0130 QByteArray AesInterface::decrypt(const QByteArray &cipherData, const QByteArray &password)
0131 {
0132     m_ok = false;
0133 
0134     if (cipherData.isEmpty()) {
0135         m_ok = true;
0136         return {};
0137     }
0138 
0139     QList<QByteArray> cipherSections(cipherData.split('$'));
0140     if (cipherSections.size() != 3) {
0141         qWarning() << "Decrypt error: It seems data is corrupted";
0142         return {};
0143     }
0144 
0145     if (cipherSections.at(0).toInt() > AesInterface::VERSION) {
0146         QMessageBox::information(nullptr, tr("Warning!"), tr("Data has been encrypted with a newer version of Falkon."
0147                                  "\nPlease install latest version of Falkon."));
0148         return {};
0149     }
0150 
0151     if (cipherSections.at(0).toInt() != 1) {
0152         qWarning() << Q_FUNC_INFO << "There is just version 1 of decoder, yet ;-)";
0153         return {};
0154     }
0155 
0156     if (!init(EVP_PKEY_MO_DECRYPT, password, QByteArray::fromBase64(cipherSections.at(1)))) {
0157         return {};
0158     }
0159 
0160     QByteArray cipherArray = QByteArray::fromBase64(cipherSections.at(2));
0161     int cipherLength = cipherArray.size();
0162     int plainTextLength = cipherLength;
0163     int finalLength = 0;
0164 
0165     auto* cipherText = (uchar*)cipherArray.data();
0166     // because we have padding ON, we must allocate an extra cipher block size of memory
0167     auto* plainText = (uchar*)malloc(plainTextLength + AES_BLOCK_SIZE);
0168 
0169     EVP_DecryptInit_ex(m_decodeCTX, NULL, NULL, NULL, NULL);
0170     EVP_DecryptUpdate(m_decodeCTX, plainText, &plainTextLength, cipherText, cipherLength);
0171     int success = EVP_DecryptFinal_ex(m_decodeCTX, plainText + plainTextLength, &finalLength);
0172 
0173     cipherLength = plainTextLength + finalLength;
0174 
0175     QByteArray result((char*)plainText, cipherLength);
0176     free(plainText);
0177 
0178     if (success != 1) {
0179         return {};
0180     }
0181 
0182     m_ok = true;
0183     return result;
0184 }
0185 
0186 QByteArray AesInterface::passwordToHash(const QString &masterPassword)
0187 {
0188     if (!masterPassword.isEmpty()) {
0189         QByteArray result = masterPassword.toUtf8();
0190         result = QCryptographicHash::hash(result, QCryptographicHash::Sha1) + result;
0191         result = QCryptographicHash::hash(result, QCryptographicHash::Sha1);
0192         return result.toBase64();
0193     }
0194     else {
0195         return {};
0196     }
0197 }
0198 
0199 QByteArray AesInterface::createRandomData(int length)
0200 {
0201     auto* randomData = (uchar*)malloc(length);
0202 
0203     RAND_bytes(randomData, length);
0204     QByteArray data((char*)randomData, length);
0205     free(randomData);
0206 
0207     return data;
0208 }