keepassxc

Форк
0
/
SymmetricCipher.cpp 
289 строк · 7.8 Кб
1
/*
2
 *  Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
3
 *
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.
8
 *
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.
13
 *
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/>.
16
 */
17

18
#include "SymmetricCipher.h"
19

20
#include "config-keepassx.h"
21
#include "format/KeePass2.h"
22

23
#include <botan/block_cipher.h>
24
#include <botan/cipher_mode.h>
25

26
bool SymmetricCipher::init(Mode mode, Direction direction, const QByteArray& key, const QByteArray& iv)
27
{
28
    m_mode = mode;
29
    if (mode == InvalidMode) {
30
        m_error = QObject::tr("SymmetricCipher::init: Invalid cipher mode.");
31
        return false;
32
    }
33

34
    try {
35
        auto botanMode = modeToString(mode);
36
        auto botanDirection =
37
            (direction == SymmetricCipher::Encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);
38

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());
42

43
        if (!m_cipher->valid_nonce_length(iv.size())) {
44
            m_mode = InvalidMode;
45
            m_cipher.reset();
46
            m_error = QObject::tr("SymmetricCipher::init: Invalid IV size of %1 for %2.").arg(iv.size()).arg(botanMode);
47
            return false;
48
        }
49
        m_cipher->start(reinterpret_cast<const uint8_t*>(iv.data()), iv.size());
50
    } catch (std::exception& e) {
51
        m_mode = InvalidMode;
52
        m_cipher.reset();
53

54
        m_error = e.what();
55
        reset();
56
        return false;
57
    }
58

59
    return true;
60
}
61

62
bool SymmetricCipher::isInitalized() const
63
{
64
    return m_cipher;
65
}
66

67
bool SymmetricCipher::process(char* data, int len)
68
{
69
    Q_ASSERT(isInitalized());
70
    if (!isInitalized()) {
71
        m_error = QObject::tr("Cipher not initialized prior to use.");
72
        return false;
73
    }
74
    if (len == 0) {
75
        m_error = QObject::tr("Cannot process 0 length data.");
76
        return false;
77
    }
78

79
    try {
80
        // Block size is checked by Botan, an exception is thrown if invalid
81
        m_cipher->process(reinterpret_cast<uint8_t*>(data), len);
82
        return true;
83
    } catch (std::exception& e) {
84
        m_error = e.what();
85
        return false;
86
    }
87
}
88

89
bool SymmetricCipher::process(QByteArray& data)
90
{
91
    return process(data.data(), data.size());
92
}
93

94
bool SymmetricCipher::finish(QByteArray& data)
95
{
96
    Q_ASSERT(isInitalized());
97
    if (!isInitalized()) {
98
        m_error = QObject::tr("Cipher not initialized prior to use.");
99
        return false;
100
    }
101

102
    try {
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());
110
        return true;
111
    } catch (std::exception& e) {
112
        m_error = e.what();
113
        return false;
114
    }
115
}
116

117
void SymmetricCipher::reset()
118
{
119
    m_error.clear();
120
    if (isInitalized()) {
121
        m_cipher.reset();
122
    }
123
}
124

125
SymmetricCipher::Mode SymmetricCipher::mode()
126
{
127
    return m_mode;
128
}
129

130
bool SymmetricCipher::aesKdf(const QByteArray& key, int rounds, QByteArray& data)
131
{
132
    try {
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());
135

136
        Botan::secure_vector<uint8_t> out(data.begin(), data.end());
137
        for (int i = 0; i < rounds; ++i) {
138
            cipher->encrypt(out);
139
        }
140
        std::copy(out.begin(), out.end(), data.begin());
141
        return true;
142
    } catch (std::exception& e) {
143
        qWarning("SymmetricCipher::aesKdf: Could not process: %s", e.what());
144
        return false;
145
    }
146
}
147

148
QString SymmetricCipher::errorString() const
149
{
150
    return m_error;
151
}
152

153
SymmetricCipher::Mode SymmetricCipher::cipherUuidToMode(const QUuid& uuid)
154
{
155
    if (uuid == KeePass2::CIPHER_AES128) {
156
        return Aes128_CBC;
157
    } else if (uuid == KeePass2::CIPHER_AES256) {
158
        return Aes256_CBC;
159
    } else if (uuid == KeePass2::CIPHER_CHACHA20) {
160
        return ChaCha20;
161
    } else if (uuid == KeePass2::CIPHER_TWOFISH) {
162
        return Twofish_CBC;
163
    }
164

165
    qWarning("SymmetricCipher: Invalid KeePass2 Cipher UUID %s", uuid.toString().toLatin1().data());
166
    return InvalidMode;
167
}
168

