Overview

Table of contents
  1. Quantum resistance
  2. NIST
  3. API overview
    1. PQC_init_context
    2. PQC_init_context_iv
    3. PQC_set_iv
    4. PQC_encrypt
    5. PQC_decrypt
    6. PQC_aead_encrypt
    7. PQC_aead_check
    8. PQC_aead_decrypt
    9. PQC_close_context
  4. Examples
    1. AES CBC
    2. AES CTR
    3. AES ECB
    4. AES OFB
    5. AES GCM
  • Algorithm type: Quantum-resistant algorithm, symmetric key encryption algorithm

  • Main cryptographic assumption: The difficulty of performing a successful brute force attack to decipher encrypted data without knowledge of the correct encryption key

  • Principal submitters: Joan Daemen and Vincent Rijmen

  • License: For AES ECB, AES CBC and AES CTR the following provision applies to this part of the software as an additional term to the license: This code is based on the code of Tiny AES in C (https://github.com/kokke/tiny-AES-c). Tiny AES in C is subject to the Unlicense and released into the public domain (https://unlicense.org/).

AES-256 is a variant of the Advanced Encryption Standard (AES) algorithm that uses a 256-bit key length. It is one of the most secure encryption methods and is often used in government and industry applications. AES operates on a fixed block size of 128 bits and with key sizes of 128, 192, or 256 bits, but in the case of AES-256, the key size as mentioned is 256 bits.

The encryption modes are methods that describe how to repeatedly apply the cipher’s single-block operation to securely transform amounts of data larger than a block. Different modes are used for different applications, offering various levels of security and efficiency based on the use case.

  • Electronic Codebook (ECB): This is the simplest encryption mode. Each block of plaintext is encrypted separately. This can lead to patterns in the ciphertext when identical blocks of plaintext are encrypted, making ECB susceptible to certain attacks (e.g., pattern analysis).

  • Cipher Block Chaining (CBC): CBC mode adds an initialization vector (IV) to the first data block before the encryption process starts. Each subsequent block of plaintext is XORed (exclusive OR) with the previous ciphertext block before being encrypted. This means that identical plain text blocks will produce different ciphertext blocks. The IV ensures that even if the same message is encrypted multiple times, it will result in different ciphertexts.

  • Output Feedback (OFB): OFB converts a block cipher into a synchronous stream cipher. It generates keystream blocks, which are then XORed with the plaintext blocks to produce ciphertext. The same keystream blocks are used to decrypt ciphertext back to plaintext. OFB ensures that the same plaintext inputs will result in different ciphertext outputs.

  • Counter (CTR): Like OFB, CTR turns a block cipher into a stream cipher. It encrypts a set of counter values and then XORs the resulting output with the plaintext to generate the ciphertext. The counter is increased by one for every subsequent block and must be unique for each encryption operation. CTR mode is known for its ability to allow random access to the encrypted data blocks.

  • Galois/Counter (GCM): The GCM algorithm provides both data authenticity (integrity) and confidentiality and belongs to the class of authenticated encryption with associated data (AEAD) methods. This means that as input it takes a key K, some plaintext P, and some associated data AD; it then encrypts the plaintext using the key to produce ciphertext C, and computes an authentication tag T from the ciphertext and the associated data (which remains unencrypted). A recipient with knowledge of K, upon reception of AD, C and T, can decrypt the ciphertext to recover the plaintext P and can check the tag T to ensure that neither ciphertext nor associated data were tampered with.

Each of these modes has a specific use case where it excels and others where it may be susceptible. Modern practices typically favor modes like CTR over ECB and CBC due to their stronger security properties and performance benefits, particularly in settings susceptible to parallel processing.

Quantum resistance

AES-256 is considered to be quantum resistant, as it has similar quantum resistance to AES-128’s resistance against traditional, non-quantum, attacks at 128 bits of security. AES-192 and AES-128 are not considered quantum resistant due to their smaller key sizes. AES-192 has a strength of 96 bits against quantum attacks and AES-128 has 64 bits of strength against quantum attacks, making them both insecure.

NIST

The NIST certification for AES (Advanced Encryption Standard) refers to the validation process overseen by the Cryptographic Algorithm Validation Program (CAVP), which is part of the NIST (National Institute of Standards and Technology). AES itself, specified under the publication FIPS (Federal Information Processing Standards) 197, is an encryption standard approved by NIST in 2001 for securing sensitive but unclassified material by U.S. government agencies and, by extension, for other organizations.

API overview

Include: pqc/aes.h

PQC_init_context

Function signature

CIPHER_HANDLE PQC_init_context(uint32_t cipher, const uint8_t* key, size_t key_length);

Purpose: This function is used to initialize an encryption context for a specific cipher with a given encryption key.

Parameters:

  • cipher: Constant to select a cipher algorithm. In this case, the possible value is PQC_CIPHER_AES for AES-256.

  • key: Pointer to the encryption key. The key length should match the used cipher, which for AES-256 should be 32 bytes.

  • key_length: Length of the encryption key.

PQC_init_context_iv

Function signature

CIPHER_HANDLE PQC_init_context_iv(uint32_t cipher, const uint8_t* key, const uint8_t* iv, size_t iv_length);

Purpose: This function is similar to PQC_init_context, but it also allows for the initialization vector (IV) to be specified, if required by the desired operation mode.

Parameters:

  • cipher: Constant to select a cipher algorithm, same as in PQC_init_context.

  • key: Pointer to the encryption key, same as in PQC_init_context.

  • iv: Pointer to the initialization vector. IV length should match the used cipher, which for AES-256 should be 16 bytes.

  • iv_length: Length of the IV.

For both functions, the return values are specified as follows:

  • PQC_BAD_CIPHER: Indicates an unknown/unsupported cipher or incorrect size of the key/IV.

  • Otherwise: Returns the handle of the created encryption context.

PQC_set_iv

Function signature

size_t PQC_set_iv(CIPHER_HANDLE ctx, const uint8_t* iv, size_t iv_length);

Purpose: This function is used to set the initialization vector for an initialized encryption context if it was not provided during the initialization with PQC_init_context_iv().

Parameters:

  • ctx: Handle of the initialized encryption context.

  • iv: Pointer to the initialization vector. The IV length should match the requirements of the used cipher, for example, 16 bytes for AES-256.

  • iv_length: Length of the IV.

The function returns the following values:

  • PQC_OK: Indicates that the operation was successful, and the IV was set for the encryption context.

  • PQC_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_CIPHER: This return value indicates that the cipher used does not require an IV.

  • PQC_BAD_LEN: Indicates that the length of the provided IV does not match the requirements of the cipher.

The provided code snippet describes a function for encrypting data using an initialized encryption context. Let’s break down the purpose and usage of the given function and its parameters.

PQC_encrypt

Function signature:

size_t PQC_encrypt(CIPHER_HANDLE ctx, uint32_t mode, uint8_t* buffer, size_t length);

Purpose: This function is used to encrypt data using the specified encryption context and encryption mode.

Parameters:

  • ctx: Handle of the initialized encryption context.

  • mode: Constant to select the encryption mode. The possible values depend on the selected cipher. For PQC_CIPHER_AES, the available modes are PQC_AES_M_ECB, PQC_AES_M_CBC, and PQC_AES_M_OFB.

  • buffer (in/out): Pointer to the data array. The data is encrypted in place within the same buffer.

  • length: Length of the data buffer.

The function returns the following values:

  • PQC_OK: Indicates that the operation was successful, and the data was encrypted.

  • PQC_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_LEN: Indicates that the length of the data does not match the requirements for the selected cipher/mode.

  • PQC_NO_IV: Indicates that an initialization vector is required for the selected cipher/mode, but it was not set.

  • PQC_BAD_MODE: Indicates that the mode parameter provided is invalid.

  • PQC_BAD_CIPHER: Indicates that the selected cipher does not support symmetric encryption.

PQC_decrypt

Function signature:

size_t PQC_decrypt(CIPHER_HANDLE ctx, uint32_t mode, uint8_t* buffer, size_t length);

Purpose: This function is used to decrypt data using the specified encryption context and encryption mode.

Parameters:

  • ctx: Handle of the initialized encryption context.

  • mode: Constant to select the encryption mode. The possible values depend on the selected cipher. For PQC_CIPHER_AES, the available modes are PQC_AES_M_ECB, PQC_AES_M_CBC and PQC_AES_M_OFB.

  • buffer (in/out): Pointer to the data array. The data is decrypted in place within the same buffer.

  • length: Length of the data buffer.

The function returns the following values:

  • PQC_OK: Indicates that the operation was successful, and the data was decrypted.

  • P_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_LEN: Indicates that the length of the data does not match the requirements for the selected cipher/mode.

  • PQC_NO_IV: Indicates that an initialization vector is required for the selected cipher/mode, but it was not set.

  • PQC_BAD_MODE: Indicates that the mode parameter provided is invalid.

  • PQC_BAD_CIPHER: Indicates that the selected cipher does not support symmetric encryption.

PQC_aead_encrypt

Function signature:

size_t PQC_aead_encrypt(CIPHER_HANDLE ctx, uint32_t mode, uint8_t* buffer, size_t length, const uint8_t * aad, size_t aad_length, uint8_t * auth_tag, size_t auth_tag_len);

Purpose: This function is used to encrypt data and create authentication tag using the specified encryption context and encryption mode.

Parameters:

  • ctx: Handle of the initialized encryption context.

  • mode: Constant to select the encryption mode. The possible values depend on the selected cipher. For PQC_CIPHER_AES, the available mode is PQC_AES_M_GCM.

  • buffer (in/out): Pointer to the data array. The data is encrypted in place within the same buffer.

  • length: Length of the data buffer.

  • aad (in): Pointer to the additional authenticated data array. This data is authenticated, but not encrypted.

  • aad_length: Length of the aad data buffer.

  • tag (out): Pointer to the authentication tag array. Size of tag should equal PQC_AES_IVLEN.

  • tag length: Length of the tag buffer.

The function returns the following values:

  • PQC_OK: Indicates that the operation was successful, and the data was encrypted.

  • PQC_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_LEN: Indicates that the length of the data does not match the requirements for the selected cipher/mode.

  • PQC_NO_IV: Indicates that an initialization vector is required for the selected cipher/mode, but it was not set.

  • PQC_BAD_MODE: Indicates that the mode parameter provided is invalid.

  • PQC_BAD_CIPHER: Indicates that the selected cipher does not support symmetric encryption.

PQC_aead_check

Function signature:

size_t PQC_aead_check(CIPHER_HANDLE ctx, uint32_t mode, uint8_t* buffer, size_t length);

Purpose: This function is used to check authentity of data without decrypting it using the specified encryption context and encryption mode.

Parameters:

  • ctx: Handle of the initialized encryption context.

  • mode: Constant to select the encryption mode. The possible values depend on the selected cipher. For PQC_CIPHER_AES, the available mode is PQC_AES_M_GCM.

  • buffer (in/out): Pointer to the data array. The data is decrypted in place within the same buffer.

  • length: Length of the data buffer.

  • aad (in): Pointer to the additional authenticated data array. This data is authenticated, but not decrypted.

  • aad_length: Length of the aad data buffer.

  • tag (in): Pointer to the authentication tag array. Size of tag should equal PQC_AES_IVLEN.

  • tag length: Length of the tag buffer.

The function returns the following values:

  • PQC_OK: Indicates that the input data is authentic.

  • PQC_AUTHENTICATION_FAILURE: Input is not authentic.

  • P_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_LEN: Indicates that the length of the data does not match the requirements for the selected cipher/mode.

  • PQC_NO_IV: Indicates that an initialization vector is required for the selected cipher/mode, but it was not set.

  • PQC_BAD_MODE: Indicates that the mode parameter provided is invalid.

  • PQC_BAD_CIPHER: Indicates that the selected cipher does not support symmetric encryption.

** Note ** PQC_aead_check do not change initialization vector, so it can be used several times before PQC_aead_decrypt without affecting decryption context state. Otherwise, initialization vector is changed by PQC_aead_decrypt, so PQC_aead_check can not be used after the data is decrypted.

PQC_aead_decrypt

Function signature:

size_t PQC_aead_decrypt(CIPHER_HANDLE ctx, uint32_t mode, uint8_t* buffer, size_t length);

Purpose: This function is used to decrypt and check authentity of data using the specified encryption context and encryption mode.

Parameters:

  • ctx: Handle of the initialized encryption context.

  • mode: Constant to select the encryption mode. The possible values depend on the selected cipher. For PQC_CIPHER_AES, the available mode is PQC_AES_M_GCM.

  • buffer (in/out): Pointer to the data array. The data is decrypted in place within the same buffer.

  • length: Length of the data buffer.

  • aad (in): Pointer to the additional authenticated data array. This data is authenticated, but not decrypted.

  • aad_length: Length of the aad data buffer.

  • tag (in): Pointer to the authentication tag array. Size of tag should equal PQC_AES_IVLEN.

  • tag length: Length of the tag buffer.

The function returns the following values:

  • PQC_OK: Indicates that the operation was successful, and the data was decrypted.

  • PQC_AUTHENTICATION_FAILURE: Input is not authentic.

  • P_BAD_CONTEXT: Indicates that the context was not properly initialized, suggesting an issue with the encryption context handle.

  • PQC_BAD_LEN: Indicates that the length of the data does not match the requirements for the selected cipher/mode.

  • PQC_NO_IV: Indicates that an initialization vector is required for the selected cipher/mode, but it was not set.

  • PQC_BAD_MODE: Indicates that the mode parameter provided is invalid.

  • PQC_BAD_CIPHER: Indicates that the selected cipher does not support symmetric encryption.

PQC_close_context

Function signature:

size_t PQC_close_context(CIPHER_HANDLE ctx);

Purpose: This function is used to release resources associated with an initialized encryption context when it is no longer needed.

Parameters:

  • ctx: Handle of the initialized encryption context.

The function returns the following value:

  • PQC_OK: Indicates that the operation to release the resources associated with the context was successful.

Examples

AES CBC

#include <cstring>
#include <iostream>

#include <pqc/aes.h>

// CBC

// parties can obtain a common key through asymmetric key exchange

// CBC takes a fixed-length iv, fixed-length key and
// fixed-length plaintext. Plaintext size MUST be mutile of AES_BLOCKLEN;

// Party A encrypts its plaintext using a key and iv. Then data, its length, key and iv must be
// transmitted to party B

void cbc_encrypt(uint8_t key[], uint8_t data[], const int data_len, uint8_t iv[])
{
    CIPHER_HANDLE context;

    context = PQC_init_context_iv(PQC_CIPHER_AES, key, PQC_AES_KEYLEN, iv, PQC_AES_IVLEN);

    PQC_encrypt(context, PQC_AES_M_CBC, data, data_len);

    PQC_close_context(context);
}

// Party B decrypts the ciphertext using the same key and iv.

void cbc_decrypt(uint8_t key[], uint8_t data[], const int data_len, uint8_t iv[])
{
    CIPHER_HANDLE context;

    context = PQC_init_context_iv(PQC_CIPHER_AES, key, PQC_AES_KEYLEN, iv, PQC_AES_IVLEN);

    PQC_decrypt(context, PQC_AES_M_CBC, data, data_len);

    PQC_close_context(context);
}


int main()
{
    // CBC

    uint8_t key[PQC_AES_KEYLEN] = {1,   2,   3,   4,   5,  6,  7,  8,  9,  10, '1', '2', '3', '4', '5', '6',
                                   '7', '8', '9', '0', 21, 22, 23, 24, 25, 26, 27,  28,  29,  30,  'A', 'B'};

    const int data_len = PQC_AES_BLOCKLEN * 2;


    uint8_t data_a[data_len] = {88, 16, 49, 23, 78, 30, 17, 99, 253, 164, 82, 97, 188, 87, 61, 13};
    uint8_t data_original[data_len] = {0};
    uint8_t data_b[data_len] = {0};

    uint8_t iv[PQC_AES_IVLEN] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

    memcpy(data_original, data_a, sizeof(data_a)); // Copy of original data for verification.

    cbc_encrypt(key, data_a, data_len, iv); // This happens at party A

    memcpy(data_b, data_a, data_len); // Emulate transfer of encrypted message to party B

    cbc_decrypt(key, data_b, data_len, iv); // This happens at party B

    if (memcmp(data_b, data_original, data_len) == 0) // Verification
    {
        std::cout << "Verification successful." << std::endl;
    }
    else
    {
        std::cout << "Verification failed." << std::endl;
    }

    return 0;
}

AES CTR

#include <cstring>
#include <iostream>

#include <pqc/aes.h>

// parties can obtain a common key through asymmetric key exchange

// CTR encryption doesn't need an iv. It takes a fixed-length key and
// fixed-length plaintext. Plaintext size MUST be mutile of AES_BLOCKLEN;
// Decryption works in the same way

// Party A encrypts its plaintext using a key. Then data, its length, and key must be
// transmitted to party B

void ctr_encrypt(uint8_t key[], uint8_t data[], const int data_len)
{
    CIPHER_HANDLE context;

    context = PQC_init_context(PQC_CIPHER_AES, key, PQC_AES_KEYLEN);
    PQC_encrypt(context, PQC_AES_M_CTR, data, data_len);
}


// Party B decrypts the ciphertext using the same key.

void ctr_decrypt(uint8_t key[], uint8_t data[], const int data_len)
{
    CIPHER_HANDLE context;

    context = PQC_init_context(PQC_CIPHER_AES, key, PQC_AES_KEYLEN);
    PQC_decrypt(context, PQC_AES_M_CTR, data, data_len);

    PQC_close_context(context);
}


int main()
{
    // CTR
    uint8_t key[PQC_AES_KEYLEN] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F',
                                   'G', 'H', 'I', 'J', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'K', 'L'};

    const int data_len = PQC_AES_BLOCKLEN;
    uint8_t data_a[data_len] = {89, 234, 87, 91, 40, 83, 179, 255, 80, 66, 19, 45, 89, 0, 64, 123};
    uint8_t data_original[data_len] = {0};
    uint8_t data_b[data_len] = {0};


    memcpy(data_original, data_a, sizeof(data_a)); // Copy of original data for verification.

    ctr_encrypt(key, data_a, data_len); // This happens at party A

    memcpy(data_b, data_a, data_len); // Emulate transfer of encrypted message to party B

    ctr_decrypt(key, data_b, data_len); // This happens at party B

    if (memcmp(data_b, data_original, data_len) == 0) // Vrification
    {
        std::cout << "Verification successful." << std::endl;
    }
    else
    {
        std::cout << "Verification failed." << std::endl;
    }


    return 0;
}

