openssl/crypto/hmac/hmac_s390x.c
Ingo Franzki d5b3c0e24b s390x: Fix HMAC digest detection
Use EVP_MD_is_a() instead of EVP_MD_get_type() to detect the digest
type. EVP_MD_get_type() does not always return the expected NID, e.g.
when running in the FIPS provider, EVP_MD_get_type() returns zero,
causing to skip the HMAC acceleration path.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>

Reviewed-by: Paul Dale <ppzgs1@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25304)
2024-09-02 10:23:22 +02:00

294 lines
8.5 KiB
C

/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "crypto/s390x_arch.h"
#include "hmac_local.h"
#include "openssl/obj_mac.h"
#include "openssl/evp.h"
#ifdef OPENSSL_HMAC_S390X
static int s390x_fc_from_md(const EVP_MD *md)
{
int fc;
if (EVP_MD_is_a(md, "SHA2-224"))
fc = S390X_HMAC_SHA_224;
else if (EVP_MD_is_a(md, "SHA2-256"))
fc = S390X_HMAC_SHA_256;
else if (EVP_MD_is_a(md, "SHA2-384"))
fc = S390X_HMAC_SHA_384;
else if (EVP_MD_is_a(md, "SHA2-512"))
fc = S390X_HMAC_SHA_512;
else
return 0;
if ((OPENSSL_s390xcap_P.kmac[1] & S390X_CAPBIT(fc)) == 0)
return 0;
return fc;
}
static void s390x_call_kmac(HMAC_CTX *ctx, const unsigned char *in, size_t len)
{
unsigned int fc = ctx->plat.s390x.fc;
if (ctx->plat.s390x.ikp)
fc |= S390X_KMAC_IKP;
if (ctx->plat.s390x.iimp)
fc |= S390X_KMAC_IIMP;
switch (ctx->plat.s390x.fc) {
case S390X_HMAC_SHA_224:
case S390X_HMAC_SHA_256:
ctx->plat.s390x.param.hmac_224_256.imbl += ((uint64_t)len * 8);
break;
case S390X_HMAC_SHA_384:
case S390X_HMAC_SHA_512:
ctx->plat.s390x.param.hmac_384_512.imbl += ((uint128_t)len * 8);
break;
default:
break;
}
s390x_kmac(in, len, fc, &ctx->plat.s390x.param);
ctx->plat.s390x.ikp = 1;
}
int s390x_HMAC_init(HMAC_CTX *ctx, const void *key, int key_len, ENGINE *impl)
{
unsigned char *key_param;
unsigned int key_param_len;
ctx->plat.s390x.fc = s390x_fc_from_md(ctx->md);
if (ctx->plat.s390x.fc == 0)
return -1; /* Not supported by kmac instruction */
ctx->plat.s390x.blk_size = EVP_MD_get_block_size(ctx->md);
if (ctx->plat.s390x.blk_size < 0)
return 0;
if (ctx->plat.s390x.size !=
(size_t)(ctx->plat.s390x.blk_size * HMAC_S390X_BUF_NUM_BLOCKS)) {
OPENSSL_clear_free(ctx->plat.s390x.buf, ctx->plat.s390x.size);
ctx->plat.s390x.size = 0;
ctx->plat.s390x.buf = OPENSSL_zalloc(ctx->plat.s390x.blk_size *
HMAC_S390X_BUF_NUM_BLOCKS);
if (ctx->plat.s390x.buf == NULL)
return 0;
ctx->plat.s390x.size = ctx->plat.s390x.blk_size *
HMAC_S390X_BUF_NUM_BLOCKS;
}
ctx->plat.s390x.num = 0;
ctx->plat.s390x.ikp = 0;
ctx->plat.s390x.iimp = 1;
switch (ctx->plat.s390x.fc) {
case S390X_HMAC_SHA_224:
case S390X_HMAC_SHA_256:
ctx->plat.s390x.param.hmac_224_256.imbl = 0;
OPENSSL_cleanse(ctx->plat.s390x.param.hmac_224_256.h,
sizeof(ctx->plat.s390x.param.hmac_224_256.h));
break;
case S390X_HMAC_SHA_384:
case S390X_HMAC_SHA_512:
ctx->plat.s390x.param.hmac_384_512.imbl = 0;
OPENSSL_cleanse(ctx->plat.s390x.param.hmac_384_512.h,
sizeof(ctx->plat.s390x.param.hmac_384_512.h));
break;
default:
return 0;
}
if (key != NULL) {
switch (ctx->plat.s390x.fc) {
case S390X_HMAC_SHA_224:
case S390X_HMAC_SHA_256:
OPENSSL_cleanse(&ctx->plat.s390x.param.hmac_224_256.key,
sizeof(ctx->plat.s390x.param.hmac_224_256.key));
key_param = ctx->plat.s390x.param.hmac_224_256.key;
key_param_len = sizeof(ctx->plat.s390x.param.hmac_224_256.key);
break;
case S390X_HMAC_SHA_384:
case S390X_HMAC_SHA_512:
OPENSSL_cleanse(&ctx->plat.s390x.param.hmac_384_512.key,
sizeof(ctx->plat.s390x.param.hmac_384_512.key));
key_param = ctx->plat.s390x.param.hmac_384_512.key;
key_param_len = sizeof(ctx->plat.s390x.param.hmac_384_512.key);
break;
default:
return 0;
}
if (!ossl_assert(ctx->plat.s390x.blk_size <= (int)key_param_len))
return 0;
if (key_len > ctx->plat.s390x.blk_size) {
if (!EVP_DigestInit_ex(ctx->md_ctx, ctx->md, impl)
|| !EVP_DigestUpdate(ctx->md_ctx, key, key_len)
|| !EVP_DigestFinal_ex(ctx->md_ctx, key_param,
&key_param_len))
return 0;
} else {
if (key_len < 0 || key_len > (int)key_param_len)
return 0;
memcpy(key_param, key, key_len);
/* remaining key bytes already zeroed out above */
}
}
return 1;
}
int s390x_HMAC_update(HMAC_CTX *ctx, const unsigned char *data, size_t len)
{
size_t remain, num;
if (len == 0)
return 1;
/* buffer is full, process it now */
if (ctx->plat.s390x.num == ctx->plat.s390x.size) {
s390x_call_kmac(ctx, ctx->plat.s390x.buf, ctx->plat.s390x.num);
ctx->plat.s390x.num = 0;
}
remain = ctx->plat.s390x.size - ctx->plat.s390x.num;
if (len > remain) {
/* data does not fit into buffer */
if (ctx->plat.s390x.num > 0) {
/* first fill buffer and process it */
memcpy(&ctx->plat.s390x.buf[ctx->plat.s390x.num], data, remain);
ctx->plat.s390x.num += remain;
s390x_call_kmac(ctx, ctx->plat.s390x.buf, ctx->plat.s390x.num);
ctx->plat.s390x.num = 0;
data += remain;
len -= remain;
}
if (!ossl_assert(ctx->plat.s390x.num == 0))
return 0;
if (len > ctx->plat.s390x.size) {
/*
* remaining data is still larger than buffer, process remaining
* full blocks of input directly
*/
remain = len % ctx->plat.s390x.blk_size;
num = len - remain;
s390x_call_kmac(ctx, data, num);
data += num;
len -= num;
}
}
/* add remaining input data (which is < buffer size) to buffer */
if (!ossl_assert(len <= ctx->plat.s390x.size))
return 0;
if (len > 0) {
memcpy(&ctx->plat.s390x.buf[ctx->plat.s390x.num], data, len);
ctx->plat.s390x.num += len;
}
return 1;
}
int s390x_HMAC_final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len)
{
void *result;
unsigned int res_len;
ctx->plat.s390x.iimp = 0; /* last block */
s390x_call_kmac(ctx, ctx->plat.s390x.buf, ctx->plat.s390x.num);
ctx->plat.s390x.num = 0;
switch (ctx->plat.s390x.fc) {
case S390X_HMAC_SHA_224:
result = &ctx->plat.s390x.param.hmac_224_256.h[0];
res_len = SHA224_DIGEST_LENGTH;
break;
case S390X_HMAC_SHA_256:
result = &ctx->plat.s390x.param.hmac_224_256.h[0];
res_len = SHA256_DIGEST_LENGTH;
break;
case S390X_HMAC_SHA_384:
result = &ctx->plat.s390x.param.hmac_384_512.h[0];
res_len = SHA384_DIGEST_LENGTH;
break;
case S390X_HMAC_SHA_512:
result = &ctx->plat.s390x.param.hmac_384_512.h[0];
res_len = SHA512_DIGEST_LENGTH;
break;
default:
return 0;
}
memcpy(md, result, res_len);
if (len != NULL)
*len = res_len;
return 1;
}
int s390x_HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx)
{
dctx->plat.s390x.fc = sctx->plat.s390x.fc;
dctx->plat.s390x.blk_size = sctx->plat.s390x.blk_size;
dctx->plat.s390x.ikp = sctx->plat.s390x.ikp;
dctx->plat.s390x.iimp = sctx->plat.s390x.iimp;
memcpy(&dctx->plat.s390x.param, &sctx->plat.s390x.param,
sizeof(dctx->plat.s390x.param));
OPENSSL_clear_free(dctx->plat.s390x.buf, dctx->plat.s390x.size);
dctx->plat.s390x.buf = NULL;
if (sctx->plat.s390x.buf != NULL) {
dctx->plat.s390x.buf = OPENSSL_memdup(sctx->plat.s390x.buf,
sctx->plat.s390x.size);
if (dctx->plat.s390x.buf == NULL)
return 0;
}
dctx->plat.s390x.size = sctx->plat.s390x.size;
dctx->plat.s390x.num = sctx->plat.s390x.num;
return 1;
}
int s390x_HMAC_CTX_cleanup(HMAC_CTX *ctx)
{
OPENSSL_clear_free(ctx->plat.s390x.buf, ctx->plat.s390x.size);
ctx->plat.s390x.buf = NULL;
ctx->plat.s390x.size = 0;
ctx->plat.s390x.num = 0;
OPENSSL_cleanse(&ctx->plat.s390x.param, sizeof(ctx->plat.s390x.param));
ctx->plat.s390x.blk_size = 0;
ctx->plat.s390x.ikp = 0;
ctx->plat.s390x.iimp = 1;
ctx->plat.s390x.fc = 0;
return 1;
}
#endif