AES harness

Test encryption

To test a function that encrypts with AES, the harness function must:

  • follow the naming convention;

  • conform to the function signature.

Naming convention

CC_AES_<mode>_encrypt

Where mode is one of: ECB, CBC, CBCPKCS7, CTR, CFB8, CFB128.

This tests all key lengths. A specific one can be indicated:

CC_AES_<mode>_<length>_encrypt

Where length is one of 128, 192, or 256.

Function signature

int AES_encrypt(
uint8_t *ciphertext,
size_t ciphertext_size,
const uint8_t *plaintext,
size_t plaintext_size,
const uint8_t *key,
size_t key_size,
const uint8_t *iv,
size_t iv_size,
)

Encrypts a plaintext with AES.

Parameters:
  • ciphertext[Out] An allocated buffer to return the resulting ciphertext.

  • ciphertext_size[In] The size of ciphertext in bytes.

  • plaintext[In] The plaintext to encrypt.

  • plaintext_size[In] The size of plaintext in bytes.

  • key[In] The symmetric key to use.

  • key_size[In] The size of key in bytes. Passed even when specifying the key size.

  • iv[In] The IV to use. Not used for ECB mode.

  • iv_size[In] The size of iv in bytes. 0 if the IV is not used.

Returns:

A status value.

Return values:
  • 1 – Operation successful.

  • 0 – An error occurred.

Example

#include <openssl/evp.h>

// Define a generic function to use for all modes.
int encrypt(const EVP_CIPHER *cipher, uint8_t *ciphertext,
            size_t ciphertext_size, const uint8_t *plaintext,
            size_t plaintext_size, const uint8_t *key, size_t key_size,
            const uint8_t *iv, size_t iv_size);

int CC_AES_ECB_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return encrypt(EVP_aes_256_ecb(), ciphertext, ciphertext_size, plaintext,
                 plaintext_size, key, key_size, iv, iv_size);
}

int CC_AES_CBC_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return encrypt(EVP_aes_256_cbc(), ciphertext, ciphertext_size, plaintext,
                 plaintext_size, key, key_size, iv, iv_size);
}

int CC_AES_CTR_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return encrypt(EVP_aes_256_ctr(), ciphertext, ciphertext_size, plaintext,
                 plaintext_size, key, key_size, iv, iv_size);
}

int CC_AES_CFB8_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                            const uint8_t *plaintext, size_t plaintext_size,
                            const uint8_t *key, size_t key_size,
                            const uint8_t *iv, size_t iv_size) {
  return encrypt(EVP_aes_256_cfb8(), ciphertext, ciphertext_size, plaintext,
                 plaintext_size, key, key_size, iv, iv_size);
}

int CC_AES_CFB128_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                              const uint8_t *plaintext, size_t plaintext_size,
                              const uint8_t *key, size_t key_size,
                              const uint8_t *iv, size_t iv_size) {
  return encrypt(EVP_aes_256_cfb128(), ciphertext, ciphertext_size, plaintext,
                 plaintext_size, key, key_size, iv, iv_size);
}

// CBC-PKCS7 uses padding, unlike the other modes.
int CC_AES_CBCPKCS7_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                                const uint8_t *plaintext, size_t plaintext_size,
                                const uint8_t *key, size_t key_size,
                                const uint8_t *iv, size_t iv_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len = 0;
  if (!ctx)
    goto error;

  if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
    goto error;
  if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_size))
    goto error;
  if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
    goto error;

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

int encrypt(const EVP_CIPHER *cipher, uint8_t *ciphertext,
            size_t ciphertext_size, const uint8_t *plaintext,
            size_t plaintext_size, const uint8_t *key, size_t key_size,
            const uint8_t *iv, size_t iv_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len = 0;

  if (!ctx)
    goto error;
  // Disable padding, as the test vectors are without it.
  EVP_CIPHER_CTX_set_padding(ctx, 0);

  if (1 != EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
    goto error;
  if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_size))
    goto error;
  if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
    goto error;

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

Compile with:

gcc -fPIC -static -o AES_openssl_encrypt.so AES_openssl_encrypt.c -lssl -lcrypto

Then test with:

crypto-condor-cli test harness AES_openssl_encrypt.so

Test authenticated encryption

To test a function that encrypts with an AEAD mode, the harness function must:

  • follow the naming convention;

  • conform to the function signature.

Naming convention

CC_AES_<mode>_encrypt

Where mode is one of: CCM, GCM.

This tests all key lengths. A specific one can be indicated:

CC_AES_<mode>_<length>_encrypt

Where length is one of 128, 192, or 256.

Function signature

int AES_encrypt_aead(
uint8_t *ciphertext,
size_t ciphertext_size,
uint8_t *mac,
size_t mac_size,
const uint8_t *plaintext,
size_t plaintext_size,
const uint8_t *key,
size_t key_size,
const uint8_t *iv,
size_t iv_size,
const uint8_t *aad,
size_t aad_size,
)

Encrypts a plaintext with AES and an AEAD mode of operation.

