From: slontis Date: Tue, 24 Feb 2026 03:29:26 +0000 (+1100) Subject: FIPS: Add HMAC key size compliance check to the MAC legacy bridge. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=87b2a6a7ffba1da96745a7a73db3883efec8fdc1;p=thirdparty%2Fopenssl.git FIPS: Add HMAC key size compliance check to the MAC legacy bridge. The hmac fips provider implementation used by the EVP_MAC API handles key size checks, but it only does the test for the internal case. Previously HMAC was implemented using EVP_DigestSign related functions, and these are implemented using a mac_legacy_sig bridge, because of this the MAC is external. For external cases the caller is responsible for doing any key checks, so a FIPS indicator has been added. Reported-by: https://github.com/taha2samy Fixes: #30012 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Paul Dale MergeDate: Tue Apr 28 07:13:24 2026 (Merged from https://github.com/openssl/openssl/pull/30150) --- diff --git a/.gitignore b/.gitignore index a7d7b19e30d..53e3e7ad4ae 100644 --- a/.gitignore +++ b/.gitignore @@ -139,6 +139,7 @@ providers/implementations/keymgmt/mlx_kmgmt.inc providers/implementations/keymgmt/slh_dsa_kmgmt.inc providers/implementations/keymgmt/template_kmgmt.inc providers/implementations/signature/eddsa_sig.inc +providers/implementations/signature/mac_legacy_sig.inc providers/implementations/signature/ml_dsa_sig.inc providers/implementations/signature/rsa_sig.inc providers/implementations/signature/slh_dsa_sig.inc diff --git a/build.info b/build.info index de909255e18..d7982a95536 100644 --- a/build.info +++ b/build.info @@ -92,6 +92,7 @@ DEPEND[]=include/openssl/asn1.h \ providers/implementations/signature/dsa_sig.inc \ providers/implementations/signature/ecdsa_sig.inc \ providers/implementations/signature/eddsa_sig.inc \ + providers/implementations/signature/mac_legacy_sig.inc \ providers/implementations/signature/ml_dsa_sig.inc \ providers/implementations/signature/rsa_sig.inc \ providers/implementations/signature/slh_dsa_sig.inc \ @@ -216,6 +217,7 @@ DEPEND[providers/implementations/asymciphers/rsa_enc.inc \ providers/implementations/signature/dsa_sig.inc \ providers/implementations/signature/ecdsa_sig.inc \ providers/implementations/signature/eddsa_sig.inc \ + providers/implementations/signature/mac_legacy_sig.inc \ providers/implementations/signature/ml_dsa_sig.inc \ providers/implementations/signature/rsa_sig.inc \ providers/implementations/signature/slh_dsa_sig.inc \ @@ -356,6 +358,8 @@ GENERATE[providers/implementations/signature/ecdsa_sig.inc]=\ providers/implementations/signature/ecdsa_sig.inc.in GENERATE[providers/implementations/signature/eddsa_sig.inc]=\ providers/implementations/signature/eddsa_sig.inc.in +GENERATE[providers/implementations/signature/mac_legacy_sig.inc]=\ + providers/implementations/signature/mac_legacy_sig.inc.in GENERATE[providers/implementations/signature/ml_dsa_sig.inc]=\ providers/implementations/signature/ml_dsa_sig.inc.in GENERATE[providers/implementations/signature/rsa_sig.inc]=\ diff --git a/providers/implementations/signature/mac_legacy_sig.c b/providers/implementations/signature/mac_legacy_sig.c index 2630715089c..cf2eff267d6 100644 --- a/providers/implementations/signature/mac_legacy_sig.c +++ b/providers/implementations/signature/mac_legacy_sig.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -18,6 +19,15 @@ #include "prov/provider_ctx.h" #include "prov/macsignature.h" #include "prov/providercommon.h" +#include "prov/securitycheck.h" +#include "internal/fips.h" +#include "internal/common.h" + +#ifndef FIPS_MODULE +#define mac_legacy_get_ctx_params_decoder +#define mac_legacy_set_ctx_params_decoder +#endif +#include "providers/implementations/signature/mac_legacy_sig.inc" static OSSL_FUNC_signature_newctx_fn mac_hmac_newctx; static OSSL_FUNC_signature_newctx_fn mac_siphash_newctx; @@ -39,6 +49,10 @@ typedef struct { char *propq; MAC_KEY *key; EVP_MAC_CTX *macctx; +#ifdef FIPS_MODULE + bool hmac_keysize_check; + OSSL_FIPS_IND_DECLARE +#endif } PROV_MAC_CTX; static void *mac_newctx(void *provctx, const char *propq, const char *macname) @@ -66,7 +80,11 @@ static void *mac_newctx(void *provctx, const char *propq, const char *macname) goto err; EVP_MAC_free(mac); - +#ifdef FIPS_MODULE + pmacctx->hmac_keysize_check = (strcmp(macname, "HMAC") == 0); + /* Set FIPS indicator to approved */ + OSSL_FIPS_IND_INIT(pmacctx) +#endif return pmacctx; err: @@ -87,6 +105,27 @@ MAC_NEWCTX(siphash, "SIPHASH") MAC_NEWCTX(poly1305, "POLY1305") MAC_NEWCTX(cmac, "CMAC") +#ifdef FIPS_MODULE +/* + * The fips indicator check is done at this level because HMAC will be created + * as an 'internal' sub-algorithm which will not perform the tests in hmac_prov.c + */ +static int hmac_check_key(PROV_MAC_CTX *macctx, const unsigned char *key, size_t keylen) +{ + int approved = ossl_mac_check_key_size(keylen); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0, + macctx->libctx, "HMAC", "keysize", + FIPS_CONFIG_HMAC_KEY_CHECK)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + static int mac_digest_sign_init(void *vpmacctx, const char *mdname, void *vkey, const OSSL_PARAM params[]) { @@ -118,6 +157,11 @@ static int mac_digest_sign_init(void *vpmacctx, const char *mdname, void *vkey, pmacctx->key->properties, params)) return 0; +#ifdef FIPS_MODULE + if (pmacctx->hmac_keysize_check + && !hmac_check_key(pmacctx, pmacctx->key->priv_key, pmacctx->key->priv_key_len)) + return 0; +#endif if (!EVP_MAC_init(pmacctx->macctx, pmacctx->key->priv_key, pmacctx->key->priv_key_len, NULL)) return 0; @@ -197,6 +241,22 @@ static int mac_set_ctx_params(void *vpmacctx, const OSSL_PARAM params[]) { PROV_MAC_CTX *ctx = (PROV_MAC_CTX *)vpmacctx; +#ifdef FIPS_MODULE + if (ctx->hmac_keysize_check) { + struct mac_legacy_set_ctx_params_st p; + + if (!mac_legacy_set_ctx_params_decoder(params, &p)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_FROM_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, p.ind_k)) + return 0; + if (p.key != NULL) { + if (p.key->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + if (!hmac_check_key(ctx, p.key->data, p.key->data_size)) + return 0; + } + } +#endif return EVP_MAC_CTX_set_params(ctx->macctx, params); } @@ -217,6 +277,33 @@ static const OSSL_PARAM *mac_settable_ctx_params(ossl_unused void *ctx, return params; } +static const OSSL_PARAM *mac_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return mac_legacy_get_ctx_params_list; +} + +static int mac_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_MAC_CTX *ctx = vctx; + + if (ctx == NULL) + return 0; + +#ifdef FIPS_MODULE + struct mac_legacy_get_ctx_params_st p; + + if (!mac_legacy_get_ctx_params_decoder(params, &p)) + return 0; + if (p.ind != NULL) { + int approved = OSSL_FIPS_IND_GET(ctx)->approved; + if (!OSSL_PARAM_set_int(p.ind, approved)) + return 0; + } +#endif + return 1; +} + #define MAC_SETTABLE_CTX_PARAMS(funcname, macname) \ static const OSSL_PARAM *mac_##funcname##_settable_ctx_params(void *ctx, \ void *provctx) \ @@ -244,6 +331,10 @@ MAC_SETTABLE_CTX_PARAMS(cmac, "CMAC") (void (*)(void))mac_set_ctx_params }, \ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ (void (*)(void))mac_##funcname##_settable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))mac_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))mac_gettable_ctx_params }, \ OSSL_DISPATCH_END \ }; diff --git a/providers/implementations/signature/mac_legacy_sig.inc.in b/providers/implementations/signature/mac_legacy_sig.inc.in new file mode 100644 index 00000000000..36f98b23db2 --- /dev/null +++ b/providers/implementations/signature/mac_legacy_sig.inc.in @@ -0,0 +1,21 @@ +/* + * Copyright 2026 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 + */ + +{- +use OpenSSL::paramnames qw(produce_param_decoder); +-} + +{- produce_param_decoder('mac_legacy_get_ctx_params', + (['OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR', 'ind', 'int', 'fips'], + )); -} + +{- produce_param_decoder('mac_legacy_set_ctx_params', + (['OSSL_MAC_PARAM_KEY', 'key', 'octet_string'], + ['OSSL_MAC_PARAM_FIPS_KEY_CHECK', 'ind_k', 'int', 'fips'], + )); -} diff --git a/test/recipes/20-test_mac.t b/test/recipes/20-test_mac.t index 35a4904188c..84a88990491 100644 --- a/test/recipes/20-test_mac.t +++ b/test/recipes/20-test_mac.t @@ -10,7 +10,7 @@ use strict; use warnings; -use OpenSSL::Test qw(:DEFAULT data_file); +use OpenSSL::Test qw(:DEFAULT data_file srctop_file); use OpenSSL::Test::Utils; use Storable qw(dclone); @@ -138,8 +138,9 @@ my @siphash_fail_tests = ( push @mac_fail_tests, @siphash_fail_tests unless disabled("siphash"); -plan tests => (scalar @mac_tests * 2) + scalar @mac_fail_tests; +plan tests => (scalar @mac_tests * 2) + (scalar @mac_fail_tests) + 2; +my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); my $test_count = 0; foreach (@mac_tests) { @@ -174,6 +175,28 @@ foreach (@mac_fail_tests) { ok(compareline($_->{cmd}, $_->{type}, $_->{input}, $_->{expected}, $_->{err}), $_->{desc}); } +SKIP: { + skip "Skipping FIPS tests", 2 + if $no_fips; + + my $fipsconf = srctop_file("test", "fips-and-base.cnf"); + + # This is only valid after OpenSSL 4.1 + run(test(["fips_version_test", "-config", $fipsconf, ">=4.1.0"]), + capture => 1, statusvar => \my $exit); + skip "FIPS provider version is too old for this test", 2 + if !$exit; + + $ENV{OPENSSL_CONF} = $fipsconf; + ok(!run(app(['openssl', 'dgst', '-provider', 'fips', '-sha256', '-hmac', + '1234', srctop_file("test", "testec-p112r1.pem")])), + "Checking bad key size fails in FIPS provider"); + ok(run(app(['openssl', 'dgst', '-provider', 'fips', '-sha256', '-hmac', + '123456789ABCDE', srctop_file("test", "testec-p112r1.pem")])), + "Checking good key size passes in FIPS provider"); + delete $ENV{OPENSSL_CONF}; +} + # Create a temp input file and save the input data into it, and # then compare the stdout output matches the expected value. sub compareline {