1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
|
.. _symmetric_crypto:
Symmetric Key Cryptography
===========================================
Block ciphers, stream ciphers and MACs are all keyed operations.
They require a particular key, which is a chosen, sampled or computed
string of bits of a specified length. The length required by any particular algorithm
may vary, depending on both the algorithm specification and the implementation.
You can query any Botan object to find out what key length(s) it supports.
To make this similarity in terms of keying explicit, all algorithms of
those types are derived from the :cpp:class:`SymmetricAlgorithm` base.
This type provides functions for setting the key, and querying
restrictions on the size of the key.
.. cpp:class:: SymmetricAlgorithm
.. cpp:function:: void set_key(const uint8_t* key, size_t length)
.. cpp:function:: void set_key(const SymmetricKey& key)
This sets the key to the value specified. Most algorithms only
accept keys of certain lengths. If you attempt to call
``set_key`` with a key length that is not supported, the
exception ``Invalid_Key_Length`` will be thrown.
In all cases, ``set_key`` must be called on an object before any
data processing (encryption, decryption, etc) is done by that
object. If this is not done, the results are undefined.
.. cpp:function:: bool valid_keylength(size_t length) const
This function returns true if and only if *length* is a valid
keylength for the algorithm.
.. cpp:function:: size_t minimum_keylength() const
Return the smallest key length (in bytes) that is acceptible for the
algorithm.
.. cpp:function:: size_t maximum_keylength() const
Return the largest key length (in bytes) that is acceptible for the
algorithm.
Block Ciphers
---------------------------------
A block cipher is a deterministic symmetric encryption algorithm, which
encrypts data of a fixed length, called block size. All block ciphers classes
in Botan are subclasses of :cpp:class:`BlockCipher` defined in `botan/block_cipher.h`.
As a symmetrically keyed algorithm, it subclasses the :cpp:class:`SymmetricAlgorithm` interface.
Note that a block cipher by itself is only secure for plaintext with the length of a single block.
When processing data larger than a single block, a block cipher mode should be used for data processing.
.. cpp:class:: BlockCipher
.. cpp:function:: size_t block_size() const
Returns the block size of the cipher in bytes.
.. cpp:function:: void encrypt_n(const uint8_t* in, \
uint8_t* out, size_t n) const
Encrypt *n* blocks of data, taking the input from the array *in*
and placing the ciphertext into *out*. The two pointers may be
identical, but should not overlap ranges.
.. cpp:function:: void encrypt(const uint8_t* in, uint8_t* out) const
Encrypt a single block, taking the input from *in* and placing
it in *out*. Acts like :cpp:func:`encrypt_n`\ (in, out, 1).
.. cpp:function:: void encrypt(const std::vector<uint8_t> in, std::vector<uint8_t> out) const
Encrypt a single or multiple full blocks, taking the input from *in* and placing it in *out*.
Acts like :cpp:func:`encrypt_n`\ (in.data(), out.data(), in.size()/ block_size()).
.. cpp:function:: void encrypt(std::vector<uint8_t> inout) const
Encrypt a single or multiple full blocks in place.
Acts like :cpp:func:`encrypt_n`\ (inout.data(), inout.data(), inout.size()/ block_size()).
.. cpp:function:: void encrypt(uint8_t* block) const
Identical to :cpp:func:`encrypt`\ (block, block)
.. cpp:function:: void decrypt_n(const uint8_t* in, uint8_t out, size_t n) const
Decrypt *n* blocks of data, taking the input from *in* and
placing the plaintext in *out*. The two pointers may be
identical, but should not overlap ranges.
.. cpp:function:: void decrypt(const uint8_t* in, uint8_t* out) const
Decrypt a single block, taking the input from *in* and placing it
in *out*. Acts like :cpp:func:`decrypt_n`\ (in, out, 1).
.. cpp:function:: void decrypt(const std::vector<uint8_t> in, std::vector<uint8_t> out) const
Decrypt a single or multiple full blocks, taking the input from *in* and placing it in *out*.
Acts like :cpp:func:`decrypt_n`\ (in.data(), out.data(), in.size()/ block_size()).
.. cpp:function:: void decrypt(std::vector<uint8_t> inout) const
Decrypt a single or multiple full blocks in place.
Acts like :cpp:func:`decrypt_n`\ (inout.data(), inout.data(), inout.size()/ block_size()).
.. cpp:function:: void decrypt(uint8_t* block) const
Identical to :cpp:func:`decrypt`\ (block, block)
.. cpp:function:: size_t parallelism() const
Returns the native parallelism of this implementation, ie how
many blocks can be processed in parallel if sufficient data is
passed to :cpp:func:`encrypt_n` or :cpp:func:`decrypt_n`.
The following block ciphers are implemented in Botan:
#. AES (AES-128, AES-192, AES-256)
#. ARIA
#. Blowfish
#. CAST (CAST-128, CAST-256)
#. Camellia (Camellia-128, Camellia-192, Camellia-256)
#. Cascade
#. DES/3DES
#. DESX
#. GOST-28147-89
#. IDEA
#. Kasumi
#. Lion
#. MISTY1
#. Noekeon
#. SEED
#. SHACAL2
#. SM4
#. Serpent
#. Threefish-512
#. Twofish
#. XTEA
Code Example
"""""""""""""""
For sheer demonstrative purposes, the following code encrypts a provided single block of
plaintext with AES-256 using two different keys.
.. code-block:: cpp
#include <botan/block_cipher.h>
#include <botan/hex.h>
#include <iostream>
int main ()
{
std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
std::vector<uint8_t> block = Botan::hex_decode("00112233445566778899AABBCCDDEEFF");
std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create("AES-256"));
cipher->set_key(key);
cipher->encrypt(block);
std::cout << std::endl <<cipher->name() << "single block encrypt: " << Botan::hex_encode(block);
//clear cipher for 2nd encryption with other key
cipher->clear();
key = Botan::hex_decode("1337133713371337133713371337133713371337133713371337133713371337");
cipher->set_key(key);
cipher->encrypt(block);
std::cout << std::endl << cipher->name() << "single block encrypt: " << Botan::hex_encode(block);
return 0;
}
Modes of Operation
---------------------------
A block cipher by itself, is only able to securely encrypt a single data block.
To be able to securely encrypt data of arbitrary length, a mode of operation applies
the block cipher's single block operation repeatedly on a padded plaintext.
Botan implements the following block cipher padding schemes
PKCS#7 [RFC5652]
The last byte in the padded block defines the padding length p, the remaining padding bytes are set to p as well.
ANSI X9.23
The last byte in the padded block defines the padding length, the remaining padding is filled with 0x00.
ISO/IEC 7816-4
The first padding byte is set to 0x80, the remaining padding bytes are set to 0x00.
and offers the following unauthenticated modes of operation:
#. ECB (Electronic Codebook Mode)
#. CBC (Cipher Block Chaining Mode)
#. CFB (Cipher Feedback Mode)
#. XTS (XEX-based tweaked-codebook mode with ciphertext stealing)
#. OFB (Output Feedback Mode)
#. CTR (Counter Mode)
The classes :cpp:class:`ECB_Mode`, :cpp:class:`CBC_Mode`, :cpp:class:`CFB_Mode` and :cpp:class:`XTS_Mode` are
are derived from the base class :cpp:class:`Cipher_Mode`, which is declared in ``botan/cipher_mode.h``.
.. cpp:class:: Cipher_Mode
.. cpp:function:: void set_key(const SymmetricKey& key)
.. cpp:function:: void set_key(const uint8_t* key, size_t length)
Set the symmetric key to be used.
.. cpp:function:: void start_msg(const uint8_t* nonce, size_t nonce_len)
Set the IV (unique per-message nonce) of the mode of operation and prepare for message processing.
.. cpp:function:: void start(const std::vector<uint8_t> nonce)
Acts like :cpp:func:`start_msg`\ (nonce.data(), nonce.size()).
.. cpp:function:: void start(const uint8_t* nonce, size_t nonce_len)
Acts like :cpp:func:`start_msg`\ (nonce, nonce_len).
.. cpp:function:: virtual size_t update_granularity() const
The :cpp:class:`Cipher_Mode` interface requires message processing in multiples of the block size.
Returns size of required blocks to update and 1, if the mode can process messages of any length.
.. cpp:function:: virtual size_t process(uint8_t* msg, size_t msg_len)
Process msg in place and returns bytes written. msg must be a multiple of :cpp:func:`update_granularity`.
.. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
Continue processing a message in the buffer in place. The passed buffer's size must be a multiple of :cpp:func:`update_granularity`.
The first *offset* bytes of the buffer will be ignored.
.. cpp:function:: size_t minimum_final_size() const
Returns the minimum size needed for :cpp:func:`finish`.
.. cpp:function:: void finish(secure_vector<uint8_t>& final_block, size_t offset = 0)
Finalize the message processing with a final block of at least :cpp:func:`minimum_final_size` size.
The first *offset* bytes of the passed final block will be ignored.
Note that :cpp:class:`CTR_BE` and :cpp:class:`OFB` are derived from the base class :cpp:class:`StreamCipher` and thus act like a stream cipher.
The class :cpp:class:`StreamCipher` is described in the respective section.
Code Example
"""""""""""""""""""""
The following code encrypts the specified plaintext using AES-128/CBC with PKCS#7 padding.
.. code-block:: cpp
#include <botan/rng.h>
#include <botan/auto_rng.h>
#include <botan/cipher_mode.h>
#include <botan/hex.h>
#include <iostream>
int main()
{
std::string plaintext("Your great-grandfather gave this watch to your granddad for good luck. Unfortunately, Dane's luck wasn't as good as his old man's.");
Botan::secure_vector<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
const std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode("AES-128/CBC/PKCS7", Botan::ENCRYPTION));
enc->set_key(key);
//generate fresh nonce (IV)
std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
std::vector<uint8_t> iv(enc->default_nonce_length());
rng->randomize(iv.data(),iv.size());
enc->start(iv);
enc->finish(pt);
std::cout << std::endl << enc->name() << " with iv " << Botan::hex_encode(iv) << std::endl << Botan::hex_encode(pt);
return 0;
}
AEAD Modes of Operation
---------------------------
.. versionadded:: 1.11.3
AEAD (Authenticated Encryption with Associated Data) modes provide message
encryption, message authentication, and the ability to authenticate additional
data that is not included in the ciphertext (such as a sequence number or
header). It is a subclass of :cpp:class:`Symmetric_Algorithm`.
The AEAD interface can be used directly, or as part of the filter system by
using :cpp:class:`AEAD_Filter` (a subclass of :cpp:class:`Keyed_Filter` which
will be returned by :cpp:func:`get_cipher` if the named cipher is an AEAD mode).
AEAD modes currently available include GCM, OCB, EAX, SIV and CCM. All
support a 128-bit block cipher such as AES. EAX and OCB also support
256 and 512 bit block ciphers.
.. cpp:class:: AEAD_Mode
.. cpp:function:: void set_key(const SymmetricKey& key)
Set the key
.. cpp:function:: Key_Length_Specification key_spec() const
Return the key length specification
.. cpp:function:: void set_associated_data(const uint8_t ad[], size_t ad_len)
Set any associated data for this message. For maximum portability between
different modes, this must be called after :cpp:func:`set_key` and before
:cpp:func:`start`.
If the associated data does not change, it is not necessary to call this
function more than once, even across multiple calls to :cpp:func:`start`
and :cpp:func:`finish`.
.. cpp:function:: void start(const uint8_t nonce[], size_t nonce_len)
Start processing a message, using *nonce* as the unique per-message
value.
.. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
Continue processing a message. The *buffer* is an in/out parameter and
may be resized. In particular, some modes require that all input be
consumed before any output is produced; with these modes, *buffer* will
be returned empty.
On input, the buffer must be sized in blocks of size
:cpp:func:`update_granularity`. For instance if the update granularity
was 64, then *buffer* could be 64, 128, 192, ... bytes.
The first *offset* bytes of *buffer* will be ignored (this allows in
place processing of a buffer that contains an initial plaintext header)
.. cpp:function:: void finish(secure_vector<uint8_t>& buffer, size_t offset = 0)
Complete processing a message with a final input of *buffer*, which is
treated the same as with :cpp:func:`update`. It must contain at least
:cpp:func:`final_minimum_size` bytes.
Note that if you have the entire message in hand, calling finish without
ever calling update is both efficient and convenient.
.. note::
During decryption, finish will throw an instance of Integrity_Failure
if the MAC does not validate. If this occurs, all plaintext previously
output via calls to update must be destroyed and not used in any
way that an attacker could observe the effects of.
One simply way to assure this could never happen is to never
call update, and instead always marshall the entire message
into a single buffer and call finish on it when decrypting.
.. cpp:function:: size_t update_granularity() const
The AEAD interface requires :cpp:func:`update` be called with blocks of
this size. This will be 1, if the mode can process any length inputs.
.. cpp:function:: size_t final_minimum_size() const
The AEAD interface requires :cpp:func:`finish` be called with at least
this many bytes (which may be zero, or greater than
:cpp:func:`update_granularity`)
.. cpp:function:: bool valid_nonce_length(size_t nonce_len) const
Returns true if *nonce_len* is a valid nonce length for this scheme. For
EAX and GCM, any length nonces are allowed. OCB allows any value between
8 and 15 bytes.
.. cpp:function:: size_t default_nonce_length() const
Returns a reasonable length for the nonce, typically either 96
bits, or the only supported length for modes which don't
support 96 bit nonces.
Stream Ciphers
---------------------------------
In contrast to block ciphers, stream ciphers operate on a plaintext stream instead
of blocks. Thus encrypting data results in changing the internal state of the
cipher and encryption of plaintext with arbitrary length is possible in one go (in byte
amounts). All implemented stream ciphers derive from the base class :cpp:class:`StreamCipher` (`botan/stream_cipher.h`), which
implements the :cpp:class:`SymmetricAlgorithm` interface. Note that some of the implemented
stream ciphers require a fresh initialisation vector.
.. cpp:class:: StreamCipher
.. cpp:function:: bool valid_iv_length(size_t iv_len) const
This function returns true if and only if *length* is a valid
IV length for the stream cipher.
.. cpp:function:: void set_iv(const uint8_t*, size_t len)
Load IV into the stream cipher state. This should happen after the key is
set and before any operation (encrypt/decrypt/seek) is called.
.. cpp:function:: void seek(uint64_t offset)
Sets the state of the stream cipher and keystream according to the passed *offset*.
Therefore the key and the IV (if required) have to be set beforehand.
.. cpp:function:: void cipher(const uint8_t* in, uint8_t* out, size_t n)
Processes *n* bytes plain/ciphertext from *in* and writes the result to *out*.
.. cpp:function:: void cipher1(uint8_t* inout, size_t n)
Processes *n* bytes plain/ciphertext in place. Acts like :cpp:func:`cipher`\ (inout, inout, n).
.. cpp:function:: void encipher(std::vector<uint8_t> inout)
.. cpp:function:: void encrypt(std::vector<uint8_t> inout)
.. cpp:function:: void decrypt(std::vector<uint8_t> inout)
Processes plain/ciphertext *inout* in place. Acts like :cpp:func:`cipher`\ (inout.data(), inout.data(), inout.size()).
Botan provides the following stream ciphers:
#. ChaCha
#. Salsa20
#. SHAKE-128
#. RC4
Code Example
""""""""""""""
The following code encrypts a provided plaintext using ChaCha20.
.. code-block:: cpp
#include <botan/stream_cipher.h>
#include <botan/rng.h>
#include <botan/auto_rng.h>
#include <botan/hex.h>
#include <iostream>
int main()
{
std::string plaintext("This is a tasty burger!");
std::vector<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
std::unique_ptr<Botan::StreamCipher> cipher(Botan::StreamCipher::create("ChaCha"));
//generate fresh nonce (IV)
std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
std::vector<uint8_t> iv(8);
rng->randomize(iv.data(),iv.size());
//set key and IV
cipher->set_key(key);
cipher->set_iv(iv.data(),iv.size());
std::cout << std::endl << cipher->name() << " with iv " << Botan::hex_encode(iv) << std::endl;
cipher->encipher(pt);
std::cout << Botan::hex_encode(pt);
return 0;
}
Message Authentication Codes (MAC)
----------------------------------
A Message Authentication Code algorithm computes a tag over a message utilizing a shared secret key.
Thus a valid tag confirms the authenticity and integrity of the associated data.
Only entities in possesion of the shared secret key are able to verify the tag.
The base class ``MessageAuthenticationCode`` (in ``botan/mac.h``) implements the interfaces
:cpp:class:`SymmetricAlgorithm` and :cpp:class:`BufferedComputation` (see Hash).
.. note::
Avoid MAC-then-encrypt if possible and use encrypt-then-MAC.
Currently the following MAC algorithms are available in Botan:
- CBC-MAC (with AES-128/DES)
- CMAC / OMAC (with AES-128/AES-192/AES-256/Blowfish/Threefish-512)
- GMAC (with AES-128/AES-192/AES-256)
- HMAC (with MD5, RIPEMD-160, SHA-1, SHA-256)
- Poly1305
- SipHash
- x9.19-MAC
The Botan MAC computation is split into five stages.
#. Instantiate the MAC algorithm.
#. Set the secret key.
#. Process IV.
#. Process data.
#. Finalize the MAC computation.
.. cpp:class:: MessageAuthenticationCode
.. cpp:function:: void set_key(const uint8_t* key, size_t length)
Set the shared MAC key for the calculation. This function has to be called before the data is processed.
.. cpp:function:: void start(const uint8_t* nonce, size_t nonce_len)
Set the IV for the MAC calculation. Note that not all MAC algorithms require an IV.
If an IV is required, the function has to be called before the data is processed.
.. cpp:function:: void update(const uint8_t* input, size_t length)
.. cpp:function:: void update(const secure_vector<uint8_t>& in)
Process the passed data.
.. cpp:function:: void update(uint8_t in)
Process a single byte.
.. cpp:function:: void final(uint8_t* out)
Complete the MAC computation and write the calculated tag to the passed byte array.
.. cpp:function:: secure_vector<uint8_t> final()
Complete the MAC computation and return the calculated tag.
.. cpp:function:: bool verify_mac(const uint8_t* mac, size_t length)
Finalize the current MAC computation and compare the result to the passed ``mac``. Returns ``true``, if the verification is successfull and false otherwise.
Code Example
""""""""""""""""""""""
The following example code computes a AES-256 GMAC and subsequently verifies the tag.
.. code-block:: cpp
#include <botan/mac.h>
#include <botan/hex.h>
#include <iostream>
int main()
{
const std::vector<uint8_t> key = Botan::hex_decode("1337133713371337133713371337133713371337133713371337133713371337");
const std::vector<uint8_t> iv = Botan::hex_decode("FFFFFFFFFFFFFFFFFFFFFFFF");
const std::vector<uint8_t> data = Botan::hex_decode("6BC1BEE22E409F96E93D7E117393172A");
std::unique_ptr<Botan::MessageAuthenticationCode> mac(Botan::MessageAuthenticationCode::create("GMAC(AES-256)"));
if(!mac)
return 1;
mac->set_key(key);
mac->start(iv);
mac->update(data);
Botan::secure_vector<uint8_t> tag = mac->final();
std::cout << mac->name() << ": " << Botan::hex_encode(tag) << std::endl;
//Verify created MAC
mac->start(iv);
mac->update(data);
std::cout << "Verification: " << (mac->verify_mac(tag) ? "success" : "failure");
return 0;
}
The following example code computes a valid AES-128 CMAC tag and modifies the data to demonstrate a MAC verification failure.
.. code-block:: cpp
#include <botan/mac.h>
#include <botan/hex.h>
#include <iostream>
int main()
{
const std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
std::vector<uint8_t> data = Botan::hex_decode("6BC1BEE22E409F96E93D7E117393172A");
std::unique_ptr<Botan::MessageAuthenticationCode> mac(Botan::MessageAuthenticationCode::create("CMAC(AES-128)"));
if(!mac)
return 1;
mac->set_key(key);
mac->update(data);
Botan::secure_vector<uint8_t> tag = mac->final();
//Corrupting data
data.back()++;
//Verify with corrupted data
mac->update(data);
std::cout << "Verification with malformed data: " << (mac->verify_mac(tag) ? "success" : "failure");
return 0;
}
|