cppcrypto library

1. Introduction
2. Hash algorithms
3. MAC algorithms
4. Block cipher algorithms
5. Block cipher encryption modes
6. Stream cipher algorithms
7. Key derivation functions
8. AEAD
9. Streaming AEAD
10. Digest utility
11. Cryptor utility
12. Portability notes
13. Download link

1. Introduction

cppcrypto is a small cross-platform BSD-licensed C++ library providing some cryptographic primitives. It has no external dependencies.

The library thrives to be as fast as possible. CPU type is detected at runtime and the fastest implementation is selected dynamically.

Supported compilers: Visual C++ 2017 or later on Windows; gcc 12 or later, clang 13 or later on Linux, OS X, FreeBSD.
Supported architectures: x86_64 and x86.

Download cppcrypto from sourceforge

Full list of supported cryptographic primitives is described below.

2. Hash algorithms

2.1. Interface description

All hash algorithms derive from crypto_hash class and implement its interface.

    class crypto_hash
    {
    public:
        virtual void init() = 0;
        virtual void update(const unsigned char* data, size_t len) = 0;
        virtual void final(unsigned char* hash) = 0;

        virtual size_t hashsize() const = 0;
        virtual size_t blocksize() const = 0;

        // Convenience functions
        void hash_string(const unsigned char* data, size_t len, unsigned char* hash);
        void hash_string(const char* data, size_t len, unsigned char* hash);
        void hash_string(const std::string& data, unsigned char* hash);
    };

If you have a short message, you can calculate the hash using a single hash_string() call. Otherwise, call init() to start hashing, then call update() any number of times to add the next chunk of data, and finally call final() to get the hash.
The hash parameter should point to a buffer large enough to accept the hash of correspondent size (length in bytes is hashsize()/8).

2.2. Supported algorithms

Classes derived from crypto_hash:

Class NameDescriptionSupported output sizes
blake BLAKE hash function (one of SHA-3 finalists).
This is one of the fastest hash functions and we recommend it you are free to choose.
224 / 256 / 384 / 512
blake2b BLAKE2b hash function with variable output size up to 512 bits.
This is a version of BLAKE-512 with reduced number of rounds and other simplifications.
8 - 512
blake2s BLAKE2s hash function with variable output size up to 256 bits.
This is a version of BLAKE-256 with reduced number of rounds and other simplifications.
8 - 256
echo ECHO hash function (one of SHA-3 Round 2 candidates).8 - 512
esch Esch hash function (one of NIST Lightweight Crypto Competition finalists).256 / 384
groestl Grøstl hash function (one of SHA-3 finalists).8 - 512
jh JH hash function (one of SHA-3 finalists).224 / 256 / 384 / 512
kupyna Kupyna hash function (Ukrainian national standard DSTU 7564:2014) with output sizes 256/512 bits.
224/384-bit functions are not provided because their output is identical to the truncated 256/512-bit output.
256 / 512
md5 MD5 hash function. Not secure and therefore not recommended; use only if you need it for compatibility.128
sha1 SHA-1 hash function. Not secure and therefore not recommended; use only if you need it for compatibility.160
sha224
sha256
sha384
sha512
SHA-2 hash function (NIST standard FIPS 180-4).
Individual functions are known as SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256.
SHA-512 supports variable output size up to 512 bits (SHA-512/t).
sha512(224) and sha512(256) are faster than sha224 and sha256 on 64-bit platforms.
224 / 256 / 384 / 512,
8 - 512 (SHA-512/t)
sha3
shake128
shake256
SHA-3 hash function (NIST standard FIPS 202) based on Keccak algorithm.
SHAKE-128 and SHAKE-256 support variable output size.
cSHAKE-128 and cSHAKE-256 as defined by NIST SP 800-185 are also supported.
224 / 256 / 384 / 512 (SHA-3)
arbitrary (SHAKE)
skein256 Skein hash function (one of SHA-3 finalists) with block size 256 bits and variable output size.
This algorithm is slower than skein512 on modern PCs, so use it only if you need them for compatibility.
arbitrary
skein512 Skein hash function (one of SHA-3 finalists) with block size 512 bits and variable output size.
This is one of the fastest hash functions and we recommend it if you are free to choose.
arbitrary
skein1024 Skein hash function (one of SHA-3 finalists) with block size 1024 bits and variable output size.arbitrary
sm3 SM3 hash function (Chinese national standard)256
streebog Streebog hash function (Russian standard GOST R 34.11-2012)256 / 512
whirlpool Whirlpool hash function (recommended by NESSIE)512

MD5 and SHA-1 algorithms are broken and should not be used for new applications. We do not recommend using Streebog because of possible back door in the algorithm. All other supported hash functions are unbroken and safe to use.

2.3. Usage example

The following snippet calculates BLAKE-256 hash of a string using a single function call:

    unsigned char hash[256/8];
    blake(256).hash_string("The quick brown fox jumps over the lazy dog", hash);
    // Now hash contains the hash

The following snippet demonstrates how to calculate the hash of a very long message using the init/update/final interface:

    blake(256) hasher;
    unsigned char hash[256/8];
    
    hasher.init();
    hasher.update(datachunk1, chunk1len);
    hasher.update(datachunk2, chunk2len);
    ...
    hasher.final(hash); // Now hash contains the hash

3. MAC algorithms

3.1. Interface description

Class crypto_mac is similar to crypto_hash. The only difference is that you need to pass a key to the init method.

