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