AES ECB

#include <cstring>
#include <iostream>

#include <pqc/aes.h>

// parties can obtain a common key through asymmetric key exchange

// ECB

// ECB encryption doesn't need an IV. It takes a fixed-length key and
// fixed-length plaintext. The length of the open text must be PQC_AES_BLOCKLEN

// Decryption works in the same way

// Party A encrypts its plaintext using a key. Then data, ints length and key must be
// transmitted to party B

void ecb_encrypt(uint8_t key[], uint8_t data[], const int data_len)
{
    CIPHER_HANDLE context = PQC_init_context(PQC_CIPHER_AES, key, PQC_AES_KEYLEN);
    PQC_encrypt(context, PQC_AES_M_ECB, data, data_len);
    PQC_close_context(context);
}

// Party B decrypts the ciphertext using the same key.
void ecb_decrypt(uint8_t key[], uint8_t data[], const int data_len)
{
    CIPHER_HANDLE context = PQC_init_context(PQC_CIPHER_AES, key, PQC_AES_KEYLEN);
    PQC_decrypt(context, PQC_AES_M_ECB, data, data_len);
    PQC_close_context(context);
}

int main()
{
    // ECB
    uint8_t key[PQC_AES_KEYLEN] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F',
                                   'G', 'H', 'I', 'J', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'K', 'L'};

    const int data_len = PQC_AES_BLOCKLEN;
    uint8_t data_a[data_len] = {89, 234, 87, 91, 40, 83, 179, 255, 80, 66, 19, 45, 89, 0, 64, 123};
    uint8_t data_original[data_len] = {0};
    uint8_t data_b[data_len] = {0};

    memcpy(data_original, data_a, sizeof(data_a)); // Copy of original data for verification.

    ecb_encrypt(key, data_a, data_len); // This happens at party A

    memcpy(data_b, data_a, data_len); // Emulate transfer of encrypted message to party B

    ecb_decrypt(key, data_b, data_len); // This happens at party B

    if (memcmp(data_b, data_original, data_len) == 0) // Verification
    {
        std::cout << "Verification successful." << std::endl;
    }
    else
    {
        std::cout << "Verification failed." << std::endl;
    }

    return 0;
}