All MAC algorithms derive from crypto_mac class and implement its interface.

    class crypto_mac
    {
    public:
        virtual void init(const unsigned char* key, size_t keylen) = 0;
        virtual void update(const unsigned char* data, size_t len) = 0;
        virtual void final(unsigned char* hash) = 0;

        virtual size_t keysize() const = 0;
        virtual size_t hashsize() const = 0;
        virtual size_t blocksize() const = 0;

        // Convenience functions
        void mac_string(const unsigned char* key, size_t key_len, const unsigned char* data, size_t len, unsigned char* hash);
        void mac_string(const char* key, size_t key_len, const unsigned char* data, size_t len, unsigned char* hash);
        void mac_string(const char* key, size_t key_len, const char* data, size_t len, unsigned char* hash);
        void mac_string(const std::string& key, const std::string& data, unsigned char* hash);
    };

If you have a short message, you can calculate the MAC using a single mac_string() call. Otherwise, call init() to start hashing, then call update() any number of times to add the next chunk of data, and finally call final() to get the MAC.
The hash parameter should point to a buffer large enough to accept the MAC of correspondent size (length in bytes is hashsize()/8).

3.2 HMAC

Class hmac is derived from crypto_mac. To specify the underlying hash function, you need to pass a reference to the hash algorithm in the constructor.

    hmac(const crypto_hash& hash);

The key can be of any length and you can use any of the supported hash algorithms to calculate HMAC. The following sample calculates HMAC-SHA-256 of a string "The quick brown fox jumps over the lazy dog" with key "key":

    unsigned char hmackey[3];
    hmackey[0] = 'k';
    hmackey[1] = 'e';
    hmackey[2] = 'y';
    hmac mac(sha256());
    unsigned char result[32];
    mac.hash_string(hmackey, sizeof(hmackey), "The quick brown fox jumps over the lazy dog", result);

3.3 Poly1305

Class poly1305 is also derived from crypto_mac and implements its interface.

The key must have length 32 bytes (256 bits). The following sample calculates Poly1305 of a string "The quick brown fox jumps over the lazy dog" with key "\x00\x01\x02...\x31":

    unsigned char key[32];
    std::iota(key, key + 32, 0);
    unsigned char result[16];
    poly.hash_string(key, sizeof(key), "The quick brown fox jumps over the lazy dog", result);

4. Block cipher algorithms

4.1. Interface description

All block cipher algorithms derive from block_cipher class and implement its interface.

    class block_cipher
    {
    public:
        enum direction { encryption, decryption };

        virtual size_t blocksize() const = 0;
        virtual size_t keysize() const = 0;

        virtual bool init(const unsigned char* key, block_cipher::direction direction) = 0;
        virtual void encrypt_block(const unsigned char* in, unsigned char* out) = 0;
        virtual void decrypt_block(const unsigned char* in, unsigned char* out) = 0;
    };

A low-level interface to the block cipher. To start encryption or decryption, call init() (key should point to a key of keysize()/8 bytes), then call encrypt_block() or decrypt_block() to encrypt or decrypt a single block of blocksize()/8 bytes.
In practice in most cases you probably want to use these block ciphers in an AEAD mode described in a separate section (using aead_etm class).

4.2. Supported algorithms

Classes derived from block_cipher:

Class NameDescriptionSupported block sizesSupported key sizes
anubis128
anubis160
anubis192
anubis224
anubis256
anubis288
anubis320
Anubis cipher128128 / 160 / 192 / 224 / 256 / 288 / 320
aria128
aria192
aria256
Aria cipher (South Korean national standard)128128 / 192 / 256
cast6_128
cast6_160
cast6_192
cast6_224
cast6_256
CAST6 (aka CAST-256) cipher128128 / 160 / 192 / 224 / 256
camellia128
camellia192
camellia256
Camellia cipher (recommended by NESSIE and CRYPTREC)128128 / 192 / 256
kalyna128_128
kalyna128_256
kalyna256_256
kalyna256_512
kalyna512_512
Kalyna cipher (Ukrainian national standard DSTU 7624:2014)128 / 256 / 512128 / 256 / 512
kuznyechik Kuznyechik cipher (Russian national standard GOST R 34.12-2015)128256
mars128
mars160
mars192
mars224
mars256
mars288
mars320
mars352
mars384
mars416
mars448
MARS cipher (one of AES finalists)128128 / 160 / 192 / 224 / 256 / 288 / 320 / 352 / 384 / 416 / 448
rijndael128_128
rijndael128_160
rijndael128_192
rijndael128_224
rijndael128_256
rijndael160_128
rijndael160_160
rijndael160_192
rijndael160_224
rijndael160_256
rijndael192_128
rijndael192_160
rijndael192_192
rijndael192_224
rijndael192_256
rijndael224_128
rijndael224_160
rijndael224_192
rijndael224_224
rijndael224_256
rijndael256_128
rijndael256_160
rijndael256_192
rijndael256_224
rijndael256_256
Rijndael cipher (AES winner)

Rijndael with block size 128 bits and key size 128, 192, 256 bits
is also known as
AES-128, AES-192, AES-256.

All 25 Rijndael variants are accelerated using AES-NI instructions,
if they are supported by CPU.
128 / 160 / 192 / 224 / 256128 / 160 / 192 / 224 / 256
serpent128
serpent192
serpent256
Serpent cipher (one of AES finalists)128128 / 192 / 256
simon128_128
simon128_192
simon128_256
Simon cipher128128 / 192 / 256
sm4 SM4 cipher (Chinese national standard for Wireless LAN)128128
speck128_128
speck128_192
speck128_256
Speck cipher128128 / 192 / 256
threefish256_256
threefish512_512
threefish1024_1024
Threefish cipher256 / 512 / 1024256 / 512 / 1024
twofish128
twofish192
twofish256
Twofish cipher (one of AES finalists)128128 / 192 / 256