Parameters:
  • ciphertext[Out] An allocated buffer to return the resulting ciphertext.

  • ciphertext_size[In] The size of ciphertext in bytes.

  • mac[Out] An allocated buffer to return the resulting MAC tag.

  • mac_size[In] The size of mac in bytes.

  • plaintext[In] The plaintext to encrypt.

  • plaintext_size[In] The size of plaintext in bytes.

  • key[In] The symmetric key to use.

  • key_size[In] The size of key in bytes. Passed even when specifying the key size.

  • iv[In] The IV to use. Not used for ECB mode.

  • iv_size[In] The size of iv in bytes. 0 if the IV is not used.

Returns:

A status value.

Return values:
  • 1 – Operation successful.

  • 0 – An error occurred.

Example

#include <openssl/evp.h>

int CC_AES_GCM_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                           uint8_t *mac, size_t mac_size,
                           const uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *nonce, size_t nonce_size,
                           const uint8_t *aad, size_t aad_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len;

  if (!ctx)
    goto error;
  if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
    goto error;
  if (1 !=
      EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_size, NULL)) {
    fprintf(stderr, "Failed to set IV len\n");
    goto error;
  }
  if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce))
    goto error;
  if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_size))
    goto error;
  if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_size))
    goto error;
  if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
    goto error;
  if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, mac_size, mac))
    goto error;

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

int CC_AES_CCM_256_encrypt(uint8_t *ciphertext, size_t ciphertext_size,
                           uint8_t *mac, size_t mac_size,
                           const uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *nonce, size_t nonce_size,
                           const uint8_t *aad, size_t aad_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len;

  if (!ctx)
    goto error;
  if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL))
    goto error;
  if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, nonce_size, NULL))
    goto error;
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, mac_size, NULL);
  if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce))
    goto error;
  // Provide total plaintext length.
  if (1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, plaintext_size))
    goto error;
  // Provide any AAD data.
  if (aad_size > 0) {
    if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_size))
      goto error;
  }
  if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_size))
    goto error;
  if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
    goto error;
  if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, mac_size, mac))
    goto error;

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

Compile with:

gcc -fPIC -static -o AES_openssl_encrypt_aead.so AES_openssl_encrypt_aead.c -lssl -lcrypto

Then test with:

crypto-condor-cli test harness AES_openssl_encrypt_aead.so

Test decryption

To test a function that decrypts with AES, the harness function must:

  • follow the naming convention;

  • conform to the function signature.

Naming convention

CC_AES_<mode>_decrypt

Where mode is one of: CCM, GCM.

This tests all key lengths. A specific one can be indicated:

CC_AES_<mode>_<length>_decrypt

Where length is one of 128, 192, or 256.

Function signature

int AES_decrypt(
uint8_t *plaintext,
size_t plaintext_size,
const uint8_t *ciphertext,
size_t ciphertext_size,
const uint8_t *key,
size_t key_size,
const uint8_t *iv,
size_t iv_size,
)

Decrypts a ciphertext with AES.

Parameters:
  • plaintext[Out] An allocated buffer to return the resulting plaintext.

  • plaintext_size[In] The size of plaintext in bytes.

  • ciphertext[In] The ciphertext to decrypt.

  • ciphertext_size[In] The size of ciphertext in bytes.

  • key[In] The symmetric key to use.

  • key_size[In] The size of key in bytes. Passed even when specifying the key size.

  • iv[In] The IV to use. Not used for ECB mode.

  • iv_size[In] The size of iv in bytes. 0 if the IV is not used.

Returns:

The actual size of the (unpadded) plaintext, or -1 if an error occurred.

Example

#include <openssl/evp.h>

int decrypt(const EVP_CIPHER *cipher, uint8_t *plaintext, size_t plaintext_size,
            const uint8_t *ciphertext, size_t ciphertext_size,
            const uint8_t *key, size_t key_size, const uint8_t *iv,
            size_t iv_size);

int CC_AES_ECB_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return decrypt(EVP_aes_256_ecb(), plaintext, plaintext_size, ciphertext,
                 ciphertext_size, key, key_size, iv, iv_size);
}
int CC_AES_CBC_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return decrypt(EVP_aes_256_cbc(), plaintext, plaintext_size, ciphertext,
                 ciphertext_size, key, key_size, iv, iv_size);
}
int CC_AES_CTR_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size) {
  return decrypt(EVP_aes_256_ctr(), plaintext, plaintext_size, ciphertext,
                 ciphertext_size, key, key_size, iv, iv_size);
}
int CC_AES_CFB8_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                            const uint8_t *ciphertext, size_t ciphertext_size,
                            const uint8_t *key, size_t key_size,
                            const uint8_t *iv, size_t iv_size) {
  return decrypt(EVP_aes_256_cfb8(), plaintext, plaintext_size, ciphertext,
                 ciphertext_size, key, key_size, iv, iv_size);
}
int CC_AES_CFB128_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                              const uint8_t *ciphertext, size_t ciphertext_size,
                              const uint8_t *key, size_t key_size,
                              const uint8_t *iv, size_t iv_size) {
  return decrypt(EVP_aes_256_cfb128(), plaintext, plaintext_size, ciphertext,
                 ciphertext_size, key, key_size, iv, iv_size);
}