AES OFB

#include <iostream>

#include <cstring>

#include <pqc/aes.h>

// parties can obtain a common key through asymmetric key exchange

// OFB

// OFB  takes a fixed length iv, fixed-length key and
// fixed-length plaintext. Plaintext size MUST be mutile of AES_BLOCKLEN;
// Decryption works in the same way

// Party A encrypts its plaintext using a key and iv. Then data, its length, key and iv must be
// transmitted to party B


void ofb_encrypt(uint8_t key[], uint8_t data[], const int data_len, uint8_t iv[])
{
    CIPHER_HANDLE context;

    context = PQC_init_context_iv(PQC_CIPHER_AES, key, PQC_AES_KEYLEN, iv, PQC_AES_IVLEN);
    PQC_encrypt(context, PQC_AES_M_OFB, data, data_len);

    PQC_close_context(context);
}

// Party B decrypts the ciphertext using the same key and iv.

void ofb_decrypt(uint8_t key[], uint8_t data[], const int data_len, uint8_t iv[])
{
    CIPHER_HANDLE context;
    context = PQC_init_context_iv(PQC_CIPHER_AES, key, PQC_AES_KEYLEN, iv, PQC_AES_IVLEN);

    PQC_decrypt(context, PQC_AES_M_OFB, data, data_len);

    PQC_close_context(context);
}


