aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manual/aead.rst98
-rw-r--r--doc/manual/contents.rst3
-rw-r--r--doc/manual/hash.rst121
-rw-r--r--doc/manual/lowlevel.rst169
-rw-r--r--doc/manual/pubkey.rst190
-rw-r--r--doc/manual/symmetric_crypto.rst570
6 files changed, 879 insertions, 272 deletions
diff --git a/doc/manual/aead.rst b/doc/manual/aead.rst
deleted file mode 100644
index d216026bb..000000000
--- a/doc/manual/aead.rst
+++ /dev/null
@@ -1,98 +0,0 @@
-AEAD Modes
-========================================
-
-.. 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 SIV 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 byte 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 byte nonce[], size_t nonce_len)
-
- Start processing a message, using *nonce* as the unique per-message
- value.
-
- .. cpp:function:: void update(secure_vector<byte>& 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<byte>& 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.
diff --git a/doc/manual/contents.rst b/doc/manual/contents.rst
index f69b7d77e..e2d88a35c 100644
--- a/doc/manual/contents.rst
+++ b/doc/manual/contents.rst
@@ -11,13 +11,14 @@ Contents
secmem
rng
filters
+ hash
+ symmetric_crypto
pubkey
mceliece
x509
ocsp
tls
credentials_manager
- aead
bigint
lowlevel
kdf
diff --git a/doc/manual/hash.rst b/doc/manual/hash.rst
new file mode 100644
index 000000000..12266207d
--- /dev/null
+++ b/doc/manual/hash.rst
@@ -0,0 +1,121 @@
+Hash Functions and Checksums
+=============================
+Hash functions are one-way functions, which map data of arbitrary size
+to a fixed output length. The class :cpp:class:`HashFunction` is derived from
+the base class :cpp:class:`BufferedComputation` and defined in `botan/hash.h`.
+A Botan :cpp:class:`BufferedComputation` is split into three stages:
+
+1. Instantiation.
+2. Data processing.
+3. Finalization.
+
+.. cpp:class:: BufferedComputation
+
+ .. cpp:function:: size_t output_length()
+
+ Return the size of the output of this function.
+
+ .. cpp:function:: void update(const byte* input, size_t length)
+
+ .. cpp:function:: void update(byte input)
+
+ .. cpp:function:: void update(const std::string& input)
+
+ Updates the computation with *input*.
+
+ .. cpp:function:: void final(byte* out)
+
+ .. cpp:function:: secure_vector<byte> final()
+
+ Finalize the calculation and place the result into ``out``.
+ For the argument taking an array, exactly ``output_length`` bytes will
+ be written. After you call ``final``, the algorithm is reset to
+ its initial state, so it may be reused immediately.
+
+ The second method of using final is to call it with no arguments at
+ all, as shown in the second prototype. It will return the result
+ value in a memory buffer.
+
+ There is also a pair of functions called ``process``. They are a
+ combination of a single ``update``, and ``final``. Both versions
+ return the final value, rather than placing it an array. Calling
+ ``process`` with a single byte value isn't available, mostly because
+ it would rarely be useful.
+
+Botan implements the following hash algorithms:
+
+1. Checksums:
+ - Adler32
+ - CRC24
+ - CRC32
+#. Cryptographic hash functions:
+ - BLAKE2
+ - GOST-34.11
+ - Keccak-1600
+ - MD4
+ - MD5
+ - RIPEMD-160
+ - SHA-1
+ - SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512-256)
+ - SHA-3
+ - SHAKE (SHAKE-128, SHAKE-256)
+ - Skein-512
+ - Tiger
+ - Whirlpool
+
+.. note:: Checksums are not suitable for cryptographic use, but can be used for error checking purposes.
+
+CLI
+---
+The Botan command line interface allows the user to compute hash digests of files or datastreams. ``botan hash --algo=SHA-256 --buf-size=4096 *files`` can be used to invoke a hash computation.
+
+CLI Example
+^^^^^^^^^^^^^
+.. code-block:: sh
+
+ $ echo -n randombit | botan hash --algo=SHA-3
+ 783A78B369AA0DE71A6155180EA9D544E7A1834CC4951548C3CE7D5E012C8C6B7EAE1C27DBDFA4DA58EE7B79B5CF07E7E6691AE39BEC1A7DDC501249266BB050``
+
+Code Example
+------------
+Assume we want to calculate the SHA-1, Whirlpool and SHA-3 hash digests of the STDIN stream using the Botan library.
+
+.. code-block:: cpp
+
+ #include <botan/hash.h>
+ #include <botan/hex.h>
+ #include <iostream>
+ int main ()
+ {
+ std::unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-1"));
+ std::unique_ptr<Botan::HashFunction> hash2(Botan::HashFunction::create("Whirlpool"));
+ std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
+ std::vector<uint8_t> buf(2048);
+
+ while(std::cin.good())
+ {
+ //read STDIN to buffer
+ std::cin.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ size_t readcount = std::cin.gcount();
+ //update hash computations with read data
+ hash1->update(buf.data(),readcount);
+ hash2->update(buf.data(),readcount);
+ hash3->update(buf.data(),readcount);
+ }
+ std::cout << "SHA-1: " << Botan::hex_encode(hash1->final()) << std::endl;
+ std::cout << "Whirlpool: " << Botan::hex_encode(hash2->final()) << std::endl;
+ std::cout << "SHA-3: " << Botan::hex_encode(hash3->final()) << std::endl;
+ return 0;
+ }
+
+
+A Note on Checksums
+--------------------
+
+Checksums are very similar to hash functions, and in fact share the
+same interface. But there are some significant differences, the major
+ones being that the output size is very small (usually in the range of
+2 to 4 bytes), and is not cryptographically secure. But for their
+intended purpose (error checking), they perform very well. Some
+examples of checksums included in Botan are the Adler32 and CRC32
+checksums. \ No newline at end of file
diff --git a/doc/manual/lowlevel.rst b/doc/manual/lowlevel.rst
index ae7337c4e..5d5fbea7b 100644
--- a/doc/manual/lowlevel.rst
+++ b/doc/manual/lowlevel.rst
@@ -70,172 +70,3 @@ Both symmetric keys and initialization values can be considered byte
.. cpp:function:: as_string() const
Returns the hex representation of the key or IV
-
-Symmetrically Keyed Algorithms
----------------------------------
-
-Block ciphers, stream ciphers, and MACs are all keyed operations; to
-be useful, they have to be set to use a particular key, which is a
-randomly chosen 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 byte* 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
----------------------------------
-
-All block ciphers classes in botan are subclasses of
-
-.. cpp:class:: BlockCipher
-
- Which subclasses the :cpp:class:`SymmetricAlgorithm` interface.
-
- .. cpp:function:: size_t block_size() const
-
- Returns the block size of the cipher in bytes
-
- .. cpp:function:: void encrypt_n(const byte* in, \
- byte* 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 byte* in, byte* 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(byte* block) const
-
- Identical to :cpp:func:`encrypt`\ (block, block)
-
- .. cpp:function:: void decrypt_n(const byte* in, byte 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 byte* in, byte* 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(byte* 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`.
-
-Stream Ciphers
----------------------------------
-
-Stream ciphers are somewhat different from block ciphers, in that
-encrypting data results in changing the internal state of the
-cipher. Also, you may encrypt any length of data in one go (in byte
-amounts).
-
-.. cpp:function:: void StreamCipher::encrypt(const byte* in, byte* out, size_t length)
-
-.. cpp:function:: void StreamCipher::encrypt(byte* data, size_t length)
-
-Stream ciphers implement the ``SymmetricAlgorithm`` interface.
-
-Hash Functions / Message Authentication Codes
-----------------------------------------------
-
-Hash functions take their input without producing any output, only
-producing anything when all input has already taken place. MACs are
-very similar, but are additionally keyed. Both of these are derived
-from the base class ``BufferedComputation``, which has the following
-functions.
-
-.. cpp:function:: size_t BufferedComputation::output_length()
-
-Return the size of the output of this function.
-
-.. cpp:function:: void BufferedComputation::update(const byte* input, size_t length)
-
-.. cpp:function:: void BufferedComputation::update(byte input)
-
-.. cpp:function:: void BufferedComputation::update(const std::string& input)
-
-Updates the hash/mac calculation with *input*.
-
-.. cpp:function:: void BufferedComputation::final(byte* out)
-
-.. cpp:function:: secure_vector<byte> BufferedComputation::final()
-
-Complete the hash/MAC calculation and place the result into ``out``.
-For the argument taking an array, exactly ``output_length`` bytes will
-be written. After you call ``final``, the hash function is reset to
-its initial state, so it may be reused immediately.
-
-The second method of using final is to call it with no arguments at
-all, as shown in the second prototype. It will return the hash/mac
-value in a memory buffer.
-
-There is also a pair of functions called ``process``. They are a
-combination of a single ``update``, and ``final``. Both versions
-return the final value, rather than placing it an array. Calling
-``process`` with a single byte value isn't available, mostly because
-it would rarely be useful.
-
-A MAC can be viewed (in most cases) as a keyed hash function, so
-classes that are derived from ``MessageAuthenticationCode`` have
-``update`` and ``final`` classes just like a ``HashFunction`` (and
-like a ``HashFunction``, after ``final`` is called, it can be used to
-make a new MAC right away; the key is kept around).
-
-A MAC has the ``SymmetricAlgorithm`` interface in addition to the
-``BufferedComputation`` interface.
-
-Checksums
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Checksums are very similar to hash functions, and in fact share the
-same interface. But there are some significant differences, the major
-ones being that the output size is very small (usually in the range of
-2 to 4 bytes), and is not cryptographically secure. But for their
-intended purpose (error checking), they perform very well. Some
-examples of checksums included in Botan are the Adler32 and CRC32
-checksums.
diff --git a/doc/manual/pubkey.rst b/doc/manual/pubkey.rst
index fdce35853..e1f9d53ad 100644
--- a/doc/manual/pubkey.rst
+++ b/doc/manual/pubkey.rst
@@ -20,7 +20,7 @@ reference to a ``Public_Key``, it can take any public key or private key, and
similiarly for ``Private_Key``.
Types of ``Public_Key`` include ``RSA_PublicKey``, ``DSA_PublicKey``,
-``ECDSA_PublicKey``, ``DH_PublicKey``, ``ECDH_PublicKey``, ``RW_PublicKey``,
+``ECDSA_PublicKey``, ``ECKCDSA_PublicKey``, ``ECGDSA_PublicKey``, ``DH_PublicKey``, ``ECDH_PublicKey``, ``RW_PublicKey``,
``NR_PublicKey``,, and ``GOST_3410_PublicKey``. There are cooresponding
``Private_Key`` classes for each of these algorithms.
@@ -41,7 +41,7 @@ call
A constructor that creates a new random RSA private key with a modulus
of length *bits*.
-Algorithms based on the discrete-logarithm problem uses what is called a
+Algorithms based on the discrete-logarithm problem use what is called a
*group*; a group can safely be used with many keys, and for some operations,
like key agreement, the two keys *must* use the same group. There are
currently two kinds of discrete logarithm groups supported in botan: the
@@ -77,6 +77,12 @@ ECDH, or GOST 34.10-2001 private key with
.. cpp:function:: ECDSA_PrivateKey::ECDSA_PrivateKey(RandomNumberGenerator& rng, \
const EC_Group& domain, const BigInt& x = 0)
+.. cpp:function:: ECKCDSA_PrivateKey::ECKCDSA_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. cpp:function:: ECGDSA_PrivateKey::ECGDSA_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
.. cpp:function:: ECDH_PrivateKey::ECDH_PrivateKey(RandomNumberGenerator& rng, \
const EC_Group& domain, const BigInt& x = 0)
@@ -282,6 +288,31 @@ You can reload a serialized group using
.. cpp:function:: void DL_Group::PEM_decode(DataSource& source)
+Code Example
+"""""""""""""""""
+The example below creates a new 2048 bit ``DL_Group``, prints the generated
+parameters and ANSI_X9_42 encodes the created group for further usage with DH.
+
+.. code-block:: cpp
+
+ #include <botan/dl_group.h>
+ #include <botan/auto_rng.h>
+ #include <botan/rng.h>
+ #include <iostream>
+
+ int main()
+ {
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
+ std::unique_ptr<Botan::DL_Group> group(new Botan::DL_Group(*rng.get(), Botan::DL_Group::Strong, 2048));
+ std::cout << std::endl << "p: " << group->get_p();
+ std::cout << std::endl << "q: " << group->get_q();
+ std::cout << std::endl << "g: " << group->get_q();
+ std::cout << std::endl << "ANSI_X9_42: " << std::endl << group->PEM_encode(Botan::DL_Group::ANSI_X9_42);
+
+ return 0;
+ }
+
+
.. _ec_group:
EC_Group
@@ -373,6 +404,19 @@ The decryption classes are named ``PK_Decryptor``, ``PK_Decryptor_EME``, and
the private key, and the processing function is named ``decrypt``.
+Botan implements the following encryption algorithms and padding schemes:
+
+1. RSA
+ - "PKCS1v15" || "EME-PKCS1-v1_5"
+ - "OAEP" || "EME-OAEP" || "EME1" || "EME1(SHA-1)" || "EME1(SHA-256)"
+
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+TODO: Example for "EME1(SHA-256)"
+
+
Signatures
---------------------------------
@@ -386,7 +430,7 @@ Signature generation is performed using
Constructs a new signer object for the private key *key* using the
signature format *emsa*. The key must support signature operations. In
- the current version of the library, this includes RSA, DSA, ECDSA, GOST
+ the current version of the library, this includes RSA, DSA, ECDSA, ECKCDSA, ECGDSA, GOST
34.10-2001, Nyberg-Rueppel, and Rabin-Williams. Other signature schemes
may be supported in the future.
@@ -399,7 +443,7 @@ Signature generation is performed using
For RSA, use EMSA4 (also called PSS) unless you need compatibility with
software that uses the older PKCS #1 v1.5 standard, in which case use
- EMSA3 (also called "EMSA-PKCS1-v1_5"). For DSA, ECDSA, GOST 34.10-2001,
+ EMSA3 (also called "EMSA-PKCS1-v1_5"). For DSA, ECDSA, ECKCDSA, ECGDSA GOST 34.10-2001,
and Nyberg-Rueppel, you should use EMSA1.
The *format* defaults to ``IEEE_1363`` which is the only available
@@ -467,6 +511,56 @@ Signatures are verified using
on *msg* and then calling :cpp:func:`PK_Verifier::check_signature`
on *sig*.
+
+Botan implements the following signature algorithms:
+
+1. RSA
+#. DSA
+#. ECDSA
+#. ECGDSA
+#. ECKDSA
+#. GOST 34.10-2001
+#. Nyberg-Rueppel
+#. Rabin-Williams
+
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The following Code generates a ECDSA keypair and signs the provided data using EMSA1 with SHA-256.
+Subsequently the signature is verified.
+
+.. code-block:: cpp
+
+ #include "botan/botan.h"
+ #include "botan/auto_rng.h"
+ #include "botan/ecdsa.h"
+ #include <botan/ec_group.h>
+ #include <botan/pubkey.h>
+ #include "botan/hex.h"
+ #include <iostream>
+
+ int main()
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ Botan::ECDSA_PrivateKey key(rng, Botan::EC_Group("secp521r1"));
+ Botan::ECDSA_PublicKey pkey(key);
+
+ std::string text("This is a tasty burger!");
+ std::vector<uint8_t> data(text.data(),text.data()+text.length());
+
+ Botan::PK_Signer signer(key, rng, "EMSA1(SHA-256)");
+ signer.update(data);
+ std::vector<uint8_t> signature = signer.signature(rng);
+ std::cout << "Signature:" << std::endl << Botan::hex_encode(signature);
+
+ Botan::PK_Verifier verifier(pkey, "EMSA1(SHA-256)");
+ verifier.update(data);
+ std::cout << std::endl << "is " << (verifier.check_signature(signature)? "valid" : "invalid");
+ return 0;
+ }
+
+
Key Agreement
---------------------------------
@@ -507,3 +601,91 @@ key agreement algorithm. It returns a ``secure_vector<byte>``.
in new applications. The X9.42 algorithm may be useful in some
circumstances, but unless you need X9.42 compatibility, KDF2 is easier
to use.
+
+
+Botan implements the following key agreement methods:
+
+1. ECDH
+#. DH
+#. DLIES
+#. ECIES
+
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+TODO: ECDH key exchange example
+
+
+eXtended Merkle Signature Scheme (XMSS)
+----------------------------------------
+
+Botan implements the single tree version of the eXtended Merkle Signature
+Scheme (XMSS) using Winternitz One Time Signatures+ (WOTS+). The implementation
+is based on IETF Internet-Draft "XMSS: Extended Hash-Based Signatures".
+
+XMSS uses the Botan interfaces for public key cryptography.
+The following algorithms are implemented:
+
+1. XMSS_SHA2-256_W16_H10
+#. XMSS_SHA2-256_W16_H16
+#. XMSS_SHA2-256_W16_H20
+#. XMSS_SHA2-512_W16_H10
+#. XMSS_SHA2-512_W16_H16
+#. XMSS_SHA2-512_W16_H20
+#. XMSS_SHAKE128_W16_H10
+#. XMSS_SHAKE128_W16_H10
+#. XMSS_SHAKE128_W16_H10
+#. XMSS_SHAKE256_W16_H10
+#. XMSS_SHAKE256_W16_H10
+#. XMSS_SHAKE256_W16_H10
+
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following code snippet shows a minimum example on how to create an XMSS
+public/private key pair and how to use these keys to create and verify a signature:
+
+.. code-block:: cpp
+
+ #include "botan/botan.h"
+ #include "botan/auto_rng.h"
+ #include "botan/xmss.h"
+
+ int main()
+ {
+ // Create a random number generator used for key generation.
+ Botan::AutoSeeded_RNG rng;
+
+ // create a new public/private key pair using SHA2 256 as hash
+ // function and a tree height of 10.
+ Botan::XMSS_PrivateKey private_key(
+ Botan::XMSS_Parameters::xmss_algorithm_t::XMSS_SHA2_256_W16_H10,
+ rng);
+ Botan::XMSS_PublicKey public_key(private_key);
+
+ // create signature operation using the private key.
+ std::unique_ptr<Botan::PK_Ops::Signature> sig_op =
+ private_key.create_signature_op(rng, "", "");
+
+ // create and sign a message using the signature operation.
+ Botan::secure_vector<byte> msg { 0x01, 0x02, 0x03, 0x04 };
+ sig_op->update(msg.data(), msg.size());
+ Botan::secure_vector<byte> sig = sig_op->sign(rng);
+
+ // create verification operation using the public key
+ std::unique_ptr<Botan::PK_Ops::Verification> ver_op =
+ public_key.create_verification_op("", "");
+
+ // verify the signature for the previously generated message.
+ ver_op->update(msg.data(), msg.size());
+ if(ver_op->is_valid_signature(sig.data(), sig.size()))
+ {
+ std::cout << "Success." << std::endl;
+ }
+ else
+ {
+ std::cout << "Error." << std::endl;
+ }
+ }
diff --git a/doc/manual/symmetric_crypto.rst b/doc/manual/symmetric_crypto.rst
new file mode 100644
index 000000000..6afb05322
--- /dev/null
+++ b/doc/manual/symmetric_crypto.rst
@@ -0,0 +1,570 @@
+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 byte* 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 byte* in, \
+ byte* 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 byte* in, byte* 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<byte> in, std::vector<byte> 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<byte> 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(byte* block) const
+
+ Identical to :cpp:func:`encrypt`\ (block, block)
+
+ .. cpp:function:: void decrypt_n(const byte* in, byte 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 byte* in, byte* 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<byte> in, std::vector<byte> 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<byte> 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(byte* 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)
+#. Serpent
+#. Twofish
+#. Threefish-512
+#. Blowfish
+#. Camellia (Camellia-128, Camellia-192, Camellia-256)
+#. DES
+#. 3DES
+#. DESX
+#. Noekeon
+#. CAST (CAST-128, CAST-256)
+#. IDEA
+#. Kasumi
+#. MISTY1
+#. SEED
+#. XTEA
+#. GOST-28147-89
+#. Cascade
+#. Lion
+
+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 byte* key, size_t length)
+
+ Set the symmetric key to be used.
+
+ .. cpp:function:: void start_msg(const byte* 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<byte> nonce)
+
+ Acts like :cpp:func:`start_msg`\ (nonce.data(), nonce.size()).
+
+ .. cpp:function:: void start(const byte* 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(byte* 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<byte>& 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<byte>& 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 SIV 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 byte 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 byte nonce[], size_t nonce_len)
+
+ Start processing a message, using *nonce* as the unique per-message
+ value.
+
+ .. cpp:function:: void update(secure_vector<byte>& 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<byte>& 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 byte*, 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(u64bit 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 byte* in, byte* out, size_t n)
+
+ Processes *n* bytes plain/ciphertext from *in* and writes the result to *out*.
+
+ .. cpp:function:: void cipher1(byte* 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<byte> inout)
+ .. cpp:function:: void encrypt(std::vector<byte> inout)
+ .. cpp:function:: void decrypt(std::vector<byte> 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 byte* 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 byte* 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 byte* input, size_t length)
+ .. cpp:function:: void update(const secure_vector<byte>& in)
+
+ Process the passed data.
+
+ .. cpp:function:: void update(byte in)
+
+ Process a single byte.
+
+ .. cpp:function:: void final(byte* out)
+
+ Complete the MAC computation and write the calculated tag to the passed byte array.
+
+ .. cpp:function:: secure_vector<byte> final()
+
+ Complete the MAC computation and return the calculated tag.
+
+ .. cpp:function:: bool verify_mac(const byte* 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;
+ }