HMAC harness

Test digest

crypto-condor tests HMAC implementations through a single digest function that is equivalent to the following pseudo-code:

def digest(key: bytes, msg: bytes) -> bytes:
    h = hmac.init(key)
    h.update(msg)
    return h.final()

Naming convention

The function must conform to the following convention:

CC_HMAC_digest_<hash function>

Where hash function is one of:

  • sha1, sha224, sha256, sha384, sha512.

  • sha3_224, sha3_256, sha3_384, sha3_512.

Function signature

Its signature must be:

int HMAC_digest(
uint8_t *mac,
const size_t mac_size,
const uint8_t *key,
const size_t key_size,
const uint8_t *msg,
const size_t msg_size,
)

Generates HMAC tags.

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

  • mac_size[In] The size of the allocated buffer in bytes.

  • key[In] The secret key.

  • key_size[In] The size of the secret key in bytes.

  • msg[In] The message to authenticate.

  • msg_size[In] The size of the message in bytes.

Returns:

A status value.

Return values:
  • 1 – OK

  • 0 – An error occurred.

Example

To test the harness for this function, we use the following OpenSSL harness:

#include <openssl/err.h>
#include <openssl/evp.h>
#include <stdint.h>
#include <stdlib.h>

int generic_digest(const char *hash, uint8_t *mac, const size_t mac_size,
                   const uint8_t *key, const size_t key_size,
                   const uint8_t *msg, const size_t msg_size);

int CC_HMAC_digest_sha256(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA-256", mac, mac_size, key, key_size, msg, msg_size);
}

int CC_HMAC_digest_sha384(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA-384", mac, mac_size, key, key_size, msg, msg_size);
}

int CC_HMAC_digest_sha512(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA-512", mac, mac_size, key, key_size, msg, msg_size);
}

int CC_HMAC_digest_sha3_256(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA3-256", mac, mac_size, key, key_size, msg, msg_size);
}

int CC_HMAC_digest_sha3_384(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA3-384", mac, mac_size, key, key_size, msg, msg_size);
}

int CC_HMAC_digest_sha3_512(uint8_t *mac, const size_t mac_size,
                          const uint8_t *key, const size_t key_size,
                          const uint8_t *msg, const size_t msg_size) {
  return generic_digest("SHA3-512", mac, mac_size, key, key_size, msg, msg_size);
}

int generic_digest(const char *hash, uint8_t *mac, const size_t mac_size,
                   const uint8_t *key, const size_t key_size,
                   const uint8_t *msg, const size_t msg_size) {
  EVP_MD_CTX *mdctx = NULL;
  const EVP_MD *md = NULL;
  EVP_PKEY *pkey = NULL;
  size_t req_size = 0;

  mdctx = EVP_MD_CTX_new();
  if (mdctx == NULL) {
    fprintf(stderr, "EVP_MD_CTX_new failed, error 0x%lx\n", ERR_get_error());
    return -1;
  }

  md = EVP_get_digestbyname(hash);
  if (md == NULL) {
    fprintf(stderr, "Failed to get digest %s by name\n", hash);
    EVP_MD_CTX_free(mdctx);
    return -1;
  }

  pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, key_size);
  if (pkey == NULL) {
    fprintf(stderr, "Failed to create HMAC key\n");
    EVP_MD_CTX_free(mdctx);
    return -1;
  }

  if (!EVP_DigestSignInit(mdctx, NULL, md, NULL, pkey)) {
    fprintf(stderr, "Failed to DigestSignInit\n");
    goto error;
  }
  if (!EVP_DigestSignUpdate(mdctx, msg, msg_size)) {
    fprintf(stderr, "Failed to DigestSignUpdate\n");
    goto error;
  }
  if (!EVP_DigestSignFinal(mdctx, NULL, &req_size)) {
    fprintf(stderr, "Failed first call to DigestSignFinal\n");
    goto error;
  }
  if (req_size != mac_size) {
    fprintf(stderr, "Required size %zu does not match given size %zu\n",
            req_size, mac_size);
    goto error;
  }
  if (!EVP_DigestSignFinal(mdctx, mac, &req_size)) {
    fprintf(stderr, "Failed to DigestSignFinal\n");
    goto error;
  }

  EVP_MD_CTX_free(mdctx);
  EVP_PKEY_free(pkey);
  return 1;

error:
  EVP_MD_CTX_free(mdctx);
  EVP_PKEY_free(pkey);
  return -1;
}

Compile the shared library with the -lssl -lcrypto options:

gcc -fPIC -shared hmac_digest_harness.c -o hmac_digest.so -lssl -lcrypto

Then test the harness.

crypto-condor-cli test harness hmac_digest.so

Test verify

HMAC tag verification requires computing the tag again to compare it with the given value.

Naming convention

The function must conform to the following convention:

CC_HMAC_verify_<hash function

Where hash function is one of:

  • sha1, sha224, sha256, sha384, sha512.

  • sha3_224, sha3_256, sha3_384, sha3_512.

Function signature

Attention

The tags used by crypto-condor may be truncated, meaning that comparing the entire MAC to the tag passed by crypto-condor may fail. The size of the regular MAC tag is equal to the output size of the underlying hash function. This is given through the md_size parameter.