int main()
{
    // OFB
    uint8_t key[PQC_AES_KEYLEN] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 11,  12,  13,  14,  15, 16,
                                   17,  18,  19,  20,  '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 31, 32};

    const int data_len = PQC_AES_BLOCKLEN * 2;

    uint8_t data_a[data_len] = {24, 76, 85, 125, 230, 234, 78, 42, 58, 39, 11, 9, 7, 12, 18, 74};
    uint8_t data_original[data_len] = {0};
    uint8_t data_b[data_len] = {0};

    memcpy(data_original, data_a, sizeof(data_a)); // Copy of original data for verification.

    uint8_t iv[PQC_AES_IVLEN] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

    ofb_encrypt(key, data_a, data_len, iv); // This happens at party A

    memcpy(data_b, data_a, data_len); // Emulate transfer of encrypted message to party B

    ofb_decrypt(key, data_b, data_len, iv); // This happens at party B

    if (memcmp(data_b, data_original, data_len) == 0) // Verification
    {
        std::cout << "Verification successful." << std::endl;
    }
    else
    {
        std::cout << "Verification failed." << std::endl;
    }

    return 0;
}

AES GCM

#include <cstring>
#include <iostream>
#include <vector>

#include <pqc/aes.h>

// parties can obtain a common key through asymmetric key exchange

