blob: 46e76316a21ad8bce7c288bb3b8e672c7c51cd5d [file]
/**
* @file sad_openssl.c
* @brief Security Association Database openssl functions
* @note Copyright (C) 2024 IBM Corporation, Clay Kaiser <Clay.Kaiser@ibm.com>
* @note SPDX-License-Identifier: GPL-2.0+
*/
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "print.h"
#include "sad.h"
#include "sad_private.h"
struct mac_data {
EVP_MAC *algorithm;
EVP_MAC_CTX *context;
};
struct mac_data *sad_init_mac(integrity_alg_type algorithm,
const unsigned char *key, size_t key_len)
{
int err;
size_t length;
const char *name;
char *param, *algo;
EVP_MAC *mac_algorithm;
EVP_MAC_CTX *context;
OSSL_PARAM params[2];
EVP_CIPHER *cipher;
struct mac_data *mac_data;
/* verify key length */
if (key_len == 0) {
pr_err("BUG: key_len is zero");
return NULL;
}
/* retrieve mac algorithm */
switch (algorithm) {
case HMAC_SHA256_128:
case HMAC_SHA256:
name = "HMAC";
param = "digest";
algo = "SHA-256";
break;
case CMAC_AES128:
name = "CMAC";
param = "cipher";
algo = "AES-128-CBC";
break;
case CMAC_AES256:
name = "CMAC";
param = "cipher";
algo = "AES-256-CBC";
break;
default:
pr_err("BUG: unknown algorithm");
return NULL;
}
/* verify key length matches for cmac only */
switch (algorithm) {
case CMAC_AES128:
case CMAC_AES256:
cipher = EVP_CIPHER_fetch(NULL, algo, NULL);
length = EVP_CIPHER_get_key_length(cipher);
EVP_CIPHER_free(cipher);
if (key_len != length) {
pr_err("BUG: cipher key_len does not match");
return NULL;
}
break;
default:
break;
}
mac_algorithm = EVP_MAC_fetch(NULL, name, NULL);
if (!mac_algorithm) {
pr_err("EVP_MAC_fetch() failed");
return NULL;
}
context = EVP_MAC_CTX_new(mac_algorithm);
if (!context) {
pr_err("EVP_MAC_CTX_new() failed");
EVP_MAC_free(mac_algorithm);
return NULL;
}
params[0] = OSSL_PARAM_construct_utf8_string(param, algo, 0);
params[1] = OSSL_PARAM_construct_end();
err = EVP_MAC_CTX_set_params(context, params);
if (err == 0) {
pr_err("EVP_MAC_CTX_set_params() failed");
EVP_MAC_free(mac_algorithm);
EVP_MAC_CTX_free(context);
return NULL;
}
/* initialize context */
err = EVP_MAC_init(context, key, key_len, NULL);
if (err == 0) {
pr_err("EVP_MAC_init() failed");
EVP_MAC_free(mac_algorithm);
EVP_MAC_CTX_free(context);
return NULL;
}
/* initialize mac_data */
mac_data = calloc(1, sizeof(*mac_data));
if (!mac_data) {
EVP_MAC_free(mac_algorithm);
EVP_MAC_CTX_free(context);
return NULL;
}
mac_data->algorithm = mac_algorithm;
mac_data->context = context;
return mac_data;
}
void sad_deinit_mac(struct mac_data *data)
{
EVP_MAC_free(data->algorithm);
EVP_MAC_CTX_free(data->context);
free(data);
}
static inline int sad_update_mac(struct mac_data *mac_data,
const void *data, size_t data_len,
unsigned char *mac, size_t mac_len)
{
int err;
size_t digest_len;
/* confirm mac length is within buffer size */
if (mac_len > MAX_DIGEST_LENGTH) {
pr_err("BUG: mac_len larger than buffer");
return 0;
}
/* update data and retrieve mac */
err = EVP_MAC_init(mac_data->context, NULL, 0, NULL);
if (err == 0) {
pr_err("EVP_MAC_init() failed");
return 0;
}
err = EVP_MAC_update(mac_data->context, data, data_len);
if (err == 0) {
pr_err("EVP_MAC_update() failed");
return 0;
}
err = EVP_MAC_final(mac_data->context, mac,
&digest_len, MAX_DIGEST_LENGTH);
if (err == 0) {
pr_err("EVP_MAC_final() failed");
return 0;
}
/* confirm mac length is within library support */
if (mac_len > digest_len) {
pr_err("BUG: mac_len larger than library support");
return 0;
}
return 1;
}
int sad_hash(struct mac_data *mac_data,
const void *data, size_t data_len,
unsigned char *mac, size_t mac_len)
{
unsigned char digest_buffer[MAX_DIGEST_LENGTH];
/* update data and retrieve mac */
if (!sad_update_mac(mac_data, data, data_len,
digest_buffer, mac_len)) {
return 0;
}
/* move mac to desired location */
memcpy(mac, digest_buffer, mac_len);
return mac_len;
}
int sad_verify(struct mac_data *mac_data,
const void *data, size_t data_len,
unsigned char *mac, size_t mac_len)
{
unsigned char digest_buffer[MAX_DIGEST_LENGTH];
/* update data and retrieve mac */
if (!sad_update_mac(mac_data, data, data_len,
digest_buffer, mac_len)) {
return -1;
}
/* compare calculated with received */
return CRYPTO_memcmp(digest_buffer, mac, mac_len);
}