All supported ciphers are unbroken and safe to use, although we do not recommend using Kuznyechik because of possible back door.

5. Block cipher encryption modes

Authenticated encryption modes are described later in a separate section.

5.1. CBC mode

Class cbc implements the following interface:

    cbc(const block_cipher& cipher);

    void init(const unsigned char* key, size_t keylen, const unsigned char* iv, size_t ivlen, block_cipher::direction direction);

    // Functions writing output to preallocated raw buffer (the fastest)
    void encrypt_update(const unsigned char* in, size_t len, unsigned char* out, size_t& resultlen);
    void encrypt_final(unsigned char* out, size_t& resultlen);
    void decrypt_update(const unsigned char* in, size_t len, unsigned char* out, size_t& resultlen);
    void decrypt_final(unsigned char* out, size_t& resultlen);

    // Functions writing output to std::vector (slower)
    void encrypt_update(const unsigned char* in, size_t len, std::vector<unsigned char>& out);
    void encrypt_final(std::vector& out);
    void decrypt_update(const unsigned char* in, size_t len, std::vector<unsigned char>& out);
    void decrypt_final(std::vector& out);

    // Functions writing output to std::ostream (the slowest)
    void encrypt_update(const unsigned char* in, size_t len, std::ostream& out);
    void encrypt_final(std::ostream& out);
    void decrypt_update(const unsigned char* in, size_t len, std::ostream& out);
    void decrypt_final(std::ostream& out);

The functions are similar to the ones from the crypto_hash class.

To start encryption, pass one of the block ciphers to the constructor and initialize encryption using init() function. The length of the key in bytes should be equal to keysize()/8 of the block cipher, and the length of the initialization vector (iv) in bytes should be equal to blocksize()/8 of the block cipher. Then call encrypt_update() any number of times to add the next chunk of data of variable length. When all input is consumed by encrypt_update() function, call encrypt_final() to finish the encryption (at this stage the final block will be written to out).

Decryption is done in a similar way.

If you use the low-level functions that write the output to a preallocated buffer, the out argument should point to the next location where the encrypted output should be written; resultlen is the number of bytes written to out as the result of the function call. Because the class implements PKCS#7 padding, the overall size of the output buffer should be equal to the size of the input rounded up to the block size of the cipher algorithm, plus one additional full block if the input size is the exact multiple of the block size. It is responsibility of the caller to make sure the buffer is large enough.

A simpler but slower interface accepts a reference to std::vector<unsigned char> and appends encryption or decryption result to this vector, which is dynamically resized as needed, so that you do not need to calculate and allocate a buffer of proper size in advance.

An even simpler but slower interface writes the output to std::ostream.

Note that regardless of which interface you use, you still need to call init() function to set up the key and initialization vector.

5.2. CTR mode

Class ctr implements the stream_cipher interface for block ciphers:

    ctr(const block_cipher& cipher);

    void init(const unsigned char* key, size_t keylen, const unsigned char* iv, size_t ivlen);
    void encrypt(const unsigned char* in, size_t len, unsigned char* out);
    void decrypt(const unsigned char* in, size_t len, unsigned char* out);

CTR mode turns block cipher into a stream cipher, so no padding is needed in this mode and the size of the output buffer must be the same as the size of the input data. The length of the key in bytes should be equal to keysize()/8 of the block cipher, and the length of the initialization vector (iv) in bytes should be equal to or smaller than blocksize()/8 of the block cipher. Since all output is produced immediately, no finalization function is needed in this mode.

CTR mode can be used in an AEAD construction via aead_etm class (described below).

6. Stream cipher algorithms

6.1. Interface description

All stream cipher algorithms derive from stream_cipher class and implement its interface.

    class stream_cipher
    {
    public:
        virtual size_t keysize() const = 0;
        virtual size_t ivsize() const = 0;

        virtual void init(const unsigned char* key, size_t keylen, const unsigned char* iv, size_t ivlen) = 0;
        virtual void encrypt(const unsigned char* in, size_t len, unsigned char* out) = 0;
        virtual void decrypt(const unsigned char* in, size_t len, unsigned char* out) = 0;
    };

To start encryption or decryption, call init() (key should point to a key of keysize()/8 bytes, and iv should point to an initialization vector of ivsize()/8 bytes). Then call encrypt() or decrypt() to encrypt or decrypt a stream of any length.

6.2. Supported algorithms

Classes derived from stream_cipher:

Class NameDescriptionSupported key sizesSupported IV/nonce sizes
chacha20_128
chacha20_256
chacha12_128
chacha12_256
ChaCha cipher (a variant of Salsa20)
including full 20-round version and reduced 12-round version
128 / 25664
hc128
hc256
HC cipher (member of eSTREAM portfolio)128 / 256128 / 256
salsa20_128
salsa20_256
salsa20_12_128
salsa20_12_256
Salsa20 cipher (member of eSTREAM portfolio)
including full 20-round version and reduced 12-round version
128 / 25664
xchacha20_128
xchacha20_256
xchacha12_128
xchacha12_256
XChaCha cipher (a variant of ChaCha with longer nonce)
including full 20-round version and reduced 12-round version
128 / 256192
xsalsa20_128
xsalsa20_256
xsalsa20_12_128
xsalsa20_12_256
XSalsa20 cipher (a variant of Salsa20 with longer nonce)
including full 20-round version and reduced 12-round version
128 / 256192