169
SymmetricCipher::Mode SymmetricCipher::stringToMode(const QString& cipher)
170
{
171
    auto cs = Qt::CaseInsensitive;
172
    if (cipher.compare("aes-128-cbc", cs) == 0 || cipher.compare("aes128-cbc", cs) == 0) {
173
        return Aes128_CBC;
174
    } else if (cipher.compare("aes-256-cbc", cs) == 0 || cipher.compare("aes256-cbc", cs) == 0) {
175
        return Aes256_CBC;
176
    } else if (cipher.compare("aes-128-ctr", cs) == 0 || cipher.compare("aes128-ctr", cs) == 0) {
177
        return Aes128_CTR;
178
    } else if (cipher.compare("aes-256-ctr", cs) == 0 || cipher.compare("aes256-ctr", cs) == 0) {
179
        return Aes256_CTR;
180
    } else if (cipher.compare("aes-256-gcm", cs) == 0 || cipher.compare("aes256-gcm", cs) == 0) {
181
        return Aes256_GCM;
182
    } else if (cipher.startsWith("twofish", cs)) {
183
        return Twofish_CBC;
184
    } else if (cipher.startsWith("salsa", cs)) {
185
        return Salsa20;
186
    } else if (cipher.startsWith("chacha", cs)) {
187
        return ChaCha20;
188
    } else {
189
        return InvalidMode;
190
    }
191
}
192

193
QString SymmetricCipher::modeToString(const Mode mode)
194
{
195
    switch (mode) {
196
    case Aes128_CBC:
197
        return QStringLiteral("AES-128/CBC");
198
    case Aes256_CBC:
199
        return QStringLiteral("AES-256/CBC");
200
    case Aes128_CTR:
201
        return QStringLiteral("CTR(AES-128)");
202
    case Aes256_CTR:
203
        return QStringLiteral("CTR(AES-256)");
204
    case Aes256_GCM:
205
        return QStringLiteral("AES-256/GCM");
206
    case Twofish_CBC:
207
        return QStringLiteral("Twofish/CBC");
208
    case Salsa20:
209
        return QStringLiteral("Salsa20");
210
    case ChaCha20:
211
        return QStringLiteral("ChaCha20");
212
    default:
213
        Q_ASSERT_X(false, "SymmetricCipher::modeToString", "Invalid Mode Specified");
214
        return {};
215
    }
216
}
217

218
int SymmetricCipher::defaultIvSize(Mode mode)
219
{
220
    switch (mode) {
221
    case Aes128_CBC:
222
    case Aes256_CBC:
223
    case Aes128_CTR:
224
    case Aes256_CTR:
225
    case Aes256_GCM:
226
    case Twofish_CBC:
227
        return 16;
228
    case Salsa20:
229
    case ChaCha20:
230
        return 12;
231
    default:
232
        return -1;
233
    }
234
}
235

236
int SymmetricCipher::keySize(Mode mode)
237
{
238
    switch (mode) {
239
    case Aes128_CBC:
240
    case Aes128_CTR:
241
        return 16;
242
    case Aes256_CBC:
243
    case Aes256_CTR:
244
    case Aes256_GCM:
245
    case Twofish_CBC:
246
    case Salsa20:
247
    case ChaCha20:
248
        return 32;
249
    default:
250
        return 0;
251
    }
252
}
253

254
int SymmetricCipher::blockSize(Mode mode)
255
{
256
    switch (mode) {
257
    case Aes128_CBC:
258
    case Aes256_CBC:
259
    case Aes256_GCM:
260
    case Twofish_CBC:
261
        return 16;
262
    case Aes128_CTR:
263
    case Aes256_CTR:
264
    case Salsa20:
265
    case ChaCha20:
266
        return 1;
267
    default:
268
        return 0;
269
    }
270
}
271

272
int SymmetricCipher::ivSize(Mode mode)
273
{
274
    switch (mode) {
275
    case Aes128_CBC:
276
    case Aes256_CBC:
277
    case Aes128_CTR:
278
    case Aes256_CTR:
279
    case Twofish_CBC:
280
        return 16;
281
    case Aes256_GCM:
282
        return 12;
283
    case Salsa20:
284
    case ChaCha20:
285
        return 8;
286
    default:
287
        return 0;
288
    }
289
}
290

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.