The following document refers to behaviour required by the OpenSSL FIPS provider, the changes should not affect the default provider.
The following information was extracted from the FIPS 140-3 IG [5] “2.4.C Approved Security Service Indicator”
Since any new FIPS restrictions added could possibly break existing applications the following additional OpenSSL requirements are also needed:
In OpenSSL most of the existing code in the FIPS provider is using implicit indicators i.e. An error occurs if existing FIPS rules are violated.
The following rules will apply to any code that currently is not FIPS approved, but needs to be.
The fipsinstall application will have a configurable item added for each algorithm that requires a change. These options will be passed to the FIPS provider in a manner similar to existing code.
A user defined callback similar to OSSL_SELF_TEST will be added. This callback will be triggered whenever an approved mode test fails.
It may be set up by the user using
typedef int (OSSL_INDICATOR_CALLBACK)(const char *type, const char *desc, const OSSL_PARAM params[]); void OSSL_INDICATOR_set_callback(OSSL_LIB_CTX *libctx, OSSL_INDICATOR_CALLBACK *cb);
The callback can be changed at any time.
void OSSL_INDICATOR_get_callback(OSSL_LIB_CTX *libctx, OSSL_INDICATOR_CALLBACK **cb);
An application's indicator OSSL_INDICATOR_CALLBACK can be used to log that an indicator was triggered. The callback should normally return non zero. Returning 0 allows the algorithm to fail, in the same way that a strict check would fail.
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FIPS_KEY_CHECK); if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->key_check)) return 0;
The setter is initially -1 (unknown) and can be set to 0 or 1 via a set_ctx call. If the setter is needed it must be set BEFORE the FIPS related check is done.
If the FIPS related approved mode check fails and either the ctx setter is zero OR the related FIPS configuration option is zero then the callback is triggered. If neither the setter of config option are zero then the algorithm should fail. If the callback is triggered a flag is set in the algorithm ctx that indicates that this algorithm is unapproved. Once the context is unapproved it will remain in this state.
p = OSSL_PARAM_locate(params, OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR); if (p != NULL && !OSSL_PARAM_set_int(p, ctx->approved)) return 0;
This initially has a value of 1, and may be set to 0 if the algorithm is unapproved. The getter allows you to access the indicator value after the operation has completed (e.g. Final or Derive related functions)
void alg_init(ALG_CTX *ctx) { ctx->strict_checks = -1; ctx->approved = 1; } void alg_set_params(ALG_CTX *ctx, OSSL_PARAMS params[]) { p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_STRICT_CHECKS); if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->strict_checks)) return 0; } int alg_check_approved(ALG_CTX *ctx) { int approved; approved = some_fips_test_passes(ctx->libctx); // Check FIPS restriction for alg if (!approved) { ctx->approved = 0; if (ctx->strict_checks == 0 || fips_config_get(ctx->libctx, op) == 0) { if (!indicator_cb(ctx->libctx, "ALG NAME", "ALG DESC")) return 0; } } return 1; }
OpenSSL already uses FIPS configuration options to perform security_checks, but the existing code needs to change to work with indicators.
e.g. existing code
if (ossl_securitycheck_enabled(ctx)) { pass = do_some_alg_test(ctx); if (!pass) return 0; /* Produce an error */ }
In updated code for indicators the test always runs.. i.e.
pass = do_some_alg_test(ctx); // Do code similar to alg_check_approved() above // which will conditionally decide whether to return an error // or trigger the indicator callback.
Normally a user would set params (such as OSSL_PKEY_PARAM_FIPS_KEY_CHECK) using set_ctx_params(), but some algorithms currently do checks in their init operation. These init functions normally pass an OSSL_PARAM[] argument, but this still requires the user to set the ctx params in their init.
e.g.
int strict = 0; params[0] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_FIPS_KEY_CHECK, strict); EVP_DigestSignInit_ex(ctx, &pctx, name, libctx, NULL, pkey, params); // using EVP_PKEY_CTX_set_params() here would be too late
Delaying the check to after the init would be possible, but it would be a change in existing behaviour. For example the keysize checks are done in the init since this is when the key is setup.
There was discussion related to also having a global config setting that could turn off FIPS mode. This will not be added at this stage.
A generic object is used internally to embed the variables required for indicators into each algorithm ctx. The object contains the ctx settables and the approved flag.
typedef struct ossl_fips_ind_st { unsigned char approved; signed char settable[OSSL_FIPS_IND_SETTABLE_MAX]; } OSSL_FIPS_IND;
Since there are a lot of algorithms where indicators are needed it was decided to use MACROS to simplify the process. The following macros are only defined for the FIPS provider.
OSSL_FIPS_IND should be placed into the algorithm objects struct that is returned by a new()
This is used to copy the OSSL_FIPS_IND when calling a dup(). If the dup() uses *dst = *src then it is not required.
Initializes the OSSL_FIPS_IND object. It should be called in the new().
This triggers the callback, id is the settable index and must also be used by OSSL_FIPS_IND_SET_CTX_PARAM(), algname and opname are strings that are passed to the indicator callback, configopt_fn is the FIPS configuration option. Where this is triggered depends on the algorithm. In most cases this can be done in the set_ctx().
This must be put into the algorithms settable ctx_params table. The name is the settable ‘key’ name such as OSSL_PKEY_PARAM_FIPS_KEY_CHECK. There should be a matching name used by OSSL_FIPS_IND_SET_CTX_PARAM(). There may be multiple of these.
This should be put at the top of the algorithms set_ctx_params(). There may be multiple of these. The name should match an OSSL_FIPS_IND_SETTABLE_CTX_PARAM() entry. The id should match an OSSL_FIPS_IND_ON_UNAPPROVED() entry.
This must be placed in the algorithms gettable_ctx_params table
This must be placed in the algorithms get_ctx_params(),
Some existing algorithms will require set_ctx_params()/settable_ctx_params() and/or get_ctx_params()/gettable_ctx_params() to be added if required.
The following changes are required for FIPS 140-3 and will require indicators. On a cases by case basis we must decide what to do when unapproved mode is detected. The mechanism using FIPS configuration options and the indicator callback should be used for most of these unapproved cases (rather than always returning an error).
There are a few places where we do not enforce key size that need to be addressed.
Any algorithms that use a digest need to make sure that the CAVP certificate lists all supported FIPS digests otherwise an indicator is required. This applies to the following algorithms:
The FIPS 140-3 IG Section C.B & C.C have notes related to Vendor affirmation.
Note many of these (such as KDF's will not support SHAKE). See https://gitlab.com/redhat/centos-stream/rpms/openssl/-/blob/c9s/0078-KDF-Add-FIPS-indicators.patch?ref_type=heads ECDSA and RSA-PSS Signatures allow use of SHAKE.
KECCAK-KMAC-128 and KECCAK-KMAC-256 should not be allowed for anything other than KMAC. Do we need to check which algorithms allow SHA1 also?
Test that Deterministic ECDSA does not allow SHAKE (IG C.K Additional Comments 6)
We should only allow AES. We currently just check the mode.