You can also use any of the supported block ciphers like a stream cipher via ctr class, which implements the same stream_cipher interface.

7. Key derivation functions

You can use KDF functions to derive an encryption key from a password or other input key material (depending on the function).

7.1. Interface description

All KDF algorithms derive from crypto_kdf class and implement its interface.

    class crypto_kdf
    {
    public:
	virtual void derive_key(const unsigned char* ikm, size_t ikm_len, const unsigned char* random_data, size_t random_data_len, unsigned char* dk, size_t dklen) = 0;

	void gen_random_data_and_derive_key(const unsigned char* ikm, size_t ikm_len, size_t random_data_len, unsigned char* random_data, unsigned char* dk, size_t dklen);
    };

Method gen_random_data_and_derive_key will generate random salt of length random_data_len and write it to random_data. It will then derive a key of length dklen from the input key material ikm and write it to dk.

Method derive_key does the same, except that it derives a key using random salt passed as a parameter instead of generating random salt internally.

7.2. PBKDF2

PBKDF2 is an old password-based key derivation function. Use it only if you need one for compatibility, otherwise prefer scrypt or argon2.

The following snippet generates a 256-bit key from password "password" and salt "salt" using PBKDF2 algorithm with 100000 iterations of HMAC-SHA256:

    unsigned char key[256 / 8];
    pbkdf2(sha256(), 100000).derive_key((const unsigned char*)"password", 8, (const unsigned char*)"salt", 4, key, sizeof(key));

7.3. scrypt

Function scrypt has the following prototype:

    class scrypt : public crypto_kdf
    {
    public:
        /*
           Scrypt key derivation function.
           Parameters:
           hash    Hash to use for HMAC (e.g. SHA-256).
           N       CPU/Memory cost parameter, must be larger than 1, a power of 2 and less than 2^(16*r).
           r       Block size factor parameter.
           p       Parallelization parameter, a positive integer less than (2^30)/r.
        */
        scrypt(const crypto_hash& hash, size_t N, size_t r, size_t p);

        // Change CPU/Memory cost parameter, must be larger than 1, a power of 2 and less than 2^(16*r)
        void set_N(size_t N);

        // Change Block size factor parameter.
        void set_r(size_t r);

        // Change Parallelization parameter, a positive integer less than (2^30)/r.
        void set_p(size_t p);

        /*
           Derive a key from a password.

           Input:
           password Passphrase.
           salt     Salt.
           dklen    Intended output length of the derived key, given in bytes.

           Output:
           dk       Derived key, of length dklen bytes.
	*/
        void derive_key(const unsigned char* password, size_t password_len, const unsigned char* salt, size_t salt_len, unsigned char* dk, size_t dklen) const override;
	};

You can use this function to derive an encryption key from a password.

The following snippet generates a 256-bit key from password "password" and salt "salt" using scrypt with N=16384, r=8 and p=16 (which takes 0.10 seconds on Intel Xeon E5-1650 v3):

    unsigned char key[256 / 8];
    scrypt(sha256(), 16384, 8, 16).derive_key((const unsigned char*)"password", 8, (const unsigned char*)"salt", 4, key, sizeof(key));

7.4. Argon2

Argon2 is a winner of Password Hashing Competition and is the recommended function for hashing passwords.

There are three versions of Argon2 function: argon2i, argon2d and argon2id. Argon2i is the safest against side-channel attacks, while Argon2d provides the highest resistance against GPU cracking attacks. If side-channel attachs are a concern, use Argon2i, otherwise, use Argon2d. If in doubt, use Argon2id.

    class argon2 : public crypto_kdf
    {
    public:
        enum class type
        {
            argon2d = 0,
            argon2i = 1,
            argon2id = 2
        };

        /*
           Argon2 key derivation function.
           Parameters:
           type     Argon2 variant (argon2i, argon2d or argon2id).
           p        A parallelism degree, which defines the number of parallel threads.
           m        A memory cost, which defines the memory usage, given in kibibytes.
           t        A time cost, which defines the amount of computation realized and therefore the execution time, given in number of iterations.
        */
        argon2(type type, uint32_t parallelism_degree = 4, uint32_t memory_cost = 4096, uint32_t time_cost = 1000);

        // Change parallelism parameter (number of parallel threads).
        void set_parallelism_degree(uint32_t parallelism_degree);

        // Change memory cost parameter (memory usage in kibibytes).
        void set_memory_cost(uint32_t memory_cost);

        // Change time cost parameter (number of iterations).
        void set_time_cost(uint32_t time_cost);

        /*
           Derive a key from a password.

           Input:
           password Passphrase.
           salt     Salt.
           dklen    Intended output length of the derived key, given in bytes.

           Optional input:
           data     Associated data which will affect the derived key.
           secret   Secret value which will affect the derived key.

           Output:
           dk       Derived key, of length dklen bytes.
        */
        void derive_key(const char* password, uint32_t pwd_len, const unsigned char* salt, uint32_t salt_len, unsigned char* dk, uint32_t dklen,
                        unsigned char* data = nullptr, uint32_t datalen = 0, unsigned char* secret = nullptr, uint32_t secretlen = 0);

    };

