Add FIPS indicator to CMAC.

There is a issue currently related to CMAC TDES, when the new provider
is tested against older branches.

The new strict check caused backwards compatibility issues when
using old branch with the new FIPS provider.

To get around this CMAC now allows TDES by default, but it can be either
enabled via config or a settable. (i.e it uses an indicator)

Where the TDES cipher check can be done turned out to be problematic.
Shifting the check in the TDES cipherout of the init doesnt work because
ciphers can run thru either final or cipher (and checking on every
cipher call seemed bad). This means it needs to stay in the cipher init.
So the check needs to be done in CMAC BEFORE the underlying TDES cipher
does it check.
When using an indicator the TDES cipher needs its "encrypt-check" set
so that needs to be propagated from the CMAC object. This requires
the ability to set the param at the time the cipher ctx is inited.
An internal function was required in order to pass params to CMAC_Init.

Note also that the check was done where it is, because EVP_Q_mac() calls
EVP_MAC_CTX_set_params(ctx, cipher_param)
EVP_MAC_CTX_set_params(ctx, params)
EVP_MAC_init(ctx, key, keylen, params)
Where the second call to set_params would set up "encrypt-check" after
"cipher".

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25022)
This commit is contained in:
slontis 2024-07-29 17:47:46 +10:00 committed by Pauli
parent efba3f1351
commit 4f5febe2c6
6 changed files with 146 additions and 18 deletions

View File

@ -19,6 +19,7 @@
#include "internal/cryptlib.h"
#include <openssl/cmac.h>
#include <openssl/err.h>
#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;

View File

@ -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<OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK>) <integer>
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<unsigned int>.
=back
=over 4
=item "block-size" (B<OSSL_MAC_PARAM_BLOCK_SIZE>) <unsigned integer>
Gets the MAC block size. The "block-size" parameter can also be retrieved with
EVP_MAC_CTX_get_block_size().
=item "fips-indicator" (B<OSSL_CIPHER_PARAM_FIPS_APPROVED_INDICATOR>) <integer>
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

22
include/crypto/cmac.h Normal file
View File

@ -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 <openssl/types.h>
# include <openssl/cmac.h>
# include <openssl/params.h>
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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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