diff --git a/crypto/cmac/cmac.c b/crypto/cmac/cmac.c index 2012774f8d..16f4b904ad 100644 --- a/crypto/cmac/cmac.c +++ b/crypto/cmac/cmac.c @@ -19,6 +19,7 @@ #include "internal/cryptlib.h" #include #include +#include "crypto/cmac.h" #define LOCAL_BUF_SIZE 2048 struct CMAC_CTX_st { @@ -107,8 +108,9 @@ int CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in) return 1; } -int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, - const EVP_CIPHER *cipher, ENGINE *impl) +int ossl_cmac_init(CMAC_CTX *ctx, const void *key, size_t keylen, + const EVP_CIPHER *cipher, ENGINE *impl, + const OSSL_PARAM param[]) { static const unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH] = { 0 }; int block_len; @@ -118,7 +120,7 @@ int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, /* Not initialised */ if (ctx->nlast_block == -1) return 0; - if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv)) + if (!EVP_EncryptInit_ex2(ctx->cctx, NULL, NULL, zero_iv, param)) return 0; block_len = EVP_CIPHER_CTX_get_block_size(ctx->cctx); if (block_len == 0) @@ -131,8 +133,13 @@ int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, if (cipher != NULL) { /* Ensure we can't use this ctx until we also have a key */ ctx->nlast_block = -1; - if (!EVP_EncryptInit_ex(ctx->cctx, cipher, impl, NULL, NULL)) - return 0; + if (impl != NULL) { + if (!EVP_EncryptInit_ex(ctx->cctx, cipher, impl, NULL, NULL)) + return 0; + } else { + if (!EVP_EncryptInit_ex2(ctx->cctx, cipher, NULL, NULL, param)) + return 0; + } } /* Non-NULL key means initialisation complete */ if (key != NULL) { @@ -144,7 +151,7 @@ int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, return 0; if (EVP_CIPHER_CTX_set_key_length(ctx->cctx, keylen) <= 0) return 0; - if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, key, zero_iv)) + if (!EVP_EncryptInit_ex2(ctx->cctx, NULL, key, zero_iv, param)) return 0; if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) < 0) return 0; @@ -154,7 +161,7 @@ int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, make_kn(ctx->k2, ctx->k1, bl); OPENSSL_cleanse(ctx->tbl, bl); /* Reset context again ready for first data block */ - if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv)) + if (!EVP_EncryptInit_ex2(ctx->cctx, NULL, NULL, zero_iv, param)) return 0; /* Zero tbl so resume works */ memset(ctx->tbl, 0, bl); @@ -163,6 +170,12 @@ int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, return 1; } +int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, + const EVP_CIPHER *cipher, ENGINE *impl) +{ + return ossl_cmac_init(ctx, key, keylen, cipher, impl, NULL); +} + int CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen) { const unsigned char *data = in; diff --git a/doc/man7/EVP_MAC-CMAC.pod b/doc/man7/EVP_MAC-CMAC.pod index 44c5fc7c44..0610d050f4 100644 --- a/doc/man7/EVP_MAC-CMAC.pod +++ b/doc/man7/EVP_MAC-CMAC.pod @@ -47,6 +47,17 @@ Sets the properties to be queried when trying to fetch the underlying cipher. This must be given together with the cipher naming parameter to be considered valid. +=item "encrypt-check" (B) + +If required this parameter should be set before EVP_MAC_init() + +The default value of 1 causes an error when a unapproved Triple-DES encryption +operation is triggered. +Setting this to 0 will ignore the error and set the approved "fips-indicator" to +0. +This option is used by the OpenSSL FIPS provider, and breaks FIPS compliance if +set to 0. + =back The following parameters can be retrieved with @@ -59,15 +70,18 @@ EVP_MAC_CTX_get_params(): The "size" parameter can also be retrieved with with EVP_MAC_CTX_get_mac_size(). The length of the "size" parameter is equal to that of an B. -=back - -=over 4 =item "block-size" (B) Gets the MAC block size. The "block-size" parameter can also be retrieved with EVP_MAC_CTX_get_block_size(). +=item "fips-indicator" (B) + +A getter that returns 1 if the operation is FIPS approved, or 0 otherwise. +This may be used after calling EVP_MAC_final(). +It may return 0 if the "encrypt-check" option is set to 0. + =back =head1 SEE ALSO diff --git a/include/crypto/cmac.h b/include/crypto/cmac.h new file mode 100644 index 0000000000..df55b68f8b --- /dev/null +++ b/include/crypto/cmac.h @@ -0,0 +1,22 @@ +/* + * 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 + */ + +#ifndef OSSL_CRYPTO_CMAC_H +# define OSSL_CRYPTO_CMAC_H +# pragma once + +# include +# include +# include + +int ossl_cmac_init(CMAC_CTX *ctx, const void *key, size_t keylen, + const EVP_CIPHER *cipher, ENGINE *impl, + const OSSL_PARAM param[]); + +#endif /* OSSL_CRYPTO_CMAC_H */ diff --git a/providers/common/include/prov/fipsindicator.h b/providers/common/include/prov/fipsindicator.h index a1f4f55e6e..9bc068ad51 100644 --- a/providers/common/include/prov/fipsindicator.h +++ b/providers/common/include/prov/fipsindicator.h @@ -114,7 +114,12 @@ void ossl_FIPS_IND_copy(OSSL_FIPS_IND *dst, const OSSL_FIPS_IND *src); # define OSSL_FIPS_IND_GET_CTX_PARAM(ctx, prms) \ ossl_FIPS_IND_get_ctx_param(&((ctx)->indicator), prms) -#define OSSL_FIPS_IND_GET(ctx) &((ctx)->indicator) +# define OSSL_FIPS_IND_GET(ctx) &((ctx)->indicator) + +# define OSSL_FIPS_IND_GET_PARAM(ctx, p, settable, id, name) \ + *settable = ossl_FIPS_IND_get_settable(&((ctx)->indicator), id); \ + if (*settable != OSSL_FIPS_IND_STATE_UNKNOWN) \ + *p = OSSL_PARAM_construct_int(name, settable); \ int ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND *ind, int id, OSSL_LIB_CTX *libctx, const RSA *rsa, const char *desc, int protect); diff --git a/providers/implementations/macs/cmac_prov.c b/providers/implementations/macs/cmac_prov.c index ec7be448eb..39ab09172b 100644 --- a/providers/implementations/macs/cmac_prov.c +++ b/providers/implementations/macs/cmac_prov.c @@ -25,6 +25,9 @@ #include "prov/provider_ctx.h" #include "prov/provider_util.h" #include "prov/providercommon.h" +#include "prov/fipscommon.h" +#include "prov/fipsindicator.h" +#include "crypto/cmac.h" /* * Forward declaration of everything implemented here. This is not strictly @@ -48,6 +51,7 @@ struct cmac_data_st { void *provctx; CMAC_CTX *ctx; PROV_CIPHER cipher; + OSSL_FIPS_IND_DECLARE }; static void *cmac_new(void *provctx) @@ -63,6 +67,7 @@ static void *cmac_new(void *provctx) macctx = NULL; } else { macctx->provctx = provctx; + OSSL_FIPS_IND_INIT(macctx) } return macctx; @@ -95,6 +100,7 @@ static void *cmac_dup(void *vsrc) cmac_free(dst); return NULL; } + OSSL_FIPS_IND_COPY(dst, src) return dst; } @@ -109,12 +115,55 @@ static size_t cmac_size(void *vmacctx) return EVP_CIPHER_CTX_get_block_size(cipherctx); } +#ifdef FIPS_MODULE +/* + * TDES Encryption is not approved in FIPS 140-3. + * + * In strict approved mode we just fail here (by returning 0). + * If we are going to bypass it using a FIPS indicator then we need to pass that + * information down to the cipher also. + * This function returns the param to pass down in 'p'. + * state will return OSSL_FIPS_IND_STATE_UNKNOWN if the param has not been set. + * + * The name 'OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK' used below matches the + * key name used by the Triple-DES. + */ +static int tdes_check_param(struct cmac_data_st *macctx, OSSL_PARAM *p, + int *state) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(macctx->provctx); + const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher); + + *state = OSSL_FIPS_IND_STATE_UNKNOWN; + if (EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "CMAC", "Triple-DES", + FIPS_tdes_encrypt_check)) + return 0; + OSSL_FIPS_IND_GET_PARAM(macctx, p, state, OSSL_FIPS_IND_SETTABLE0, + OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK) + } + return 1; +} +#endif + static int cmac_setkey(struct cmac_data_st *macctx, const unsigned char *key, size_t keylen) { - int rv = CMAC_Init(macctx->ctx, key, keylen, - ossl_prov_cipher_cipher(&macctx->cipher), - ossl_prov_cipher_engine(&macctx->cipher)); + int rv; + OSSL_PARAM *p = NULL; +#ifdef FIPS_MODULE + int state = OSSL_FIPS_IND_STATE_UNKNOWN; + OSSL_PARAM prms[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + if (!tdes_check_param(macctx, &prms[0], &state)) + return 0; + if (state != OSSL_FIPS_IND_STATE_UNKNOWN) + p = prms; +#endif + rv = ossl_cmac_init(macctx->ctx, key, keylen, + ossl_prov_cipher_cipher(&macctx->cipher), + ossl_prov_cipher_engine(&macctx->cipher), p); ossl_prov_cipher_reset(&macctx->cipher); return rv; } @@ -154,6 +203,7 @@ static int cmac_final(void *vmacctx, unsigned char *out, size_t *outl, static const OSSL_PARAM known_gettable_ctx_params[] = { OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() OSSL_PARAM_END }; static const OSSL_PARAM *cmac_gettable_ctx_params(ossl_unused void *ctx, @@ -174,6 +224,8 @@ static int cmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) && !OSSL_PARAM_set_size_t(p, cmac_size(vmacctx))) return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM((struct cmac_data_st *)vmacctx, params)) + return 0; return 1; } @@ -181,6 +233,7 @@ static const OSSL_PARAM known_settable_ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_CIPHER, NULL, 0), OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0), OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK) OSSL_PARAM_END }; static const OSSL_PARAM *cmac_settable_ctx_params(ossl_unused void *ctx, @@ -201,6 +254,11 @@ static int cmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) if (params == NULL) return 1; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(macctx, + OSSL_FIPS_IND_SETTABLE0, params, + OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CIPHER)) != NULL) { if (!ossl_prov_cipher_load_from_params(&macctx->cipher, params, ctx)) return 0; @@ -213,11 +271,11 @@ static int cmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) #ifdef FIPS_MODULE { const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher); - int approved = EVP_CIPHER_is_a(cipher, "AES-256-CBC") - || EVP_CIPHER_is_a(cipher, "AES-192-CBC") - || EVP_CIPHER_is_a(cipher, "AES-128-CBC"); - if (!approved) { + if (!EVP_CIPHER_is_a(cipher, "AES-256-CBC") + && !EVP_CIPHER_is_a(cipher, "AES-192-CBC") + && !EVP_CIPHER_is_a(cipher, "AES-128-CBC") + && !EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) { ERR_raise(ERR_LIB_PROV, EVP_R_UNSUPPORTED_CIPHER); return 0; } diff --git a/test/recipes/30-test_evp_data/evpmac_cmac_des.txt b/test/recipes/30-test_evp_data/evpmac_cmac_des.txt index d23abe1f97..14169c45b4 100644 --- a/test/recipes/30-test_evp_data/evpmac_cmac_des.txt +++ b/test/recipes/30-test_evp_data/evpmac_cmac_des.txt @@ -27,3 +27,19 @@ Algorithm = DES-EDE3-CBC Key = 89BCD952A8C8AB371AF48AC7D07085D5EFF702E6D62CDC23 Input = FA620C1BBE97319E9A0CF0492121F7A20EB08A6A709DCBD00AAF38E4F99E754E Output = 8F49A1B7D6AA2258 + +FIPSversion = >=3.4.0 +MAC = CMAC +Algorithm = DES-EDE3-CBC +Key = 89BCD952A8C8AB371AF48AC7D07085D5EFF702E6D62CDC23 +Input = FA620C1BBE97319E9A0CF0492121F7A20EB08A6A709DCBD00AAF38E4F99E754E +Result = MAC_INIT_ERROR + +FIPSversion = >=3.4.0 +MAC = CMAC +Unapproved = 1 +Ctrl = encrypt-check:0 +Algorithm = DES-EDE3-CBC +Key = 89BCD952A8C8AB371AF48AC7D07085D5EFF702E6D62CDC23 +Input = FA620C1BBE97319E9A0CF0492121F7A20EB08A6A709DCBD00AAF38E4F99E754E +Output = 8F49A1B7D6AA2258