The following snippet generates a 256-bit key from password "password" and salt "salt" using argon2i function with p=4, m=4096 and t=1000 (which takes 0.68 seconds on Intel Xeon E5-1650 v3):

    unsigned char key[256 / 8];
    argon2(argon2::type::argon2i, 4, 4096, 1000).derive_key("password", 8, (const unsigned char*)"salt", 4, key, sizeof(key));

Our implementation of Argon2 is faster than the optimized reference implementation, which needs 1.05 seconds to compute the same Argon2i hash on the same PC (both implementations were compiled by VC++ 2015 with /O2 optimization).

7.5. HKDF

HKDF is a key derivation function based on HMAC.

It can be used to extract randomness from the source key material and/or expand the key into a larger output, for example, to create multiple keys from a single key.

Note that HKDF is not suitable for generating keys from passwords. For that purpose, use password-based KDFs such as argon2.

class hkdf : public crypto_kdf
{
public:
        hkdf(const crypto_hash& hash);

        /*
           Extract pseudorandom key from source key material and optional extractor salt.

           Input:
           skm      Source key material (must not be a password).
           xts      Extractor salt (optional).
           prk_len  Length of the extracted key in bytes. Must be equal to hash size of the hash function.

           Output:
           prk      Extracted pseudo-random key.
	*/
	void extract(const unsigned char* xts, size_t xts_len, const unsigned char* skm, size_t skm_len, unsigned char* prk, size_t prk_len);

        /*
           Expand pseudorandom key into desired size.

           Input:
           prk      Pseudorandom key to be expanded.
           ctxinfo  Context information.
           okm_len  Length of the output keying material in bytes.

           Output:
           okm      Output keying material (expanded key).
        */
        void expand(const unsigned char* prk, size_t prk_len, const unsigned char* ctxinfo, size_t ctxinfo_len, unsigned char* okm, size_t okm_len);

        /*
           Extract pseudorandom key from source key material and optional extractor salt, then expand pseudorandom key into desired size.

           Input:
           skm      Source key material (must not be a password).
           xts      Extractor salt (optional).
           ctxinfo  Context information.
           okm_len  Length of the output keying material in bytes.

           Output:
           okm      Output keying material (expanded key).
        */
        void extract_and_expand(const unsigned char* xts, size_t xts_len, const unsigned char* ctxinfo, size_t ctxinfo_len, const unsigned char* skm, size_t skm_len, unsigned char* okm, size_t okm_len);

        /*
           Derive key from the input key material and random data. Implements the crypto_kdf interface. The key will be derived via extract_and_expand.
           This method does not allow to specify extractor salt, if it is needed, use extract_and_expand directly.

           Input:
           ikm         Input key material. Must not be a password. Will be used passed as a source key material to extract_and_expand.
           random_data Random data. Will be passed as context information to extract_and_expand.
           okm_len     Length of the derived key in in bytes.

           Output:
           dk          Derived key.
        */
        virtual void derive_key(const unsigned char* ikm, size_t ikm_len, const unsigned char* random_data, size_t random_data_len, unsigned char* dk, size_t dklen) override;

        /*
           Initial counter value for the Expand stage.
           By default the initial counter value is 1, as specified in RFC 5869.
           If compatibility with RFC the original paper is needed, it can be changed to 0.
        */
        void set_initial_counter_value(uint8_t counter);
};

8. Authenticated encryption with associated data (AEAD)

8.1. Interface description

All AEAD algorithms derive from aead class and implement its interface.

    class aead
    {
    public:
	// key setup
	virtual void set_key(const unsigned char* key, size_t keylen) = 0;
	virtual void set_tagsize_in_bits(size_t tagsize_in_bits) = 0;

	// encrypt
	void encrypt(const unsigned char* plaintext, size_t plaintext_len, const unsigned char* associated_data, size_t associated_data_len, unsigned char* result, size_t result_buffer_len);
	void encrypt_with_explicit_iv(const unsigned char* plaintext, size_t plaintext_len, const unsigned char* associated_data, size_t associated_data_len, const unsigned char* iv, size_t iv_len, unsigned char* result, size_t result_buffer_len);

	// decrypt 
	bool decrypt(const unsigned char* ciphertext, size_t ciphertext_len, const unsigned char* associated_data, size_t associated_data_len, unsigned char* result, size_t result_buffer_len);
	bool decrypt_with_explicit_iv(const unsigned char* ciphertext, size_t ciphertext_len, const unsigned char* associated_data, size_t associated_data_len, const unsigned char* iv, size_t iv_len, unsigned char* result, size_t result_buffer_len);

	// informational getters
	virtual size_t key_bytes() const = 0;
	virtual size_t iv_bytes() const = 0;
	virtual size_t tag_bytes() const = 0;
    };

To encrypt and tag plaintext, use encrypt function which will generate a random initializatio vector of required size and prepend it to the ciphertext. This is the recommended mode for most use cases. Buffer for the result must be preallocated in advance. Required buffer size for the result buffer is equal to the plaintext_len plus tag_bytes() plus iv_bytes().

If you need to use your own initialization vector, you can use encrypt_with_explicit_iv funtion. This function won't prepend initialization vector to the ciphertext, so the required buffer size for the result buffer is plaintext_len plus tag_bytes().

Required buffer size for decrypt function is ciphertext_len minus tag_bytes() minus iv_bytes().

Required buffer size for decrypt_with_explicit_iv function is ciphertext_len minus tag_bytes().

The key should be set before encryption or decryption via set_key() method.

8.2. Encrypt-then-MAC