int CC_AES_CBCPKCS7_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                              const uint8_t *ciphertext, size_t ciphertext_size,
                              const uint8_t *key, size_t key_size,
                              const uint8_t *iv, size_t iv_size) {
  int pt_len = 0, len = 0;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  if (!ctx)
    goto error;

  if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
    goto error;
  if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_size))
    goto error;
  pt_len += len;
  if (!EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
    goto error;
  pt_len += len;

  EVP_CIPHER_CTX_free(ctx);
  return pt_len;

error:
  EVP_CIPHER_CTX_free(ctx);
  return -1;
}

int decrypt(const EVP_CIPHER *cipher, uint8_t *plaintext, size_t plaintext_size,
            const uint8_t *ciphertext, size_t ciphertext_size,
            const uint8_t *key, size_t key_size, const uint8_t *iv,
            size_t iv_size) {
  int len = 0, pt_len = 0;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();

  if (!ctx)
    goto error;

  if (1 != EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv))
    goto error;
  EVP_CIPHER_CTX_set_padding(ctx, 0);

  if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_size))
    goto error;
  pt_len += len;
  if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
    goto error;
  pt_len += len;

  EVP_CIPHER_CTX_free(ctx);
  return pt_len;

error:
  EVP_CIPHER_CTX_free(ctx);
  return -1;
}

Compile with:

gcc -fPIC -static -o AES_openssl_decrypt.so AES_openssl_decrypt.c -lssl -lcrypto

Then test with:

crypto-condor-cli test harness AES_openssl_decrypt.so

Test authenticated decryption

To test a function that decrypts with an AEAD mode, the harness function must:

  • follow the naming convention;

  • conform to the function signature.

Naming convention

CC_AES_<mode>_decrypt

Where mode is one of: CCM, GCM.

This tests all key lengths. A specific one can be indicated:

CC_AES_<mode>_<length>_decrypt

Where length is one of 128, 192, or 256.

Function signature

int AES_decrypt_aead(
uint8_t *plaintext,
size_t plaintext_size,
const uint8_t *ciphertext,
size_t ciphertext_size,
const uint8_t *mac,
size_t mac_size,
const uint8_t *key,
size_t key_size,
const uint8_t *iv,
size_t iv_size,
const uint8_t *aad,
size_t aad_size,
)

Decrypts a ciphertext with AES and an AEAD mode of operation.

Parameters:
  • plaintext[Out] An allocated buffer to return the resulting plaintext.

  • plaintext_size[In] The size of plaintext in bytes.

  • ciphertext[In] The ciphertext to decrypt.

  • ciphertext_size[In] The size of ciphertext in bytes.

  • mac[In] The MAC tag to verify.

  • mac_size[In] The size of mac in bytes.

  • key[In] The symmetric key to use.

  • key_size[In] The size of key in bytes. Passed even when specifying the key size.

  • iv[In] The IV to use. Not used for ECB mode.

  • iv_size[In] The size of iv in bytes. 0 if the IV is not used.

Returns:

A status value.

Return values:
  • 1 – Operation successful.

  • 0 – An error occurred.

  • -1 – The MAC tag is invalid.

Example

#include <openssl/evp.h>

int CC_AES_GCM_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *mac, size_t mac_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size,
                           const uint8_t *aad, size_t aad_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len = 0;

  if (!ctx)
    goto error;
  if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
    goto error;
  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_size, NULL))
    goto error;
  if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
    goto error;
  if (aad_size > 0)
    if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_size))
      goto error;
  if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_size))
    goto error;
  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, mac_size, (void *)mac))
    goto error;
  if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
    EVP_CIPHER_CTX_free(ctx);
    return -1;
  }

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

int CC_AES_CCM_256_decrypt(uint8_t *plaintext, size_t plaintext_size,
                           const uint8_t *ciphertext, size_t ciphertext_size,
                           const uint8_t *mac, size_t mac_size,
                           const uint8_t *key, size_t key_size,
                           const uint8_t *iv, size_t iv_size,
                           const uint8_t *aad, size_t aad_size) {
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  int len = 0;

  if (!ctx)
    goto error;
  if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL))
    goto error;
  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_size, NULL))
    goto error;
  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, mac_size, (void *)mac))
    goto error;
  if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
    goto error;
  // Provide total ciphertext length.
  if (!EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_size))
    goto error;
  if (aad_size > 0)
    if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_size))
      goto error;
  if (1 !=
      EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_size)) {
    EVP_CIPHER_CTX_free(ctx);
    return -1;
  }

  EVP_CIPHER_CTX_free(ctx);
  return 1;

error:
  EVP_CIPHER_CTX_free(ctx);
  return 0;
}

Compile with:

gcc -fPIC -static -o AES_openssl_decrypt_aead.so AES_openssl_decrypt_aead.c -lssl -lcrypto

Then test with:

crypto-condor-cli test harness AES_openssl_decrypt_aead.so