// Party A encrypts its plaintext using a key. Then data, its length, and key must be
// transmitted to party B

void gcm_encrypt(
    const std::vector<uint8_t> & key, std::vector<uint8_t> & data, const std::vector<uint8_t> & aad,
    std::vector<uint8_t> & tag
)
{
    CIPHER_HANDLE context;

    context = PQC_init_context(PQC_CIPHER_AES, key.data(), PQC_AES_KEYLEN);
    PQC_aead_encrypt(
        context, PQC_AES_M_CTR, data.data(), data.size(), aad.data(), aad.size(), tag.data(), PQC_AES_IVLEN
    );
}


// Party B decrypts the ciphertext using the same key.

void gcm_decrypt(
    const std::vector<uint8_t> & key, std::vector<uint8_t> data, std::vector<uint8_t> aad,
    const std::vector<uint8_t> tag
)
{
    CIPHER_HANDLE context;

    context = PQC_init_context(PQC_CIPHER_AES, key.data(), PQC_AES_KEYLEN);
    PQC_aead_decrypt(
        context, PQC_AES_M_CTR, data.data(), data.size(), aad.data(), aad.size(), tag.data(), PQC_AES_IVLEN
    );

    PQC_close_context(context);
}


int main()
{
    // CTR
    std::vector<uint8_t> key = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F',
                                'G', 'H', 'I', 'J', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'K', 'L'};

    const int data_len = PQC_AES_BLOCKLEN;
    std::vector<uint8_t> data_a = {89, 234, 87, 91, 40, 83, 179, 255, 80, 66, 19, 45, 89, 0, 64, 123};
    std::vector<uint8_t> data_original(data_a);
    std::vector<uint8_t> data_b(data_len);
    std::vector<uint8_t> aad = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
    std::vector<uint8_t> tag(PQC_AES_IVLEN);

    gcm_encrypt(key, data_a, aad, tag); // This happens at party A

    data_b = data_a; // Emulate transfer of encrypted message to party B

    gcm_decrypt(key, data_b, aad, tag); // This happens at party B

    if (data_b == data_original) // Vrification
    {
        std::cout << "Verification successful." << std::endl;
    }
    else
    {
        std::cout << "Verification failed." << std::endl;
    }

    return 0;
}


© Copyright 2024, Terra Quantum AG.