Encrypt-then-MAC (EtM) is a universal AEAD mode which can be used to combine any block cipher in ctr encryption mode, or any stream cipher, with any MAC function to create a secure AEAD construction.

This mode is implemented by aead_etm class. Cipher and MAC algorithms should be passed to the constructor. The size of the key passed to set_key function must be equal to the sum of cipher key size and MAC key size.

Some examples:

	aead_etm etm1(ctr(rijndael128_256()), (hmac(sha256()))); // AES-256-HMAC-SHA-256
	aead_etm etm2(ctr(rijndael128_128()), hmac(sha512(128))); // AES-128-HMAC-SHA-512/128
	aead_etm etm3(ctr(rijndael160_224()), hmac(blake(224))); // Rijndael-160-224-HMAC-BLAKE-224
	aead_etm etm4(ctr(serpent256()), hmac(groestl(256))); // Serpent-256-HMAC-Grøstl-256
	aead_etm etm5(xsalsa20_256(), (poly1305())); // XSalsa20/20-Poly1305
	aead_etm etm6(ctr(simon128_128()), (poly1305())); // Simon-128-Poly1305
	aead_etm etm7(ctr(anubis256()), hmac(jh(256))); // Anubis-256-HMAC-JH-256
	// ...
	// Any combination possible!

In addition to the generic Encrypt-then-MAC AEAD, cppcrypto implements some specific AEAD modes described below.

8.3. GCM

Galois-Counter Mode (GCM) is an AEAD mode which can be used with any block cipher with block size of 128 bits.

This mode is implemented by aead_gcm class. Block cipher should be passed to the constructor.

Some examples:

	aead_gcm gcm1((rijndael128_256())); // AES-256-GCM
	aead_gcm gcm2((serpent256())); // Serpent-256-GCM
	aead_gcm gcm3((camellia192())); // Camellia-192-GCM
	// ...

8.4. OCB

Offset Codebook (OCB) is an AEAD mode which can be used with any block cipher of any block size.

This mode is implemented by aead_ocb class. Block cipher should be passed to the constructor.

Some examples:

	aead_ocb ocb1((rijndael128_256())); // AES-256-OCB
	aead_ocb ocb3((serpent256())); // Serpent-256-OCB
	aead_ocb ocb4((rijndael224_256())); // Rijndael-224-256-OCB
	aead_ocb ocb5((threefish512_512())); // Threefish-512-OCB
	// ...

Default tag size is equal to the minimum of 256 or block size of the cipher, it can be changed via set_tagsize_in_bits method.

8.5. IETF-ChaCha-Poly

IETF-ChaCha-Poly1305 is an AEAD mode which combines chacha20_256 stream cipher with poly1305 message authentication code.

While chacha20_256 and poly1305 can also be combined via generic aead_etm combiner, aead_ietf_chacha_poly is provided for compatibility with RFC-8439. There are several minor differences between using aead_ietf_chacha_poly and combining chacha20_256 and poly1305 via aead_etm. The first difference is that aead_ietf_chacha_poly takes a single 256-bit key and derives Poly1305 key from it using ChaCha, while aead_etm takes a combined key, the first half of which is used for ChaCha and the second half is used for Poly1305. Additionally, there are minor differences in the pre-authentication encoding, for example, aead_ietf_chacha_poly pads plaintext and associated data to 16-byte boundary while aead_etm does not.

From the security standpoint there is no difference, both options are secure, so if you do not need to be compatible with RFC-8439, you can just as well use the universal aead_etm combiner.

aead_ietf_chacha_poly can be used not only with chacha20_256, but also with any variant of ChaCha or Salsa20, for example, with extended-nonce variant xchacha20_256. Just pass the required cipher variant to the constructor of aead_ietf_chacha_poly.

Examples:

	aead_ietf_chacha_poly aead1;  // chacha20-poly1305
	aead_ietf_chacha_poly aead2((xchacha20_256())); // xchacha20-poly1305
	aead_ietf_chacha_poly aead3((salsa20_256())); // xsalsa20/20-poly1305
	aead_ietf_chacha_poly aead4((salsa20_12_128())); // xsalsa20/12/128-poly1305

8.6. Schwaemm

Schwaemm is one of the finalists of the NIST Lightweight Cryptography Competition. We support three out of four instances: schwaemm256-256, schwaemm256-128 and schwaemm192-192. All instances are implemented via schwaemm class.

Examples:

	schwaemm aead1(schwaemm::variant::schwaemm256_256);
	schwaemm aead2(schwaemm::variant::schwaemm256_128);
	schwaemm aead3(schwaemm::variant::schwaemm256_192);

9. Streaming authenticated encryption with associated data (Streaming AEAD)

Class streaming_aead can be used to convert any aead into streaming (segmented) AEAD in order to encrypt/decrypt large input using multiple segments. Each segment will be authenticated separately and will have its own authentication tag.

Streaming AEAD is useful for encrypting large files or other data that is too big to be encrypted in one step. The implementation uses the approach described in paper Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance. It is similar but not identical to libsodium's crypto_secretstream or Google Tink's Streaming AEAD.

To start streaming encryption, call init_encryption(), which creates the stream header, then call encrypt_segment() for each segment that needs to be encrypted. The last call to encrypt_segment must indicate that the segment being encrypted is the final segment of the stream.

