2
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 2 or (at your option)
7
* version 3 of the License.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "SymmetricCipher.h"
20
#include "config-keepassx.h"
21
#include "format/KeePass2.h"
23
#include <botan/block_cipher.h>
24
#include <botan/cipher_mode.h>
26
bool SymmetricCipher::init(Mode mode, Direction direction, const QByteArray& key, const QByteArray& iv)
29
if (mode == InvalidMode) {
30
m_error = QObject::tr("SymmetricCipher::init: Invalid cipher mode.");
35
auto botanMode = modeToString(mode);
37
(direction == SymmetricCipher::Encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);
39
auto cipher = Botan::Cipher_Mode::create_or_throw(botanMode.toStdString(), botanDirection);
40
m_cipher.reset(cipher.release());
41
m_cipher->set_key(reinterpret_cast<const uint8_t*>(key.data()), key.size());
43
if (!m_cipher->valid_nonce_length(iv.size())) {
46
m_error = QObject::tr("SymmetricCipher::init: Invalid IV size of %1 for %2.").arg(iv.size()).arg(botanMode);
49
m_cipher->start(reinterpret_cast<const uint8_t*>(iv.data()), iv.size());
50
} catch (std::exception& e) {
62
bool SymmetricCipher::isInitalized() const
67
bool SymmetricCipher::process(char* data, int len)
69
Q_ASSERT(isInitalized());
70
if (!isInitalized()) {
71
m_error = QObject::tr("Cipher not initialized prior to use.");
75
m_error = QObject::tr("Cannot process 0 length data.");
80
// Block size is checked by Botan, an exception is thrown if invalid
81
m_cipher->process(reinterpret_cast<uint8_t*>(data), len);
83
} catch (std::exception& e) {
89
bool SymmetricCipher::process(QByteArray& data)
91
return process(data.data(), data.size());
94
bool SymmetricCipher::finish(QByteArray& data)
96
Q_ASSERT(isInitalized());
97
if (!isInitalized()) {
98
m_error = QObject::tr("Cipher not initialized prior to use.");
103
// Error checking is done by Botan, an exception is thrown if invalid
104
Botan::secure_vector<uint8_t> input(data.begin(), data.end());
105
m_cipher->finish(input);
106
// Post-finished data may be larger than before due to padding
107
data.resize(input.size());
108
// Direct copy the finished data back into the QByteArray
109
std::copy(input.begin(), input.end(), data.begin());
111
} catch (std::exception& e) {
117
void SymmetricCipher::reset()
120
if (isInitalized()) {
125
SymmetricCipher::Mode SymmetricCipher::mode()
130
bool SymmetricCipher::aesKdf(const QByteArray& key, int rounds, QByteArray& data)
133
std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create("AES-256"));
134
cipher->set_key(reinterpret_cast<const uint8_t*>(key.data()), key.size());
136
Botan::secure_vector<uint8_t> out(data.begin(), data.end());
137
for (int i = 0; i < rounds; ++i) {
138
cipher->encrypt(out);
140
std::copy(out.begin(), out.end(), data.begin());
142
} catch (std::exception& e) {
143
qWarning("SymmetricCipher::aesKdf: Could not process: %s", e.what());
148
QString SymmetricCipher::errorString() const
153
SymmetricCipher::Mode SymmetricCipher::cipherUuidToMode(const QUuid& uuid)
155
if (uuid == KeePass2::CIPHER_AES128) {
157
} else if (uuid == KeePass2::CIPHER_AES256) {
159
} else if (uuid == KeePass2::CIPHER_CHACHA20) {
161
} else if (uuid == KeePass2::CIPHER_TWOFISH) {
165
qWarning("SymmetricCipher: Invalid KeePass2 Cipher UUID %s", uuid.toString().toLatin1().data());
169
SymmetricCipher::Mode SymmetricCipher::stringToMode(const QString& cipher)
171
auto cs = Qt::CaseInsensitive;
172
if (cipher.compare("aes-128-cbc", cs) == 0 || cipher.compare("aes128-cbc", cs) == 0) {
174
} else if (cipher.compare("aes-256-cbc", cs) == 0 || cipher.compare("aes256-cbc", cs) == 0) {
176
} else if (cipher.compare("aes-128-ctr", cs) == 0 || cipher.compare("aes128-ctr", cs) == 0) {
178
} else if (cipher.compare("aes-256-ctr", cs) == 0 || cipher.compare("aes256-ctr", cs) == 0) {
180
} else if (cipher.compare("aes-256-gcm", cs) == 0 || cipher.compare("aes256-gcm", cs) == 0) {
182
} else if (cipher.startsWith("twofish", cs)) {
184
} else if (cipher.startsWith("salsa", cs)) {
186
} else if (cipher.startsWith("chacha", cs)) {
193
QString SymmetricCipher::modeToString(const Mode mode)
197
return QStringLiteral("AES-128/CBC");
199
return QStringLiteral("AES-256/CBC");
201
return QStringLiteral("CTR(AES-128)");
203
return QStringLiteral("CTR(AES-256)");
205
return QStringLiteral("AES-256/GCM");
207
return QStringLiteral("Twofish/CBC");
209
return QStringLiteral("Salsa20");
211
return QStringLiteral("ChaCha20");
213
Q_ASSERT_X(false, "SymmetricCipher::modeToString", "Invalid Mode Specified");
218
int SymmetricCipher::defaultIvSize(Mode mode)
236
int SymmetricCipher::keySize(Mode mode)
254
int SymmetricCipher::blockSize(Mode mode)
272
int SymmetricCipher::ivSize(Mode mode)