Falkon Develop
Cross-platform Qt-based web browser
aesinterface.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2013-2014 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
4* Copyright (C) 2013-2017 David Rosca <nowrep@gmail.com>
5*
6* This is based on a work by Saju Pillai <saju.pillai@gmail.com>
7*
8* This program is free software: you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by
10* the Free Software Foundation, either version 3 of the License, or
11* (at your option) any later version.
12*
13* This program is distributed in the hope that it will be useful,
14* but WITHOUT ANY WARRANTY; without even the implied warranty of
15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16* GNU General Public License for more details.
17*
18* You should have received a copy of the GNU General Public License
19* along with this program. If not, see <http://www.gnu.org/licenses/>.
20* ============================================================ */
21
22#include "aesinterface.h"
23
24#include <openssl/aes.h>
25#include <openssl/rand.h>
26#include <openssl/sha.h>
27
28#include <QCryptographicHash>
29#include <QByteArray>
30#include <QMessageBox>
31
36const int AesInterface::VERSION = 1;
37
39 : QObject(parent)
40{
41 m_encodeCTX = EVP_CIPHER_CTX_new();
42 m_decodeCTX = EVP_CIPHER_CTX_new();
43 EVP_CIPHER_CTX_init(m_encodeCTX);
44 EVP_CIPHER_CTX_init(m_decodeCTX);
45}
46
48{
49 EVP_CIPHER_CTX_cleanup(m_encodeCTX);
50 EVP_CIPHER_CTX_cleanup(m_decodeCTX);
51 EVP_CIPHER_CTX_free(m_encodeCTX);
52 EVP_CIPHER_CTX_free(m_decodeCTX);
53}
54
56{
57 return m_ok;
58}
59
60// Create an 256 bit 'key' using the supplied password, and creates a random 'iv'.
61// saltArray is an array of 8 bytes can be added for taste.
62// Fills in the encryption and decryption ctx objects and returns true on success
63bool AesInterface::init(int evpMode, const QByteArray &password, const QByteArray &iVector)
64{
65 m_iVector.clear();
66
67 int i;
68 const int nrounds = 5;
69 uchar key[EVP_MAX_KEY_LENGTH];
70
71 // Gen "key" for AES 256 CBC mode. A SHA1 digest is used to hash the supplied
72 // key material. nrounds is the number of times that we hash the material.
73 // More rounds are more secure but slower.
74 i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), nullptr, (uchar*)password.data(), password.size(), nrounds, key, nullptr);
75
76 if (i != 32) {
77 qWarning("Key size is %d bits - should be 256 bits", i * 8);
78 return false;
79 }
80
81 int result = 0;
82 if (evpMode == EVP_PKEY_MO_ENCRYPT) {
83 m_iVector = createRandomData(EVP_MAX_IV_LENGTH);
84 result = EVP_EncryptInit_ex(m_encodeCTX, EVP_aes_256_cbc(), NULL, key, (uchar*)m_iVector.constData());
85 }
86 else if (evpMode == EVP_PKEY_MO_DECRYPT) {
87 result = EVP_DecryptInit_ex(m_decodeCTX, EVP_aes_256_cbc(), NULL, key, (uchar*)iVector.constData());
88 }
89
90 if (result == 0) {
91 qWarning("EVP is not initialized!");
92 return false;
93 }
94
95 return true;
96}
97
98QByteArray AesInterface::encrypt(const QByteArray &plainData, const QByteArray &password)
99{
100 if (!init(EVP_PKEY_MO_ENCRYPT, password)) {
101 m_ok = false;
102 return plainData;
103 }
104
105 // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes
106 int dataLength = plainData.size();
107 int cipherlength = dataLength + AES_BLOCK_SIZE;
108 int finalLength = 0;
109 auto* ciphertext = (uchar*)malloc(cipherlength);
110
111 // allows reusing of 'm_encodeCTX' for multiple encryption cycles
112 EVP_EncryptInit_ex(m_encodeCTX, NULL, NULL, NULL, NULL);
113
114 // update ciphertext, c_len is filled with the length of ciphertext generated,
115 // dataLength is the size of plaintext in bytes
116 EVP_EncryptUpdate(m_encodeCTX, ciphertext, &cipherlength, (uchar*)plainData.data(), dataLength);
117
118 // update ciphertext with the final remaining bytes
119 EVP_EncryptFinal_ex(m_encodeCTX, ciphertext + cipherlength, &finalLength);
120
121 dataLength = cipherlength + finalLength;
122 QByteArray out((char*)ciphertext, dataLength);
123 out = QByteArray::number(AesInterface::VERSION) + '$' + m_iVector.toBase64() + '$' + out.toBase64();
124 free(ciphertext);
125
126 m_ok = true;
127 return out;
128}
129
130QByteArray AesInterface::decrypt(const QByteArray &cipherData, const QByteArray &password)
131{
132 m_ok = false;
133
134 if (cipherData.isEmpty()) {
135 m_ok = true;
136 return {};
137 }
138
139 QList<QByteArray> cipherSections(cipherData.split('$'));
140 if (cipherSections.size() != 3) {
141 qWarning() << "Decrypt error: It seems data is corrupted";
142 return {};
143 }
144
145 if (cipherSections.at(0).toInt() > AesInterface::VERSION) {
146 QMessageBox::information(nullptr, tr("Warning!"), tr("Data has been encrypted with a newer version of Falkon."
147 "\nPlease install latest version of Falkon."));
148 return {};
149 }
150
151 if (cipherSections.at(0).toInt() != 1) {
152 qWarning() << Q_FUNC_INFO << "There is just version 1 of decoder, yet ;-)";
153 return {};
154 }
155
156 if (!init(EVP_PKEY_MO_DECRYPT, password, QByteArray::fromBase64(cipherSections.at(1)))) {
157 return {};
158 }
159
160 QByteArray cipherArray = QByteArray::fromBase64(cipherSections.at(2));
161 int cipherLength = cipherArray.size();
162 int plainTextLength = cipherLength;
163 int finalLength = 0;
164
165 auto* cipherText = (uchar*)cipherArray.data();
166 // because we have padding ON, we must allocate an extra cipher block size of memory
167 auto* plainText = (uchar*)malloc(plainTextLength + AES_BLOCK_SIZE);
168
169 EVP_DecryptInit_ex(m_decodeCTX, NULL, NULL, NULL, NULL);
170 EVP_DecryptUpdate(m_decodeCTX, plainText, &plainTextLength, cipherText, cipherLength);
171 int success = EVP_DecryptFinal_ex(m_decodeCTX, plainText + plainTextLength, &finalLength);
172
173 cipherLength = plainTextLength + finalLength;
174
175 QByteArray result((char*)plainText, cipherLength);
176 free(plainText);
177
178 if (success != 1) {
179 return {};
180 }
181
182 m_ok = true;
183 return result;
184}
185
186QByteArray AesInterface::passwordToHash(const QString &masterPassword)
187{
188 if (!masterPassword.isEmpty()) {
189 QByteArray result = masterPassword.toUtf8();
190 result = QCryptographicHash::hash(result, QCryptographicHash::Sha1) + result;
191 result = QCryptographicHash::hash(result, QCryptographicHash::Sha1);
192 return result.toBase64();
193 }
194 else {
195 return {};
196 }
197}
198
199QByteArray AesInterface::createRandomData(int length)
200{
201 auto* randomData = (uchar*)malloc(length);
202
203 RAND_bytes(randomData, length);
204 QByteArray data((char*)randomData, length);
205 free(randomData);
206
207 return data;
208}
bool isOk() const
AesInterface(QObject *parent=nullptr)
static QByteArray createRandomData(int length)
QByteArray decrypt(const QByteArray &cipherData, const QByteArray &password)
QByteArray encrypt(const QByteArray &plainData, const QByteArray &password)
static QByteArray passwordToHash(const QString &masterPassword)
static const int VERSION
Definition: aesinterface.h:37
i
Definition: i18n.py:23