Segments can be of any size and different segments can have different sizes, however, the encryption code should match the decryption code. For example, if the encryption code encrypts a segment of 1 Mb followed by another segment of 4 Kb, then the decryption code should also decrypt a segment of 1 Mb (enlarged by tag_bytes()) followed by another segment of 4 Kb (enlarged by tag_bytes()).

The encryption key is generated internally from the input key material using random salt and specified key derivation function. If you already have a key (not a password), you can use hkdf as the key derivation function. If you have a password, use password based kdf such as argon2.

Format of the generated stream header is:

Format of the generated stream is:

Each segment is encrypted using encryption key generated as described above and nonce. Nonce has the following format:

To decrypt the stream, first call init_decryption() with the stream header, then call decrypt_segment for each segment. The last call to decrypt_segment must indicate that the segment being decrypted is the final segment of the stream.

The ciphertext for each segment is longer than plaintext by tag_bytes(). The overall stream size is longer than plaintext by header_bytes() + N * tag_bytes(), where N is the number of segments.

    class streaming_aead
    {
    public:
        enum class segment_type : uint8_t
        {
            non_final = 0,
            final = 1
        };

        streaming_aead(const aead& aead);

        ~streaming_aead();

        // Initialize streaming authenticated encryption.
        // Must be done once before encryption or decryption.
        // The key will be derived from the input key material ikm using key derivation function kdf.
        // Header length must be equal to header_bytes().
        void init_encryption(const unsigned char* ikm, size_t ikmlen, unsigned char* header, size_t header_len, const crypto_kdf& kdf = hkdf(sha256()));

        // Initialize streaming authenticated decryption.
        // Must be done once before encryption or decryption.
        // The key will be derived from the input key material ikm using key derivation function kdf.
        // Header length must be equal to header_bytes().
        void init_decryption(const unsigned char* ikm, size_t ikmlen, const unsigned char* header, size_t header_len, const crypto_kdf& kdf = hkdf(sha256()));

        // Encrypt a segment.
        // The size of the result buffer must be equal to plaintext_len plus tag_bytes().
        // Type must be set to streaming_aead::segment_type::final for the last segment in the stream.
        void encrypt_segment(segment_type type, const unsigned char* plaintext, size_t plaintext_len, const unsigned char* associated_data, size_t associated_data_len, unsigned char* result, size_t result_buffer_len);

        // Decrypt a segment.
        // The size of the result buffer must be equal to ciphertext_len minus tag_bytes().
        // Type must be set to streaming_aead::segment_type::final for the last segment in the stream.
        bool decrypt_segment(segment_type type, const unsigned char* ciphertext, size_t ciphertext_len, const unsigned char* associated_data, size_t associated_data_len, unsigned char* result, size_t result_buffer_len);

        // informational getters

        virtual size_t tag_bytes() const;
        virtual size_t header_bytes() const;
        virtual size_t nonce_prefix_bytes() const;
        virtual size_t salt_bytes() const;
    };

The following sample encrypts a file in segments of 1 Mb using Serpent algorithm with 256-bit key in CTR mode and authenticated using HMAC-Grøstl-256. The encryption key is derived from user-provided password using Argon2d key derivation function.

void encrypt_file(std::string in_filename, std::string out_filename, std::string password)
{
    // Initialize streaming AEAD encryption using password based key derivation function Argon2d
    streaming_aead aead(aead_etm(ctr(serpent256()), hmac(groestl(256))));
    argon2 kdf(argon2::type::argon2d, 4, 4096, 1000);
    std::vector<char> header(aead.header_bytes());
    aead.init_encryption(reinterpret_cast<const unsigned char*>(password.data()), password.length(),
        reinterpret_cast<unsigned char*>(&header[0]), header.size(), kdf);

    std::ifstream input_file(in_filename, std::ios::in | std::ios::binary);
    if (!input_file)
    {
        std::cerr << "cannot open input file" << std::endl;
        return;
    }

    std::ofstream output_file(out_filename, std::ios::out | std::ios::binary);
    if (!output_file)
    {
        std::cerr << "cannot open output file" << std::endl;
        return;
    }

    output_file.write(header.data(), header.size());

    // Encrypt a file in blocks of 1 megabyte
    std::uintmax_t segment_size = 1024 * 1024;
    std::uintmax_t file_size = std::filesystem::file_size(in_filename);
    std::uintmax_t buffer_size = std::min(segment_size, file_size);
    std::vector<char> in_buffer(buffer_size), out_buffer(buffer_size + aead.tag_bytes());
    std::uintmax_t remaining = file_size;
    do
    {
        segment_size = std::min(remaining, buffer_size);
        if (!input_file.read(&in_buffer[0], segment_size))
        {
            std::cerr << "error reading input file" << std::endl;
            return;
        }
        size_t output_size = segment_size + aead.tag_bytes();
        aead.encrypt_segment(remaining == segment_size ? streaming_aead::segment_type::final : streaming_aead::segment_type::non_final,
            reinterpret_cast<const unsigned char*>(in_buffer.data()), segment_size, nullptr, 0,
            reinterpret_cast<unsigned char*>(&out_buffer[0]), output_size);
        output_file.write(out_buffer.data(), output_size);
        remaining -= segment_size;
    } while (remaining);
}

