blob: 3f138921bf0af58ce4e7f3433b0292c303850a37 [file] [log] [blame]
NingSun0c89b3c2018-02-08 08:34:03 -08001/*
2 * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27// TODO: Store context in securely allocated memory
28
29/*****************************************************************************
30 BotanSymmetricAlgorithm.cpp
31
32 Botan symmetric algorithm implementation
33 *****************************************************************************/
34
35#include "config.h"
36#include "BotanSymmetricAlgorithm.h"
37#include "BotanUtil.h"
38#include "salloc.h"
39#include <iostream>
40
41#include <botan/symkey.h>
42#include <botan/botan.h>
43#include <botan/version.h>
44
45#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,14)
46#include <botan/key_filt.h>
47#endif
48
49#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,0,0)
50#include "Botan_ecb.h"
51#include <botan/cipher_filter.h>
52#endif
53
54#ifdef WITH_AES_GCM
55#include <botan/aead.h>
56#endif
57
58// Constructor
59BotanSymmetricAlgorithm::BotanSymmetricAlgorithm()
60{
61 cryption = NULL;
62 maximumBytes = Botan::BigInt(1);
63 maximumBytes.flip_sign();
64 counterBytes = Botan::BigInt(0);
65}
66
67// Destructor
68BotanSymmetricAlgorithm::~BotanSymmetricAlgorithm()
69{
70 delete cryption;
71 cryption = NULL;
72}
73
74// Encryption functions
75bool BotanSymmetricAlgorithm::encryptInit(const SymmetricKey* key, const SymMode::Type mode /* = SymMode:CBC */, const ByteString& IV /* = ByteString()*/, bool padding /* = true */, size_t counterBits /* = 0 */, const ByteString& aad /* = ByteString() */, size_t tagBytes /* = 0 */)
76{
77 // Call the superclass initialiser
78 if (!SymmetricAlgorithm::encryptInit(key, mode, IV, padding, counterBits, aad, tagBytes))
79 {
80 return false;
81 }
82
83 // Check the IV
84 if (mode != SymMode::GCM && (IV.size() > 0) && (IV.size() != getBlockSize()))
85 {
86 ERROR_MSG("Invalid IV size (%d bytes, expected %d bytes)", IV.size(), getBlockSize());
87
88 ByteString dummy;
89 SymmetricAlgorithm::encryptFinal(dummy);
90
91 return false;
92 }
93
94 ByteString iv;
95
96 if (IV.size() > 0)
97 {
98 iv = IV;
99 }
100 else
101 {
102 iv.wipe(getBlockSize());
103 }
104
105 // Check the counter bits
106 if (counterBits > 0)
107 {
108 Botan::BigInt counter = BotanUtil::byteString2bigInt(iv);
109 counter.mask_bits(counterBits);
110
111 // Reverse the bits
112 while (counterBits > 0)
113 {
114 counterBits--;
115 if (counter.get_bit(counterBits))
116 {
117 counter.clear_bit(counterBits);
118 }
119 else
120 {
121 counter.set_bit(counterBits);
122 }
123 }
124
125 // Set the maximum bytes
126 maximumBytes = (counter + 1) * getBlockSize();
127 counterBytes = Botan::BigInt(0);
128 }
129 else
130 {
131 maximumBytes = Botan::BigInt(1);
132 maximumBytes.flip_sign();
133 }
134
135 // Determine the cipher
136 std::string cipherName = getCipher();
137
138 if (cipherName == "")
139 {
140 ERROR_MSG("Invalid encryption cipher");
141
142 ByteString dummy;
143 SymmetricAlgorithm::encryptFinal(dummy);
144
145 return false;
146 }
147
148 // Allocate the context
149 try
150 {
151 Botan::SymmetricKey botanKey = Botan::SymmetricKey(key->getKeyBits().const_byte_str(), key->getKeyBits().size());
152 if (mode == SymMode::ECB)
153 {
154#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,0,0)
155 // ECB cipher mode was dropped in Botan 2.0
156 const std::vector<std::string> algo_parts = Botan::split_on(cipherName, '/');
157 const std::string cipher_name = algo_parts[0];
158 Botan::BlockCipherModePaddingMethod* pad;
159 if (algo_parts.size() == 3 && algo_parts[2] == "PKCS7")
160 {
161 pad = new Botan::PKCS7_Padding();
162 }
163 else
164 {
165 pad = new Botan::Null_Padding();
166 }
167 std::unique_ptr<Botan::BlockCipher> bc(Botan::BlockCipher::create(cipher_name));
168 Botan::Keyed_Filter* cipher = new Botan::Cipher_Mode_Filter(new Botan::ECB_Encryption(bc.release(),pad));
169 cipher->set_key(botanKey);
170 cryption = new Botan::Pipe(cipher);
171#else
172 cryption = new Botan::Pipe(Botan::get_cipher(cipherName, botanKey, Botan::ENCRYPTION));
173#endif
174 }
175#ifdef WITH_AES_GCM
176 else if (mode == SymMode::GCM)
177 {
178 Botan::AEAD_Mode* aead = Botan::get_aead(cipherName, Botan::ENCRYPTION);
179 aead->set_key(botanKey);
180 aead->set_associated_data(aad.const_byte_str(), aad.size());
181
182 Botan::InitializationVector botanIV = Botan::InitializationVector(IV.const_byte_str(), IV.size());
183 Botan::Keyed_Filter* filter = new Botan::Cipher_Mode_Filter(aead);
184 filter->set_iv(botanIV);
185 cryption = new Botan::Pipe(filter);
186 }
187#endif
188 else
189 {
190 Botan::InitializationVector botanIV = Botan::InitializationVector(IV.const_byte_str(), IV.size());
191 cryption = new Botan::Pipe(Botan::get_cipher(cipherName, botanKey, botanIV, Botan::ENCRYPTION));
192 }
193 cryption->start_msg();
194 }
195 catch (std::exception &e)
196 {
197 ERROR_MSG("Failed to create the encryption token: %s", e.what());
198
199 ByteString dummy;
200 SymmetricAlgorithm::encryptFinal(dummy);
201
202 delete cryption;
203 cryption = NULL;
204
205 return false;
206 }
207
208 return true;
209}
210
211bool BotanSymmetricAlgorithm::encryptUpdate(const ByteString& data, ByteString& encryptedData)
212{
213 if (!SymmetricAlgorithm::encryptUpdate(data, encryptedData))
214 {
215 delete cryption;
216 cryption = NULL;
217
218 return false;
219 }
220
221 // Write data
222 try
223 {
224 if (data.size() > 0)
225 cryption->write(data.const_byte_str(), data.size());
226 }
227 catch (...)
228 {
229 ERROR_MSG("Failed to write to the encryption token");
230
231 ByteString dummy;
232 SymmetricAlgorithm::encryptFinal(dummy);
233
234 delete cryption;
235 cryption = NULL;
236
237 return false;
238 }
239
240 // Count number of bytes written
241 if (maximumBytes.is_positive())
242 {
243 counterBytes += data.size();
244 }
245
246 // Read data
247 int bytesRead = 0;
248 try
249 {
250 size_t outLen = cryption->remaining();
251 encryptedData.resize(outLen);
252 if (outLen > 0)
253 bytesRead = cryption->read(&encryptedData[0], outLen);
254 }
255 catch (...)
256 {
257 ERROR_MSG("Failed to encrypt the data");
258
259 ByteString dummy;
260 SymmetricAlgorithm::encryptFinal(dummy);
261
262 delete cryption;
263 cryption = NULL;
264
265 return false;
266 }
267
268 // Resize the output block
269 encryptedData.resize(bytesRead);
270 currentBufferSize -= bytesRead;
271
272 return true;
273}
274
275bool BotanSymmetricAlgorithm::encryptFinal(ByteString& encryptedData)
276{
277 if (!SymmetricAlgorithm::encryptFinal(encryptedData))
278 {
279 delete cryption;
280 cryption = NULL;
281
282 return false;
283 }
284
285 // Read data
286 int bytesRead = 0;
287 try
288 {
289 cryption->end_msg();
290 size_t outLen = cryption->remaining();
291 encryptedData.resize(outLen);
292 if (outLen > 0)
293 bytesRead = cryption->read(&encryptedData[0], outLen);
294 }
295 catch (...)
296 {
297 ERROR_MSG("Failed to encrypt the data");
298
299 delete cryption;
300 cryption = NULL;
301
302 return false;
303 }
304
305 // Clean up
306 delete cryption;
307 cryption = NULL;
308
309 // Resize the output block
310 encryptedData.resize(bytesRead);
311
312 return true;
313}
314
315// Decryption functions
316bool BotanSymmetricAlgorithm::decryptInit(const SymmetricKey* key, const SymMode::Type mode /* = SymMode::CBC */, const ByteString& IV /* = ByteString() */, bool padding /* = true */, size_t counterBits /* = 0 */, const ByteString& aad /* = ByteString() */, size_t tagBytes /* = 0 */)
317{
318 // Call the superclass initialiser
319 if (!SymmetricAlgorithm::decryptInit(key, mode, IV, padding, counterBits, aad, tagBytes))
320 {
321 return false;
322 }
323
324 // Check the IV
325 if (mode != SymMode::GCM && (IV.size() > 0) && (IV.size() != getBlockSize()))
326 {
327 ERROR_MSG("Invalid IV size (%d bytes, expected %d bytes)", IV.size(), getBlockSize());
328
329 ByteString dummy;
330 SymmetricAlgorithm::decryptFinal(dummy);
331
332 return false;
333 }
334
335 ByteString iv;
336
337 if (IV.size() > 0)
338 {
339 iv = IV;
340 }
341 else
342 {
343 iv.wipe(getBlockSize());
344 }
345
346 // Check the counter bits
347 if (counterBits > 0)
348 {
349 Botan::BigInt counter = BotanUtil::byteString2bigInt(iv);
350 counter.mask_bits(counterBits);
351
352 // Reverse the bits
353 while (counterBits > 0)
354 {
355 counterBits--;
356 if (counter.get_bit(counterBits))
357 {
358 counter.clear_bit(counterBits);
359 }
360 else
361 {
362 counter.set_bit(counterBits);
363 }
364 }
365
366 // Set the maximum bytes
367 maximumBytes = (counter + 1) * getBlockSize();
368 counterBytes = Botan::BigInt(0);
369 }
370 else
371 {
372 maximumBytes = Botan::BigInt(1);
373 maximumBytes.flip_sign();
374 }
375
376 // Determine the cipher class
377 std::string cipherName = getCipher();
378
379 if (cipherName == "")
380 {
381 ERROR_MSG("Invalid decryption cipher");
382
383 ByteString dummy;
384 SymmetricAlgorithm::decryptFinal(dummy);
385
386 return false;
387 }
388
389 // Allocate the context
390 try
391 {
392 Botan::SymmetricKey botanKey = Botan::SymmetricKey(key->getKeyBits().const_byte_str(), key->getKeyBits().size());
393 if (mode == SymMode::ECB)
394 {
395#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,0,0)
396 // ECB cipher mode was dropped in Botan 2.0
397 const std::vector<std::string> algo_parts = Botan::split_on(cipherName, '/');
398 const std::string cipher_name = algo_parts[0];
399 Botan::BlockCipherModePaddingMethod* pad;
400 if (algo_parts.size() == 3 && algo_parts[2] == "PKCS7")
401 {
402 pad = new Botan::PKCS7_Padding();
403 }
404 else
405 {
406 pad = new Botan::Null_Padding();
407 }
408 std::unique_ptr<Botan::BlockCipher> bc(Botan::BlockCipher::create(cipher_name));
409 Botan::Keyed_Filter* cipher = new Botan::Cipher_Mode_Filter(new Botan::ECB_Decryption(bc.release(),pad));
410 cipher->set_key(botanKey);
411 cryption = new Botan::Pipe(cipher);
412#else
413 cryption = new Botan::Pipe(Botan::get_cipher(cipherName, botanKey, Botan::DECRYPTION));
414#endif
415 }
416#ifdef WITH_AES_GCM
417 else if (mode == SymMode::GCM)
418 {
419 Botan::AEAD_Mode* aead = Botan::get_aead(cipherName, Botan::DECRYPTION);
420 aead->set_key(botanKey);
421 aead->set_associated_data(aad.const_byte_str(), aad.size());
422
423 Botan::InitializationVector botanIV = Botan::InitializationVector(IV.const_byte_str(), IV.size());
424 Botan::Keyed_Filter* filter = new Botan::Cipher_Mode_Filter(aead);
425 filter->set_iv(botanIV);
426 cryption = new Botan::Pipe(filter);
427 }
428#endif
429 else
430 {
431 Botan::InitializationVector botanIV = Botan::InitializationVector(IV.const_byte_str(), IV.size());
432 cryption = new Botan::Pipe(Botan::get_cipher(cipherName, botanKey, botanIV, Botan::DECRYPTION));
433 }
434 cryption->start_msg();
435 }
436 catch (...)
437 {
438 ERROR_MSG("Failed to create the decryption token");
439
440 ByteString dummy;
441 SymmetricAlgorithm::decryptFinal(dummy);
442
443 delete cryption;
444 cryption = NULL;
445
446 return false;
447 }
448
449 return true;
450}
451
452bool BotanSymmetricAlgorithm::decryptUpdate(const ByteString& encryptedData, ByteString& data)
453{
454 if (!SymmetricAlgorithm::decryptUpdate(encryptedData, data))
455 {
456 delete cryption;
457 cryption = NULL;
458
459 return false;
460 }
461
462 // AEAD ciphers should not return decrypted data until final is called
463 if (currentCipherMode == SymMode::GCM)
464 {
465 data.resize(0);
466 return true;
467 }
468
469 // Write data
470 try
471 {
472 if (encryptedData.size() > 0)
473 cryption->write(encryptedData.const_byte_str(), encryptedData.size());
474 }
475 catch (...)
476 {
477 ERROR_MSG("Failed to write to the decryption token");
478
479 ByteString dummy;
480 SymmetricAlgorithm::decryptFinal(dummy);
481
482 delete cryption;
483 cryption = NULL;
484
485 return false;
486 }
487
488 // Count number of bytes written
489 if (maximumBytes.is_positive())
490 {
491 counterBytes += encryptedData.size();
492 }
493
494 // Read data
495 int bytesRead = 0;
496 try
497 {
498 size_t outLen = cryption->remaining();
499 data.resize(outLen);
500 if (outLen > 0)
501 bytesRead = cryption->read(&data[0], outLen);
502 }
503 catch (...)
504 {
505 ERROR_MSG("Failed to decrypt the data");
506
507 ByteString dummy;
508 SymmetricAlgorithm::decryptFinal(dummy);
509
510 delete cryption;
511 cryption = NULL;
512
513 return false;
514 }
515
516 // Resize the output block
517 data.resize(bytesRead);
518 currentBufferSize -= bytesRead;
519
520 return true;
521}
522
523bool BotanSymmetricAlgorithm::decryptFinal(ByteString& data)
524{
525 SymMode::Type mode = currentCipherMode;
526 ByteString aeadBuffer = currentAEADBuffer;
527
528 if (!SymmetricAlgorithm::decryptFinal(data))
529 {
530 delete cryption;
531 cryption = NULL;
532
533 return false;
534 }
535
536 if (mode == SymMode::GCM)
537 {
538 // Write data
539 try
540 {
541 if (aeadBuffer.size() > 0)
542 cryption->write(aeadBuffer.const_byte_str(), aeadBuffer.size());
543 }
544 catch (...)
545 {
546 ERROR_MSG("Failed to write to the decryption token");
547
548 delete cryption;
549 cryption = NULL;
550
551 return false;
552 }
553 }
554
555 // Read data
556 int bytesRead = 0;
557 try
558 {
559 cryption->end_msg();
560 size_t outLen = cryption->remaining();
561 data.resize(outLen);
562 if (outLen > 0)
563 bytesRead = cryption->read(&data[0], outLen);
564 }
565 catch (std::exception &e)
566 {
567 ERROR_MSG("Failed to decrypt the data: %s", e.what());
568
569 delete cryption;
570 cryption = NULL;
571
572 return false;
573 }
574
575 // Clean up
576 delete cryption;
577 cryption = NULL;
578
579 // Resize the output block
580 data.resize(bytesRead);
581
582 return true;
583}
584
585// Check if more bytes of data can be encrypted
586bool BotanSymmetricAlgorithm::checkMaximumBytes(unsigned long bytes)
587{
588 if (maximumBytes.is_negative()) return true;
589
590 if (maximumBytes.cmp(counterBytes + bytes) >= 0) return true;
591
592 return false;
593}