Its signature must be:

int HMAC_verify(
const uint8_t *mac,
const size_t mac_size,
const size_t md_size,
const uint8_t *key,
const size_t key_size,
const uint8_t *msg,
const size_t msg_size,
)

Verifies HMAC tags.

Parameters:
  • mac[In] The MAC tag.

  • mac_size[In] The size of the MAC tag in bytes. Note that the tag may be truncated, so the size may differ from md_size.

  • md_size[In] The output size of the hash function in bytes. This is the size of a full MAC tag and may differ from mac_size.

  • key[In] The secret key.

  • key_size[In] The size of the secret key in bytes.

  • msg[In] The message to authenticate.

  • msg_size[In] The size of the message in bytes.

Returns:

A status value.

Return values:
  • 1 – Tag is valid.

  • 0 – Tag is invalid.

  • -1 – An error occurred.

Example

To test the harness for this function, we use the following OpenSSL harness:

#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <stdint.h>
#include <stdlib.h>

int generic_verify(const char *hash, const uint8_t *mac, const size_t mac_size,
                   const size_t md_size, const uint8_t *key,
                   const size_t key_size, const uint8_t *msg,
                   const size_t msg_size);

int CC_HMAC_verify_sha256(const uint8_t *mac, const size_t mac_size,
                          const size_t md_size, const uint8_t *key,
                          const size_t key_size, const uint8_t *msg,
                          const size_t msg_size) {
  return generic_verify("SHA-256", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}

int CC_HMAC_verify_sha384(const uint8_t *mac, const size_t mac_size,
                          const size_t md_size, const uint8_t *key,
                          const size_t key_size, const uint8_t *msg,
                          const size_t msg_size) {
  return generic_verify("SHA-384", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}

int CC_HMAC_verify_sha512(const uint8_t *mac, const size_t mac_size,
                          const size_t md_size, const uint8_t *key,
                          const size_t key_size, const uint8_t *msg,
                          const size_t msg_size) {
  return generic_verify("SHA-512", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}

int CC_HMAC_verify_sha3_256(const uint8_t *mac, const size_t mac_size,
                            const size_t md_size, const uint8_t *key,
                            const size_t key_size, const uint8_t *msg,
                            const size_t msg_size) {
  return generic_verify("SHA3-256", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}

int CC_HMAC_verify_sha3_384(const uint8_t *mac, const size_t mac_size,
                            const size_t md_size, const uint8_t *key,
                            const size_t key_size, const uint8_t *msg,
                            const size_t msg_size) {
  return generic_verify("SHA3-384", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}
int CC_HMAC_verify_sha3_512(const uint8_t *mac, const size_t mac_size,
                            const size_t md_size, const uint8_t *key,
                            const size_t key_size, const uint8_t *msg,
                            const size_t msg_size) {
  return generic_verify("SHA3-512", mac, mac_size, md_size, key, key_size, msg,
                        msg_size);
}
int generic_verify(const char *hash, const uint8_t *mac, const size_t mac_size,
                   const size_t md_size, const uint8_t *key,
                   const size_t key_size, const uint8_t *msg,
                   const size_t msg_size) {
  EVP_MD_CTX *mdctx = NULL;
  const EVP_MD *md = NULL;
  EVP_PKEY *pkey = NULL;
  uint8_t buf[EVP_MAX_MD_SIZE];
  size_t buf_size = md_size;

  mdctx = EVP_MD_CTX_new();
  if (mdctx == NULL) {
    fprintf(stderr, "EVP_MD_CTX_new failed, error 0x%lx\n", ERR_get_error());
    return -1;
  }

  md = EVP_get_digestbyname(hash);
  if (md == NULL) {
    fprintf(stderr, "Failed to get digest %s by name\n", hash);
    EVP_MD_CTX_free(mdctx);
    return -1;
  }

  pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, key_size);
  if (pkey == NULL) {
    fprintf(stderr, "Failed to create HMAC key\n");
    EVP_MD_CTX_free(mdctx);
    return -1;
  }

  if (!EVP_DigestSignInit(mdctx, NULL, md, NULL, pkey)) {
    fprintf(stderr, "Failed to DigestSignInit\n");
    goto error;
  }
  if (!EVP_DigestSignUpdate(mdctx, msg, msg_size)) {
    fprintf(stderr, "Failed to DigestSignUpdate\n");
    goto error;
  }
  if (!EVP_DigestSignFinal(mdctx, buf, &buf_size)) {
    fprintf(stderr, "Failed to DigestSignFinal, error 0x%lx\n",
            ERR_get_error());
    goto error;
  }

  EVP_MD_CTX_free(mdctx);
  EVP_PKEY_free(pkey);

  return (CRYPTO_memcmp(mac, buf, mac_size) == 0);

error:
  EVP_MD_CTX_free(mdctx);
  EVP_PKEY_free(pkey);
  return -1;
}

Compile the shared library with the -lssl -lcrypto options:

gcc -fPIC -shared hmac_verify_harness.c -o hmac_verify.so -lssl -lcrypto

Then test the harness.

crypto-condor-cli test harness hmac_verify.so