void decrypt_file(std::string in_filename, std::string out_filename, std::string password)
{
    // Initialize streaming AEAD encryption using password based key derivation function Argon2d
    streaming_aead aead(aead_etm(ctr(serpent256()), hmac(groestl(256))));
    argon2 kdf(argon2::type::argon2d, 4, 4096, 1000);

    std::uintmax_t file_size = std::filesystem::file_size(in_filename);
    if (file_size < aead.header_bytes() + aead.tag_bytes())
    {
        std::cerr << "Invalid input file" << std::endl;
        return;
    }

    std::ifstream input_file(in_filename, std::ios::in | std::ios::binary);
    if (!input_file)
    {
        std::cerr << "cannot open input file" << std::endl;
        return;
    }

    std::vector<char> header(aead.header_bytes());
    if (!input_file.read(&header[0], header.size()))
    {
        std::cerr << "Invalid input file" << std::endl;
        return;
    }

    aead.init_decryption(reinterpret_cast<const unsigned char*>(password.data()), password.length(),
        reinterpret_cast<const unsigned char*>(&header[0]), header.size(), kdf);

    // Decrypt a file in blocks of 1 megabyte
    std::uintmax_t segment_size = 1024 * 1024;
    std::uintmax_t remaining = file_size - aead.header_bytes();
    std::uintmax_t buffer_size = std::min(segment_size + aead.tag_bytes(), remaining);
    std::vector<char> in_buffer(buffer_size), out_buffer(buffer_size - aead.tag_bytes());
    std::ofstream output_file(out_filename, std::ios::out | std::ios::binary);

    if (!output_file)
    {
        std::cerr << "cannot open output file" << std::endl;
        return;
    }

    do
    {
        segment_size = std::min(remaining, buffer_size);
        if (segment_size < aead.tag_bytes())
        {
            std::cerr << "Invalid input file" << std::endl;
            return;
        }
        if (!input_file.read(&in_buffer[0], segment_size))
        {
            std::cerr << "error reading input file" << std::endl;
            return;
        }
        size_t output_size = segment_size - aead.tag_bytes();
        aead.decrypt_segment(remaining == segment_size ? streaming_aead::segment_type::final : streaming_aead::segment_type::non_final,
            reinterpret_cast<const unsigned char*>(in_buffer.data()), segment_size, nullptr, 0,
            reinterpret_cast<unsigned char*>(&out_buffer[0]), output_size);
        output_file.write(out_buffer.data(), output_size);
        remaining -= segment_size;
    } while (remaining);
}

10. Digest utility

Also included is a command-line utility 'digest.exe' which can be used to calculate message digest of any file(s) using any of the supported hash algorithms (similar to md5sum), for example, if you want to calculate Skein-512/256 hash of a file, you can run:

   digest skein512/256 file.ext
You can save the checksums to a text file using the following command:

   digest skein512/256 * > checksums.skein
Like md5sum, the utility can also verify checksums saved in a file, for example, if you want to verify the hashes saved by the previous command, you can run:

   digest -c skein512/256 checksums.skein

11. Cryptor utility

Command-line utility 'cryptor.exe' is intended to demonstrate how cppcrypto library can be utilized to encrypt or decrypt a file. To encrypt a file, run:

   cryptor enc file.ext

To decrypt a file, run:

   cryptor dec file.ext

The utility demonstrates the steps needed for encryption. The file is encrypted using streaming_aead mode with Serpent algorithm with 256-bit key in CTR mode and authenticated using HMAC-Grøstl-256. The encryption key is derived from user-provided password using argon2d key derivation function.

The utility is provided mainly for demonstration purposes, algorithm choice is arbitrary.

12. Portability notes

At the moment we support only x86 processors (in 32-bit or 64-bit mode).
To build cppcrypto from the sources you need to have yasm installed.
We have successfully compiled and tested cppcrypto under the following operating systems.

12.1. Windows

Tested OS versions: Windows 10.
Tested compilers: Visual C++ 2017, Visual C++ 2019, Visual C++ 2022.

First, download the latest yasm for Visual Studio and install it according to the instructions in its readme.txt. Note that the latest stable release of yasm (1.3.0) ships vsyasm.props file which is not compatible with Visual Studio 2013 or later. The fixed version of vsyasm.props is included in the cppcrypto source archive, so you just need to replace that single file. (Alternatively, use the development version of yasm from their git repository, which reportedly fixes the compatibility issue.)

Then, open the supplied Visual C++ project files or solution file and build cppcrypto. If you're using Visual Studio 2019 or later, right-click the solution and choose "Retarget" menu item, since the provided project files are from VS2017.

Experimental support for Cygwin gcc builds has been added recently; to build in cygwin, run 'make UNAME=Cygwin' and 'make UNAME=Cygwin install'.

12.2. Linux

Tested OS versions: Fedora 37 (x86_64).
Tested compilers: gcc 12.2.1, clang 15.0.7.

Make sure yasm is installed, then run make and [sudo] make install as usual.

Note that if you do not use recent versions of libstdc++ and try to run the vectortest.sh script on Linux, you may get a crash during checking sha384 and sha512 test vectors. This is caused by a bug in libstdc++ (stack overflow on long regexes), which is fixed in latest versions. This bug does not affect the functionality of cppcrypto library anyhow, C++ regexes are only used in the test application for parsing the test vectors.

12.3. Mac OS X

NOTE: Mac OS X is untested as of cppcrypto-0.20.

Download and install yasm. Then run make and [sudo] make install to build and install cppcrypto.

12.4. FreeBSD

Tested OS versions: FreeBSD 13.1-RELEASE.
Tested compilers: clang 13.0.0.

Install yasm (pkg install yasm) and GNU make (pkg install gmake). Then, run gmake and [sudo] gmake install to build and install cppcrypto.

13. Download link

Download cppcrypto from sourceforge