diff options
author | Daiki Ueno <ueno@gnu.org> | 2022-01-08 10:04:40 +0000 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2022-01-08 10:04:40 +0000 |
commit | 451002bd7db940038fd3a82308fbb5a8f0f76634 (patch) | |
tree | 90d161365301fd01bcde9477320b2d0099f2eb6b | |
parent | 527f2f799fa18f80941470ce89706c33e671b5ef (diff) | |
parent | db74acb11bc807d108425a16146182e93d6a73fe (diff) | |
download | gnutls-451002bd7db940038fd3a82308fbb5a8f0f76634.tar.gz |
Merge branch 'wip/dueno/fipscontext' into 'master'
fips: add functions to inspect thread-local FIPS operation state
See merge request gnutls/gnutls!1465
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | devel/libgnutls.abignore | 18 | ||||
-rw-r--r-- | devel/symbols.last | 5 | ||||
-rw-r--r-- | doc/Makefile.am | 11 | ||||
-rw-r--r-- | doc/cha-internals.texi | 24 | ||||
-rw-r--r-- | doc/manpages/Makefile.am | 5 | ||||
-rw-r--r-- | lib/cipher_int.c | 2 | ||||
-rw-r--r-- | lib/crypto-api.c | 473 | ||||
-rw-r--r-- | lib/fips.c | 182 | ||||
-rw-r--r-- | lib/fips.h | 175 | ||||
-rw-r--r-- | lib/hash_int.c | 4 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 39 | ||||
-rw-r--r-- | lib/libgnutls.map | 5 | ||||
-rw-r--r-- | lib/nettle/pk.c | 654 | ||||
-rw-r--r-- | lib/pk.c | 12 | ||||
-rw-r--r-- | lib/pubkey.c | 13 | ||||
-rw-r--r-- | tests/dh-compute.c | 72 | ||||
-rw-r--r-- | tests/fips-test.c | 232 | ||||
-rw-r--r-- | tests/kdf-api.c | 38 | ||||
-rw-r--r-- | tests/privkey-keygen.c | 63 | ||||
-rw-r--r-- | tests/slow/cipher-api-test.c | 5 |
21 files changed, 1683 insertions, 355 deletions
@@ -32,6 +32,12 @@ gnutls_sign_set_secure: Added. gnutls_sign_set_secure_for_certs: Added. gnutls_digest_set_secure: Added. gnutls_protocol_set_enabled: Added. +gnutls_fips140_context_init: New function +gnutls_fips140_context_deinit: New function +gnutls_fips140_push_context: New function +gnutls_fips140_pop_context: New function +gnutls_fips140_get_operation_state: New function +gnutls_fips140_operation_state_t: New enum * Version 3.7.2 (released 2021-05-29) diff --git a/devel/libgnutls.abignore b/devel/libgnutls.abignore index 1589707eeb..afa9168b79 100644 --- a/devel/libgnutls.abignore +++ b/devel/libgnutls.abignore @@ -105,3 +105,21 @@ name = gnutls_x509_ct_sct_get [suppress_function] name = gnutls_transport_is_ktls_enabled + +[suppress_function] +name = gnutls_fips140_context_init + +[suppress_function] +name = gnutls_fips140_context_deinit + +[suppress_function] +name = gnutls_fips140_push_context + +[suppress_function] +name = gnutls_fips140_pop_context + +[suppress_function] +name = gnutls_fips140_get_operation_state + +[suppress_type] +name = gnutls_fips140_operation_state_t diff --git a/devel/symbols.last b/devel/symbols.last index 9142d5a3ed..d10b12d3ed 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -250,7 +250,12 @@ gnutls_ffdhe_8192_group_prime@GNUTLS_3_4 gnutls_ffdhe_8192_group_q@GNUTLS_3_6_8 gnutls_ffdhe_8192_key_bits@GNUTLS_3_4 gnutls_fingerprint@GNUTLS_3_4 +gnutls_fips140_context_deinit@GNUTLS_3_7_3 +gnutls_fips140_context_init@GNUTLS_3_7_3 +gnutls_fips140_get_operation_state@GNUTLS_3_7_3 gnutls_fips140_mode_enabled@GNUTLS_3_4 +gnutls_fips140_pop_context@GNUTLS_3_7_3 +gnutls_fips140_push_context@GNUTLS_3_7_3 gnutls_fips140_set_mode@GNUTLS_3_6_3 gnutls_free@GNUTLS_3_4 gnutls_get_system_config_file@GNUTLS_3_6_9 diff --git a/doc/Makefile.am b/doc/Makefile.am index 3c2d5e68d3..ab414add8b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -550,6 +550,7 @@ ENUMS += enums/gnutls_digest_algorithm_t ENUMS += enums/gnutls_ecc_curve_t ENUMS += enums/gnutls_ext_flags_t ENUMS += enums/gnutls_ext_parse_type_t +ENUMS += enums/gnutls_fips140_operation_state_t ENUMS += enums/gnutls_fips_mode_t ENUMS += enums/gnutls_gost_paramset_t ENUMS += enums/gnutls_group_t @@ -1042,8 +1043,18 @@ FUNCS += functions/gnutls_ext_set_data FUNCS += functions/gnutls_ext_set_data.short FUNCS += functions/gnutls_fingerprint FUNCS += functions/gnutls_fingerprint.short +FUNCS += functions/gnutls_fips140_context_deinit +FUNCS += functions/gnutls_fips140_context_deinit.short +FUNCS += functions/gnutls_fips140_context_init +FUNCS += functions/gnutls_fips140_context_init.short +FUNCS += functions/gnutls_fips140_get_operation_state +FUNCS += functions/gnutls_fips140_get_operation_state.short FUNCS += functions/gnutls_fips140_mode_enabled FUNCS += functions/gnutls_fips140_mode_enabled.short +FUNCS += functions/gnutls_fips140_pop_context +FUNCS += functions/gnutls_fips140_pop_context.short +FUNCS += functions/gnutls_fips140_push_context +FUNCS += functions/gnutls_fips140_push_context.short FUNCS += functions/gnutls_fips140_set_mode FUNCS += functions/gnutls_fips140_set_mode.short FUNCS += functions/gnutls_get_system_config_file diff --git a/doc/cha-internals.texi b/doc/cha-internals.texi index f188caecc9..9dd1d8b6cc 100644 --- a/doc/cha-internals.texi +++ b/doc/cha-internals.texi @@ -754,3 +754,27 @@ Applications could also switch FIPS140-2 mode explicitly off, by calling @example gnutls_fips140_set_mode(GNUTLS_FIPS140_LAX, 0); @end example + +@subheading Service indicator + +The above restrictions may not cover all the requirements in every +usage context, and as the FIPS140 standard evolves (like FIPS140-3), +GnuTLS may not be able to add new restrictions without breaking +compatibility. + +Therefore an additional set of API functions is provided to +communicate with the user whether any approved mode of operations is +performed within a given context. + +@showfuncD{gnutls_fips140_context_init,gnutls_fips140_context_deinit,gnutls_fips140_push_context,gnutls_fips140_pop_context} + +The @code{gnutls_fips140_context_t} represents the FIPS140-2 mode of +operation. It can be attached to the current execution thread with +@funcref{gnutls_fips140_push_context} and its internal state will be +updated until it is detached with +@funcref{gnutls_fips140_pop_context}. Afterwards +@funcref{gnutls_fips140_get_operation_state} allows the user +to examine whether any approved (or non-approved) security function is +invoked. + +@showfuncdesc{gnutls_fips140_get_operation_state} diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 37ed4eb66e..c3bcac9f3b 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -323,7 +323,12 @@ APIMANS += gnutls_ext_raw_parse.3 APIMANS += gnutls_ext_register.3 APIMANS += gnutls_ext_set_data.3 APIMANS += gnutls_fingerprint.3 +APIMANS += gnutls_fips140_context_deinit.3 +APIMANS += gnutls_fips140_context_init.3 +APIMANS += gnutls_fips140_get_operation_state.3 APIMANS += gnutls_fips140_mode_enabled.3 +APIMANS += gnutls_fips140_pop_context.3 +APIMANS += gnutls_fips140_push_context.3 APIMANS += gnutls_fips140_set_mode.3 APIMANS += gnutls_get_system_config_file.3 APIMANS += gnutls_global_deinit.3 diff --git a/lib/cipher_int.c b/lib/cipher_int.c index e01157cde1..6680442fae 100644 --- a/lib/cipher_int.c +++ b/lib/cipher_int.c @@ -55,7 +55,7 @@ int _gnutls_cipher_exists(gnutls_cipher_algorithm_t cipher) const gnutls_crypto_cipher_st *cc; int ret; - if (is_cipher_algo_forbidden(cipher)) + if (!is_cipher_algo_allowed(cipher)) return 0; /* All the other ciphers are disabled on the back-end library. diff --git a/lib/crypto-api.c b/lib/crypto-api.c index a43aaf9b89..fa4a4eb3fd 100644 --- a/lib/crypto-api.c +++ b/lib/crypto-api.c @@ -62,17 +62,25 @@ gnutls_cipher_init(gnutls_cipher_hd_t * handle, api_cipher_hd_st *h; int ret; const cipher_entry_st* e; + bool not_approved = false; - if (is_cipher_algo_forbidden(cipher)) + if (!is_cipher_algo_allowed(cipher)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_cipher_algo_approved_in_fips(cipher)) { + not_approved = true; + } e = cipher_to_entry(cipher); - if (e == NULL || (e->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD)) + if (e == NULL || (e->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } h = gnutls_calloc(1, sizeof(api_cipher_hd_st)); if (h == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return GNUTLS_E_MEMORY_ERROR; } @@ -81,6 +89,7 @@ gnutls_cipher_init(gnutls_cipher_hd_t * handle, iv, 1); if (ret < 0) { gnutls_free(h); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return ret; } @@ -89,12 +98,19 @@ gnutls_cipher_init(gnutls_cipher_hd_t * handle, _gnutls_cipher_init(&h->ctx_dec, e, key, iv, 0); if (ret < 0) { gnutls_free(h); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return ret; } } *handle = h; + if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } @@ -145,11 +161,18 @@ gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext, size_t ptext_size) { api_cipher_hd_st *h = handle; + int ret; - if (_gnutls_cipher_is_aead(&h->ctx_enc) == 0) + if (_gnutls_cipher_is_aead(&h->ctx_enc) == 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } - return _gnutls_cipher_auth(&h->ctx_enc, ptext, ptext_size); + ret = _gnutls_cipher_auth(&h->ctx_enc, ptext, ptext_size); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } + return ret; } /** @@ -170,12 +193,15 @@ gnutls_cipher_set_iv(gnutls_cipher_hd_t handle, void *iv, size_t ivlen) if (_gnutls_cipher_setiv(&h->ctx_enc, iv, ivlen) < 0) { _gnutls_switch_lib_state(LIB_STATE_ERROR); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); } - if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK) + if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK) { if (_gnutls_cipher_setiv(&h->ctx_dec, iv, ivlen) < 0) { _gnutls_switch_lib_state(LIB_STATE_ERROR); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); } + } } /*- @@ -227,8 +253,14 @@ int _gnutls_cipher_set_key(gnutls_cipher_hd_t handle, void *key, size_t keylen) { api_cipher_hd_st *h = handle; + int ret; - return _gnutls_cipher_setkey(&h->ctx_enc, key, keylen); + ret = _gnutls_cipher_setkey(&h->ctx_enc, key, keylen); + + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } + return ret; } /** @@ -249,8 +281,15 @@ gnutls_cipher_encrypt(gnutls_cipher_hd_t handle, void *ptext, size_t ptext_len) { api_cipher_hd_st *h = handle; + int ret; - return _gnutls_cipher_encrypt(&h->ctx_enc, ptext, ptext_len); + ret = _gnutls_cipher_encrypt(&h->ctx_enc, ptext, ptext_len); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -274,13 +313,22 @@ gnutls_cipher_decrypt(gnutls_cipher_hd_t handle, void *ctext, size_t ctext_len) { api_cipher_hd_st *h = handle; + int ret; - if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) - return _gnutls_cipher_decrypt(&h->ctx_enc, ctext, - ctext_len); - else - return _gnutls_cipher_decrypt(&h->ctx_dec, ctext, - ctext_len); + if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) { + ret = _gnutls_cipher_decrypt(&h->ctx_enc, ctext, + ctext_len); + } else { + ret = _gnutls_cipher_decrypt(&h->ctx_dec, ctext, + ctext_len); + } + + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -306,9 +354,16 @@ gnutls_cipher_encrypt2(gnutls_cipher_hd_t handle, const void *ptext, size_t ctext_len) { api_cipher_hd_st *h = handle; + int ret; - return _gnutls_cipher_encrypt2(&h->ctx_enc, ptext, ptext_len, - ctext, ctext_len); + ret = _gnutls_cipher_encrypt2(&h->ctx_enc, ptext, ptext_len, + ctext, ctext_len); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -336,15 +391,24 @@ gnutls_cipher_decrypt2(gnutls_cipher_hd_t handle, const void *ctext, size_t ctext_len, void *ptext, size_t ptext_len) { api_cipher_hd_st *h = handle; + int ret; - if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) - return _gnutls_cipher_decrypt2(&h->ctx_enc, ctext, - ctext_len, ptext, - ptext_len); - else - return _gnutls_cipher_decrypt2(&h->ctx_dec, ctext, - ctext_len, ptext, - ptext_len); + if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) { + ret = _gnutls_cipher_decrypt2(&h->ctx_enc, ctext, + ctext_len, ptext, + ptext_len); + } else { + ret = _gnutls_cipher_decrypt2(&h->ctx_dec, ctext, + ctext_len, ptext, + ptext_len); + } + + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -394,18 +458,39 @@ gnutls_hmac_init(gnutls_hmac_hd_t * dig, gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen) { + int ret; + bool not_approved = false; + /* MD5 is only allowed internally for TLS */ - if (is_mac_algo_forbidden(algorithm)) + if (!is_mac_algo_allowed(algorithm)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_mac_algo_approved_in_fips(algorithm)) { + not_approved = true; + } + + /* Key lengthes less than 112 bits are not approved */ + if (keylen < 14) { + not_approved = true; + } *dig = gnutls_malloc(sizeof(mac_hd_st)); if (*dig == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return GNUTLS_E_MEMORY_ERROR; } - return _gnutls_mac_init(((mac_hd_st *) * dig), - mac_to_entry(algorithm), key, keylen); + ret = _gnutls_mac_init(((mac_hd_st *) * dig), + mac_to_entry(algorithm), key, keylen); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -440,7 +525,15 @@ gnutls_hmac_set_nonce(gnutls_hmac_hd_t handle, const void *nonce, **/ int gnutls_hmac(gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len) { - return _gnutls_mac((mac_hd_st *) handle, ptext, ptext_len); + int ret; + + ret = _gnutls_mac((mac_hd_st *) handle, ptext, ptext_len); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -529,11 +622,31 @@ gnutls_hmac_fast(gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen, const void *ptext, size_t ptext_len, void *digest) { - if (is_mac_algo_forbidden(algorithm)) + int ret; + bool not_approved = false; + + if (!is_mac_algo_allowed(algorithm)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_mac_algo_approved_in_fips(algorithm)) { + not_approved = true; + } - return _gnutls_mac_fast(algorithm, key, keylen, ptext, ptext_len, - digest); + /* Key lengthes less than 112 bits are not approved */ + if (keylen < 14) { + not_approved = true; + } + + ret = _gnutls_mac_fast(algorithm, key, keylen, ptext, ptext_len, + digest); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -558,12 +671,14 @@ gnutls_hmac_hd_t gnutls_hmac_copy(gnutls_hmac_hd_t handle) dig = gnutls_malloc(sizeof(mac_hd_st)); if (dig == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return NULL; } if (_gnutls_mac_copy((const mac_hd_st *) handle, (mac_hd_st *)dig) != GNUTLS_E_SUCCESS) { gnutls_assert(); gnutls_free(dig); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return NULL; } @@ -590,17 +705,33 @@ int gnutls_hash_init(gnutls_hash_hd_t * dig, gnutls_digest_algorithm_t algorithm) { - if (is_mac_algo_forbidden(DIG_TO_MAC(algorithm))) + int ret; + bool not_approved = false; + + if (!is_mac_algo_allowed(DIG_TO_MAC(algorithm))) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_mac_algo_approved_in_fips(DIG_TO_MAC(algorithm))) { + not_approved = true; + } *dig = gnutls_malloc(sizeof(digest_hd_st)); if (*dig == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return GNUTLS_E_MEMORY_ERROR; } - return _gnutls_hash_init(((digest_hd_st *) * dig), - hash_to_entry(algorithm)); + ret = _gnutls_hash_init(((digest_hd_st *) * dig), + hash_to_entry(algorithm)); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } /** @@ -618,7 +749,13 @@ gnutls_hash_init(gnutls_hash_hd_t * dig, **/ int gnutls_hash(gnutls_hash_hd_t handle, const void *ptext, size_t ptext_len) { - return _gnutls_hash((digest_hd_st *) handle, ptext, ptext_len); + int ret; + + ret = _gnutls_hash((digest_hd_st *) handle, ptext, ptext_len); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } + return ret; } /** @@ -686,10 +823,24 @@ int gnutls_hash_fast(gnutls_digest_algorithm_t algorithm, const void *ptext, size_t ptext_len, void *digest) { - if (is_mac_algo_forbidden(DIG_TO_MAC(algorithm))) + int ret; + bool not_approved = false; + + if (!is_mac_algo_allowed(DIG_TO_MAC(algorithm))) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_mac_algo_approved_in_fips(DIG_TO_MAC(algorithm))) { + not_approved = true; + } - return _gnutls_hash_fast(algorithm, ptext, ptext_len, digest); + ret = _gnutls_hash_fast(algorithm, ptext, ptext_len, digest); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + + return ret; } /** @@ -714,12 +865,14 @@ gnutls_hash_hd_t gnutls_hash_copy(gnutls_hash_hd_t handle) dig = gnutls_malloc(sizeof(digest_hd_st)); if (dig == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return NULL; } if (_gnutls_hash_copy((const digest_hd_st *) handle, (digest_hd_st *)dig) != GNUTLS_E_SUCCESS) { gnutls_assert(); gnutls_free(dig); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return NULL; } @@ -795,28 +948,43 @@ int gnutls_aead_cipher_init(gnutls_aead_cipher_hd_t *handle, api_aead_cipher_hd_st *h; const cipher_entry_st *e; int ret; + bool not_approved = false; - if (is_cipher_algo_forbidden(cipher)) + if (!is_cipher_algo_allowed(cipher)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_cipher_algo_approved_in_fips(cipher)) { + not_approved = true; + } e = cipher_to_entry(cipher); - if (e == NULL || e->type != CIPHER_AEAD) + if (e == NULL || e->type != CIPHER_AEAD) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } h = gnutls_calloc(1, sizeof(api_aead_cipher_hd_st)); if (h == NULL) { gnutls_assert(); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return GNUTLS_E_MEMORY_ERROR; } ret = _gnutls_aead_cipher_init(h, cipher, key); if (ret < 0) { gnutls_free(h); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return ret; } *handle = h; + if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } @@ -857,11 +1025,15 @@ gnutls_aead_cipher_decrypt(gnutls_aead_cipher_hd_t handle, if (tag_size == 0) tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e); - else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) + else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } - if (unlikely(ctext_len < tag_size)) + if (unlikely(ctext_len < tag_size)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + } ret = _gnutls_aead_cipher_decrypt(&h->ctx_enc, nonce, nonce_len, @@ -869,8 +1041,12 @@ gnutls_aead_cipher_decrypt(gnutls_aead_cipher_hd_t handle, tag_size, ctext, ctext_len, ptext, *ptext_len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } /* That assumes that AEAD ciphers are stream */ *ptext_len = ctext_len - tag_size; @@ -913,11 +1089,15 @@ gnutls_aead_cipher_encrypt(gnutls_aead_cipher_hd_t handle, if (tag_size == 0) tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e); - else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) + else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } - if (unlikely(*ctext_len < ptext_len + tag_size)) + if (unlikely(*ctext_len < ptext_len + tag_size)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + } ret = _gnutls_aead_cipher_encrypt(&h->ctx_enc, nonce, nonce_len, @@ -925,8 +1105,12 @@ gnutls_aead_cipher_encrypt(gnutls_aead_cipher_hd_t handle, tag_size, ptext, ptext_len, ctext, *ctext_len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } /* That assumes that AEAD ciphers are stream */ *ctext_len = ptext_len + tag_size; @@ -1052,8 +1236,10 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, if (tag_size == 0) tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e); - else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) + else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } if ((handle->ctx_enc.e->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD) || handle->ctx_enc.encrypt == NULL) { /* ciphertext cannot be produced in a piecemeal approach */ @@ -1061,12 +1247,15 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, struct iov_store_st ptext; ret = copy_from_iov(&auth, auth_iov, auth_iovcnt); - if (ret < 0) + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = copy_from_iov(&ptext, iov, iovcnt); if (ret < 0) { iov_store_free(&auth); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); } @@ -1078,58 +1267,77 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, iov_store_free(&auth); iov_store_free(&ptext); + /* FIPS operation state is set by gnutls_aead_cipher_encrypt */ return ret; } ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } } dst = ctext; dst_size = *ctext_len; ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; len = ret; ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p, len, dst, dst_size); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } + DECR_LEN(dst_size, len); dst += len; total += len; } - if (dst_size < tag_size) + if (dst_size < tag_size) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + } _gnutls_cipher_tag(&handle->ctx_enc, dst, tag_size); total += tag_size; *ctext_len = total; + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); return 0; } @@ -1172,8 +1380,10 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, else _tag_size = *tag_size; - if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) + if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } /* Limitation: this function provides an optimization under the internally registered * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(), @@ -1186,8 +1396,10 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, size_t ptext_size; ret = copy_from_iov(&auth, auth_iov, auth_iovcnt); - if (ret < 0) + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = copy_from_iov(&ptext, iov, iovcnt); if (ret < 0) { @@ -1231,25 +1443,37 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, iov_store_free(&auth); iov_store_free(&ptext); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } + /* FIPS operation state is set by gnutls_aead_cipher_encrypt */ return ret; } ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } } ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize); @@ -1257,19 +1481,25 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; len = ret; ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p, len, p, len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = _gnutls_iov_iter_sync(&iter, p, len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } } if (tag != NULL) @@ -1277,6 +1507,7 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, if (tag_size != NULL) *tag_size = _tag_size; + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); return 0; } @@ -1316,8 +1547,10 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, if (tag_size == 0) tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e); - else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) + else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } /* Limitation: this function provides an optimization under the internally registered * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(), @@ -1330,8 +1563,10 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, size_t ctext_size; ret = copy_from_iov(&auth, auth_iov, auth_iovcnt); - if (ret < 0) + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = copy_from_iov(&ctext, iov, iovcnt); if (ret < 0) { @@ -1369,53 +1604,76 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, iov_store_free(&auth); iov_store_free(&ctext); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } + /* FIPS operation state is set by gnutls_aead_cipher_decrypt */ return ret; } ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } } ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } while (1) { ret = _gnutls_iov_iter_next(&iter, &p); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } if (ret == 0) break; len = ret; ret = _gnutls_cipher_decrypt2(&handle->ctx_enc, p, len, p, len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } ret = _gnutls_iov_iter_sync(&iter, p, len); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(ret); + } } if (tag != NULL) { _gnutls_cipher_tag(&handle->ctx_enc, _tag, tag_size); - if (gnutls_memcmp(_tag, tag, tag_size) != 0) + if (gnutls_memcmp(_tag, tag, tag_size) != 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + } } + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); return 0; } @@ -1456,14 +1714,27 @@ gnutls_hkdf_extract(gnutls_mac_algorithm_t mac, const gnutls_datum_t *salt, void *output) { + int ret; + /* MD5 is only allowed internally for TLS */ - if (is_mac_algo_forbidden(mac)) + if (!is_mac_algo_allowed(mac)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } + + /* We don't check whether MAC is approved, because HKDF is + * only approved in TLS, which is handled separately. */ - return _gnutls_kdf_ops.hkdf_extract(mac, key->data, key->size, - salt ? salt->data : NULL, - salt ? salt->size : 0, - output); + ret = _gnutls_kdf_ops.hkdf_extract(mac, key->data, key->size, + salt ? salt->data : NULL, + salt ? salt->size : 0, + output); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; } /** @@ -1488,13 +1759,26 @@ gnutls_hkdf_expand(gnutls_mac_algorithm_t mac, const gnutls_datum_t *info, void *output, size_t length) { + int ret; + /* MD5 is only allowed internally for TLS */ - if (is_mac_algo_forbidden(mac)) + if (!is_mac_algo_allowed(mac)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } - return _gnutls_kdf_ops.hkdf_expand(mac, key->data, key->size, - info->data, info->size, - output, length); + /* We don't check whether MAC is approved, because HKDF is + * only approved in TLS, which is handled separately. */ + + ret = _gnutls_kdf_ops.hkdf_expand(mac, key->data, key->size, + info->data, info->size, + output, length); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; } /** @@ -1520,11 +1804,26 @@ gnutls_pbkdf2(gnutls_mac_algorithm_t mac, unsigned iter_count, void *output, size_t length) { + int ret; + bool not_approved = false; + /* MD5 is only allowed internally for TLS */ - if (is_mac_algo_forbidden(mac)) + if (!is_mac_algo_allowed(mac)) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + } else if (!is_mac_algo_approved_in_fips(mac)) { + not_approved = true; + } - return _gnutls_kdf_ops.pbkdf2(mac, key->data, key->size, - salt->data, salt->size, iter_count, - output, length); + ret = _gnutls_kdf_ops.pbkdf2(mac, key->data, key->size, + salt->data, salt->size, iter_count, + output, length); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + return ret; } diff --git a/lib/fips.c b/lib/fips.c index 51567953df..457a8c056e 100644 --- a/lib/fips.c +++ b/lib/fips.c @@ -33,6 +33,12 @@ #include "gthreads.h" unsigned int _gnutls_lib_state = LIB_STATE_POWERON; + +struct gnutls_fips140_context_st { + gnutls_fips140_operation_state_t state; + struct gnutls_fips140_context_st *next; +}; + #ifdef ENABLE_FIPS140 #include <dlfcn.h> @@ -46,6 +52,8 @@ unsigned int _gnutls_lib_state = LIB_STATE_POWERON; static gnutls_fips_mode_t _global_fips_mode = -1; static _Thread_local gnutls_fips_mode_t _tfips_mode = -1; +static _Thread_local gnutls_fips140_context_t _tfips_context = NULL; + static int _skip_integrity_checks = 0; /* Returns: @@ -599,3 +607,177 @@ void _gnutls_lib_force_operational(void) { _gnutls_switch_lib_state(LIB_STATE_OPERATIONAL); } + +/** + * gnutls_fips140_context_init: + * @context: location to store @gnutls_fips140_context_t + * + * Create and initialize the FIPS context object. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_context_init(gnutls_fips140_context_t *context) +{ + *context = gnutls_malloc(sizeof(struct gnutls_fips140_context_st)); + if (!*context) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + (*context)->state = GNUTLS_FIPS140_OP_INITIAL; + return 0; +} + +/** + * gnutls_fips140_context_deinit: + * @context: a #gnutls_fips140_context_t + * + * Uninitialize and release the FIPS context @context. + * + * Since: 3.7.3 + */ +void +gnutls_fips140_context_deinit(gnutls_fips140_context_t context) +{ + gnutls_free(context); +} + +/** + * gnutls_fips140_get_operation_state: + * @context: a #gnutls_fips140_context_t + * + * Get the previous operation state of @context in terms of FIPS. + * + * Returns: a #gnutls_fips140_operation_state_t + * + * Since: 3.7.3 + */ +gnutls_fips140_operation_state_t +gnutls_fips140_get_operation_state(gnutls_fips140_context_t context) +{ + return context->state; +} + +/** + * gnutls_fips140_push_context: + * @context: a #gnutls_fips140_context_t + * + * Associate the FIPS @context to the current thread, diverting the + * currently active context. If a cryptographic operation is ongoing + * in the current thread, e.g., gnutls_aead_cipher_init() is called + * but gnutls_aead_cipher_deinit() is not yet called, it returns an + * error %GNUTLS_E_INVALID_REQUEST. + * + * The operation state of @context will be reset to + * %GNUTLS_FIPS140_OP_INITIAL. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_push_context(gnutls_fips140_context_t context) +{ +#ifdef ENABLE_FIPS140 + context->next = _tfips_context; + _tfips_context = context; + + context->state = GNUTLS_FIPS140_OP_INITIAL; + return 0; +#else + return GNUTLS_E_INVALID_REQUEST; +#endif +} + +/** + * gnutls_fips140_pop_context: + * + * Dissociate the FIPS context currently + * active on the current thread, reverting to the previously active + * context. If a cryptographic operation is ongoing in the current + * thread, e.g., gnutls_aead_cipher_init() is called but + * gnutls_aead_cipher_deinit() is not yet called, it returns an error + * %GNUTLS_E_INVALID_REQUEST. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_pop_context(void) +{ +#ifdef ENABLE_FIPS140 + if (!_tfips_context) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + _tfips_context = _tfips_context->next; + return 0; +#else + return GNUTLS_E_INVALID_REQUEST; +#endif +} + +static inline const char * +operation_state_to_string(gnutls_fips140_operation_state_t state) +{ + switch (state) { + case GNUTLS_FIPS140_OP_INITIAL: + return "initial"; + case GNUTLS_FIPS140_OP_APPROVED: + return "approved"; + case GNUTLS_FIPS140_OP_NOT_APPROVED: + return "not-approved"; + case GNUTLS_FIPS140_OP_ERROR: + return "error"; + default: + /*NOTREACHED*/ + assert(0); + return NULL; + } +} + +gnutls_fips140_operation_state_t +_gnutls_transit_fips_state(gnutls_fips140_operation_state_t current, + gnutls_fips140_operation_state_t next) +{ + switch (current) { + case GNUTLS_FIPS140_OP_INITIAL: + /* initial can be transitioned to any state */ + _gnutls_debug_log("FIPS140-2 operation mode switched from initial to %s\n", + operation_state_to_string(next)); + return next; + case GNUTLS_FIPS140_OP_APPROVED: + /* approved can only be transitioned to not-approved */ + if (next == GNUTLS_FIPS140_OP_NOT_APPROVED) { + _gnutls_debug_log("FIPS140-2 operation mode switched from approved to %s\n", + operation_state_to_string(next)); + return next; + } + FALLTHROUGH; + default: + /* other transitions are prohibited */ + if (next != current) { + _gnutls_debug_log("FIPS140-2 operation mode cannot be switched from %s to %s\n", + operation_state_to_string(current), + operation_state_to_string(next)); + } + return current; + } +} + +void +_gnutls_switch_fips_state(gnutls_fips140_operation_state_t state) +{ +#ifdef ENABLE_FIPS140 + if (!_tfips_context) { + _gnutls_debug_log("FIPS140-2 context is not set\n"); + return; + } + _tfips_context->state = + _gnutls_transit_fips_state(_tfips_context->state, state); +#else + (void)state; +#endif +} diff --git a/lib/fips.h b/lib/fips.h index f76f24da75..2f0a808945 100644 --- a/lib/fips.h +++ b/lib/fips.h @@ -41,6 +41,12 @@ typedef enum { extern unsigned int _gnutls_lib_state; extern gnutls_crypto_rnd_st _gnutls_fips_rnd_ops; +gnutls_fips140_operation_state_t +_gnutls_transit_fips_state(gnutls_fips140_operation_state_t current, + gnutls_fips140_operation_state_t next); + +void _gnutls_switch_fips_state(gnutls_fips140_operation_state_t state); + inline static void _gnutls_switch_lib_state(gnutls_lib_state_t state) { @@ -73,6 +79,77 @@ void _gnutls_switch_lib_state(gnutls_lib_state_t state); void _gnutls_lib_simulate_error(void); void _gnutls_lib_force_operational(void); +inline static bool +is_mac_algo_approved_in_fips(gnutls_mac_algorithm_t algo) +{ + switch (algo) { + case GNUTLS_MAC_SHA1: + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + case GNUTLS_MAC_SHA3_224: + case GNUTLS_MAC_SHA3_256: + case GNUTLS_MAC_SHA3_384: + case GNUTLS_MAC_SHA3_512: + case GNUTLS_MAC_AES_CMAC_128: + case GNUTLS_MAC_AES_CMAC_256: + case GNUTLS_MAC_AES_GMAC_128: + case GNUTLS_MAC_AES_GMAC_192: + case GNUTLS_MAC_AES_GMAC_256: + return true; + default: + return false; + } +} + +inline static bool +is_mac_algo_allowed_in_fips(gnutls_mac_algorithm_t algo) +{ + return is_mac_algo_approved_in_fips(algo); +} + +inline static bool +is_cipher_algo_approved_in_fips(gnutls_cipher_algorithm_t algo) +{ + switch (algo) { + case GNUTLS_CIPHER_AES_128_CBC: + case GNUTLS_CIPHER_AES_256_CBC: + case GNUTLS_CIPHER_AES_192_CBC: + case GNUTLS_CIPHER_AES_128_CCM: + case GNUTLS_CIPHER_AES_256_CCM: + case GNUTLS_CIPHER_3DES_CBC: + case GNUTLS_CIPHER_AES_128_CCM_8: + case GNUTLS_CIPHER_AES_256_CCM_8: + case GNUTLS_CIPHER_AES_128_CFB8: + case GNUTLS_CIPHER_AES_192_CFB8: + case GNUTLS_CIPHER_AES_256_CFB8: + case GNUTLS_CIPHER_AES_128_XTS: + case GNUTLS_CIPHER_AES_256_XTS: + return true; + default: + return false; + } +} + +inline static bool +is_cipher_algo_allowed_in_fips(gnutls_cipher_algorithm_t algo) +{ + if (is_cipher_algo_approved_in_fips(algo)) { + return true; + } + + /* GCM is only approved in TLS */ + switch (algo) { + case GNUTLS_CIPHER_AES_128_GCM: + case GNUTLS_CIPHER_AES_192_GCM: + case GNUTLS_CIPHER_AES_256_GCM: + return true; + default: + return false; + } +} + #ifdef ENABLE_FIPS140 /* This will test the condition when in FIPS140-2 mode * and return an error if necessary or ignore */ @@ -89,85 +166,53 @@ void _gnutls_lib_force_operational(void); } \ }} -inline -static unsigned is_mac_algo_forbidden(gnutls_mac_algorithm_t algo) +inline static bool +is_mac_algo_allowed(gnutls_mac_algorithm_t algo) { gnutls_fips_mode_t mode = _gnutls_fips_mode_enabled(); - if (mode != GNUTLS_FIPS140_DISABLED && - _gnutls_get_lib_state() != LIB_STATE_SELFTEST) { - switch(algo) { - case GNUTLS_MAC_SHA1: - case GNUTLS_MAC_SHA256: - case GNUTLS_MAC_SHA384: - case GNUTLS_MAC_SHA512: - case GNUTLS_MAC_SHA224: - case GNUTLS_MAC_SHA3_224: - case GNUTLS_MAC_SHA3_256: - case GNUTLS_MAC_SHA3_384: - case GNUTLS_MAC_SHA3_512: - case GNUTLS_MAC_AES_CMAC_128: - case GNUTLS_MAC_AES_CMAC_256: - case GNUTLS_MAC_AES_GMAC_128: - case GNUTLS_MAC_AES_GMAC_192: - case GNUTLS_MAC_AES_GMAC_256: - return 0; - default: - if (mode == GNUTLS_FIPS140_LAX) - return 0; - else if (mode == GNUTLS_FIPS140_LOG) { - _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n", - gnutls_mac_get_name(algo)); - return 0; - } - return 1; + if (_gnutls_get_lib_state() != LIB_STATE_SELFTEST && + !is_mac_algo_allowed_in_fips(algo)) { + switch (mode) { + case GNUTLS_FIPS140_LOG: + _gnutls_audit_log(NULL, + "fips140-2: allowing access to %s\n", + gnutls_mac_get_name(algo)); + FALLTHROUGH; + case GNUTLS_FIPS140_DISABLED: + case GNUTLS_FIPS140_LAX: + return true; + default: + return false; } } - return 0; + return true; } -inline -static unsigned is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo) +inline static bool +is_cipher_algo_allowed(gnutls_cipher_algorithm_t algo) { gnutls_fips_mode_t mode = _gnutls_fips_mode_enabled(); - if (mode != GNUTLS_FIPS140_DISABLED && - _gnutls_get_lib_state() != LIB_STATE_SELFTEST) { - - switch(algo) { - case GNUTLS_CIPHER_AES_128_CBC: - case GNUTLS_CIPHER_AES_256_CBC: - case GNUTLS_CIPHER_AES_192_CBC: - case GNUTLS_CIPHER_AES_128_GCM: - case GNUTLS_CIPHER_AES_192_GCM: - case GNUTLS_CIPHER_AES_256_GCM: - case GNUTLS_CIPHER_AES_128_CCM: - case GNUTLS_CIPHER_AES_256_CCM: - case GNUTLS_CIPHER_3DES_CBC: - case GNUTLS_CIPHER_AES_128_CCM_8: - case GNUTLS_CIPHER_AES_256_CCM_8: - case GNUTLS_CIPHER_AES_128_CFB8: - case GNUTLS_CIPHER_AES_192_CFB8: - case GNUTLS_CIPHER_AES_256_CFB8: - case GNUTLS_CIPHER_AES_128_XTS: - case GNUTLS_CIPHER_AES_256_XTS: - return 0; - default: - if (mode == GNUTLS_FIPS140_LAX) - return 0; - else if (mode == GNUTLS_FIPS140_LOG) { - _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n", - gnutls_cipher_get_name(algo)); - return 0; - } - return 1; + if (_gnutls_get_lib_state() != LIB_STATE_SELFTEST && + !is_cipher_algo_allowed_in_fips(algo)) { + switch (mode) { + case GNUTLS_FIPS140_LOG: + _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n", + gnutls_cipher_get_name(algo)); + FALLTHROUGH; + case GNUTLS_FIPS140_DISABLED: + case GNUTLS_FIPS140_LAX: + return true; + default: + return false; } } - return 0; + return true; } #else -# define is_mac_algo_forbidden(x) 0 -# define is_cipher_algo_forbidden(x) 0 +# define is_mac_algo_allowed(x) true +# define is_cipher_algo_allowed(x) true # define FIPS_RULE(condition, ret_error, ...) #endif diff --git a/lib/hash_int.c b/lib/hash_int.c index 59eddeba37..289b6b9dd6 100644 --- a/lib/hash_int.c +++ b/lib/hash_int.c @@ -80,7 +80,7 @@ int _gnutls_digest_exists(gnutls_digest_algorithm_t algo) { const gnutls_crypto_digest_st *cc = NULL; - if (is_mac_algo_forbidden(DIG_TO_MAC(algo))) + if (!is_mac_algo_allowed(DIG_TO_MAC(algo))) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); cc = _gnutls_get_crypto_digest(algo); @@ -197,7 +197,7 @@ int _gnutls_mac_exists(gnutls_mac_algorithm_t algo) if (algo == GNUTLS_MAC_AEAD) return 1; - if (is_mac_algo_forbidden(algo)) + if (!is_mac_algo_allowed(algo)) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); cc = _gnutls_get_crypto_mac(algo); diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 1e883aa8eb..0e96be81e8 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -3348,6 +3348,45 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags); gnutls_fips140_set_mode(GNUTLS_FIPS140_STRICT, GNUTLS_FIPS140_SET_MODE_THREAD); \ } while(0) +typedef struct gnutls_fips140_context_st *gnutls_fips140_context_t; + +int gnutls_fips140_context_init(gnutls_fips140_context_t *context); +void gnutls_fips140_context_deinit(gnutls_fips140_context_t context); + +/** + * gnutls_fips140_operation_state_t: + * @GNUTLS_FIPS140_OP_INITIAL: no previous operation has done + * @GNUTLS_FIPS140_OP_APPROVED: the previous operation was FIPS approved + * @GNUTLS_FIPS140_OP_NOT_APPROVED: the previous operation was not FIPS approved + * @GNUTLS_FIPS140_OP_ERROR: the previous operation caused an error regardless of FIPS + * + * The FIPS operation state set by the preceding operation. + * + * There are state transition rules among the enum values: + * - When the context is attached to a thread, it will be set to reset + * to the %GNUTLS_FIPS140_OP_INITIAL state + * - From the %GNUTLS_FIPS140_OP_INITIAL state, the context can + * transition to either %GNUTLS_FIPS140_OP_APPROVED, + * %GNUTLS_FIPS140_OP_NOT_APPROVED, or %GNUTLS_FIPS140_OP_ERROR + * - From the %GNUTLS_FIPS140_OP_APPROVED state, the context can + * transition to %GNUTLS_FIPS140_OP_NOT_APPROVED + * - All other transitions are prohibited. + * + * Since: 3.7.3 + */ +typedef enum { + GNUTLS_FIPS140_OP_INITIAL, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_NOT_APPROVED, + GNUTLS_FIPS140_OP_ERROR +} gnutls_fips140_operation_state_t; + +gnutls_fips140_operation_state_t +gnutls_fips140_get_operation_state(gnutls_fips140_context_t context); + +int gnutls_fips140_push_context(gnutls_fips140_context_t context); +int gnutls_fips140_pop_context(void); + /* Gnutls error codes. The mapping to a TLS alert is also shown in * comments. */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index e60f10104a..40a3984cbf 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1370,6 +1370,11 @@ GNUTLS_3_7_3 gnutls_digest_set_secure; gnutls_protocol_set_enabled; gnutls_transport_is_ktls_enabled; + gnutls_fips140_context_init; + gnutls_fips140_context_deinit; + gnutls_fips140_get_operation_state; + gnutls_fips140_push_context; + gnutls_fips140_pop_context; local: *; } GNUTLS_3_7_2; diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index 6af19c459f..a146568266 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -293,6 +293,7 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, unsigned int flags) { int ret; + bool not_approved = false; switch (algo) { case GNUTLS_PK_DH: { @@ -300,8 +301,10 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, bigint_t k = NULL, primesub1 = NULL, r = NULL; unsigned int bits; - if (nonce != NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (nonce != NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } f = pub->params[DH_Y]; x = priv->params[DH_X]; @@ -309,8 +312,10 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, prime = priv->params[DH_P]; ret = _gnutls_mpi_init_multi(&k, &primesub1, &r, NULL); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } ret = _gnutls_mpi_sub_ui(primesub1, prime, 1); if (ret < 0) { @@ -343,7 +348,7 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, } else if ((flags & PK_DERIVE_TLS13) && _gnutls_fips_mode_enabled()) { /* Mandatory in FIPS mode for TLS 1.3 */ - ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); goto dh_cleanup; } @@ -355,6 +360,9 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, goto dh_cleanup; } + if (bits < 2048) { + not_approved = true; + } ret = _gnutls_mpi_powm(k, f, x, prime); if (ret < 0) { @@ -366,8 +374,7 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, if ((_gnutls_mpi_cmp_ui(k, 1) == 0) || (_gnutls_mpi_cmp_ui(k, 0) == 0) || (_gnutls_mpi_cmp(k, primesub1) == 0)) { - gnutls_assert(); - ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); goto dh_cleanup; } @@ -402,24 +409,34 @@ dh_cleanup: out->data = NULL; - if (nonce != NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (nonce != NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } curve = get_supported_nist_curve(priv->curve); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } + + /* P-192 is not supported in FIPS 140-3 */ + if (priv->curve == GNUTLS_ECC_CURVE_SECP192R1) { + not_approved = true; + } ret = _ecc_params_to_pubkey(pub, &ecc_pub, curve); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } ret = _ecc_params_to_privkey(priv, &ecc_priv, curve); if (ret < 0) { ecc_point_clear(&ecc_pub); - return gnutls_assert_val(ret); + gnutls_assert(); + goto cleanup; } out->size = gnutls_ecc_curve_get_size(priv->curve); @@ -449,13 +466,20 @@ dh_cleanup: { unsigned size = gnutls_ecc_curve_get_size(priv->curve); - if (nonce != NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + /* Edwards curves are not approved */ + not_approved = true; + + if (nonce != NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } /* The point is in pub, while the private part (scalar) in priv. */ - if (size == 0 || priv->raw_priv.size != size) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (size == 0 || priv->raw_priv.size != size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } out->data = gnutls_malloc(size); if (out->data == NULL) { @@ -486,25 +510,35 @@ dh_cleanup: struct ecc_point ecc_pub; const struct ecc_curve *curve; + /* GOST curves are not approved */ + not_approved = true; + out->data = NULL; curve = get_supported_gost_curve(priv->curve); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto cleanup; + } - if (nonce == NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (nonce == NULL) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } ret = _gost_params_to_pubkey(pub, &ecc_pub, curve); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } ret = _gost_params_to_privkey(priv, &ecc_priv, curve); if (ret < 0) { ecc_point_clear(&ecc_pub); - return gnutls_assert_val(ret); + gnutls_assert(); + goto cleanup; } out->size = 2 * gnutls_ecc_curve_get_size(priv->curve); @@ -535,6 +569,13 @@ dh_cleanup: ret = 0; cleanup: + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } return ret; } @@ -598,6 +639,12 @@ _wrap_nettle_pk_encrypt(gnutls_pk_algorithm_t algo, ret = 0; cleanup: + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + mpz_clear(p); FAIL_IF_LIB_ERROR; @@ -628,14 +675,15 @@ _wrap_nettle_pk_decrypt(gnutls_pk_algorithm_t algo, _rsa_params_to_privkey(pk_params, &priv); ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return - gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } - if (ciphertext->size != pub.size) - return - gnutls_assert_val - (GNUTLS_E_DECRYPTION_FAILED); + if (ciphertext->size != pub.size) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + goto cleanup; + } if (_gnutls_mpi_init_scan_nz (&c, ciphertext->data, @@ -684,8 +732,12 @@ _wrap_nettle_pk_decrypt(gnutls_pk_algorithm_t algo, ret = 0; cleanup: - if (ret < 0) + if (ret < 0) { gnutls_free(plaintext->data); + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } FAIL_IF_LIB_ERROR; return ret; @@ -711,21 +763,26 @@ _wrap_nettle_pk_decrypt2(gnutls_pk_algorithm_t algo, FAIL_IF_LIB_ERROR; if (algo != GNUTLS_PK_RSA || plaintext == NULL) { - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + goto fail; } _rsa_params_to_privkey(pk_params, &priv); ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto fail; + } - if (ciphertext->size != pub.size) - return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + if (ciphertext->size != pub.size) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + goto fail; + } if (_gnutls_mpi_init_scan_nz(&c, ciphertext->data, ciphertext->size) != 0) { - return gnutls_assert_val (GNUTLS_E_MPI_SCAN_FAILED); + ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED); + goto fail; } if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST) @@ -734,6 +791,13 @@ _wrap_nettle_pk_decrypt2(gnutls_pk_algorithm_t algo, random_func = rnd_nonce_func; ret = rsa_sec_decrypt(&pub, &priv, NULL, random_func, plaintext_size, plaintext, TOMPZ(c)); + + /* The decrypt operation is infallible; treat the approved + * operation as complete at this point, regardless of any + * decryption failure detected below. + */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + /* after this point, any conditional on failure that cause differences * in execution may create a timing or cache access pattern side * channel that can be used as an oracle, so thread very carefully */ @@ -747,6 +811,11 @@ _wrap_nettle_pk_decrypt2(gnutls_pk_algorithm_t algo, is_err |= CONSTCHECK_EQUAL(ret, 0); /* then return GNUTLS_E_DECRYPTION_FAILED */ return (int)((is_err * UINT_MAX) & GNUTLS_E_DECRYPTION_FAILED); + + fail: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + + return ret; } #define CHECK_INVALID_RSA_PSS_PARAMS(dig_size, salt_size, pub_size, err) \ @@ -881,22 +950,25 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, int ret; unsigned int hash_len; const mac_entry_st *me; + bool not_approved = false; FAIL_IF_LIB_ERROR; - if (IS_EC(algo)) { - /* check if the curve relates to the algorithm used */ - if (gnutls_ecc_curve_get_pk(pk_params->curve) != algo) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + /* check if the curve relates to the algorithm used */ + if (IS_EC(algo) && gnutls_ecc_curve_get_pk(pk_params->curve) != algo) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; } /* deterministic ECDSA/DSA is prohibited under FIPS except in * the selftests */ - if (_gnutls_fips_mode_enabled() && - _gnutls_get_lib_state() != LIB_STATE_SELFTEST && - (algo == GNUTLS_PK_DSA || algo == GNUTLS_PK_ECDSA) && - (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if ((algo == GNUTLS_PK_DSA || algo == GNUTLS_PK_ECDSA) && + (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE) && + _gnutls_fips_mode_enabled() && + _gnutls_get_lib_state() != LIB_STATE_SELFTEST) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } switch (algo) { case GNUTLS_PK_EDDSA_ED25519: /* we do EdDSA */ @@ -904,12 +976,19 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, { const gnutls_ecc_curve_entry_st *e; - if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + /* EdDSA is not approved yet */ + not_approved = true; + + if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } e = _gnutls_ecc_curve_get_params(pk_params->curve); - if (e == NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (e == NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } signature->data = gnutls_malloc(e->sig_size); if (signature->data == NULL) { @@ -943,28 +1022,33 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, struct dsa_signature sig; const struct ecc_curve *curve; + /* GOSTDSA is not approved */ + not_approved = true; + curve = get_supported_gost_curve(pk_params->curve); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } ret = _ecc_params_to_privkey(pk_params, &priv, curve); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } /* This call will return a valid MAC entry and * getters will check that is not null anyway. */ me = hash_to_entry(_gnutls_gost_digest(pk_params->algo)); if (_gnutls_mac_get_algo_len(me) != vdata->size) { - gnutls_assert(); _gnutls_debug_log ("Security level of algorithm requires hash %s(%zd)\n", _gnutls_mac_get_name(me), _gnutls_mac_get_algo_len(me)); - return GNUTLS_E_INVALID_REQUEST; + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; } dsa_signature_init(&sig); @@ -997,22 +1081,40 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, nettle_random_func *random_func; curve = get_supported_nist_curve(curve_id); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } + + /* P-192 is not supported in FIPS 140-3 */ + if (curve_id == GNUTLS_ECC_CURVE_SECP192R1) { + not_approved = true; + } ret = _ecc_params_to_privkey(pk_params, &priv, curve); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } dsa_signature_init(&sig); me = _gnutls_dsa_q_to_hash(pk_params, &hash_len); + /* Only SHA-2 is allowed in FIPS 140-3 */ + switch (me->id) { + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + break; + default: + not_approved = true; + } + if (hash_len > vdata->size) { gnutls_assert(); _gnutls_debug_log @@ -1071,6 +1173,10 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, void *random_ctx; nettle_random_func *random_func; + /* DSA is currently being defined as sunset with the + * current draft of FIPS 186-5 */ + not_approved = true; + memset(&priv, 0, sizeof(priv)); memset(&pub, 0, sizeof(pub)); _dsa_params_get(pk_params, &pub); @@ -1140,10 +1246,20 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, mpz_t s; _rsa_params_to_privkey(pk_params, &priv); + + /* RSA key size should be 2048-bit or larger in FIPS + * 140-3. In addition to this, only SHA-2 is allowed + * for SigGen; it is checked in pk_prepare_hash lib/pk.c + */ + if (unlikely(priv.size < 256)) { + not_approved = true; + } + ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return - gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } mpz_init(s); @@ -1181,10 +1297,22 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, mpz_t s; _rsa_params_to_privkey(pk_params, &priv); + + /* RSA key size should be 2048-bit or larger in FIPS + * 140-3. In addition to this, only SHA-2 is allowed + * for SigGen; however, Nettle only support SHA256, + * SHA384, and SHA512 for RSA-PSS (see + * _rsa_pss_sign_digest_tr in this file for details). + */ + if (unlikely(priv.size < 256)) { + not_approved = true; + } + ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return - gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } mpz_init(s); @@ -1223,6 +1351,13 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, ret = 0; cleanup: + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } FAIL_IF_LIB_ERROR; return ret; @@ -1302,13 +1437,14 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, int ret; unsigned int hash_len; bigint_t tmp[2] = { NULL, NULL }; + bool not_approved = false; FAIL_IF_LIB_ERROR; - if (IS_EC(algo)) { - /* check if the curve relates to the algorithm used */ - if (gnutls_ecc_curve_get_pk(pk_params->curve) != algo) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + /* check if the curve relates to the algorithm used */ + if (IS_EC(algo) && gnutls_ecc_curve_get_pk(pk_params->curve) != algo) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; } switch (algo) { @@ -1317,18 +1453,29 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, { const gnutls_ecc_curve_entry_st *e; - if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + /* EdDSA is not approved yet */ + not_approved = true; + + if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } e = _gnutls_ecc_curve_get_params(pk_params->curve); - if (e == NULL) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (e == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } - if (signature->size != e->sig_size) - return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + if (signature->size != e->sig_size) { + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + goto cleanup; + } - if (pk_params->raw_pub.size != e->size) - return gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED); + if (pk_params->raw_pub.size != e->size) { + ret = gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED); + goto cleanup; + } ret = eddsa_verify(algo, pk_params->raw_pub.data, @@ -1346,23 +1493,30 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, const struct ecc_curve *curve; const mac_entry_st *me; + /* GOSTDSA is not approved */ + not_approved = true; + curve = get_supported_gost_curve(pk_params->curve); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } /* This call will return a valid MAC entry and * getters will check that is not null anyway. */ me = hash_to_entry(_gnutls_gost_digest(pk_params->algo)); - if (_gnutls_mac_get_algo_len(me) != vdata->size) - return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + if (_gnutls_mac_get_algo_len(me) != vdata->size) { + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + goto cleanup; + } ret = _gnutls_decode_gost_rs(signature, &tmp[0], &tmp[1]); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } ret = _gost_params_to_pubkey(pk_params, &pub, curve); @@ -1391,18 +1545,21 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, struct dsa_signature sig; int curve_id = pk_params->curve; const struct ecc_curve *curve; + const mac_entry_st *me; curve = get_supported_nist_curve(curve_id); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } ret = _gnutls_decode_ber_rs(signature, &tmp[0], &tmp[1]); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } ret = _ecc_params_to_pubkey(pk_params, &pub, curve); @@ -1414,7 +1571,20 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, memcpy(sig.r, tmp[0], SIZEOF_MPZT); memcpy(sig.s, tmp[1], SIZEOF_MPZT); - _gnutls_dsa_q_to_hash(pk_params, &hash_len); + me = _gnutls_dsa_q_to_hash(pk_params, &hash_len); + + /* SHA-1 is allowed for SigVer in FIPS 140-3 in legacy + * mode */ + switch (me->id) { + case GNUTLS_MAC_SHA1: + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + break; + default: + not_approved = true; + } if (hash_len > vdata->size) hash_len = vdata->size; @@ -1437,6 +1607,10 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, struct dsa_signature sig; bigint_t y; + /* DSA is currently being defined as sunset with the + * current draft of FIPS 186-5 */ + not_approved = true; + ret = _gnutls_decode_ber_rs(signature, &tmp[0], &tmp[1]); @@ -1471,14 +1645,24 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, struct rsa_public_key pub; ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return - gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } - if (signature->size != pub.size) - return - gnutls_assert_val - (GNUTLS_E_PK_SIG_VERIFY_FAILED); + /* RSA key size should be 2048-bit or larger in FIPS + * 140-3. In addition to this, only SHA-1 and SHA-2 are + * allowed for SigVer; it is checked in + * _pkcs1_rsa_verify_sig in lib/pubkey.c + */ + if (unlikely(pub.size < 256)) { + not_approved = true; + } + + if (signature->size != pub.size) { + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + goto cleanup; + } ret = _gnutls_mpi_init_scan_nz(&tmp[0], signature->data, @@ -1507,19 +1691,30 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, if ((sign_params->flags & GNUTLS_PK_FLAG_RSA_PSS_FIXED_SALT_LENGTH) && sign_params->salt_size != vdata->size) { - return gnutls_assert_val - (GNUTLS_E_PK_SIG_VERIFY_FAILED); + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + goto cleanup; } ret = _rsa_params_to_pubkey(pk_params, &pub); - if (ret < 0) - return - gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } - if (signature->size != pub.size) - return - gnutls_assert_val - (GNUTLS_E_PK_SIG_VERIFY_FAILED); + /* RSA key size should be 2048-bit or larger in FIPS + * 140-3. In addition to this, only SHA-1 and SHA-2 are + * allowed for SigVer, while Nettle only supports + * SHA256, SHA384, and SHA512 for RSA-PSS (see + * _rsa_pss_verify_digest in this file for the details). + */ + if (unlikely(pub.size < 256)) { + not_approved = true; + } + + if (signature->size != pub.size) { + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + goto cleanup; + } ret = _gnutls_mpi_init_scan_nz(&tmp[0], signature->data, @@ -1550,6 +1745,13 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, } cleanup: + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } _gnutls_mpi_release(&tmp[0]); _gnutls_mpi_release(&tmp[1]); @@ -2081,15 +2283,16 @@ int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve, static int pct_test(gnutls_pk_algorithm_t algo, const gnutls_pk_params_st* params) { -int ret; -gnutls_datum_t sig = {NULL, 0}; -const char const_data[20] = "onetwothreefourfive"; -const char const_data_sha256[32] = "onetwothreefourfivesixseveneight"; -const char const_data_sha384[48] = "onetwothreefourfivesixseveneightnineteneleventwe"; -const char const_data_sha512[64] = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourt"; -gnutls_datum_t ddata, tmp = {NULL,0}; -char* gen_data = NULL; -gnutls_x509_spki_st spki; + int ret; + gnutls_datum_t sig = {NULL, 0}; + const char const_data[20] = "onetwothreefourfive"; + const char const_data_sha256[32] = "onetwothreefourfivesixseveneight"; + const char const_data_sha384[48] = "onetwothreefourfivesixseveneightnineteneleventwe"; + const char const_data_sha512[64] = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourt"; + gnutls_datum_t ddata, tmp = {NULL,0}; + char* gen_data = NULL; + gnutls_x509_spki_st spki; + gnutls_fips140_context_t context; memcpy(&spki, ¶ms->spki, sizeof(spki)); @@ -2136,28 +2339,44 @@ gnutls_x509_spki_st spki; switch (algo) { case GNUTLS_PK_RSA: - ret = _gnutls_pk_encrypt(algo, &sig, &ddata, params); - if (ret < 0) { + /* Push a temporary FIPS context because _gnutls_pk_encrypt and + * _gnutls_pk_decrypt below will mark RSAES-PKCS1-v1_5 operation + * non-approved */ + if (gnutls_fips140_context_init(&context) < 0) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); goto cleanup; } - - if (ddata.size == sig.size && memcmp(ddata.data, sig.data, sig.size) == 0) { + if (gnutls_fips140_push_context(context) < 0) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); - gnutls_assert(); + gnutls_fips140_context_deinit(context); goto cleanup; } - ret = _gnutls_pk_decrypt(algo, &tmp, &sig, params); + ret = _gnutls_pk_encrypt(algo, &sig, &ddata, params); if (ret < 0) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); - gnutls_assert(); - goto cleanup; + } + if (ret == 0 && + ddata.size == sig.size && + memcmp(ddata.data, sig.data, sig.size) == 0) { + ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); + } + if (ret == 0 && + _gnutls_pk_decrypt(algo, &tmp, &sig, params) < 0) { + ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); + } + if (ret == 0 && + !(tmp.size == ddata.size && + memcmp(tmp.data, ddata.data, tmp.size) == 0)) { + ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); } - if (tmp.size != ddata.size || memcmp(tmp.data, ddata.data, tmp.size) != 0) { + if (unlikely(gnutls_fips140_pop_context() < 0)) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); - gnutls_assert(); + } + gnutls_fips140_context_deinit(context); + + if (ret < 0) { goto cleanup; } @@ -2300,13 +2519,14 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, unsigned int i; unsigned rnd_level; nettle_random_func *rnd_func; + bool not_approved = false; FAIL_IF_LIB_ERROR; - if (IS_EC(algo)) { - /* check if the curve relates to the algorithm used */ - if (gnutls_ecc_curve_get_pk(level) != algo) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + /* check if the curve relates to the algorithm used */ + if (IS_EC(algo) && gnutls_ecc_curve_get_pk(level) != algo) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; } if (ephemeral) { @@ -2324,8 +2544,14 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, struct dsa_params pub; mpz_t x, y; - if (params->params[DSA_Q] == NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + /* DSA is currently being defined as sunset with the + * current draft of FIPS 186-5 */ + not_approved = true; + + if (params->params[DSA_Q] == NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } _dsa_params_get(params, &pub); @@ -2357,7 +2583,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, mpz_clear(y); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2374,8 +2600,10 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, mpz_t primesub1; mpz_t ypowq; - if (algo != params->algo) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (algo != params->algo) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } _dsa_params_get(params, &pub); @@ -2383,8 +2611,10 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, have_q = 1; /* This check is for the case !ENABLE_FIPS140 */ - if (algo == GNUTLS_PK_DSA && have_q == 0) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (algo == GNUTLS_PK_DSA && have_q == 0) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } mpz_init(r); mpz_init(x); @@ -2480,7 +2710,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, mpz_clear(ypowq); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2526,6 +2756,8 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, } while (ret != 1 && ++retries < 3); } } else { + not_approved = true; + ret = rsa_generate_keypair(&pub, &priv, NULL, rnd_func, NULL, NULL, @@ -2547,6 +2779,10 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, params->params_nr++; } + /* In FIPS 140-3, pub.n should be 2048-bit or larger; it + * is assured in rsa_generate_fips186_4_keypair in + * lib/nettle/int/rsa-keygen-fips186.c. */ + mpz_set(TOMPZ(params->params[RSA_MODULUS]), pub.n); mpz_set(TOMPZ(params->params[RSA_PUB]), pub.e); mpz_set(TOMPZ(params->params[RSA_PRIV]), priv.d); @@ -2563,7 +2799,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, rsa_public_key_clear(&pub); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2572,31 +2808,42 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, { unsigned size = gnutls_ecc_curve_get_size(level); - if (params->pkflags & GNUTLS_PK_FLAG_PROVABLE) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + /* EdDSA is not approved yet */ + not_approved = true; + + if (params->pkflags & GNUTLS_PK_FLAG_PROVABLE) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } - if (unlikely(get_eddsa_curve(algo) != level)) - return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (unlikely(get_eddsa_curve(algo) != level)) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } - if (size == 0) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (size == 0) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } params->curve = level; params->raw_priv.data = gnutls_malloc(size); - if (params->raw_priv.data == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + if (params->raw_priv.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } params->raw_pub.data = gnutls_malloc(size); if (params->raw_pub.data == NULL) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - goto fail; + goto cleanup; } ret = gnutls_rnd(rnd_level, params->raw_priv.data, size); if (ret < 0) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - goto fail; + goto cleanup; } params->raw_pub.size = size; params->raw_priv.size = size; @@ -2605,7 +2852,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, params->raw_pub.data, params->raw_priv.data); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2623,10 +2870,15 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, mpz_t x, y, xx, yy, nn, mm; curve = get_supported_nist_curve(level); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } + + /* P-192 is not supported in FIPS 140-3 */ + if (level == GNUTLS_ECC_CURVE_SECP192R1) { + not_approved = true; + } mpz_init(x); mpz_init(y); @@ -2734,6 +2986,8 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); goto ecc_fail; } + } else { + not_approved = true; } #endif @@ -2758,7 +3012,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, ecc_scalar_clear(&m); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2775,15 +3029,20 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, const struct ecc_curve *curve; const mac_entry_st *me; + /* GOST curves are not approved */ + not_approved = true; + curve = get_supported_gost_curve(level); - if (curve == NULL) - return - gnutls_assert_val - (GNUTLS_E_ECC_UNSUPPORTED_CURVE); + if (curve == NULL) { + ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + goto cleanup; + } me = hash_to_entry(_gnutls_gost_digest(algo)); - if (!me || me->output_size * 8 != ecc_bit_size(curve)) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (!me || me->output_size * 8 != ecc_bit_size(curve)) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } ecc_scalar_init(&key, curve); ecc_point_init(&pub, curve); @@ -2816,7 +3075,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, ecc_scalar_clear(&key); if (ret < 0) - goto fail; + goto cleanup; break; } @@ -2826,32 +3085,38 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, { unsigned size = gnutls_ecc_curve_get_size(level); - if (size == 0) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + not_approved = true; + + if (size == 0) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } params->curve = level; params->raw_priv.data = gnutls_malloc(size); - if (params->raw_priv.data == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + if (params->raw_priv.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } params->raw_pub.data = gnutls_malloc(size); if (params->raw_pub.data == NULL) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - goto fail; + goto cleanup; } ret = gnutls_rnd(rnd_level, params->raw_priv.data, size); if (ret < 0) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - goto fail; + goto cleanup; } params->raw_pub.size = size; params->raw_priv.size = size; ret = edwards_curve_mul_g(algo, params->raw_pub.data, params->raw_priv.data); if (ret < 0) - goto fail; + goto cleanup; break; } default: @@ -2865,21 +3130,24 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, ret = pct_test(algo, params); if (ret < 0) { gnutls_assert(); - goto fail; + goto cleanup; } #endif - FAIL_IF_LIB_ERROR; - return 0; - - fail: - - for (i = 0; i < params->params_nr; i++) { - _gnutls_mpi_release(¶ms->params[i]); + cleanup: + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + for (i = 0; i < params->params_nr; i++) { + _gnutls_mpi_release(¶ms->params[i]); + } + params->params_nr = 0; + gnutls_free(params->raw_priv.data); + gnutls_free(params->raw_pub.data); + } else if (not_approved) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); } - params->params_nr = 0; - gnutls_free(params->raw_priv.data); - gnutls_free(params->raw_pub.data); FAIL_IF_LIB_ERROR; return ret; @@ -1200,6 +1200,18 @@ pk_prepare_hash(gnutls_pk_algorithm_t pk, case GNUTLS_PK_RSA: if (unlikely(hash == NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* Only SHA-2 is allowed in FIPS 140-3 */ + switch (hash->id) { + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + break; + default: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + /* Encode the digest as a DigestInfo */ if ((ret = diff --git a/lib/pubkey.c b/lib/pubkey.c index 14171f68d7..eba1f5b7b3 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -2367,6 +2367,19 @@ _pkcs1_rsa_verify_sig(gnutls_pk_algorithm_t pk, d.size = digest_size; if (pk == GNUTLS_PK_RSA) { + /* SHA-1 is allowed for SigVer in FIPS 140-3 in legacy + * mode */ + switch (me->id) { + case GNUTLS_MAC_SHA1: + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + break; + default: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + /* decrypted is a BER encoded data of type DigestInfo */ ret = encode_ber_digest_info(me, &d, &di); diff --git a/tests/dh-compute.c b/tests/dh-compute.c index 64eb2c5804..828fb05e9c 100644 --- a/tests/dh-compute.c +++ b/tests/dh-compute.c @@ -95,6 +95,8 @@ struct dh_test_data { const gnutls_datum_t generator; const gnutls_datum_t peer_key; int expected_error; + gnutls_fips140_operation_state_t fips_state_genkey; + gnutls_fips140_operation_state_t fips_state_compute_key; }; void doit(void) @@ -106,7 +108,10 @@ void doit(void) gnutls_ffdhe_2048_group_q, gnutls_ffdhe_2048_group_generator, { (void *)"\x00", 1 }, - GNUTLS_E_MPI_SCAN_FAILED + GNUTLS_E_MPI_SCAN_FAILED, + GNUTLS_FIPS140_OP_APPROVED, + /* does not reach _wrap_nettle_pk_derive */ + GNUTLS_FIPS140_OP_INITIAL, }, { "[y < 2]", @@ -114,7 +119,9 @@ void doit(void) gnutls_ffdhe_2048_group_q, gnutls_ffdhe_2048_group_generator, { (void *)"\x01", 1 }, - GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER + GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_ERROR, }, { "[y > p - 2]", @@ -122,7 +129,9 @@ void doit(void) gnutls_ffdhe_2048_group_q, gnutls_ffdhe_2048_group_generator, gnutls_ffdhe_2048_group_prime, - GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER + GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_ERROR, }, { "[y ^ q mod p == 1]", @@ -130,7 +139,9 @@ void doit(void) gnutls_ffdhe_2048_group_q, gnutls_ffdhe_2048_group_generator, gnutls_ffdhe_2048_group_q, - GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER + GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_ERROR, }, { "Legal Input", @@ -138,27 +149,76 @@ void doit(void) gnutls_ffdhe_2048_group_q, gnutls_ffdhe_2048_group_generator, { (void *)"\x02", 1 }, - 0 + 0, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_APPROVED, }, { NULL } }; +#define FIPS_PUSH_CONTEXT() do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_push_context(fips_context); \ + if (ret < 0) { \ + fail("gnutls_fips140_push_context failed\n"); \ + } \ + } \ +} while (0) + +#define FIPS_POP_CONTEXT(state) do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_pop_context(); \ + if (ret < 0) { \ + fail("gnutls_fips140_context_pop failed\n"); \ + } \ + fips_state = gnutls_fips140_get_operation_state(fips_context); \ + if (fips_state != state) { \ + fail("operation state is not %d (%d)\n", \ + state, fips_state); \ + } \ + } \ +} while (0) + for (int i = 0; test_data[i].name != NULL; i++) { gnutls_datum_t priv_key, pub_key; gnutls_dh_params_t dh_params; - + gnutls_fips140_context_t fips_context; + gnutls_fips140_operation_state_t fips_state; + int ret; + + if (gnutls_fips140_mode_enabled()) { + ret = gnutls_fips140_context_init(&fips_context); + if (ret < 0) { + fail("Cannot initialize FIPS context\n"); + } + } + + FIPS_PUSH_CONTEXT(); params(&dh_params, &test_data[i].prime, &test_data[i].q, &test_data[i].generator); + FIPS_POP_CONTEXT(GNUTLS_FIPS140_OP_INITIAL); + success("%s genkey\n", test_data[i].name); + + FIPS_PUSH_CONTEXT(); genkey(dh_params, &priv_key, &pub_key); + FIPS_POP_CONTEXT(test_data[i].fips_state_genkey); + success("%s compute_key\n", test_data[i].name); + + FIPS_PUSH_CONTEXT(); compute_key(test_data[i].name, dh_params, &priv_key, &pub_key, &test_data[i].peer_key, test_data[i].expected_error, NULL, 0); + FIPS_POP_CONTEXT(test_data[i].fips_state_compute_key); gnutls_dh_params_deinit(dh_params); gnutls_free(priv_key.data); gnutls_free(pub_key.data); + + if (gnutls_fips140_mode_enabled()) { + gnutls_fips140_context_deinit(fips_context); + } } success("all ok\n"); diff --git a/tests/fips-test.c b/tests/fips-test.c index 23d2318122..d72b5d2bce 100644 --- a/tests/fips-test.c +++ b/tests/fips-test.c @@ -22,6 +22,93 @@ static void tls_log_func(int level, const char *str) static uint8_t key16[16]; static uint8_t iv16[16]; +static const gnutls_datum_t data = { .data = (unsigned char *)"foo", 3 }; +static const uint8_t rsa2342_sha1_sig_data[] = { + 0x9b, 0x3e, 0x15, 0x36, 0xec, 0x9d, 0x51, 0xd7, 0xa2, 0xb1, 0x3a, 0x15, + 0x1a, 0xfe, 0x4e, 0x12, 0x43, 0x3c, 0xa8, 0x58, 0x4c, 0x2a, 0x82, 0xc1, + 0x02, 0x3f, 0xc0, 0x6f, 0xa2, 0x23, 0xba, 0x58, 0x9f, 0xc0, 0xfc, 0x87, + 0x5e, 0xfd, 0x13, 0x32, 0xa6, 0xd9, 0x72, 0x63, 0x04, 0x68, 0xb9, 0x0f, + 0x46, 0x21, 0x3f, 0x7f, 0xe1, 0xa2, 0xb0, 0xfa, 0x66, 0x84, 0xd9, 0x64, + 0x87, 0x40, 0x31, 0x27, 0xec, 0xb3, 0xbb, 0x53, 0xb5, 0x8f, 0xf9, 0x3c, + 0x45, 0x1c, 0xcc, 0x30, 0xf5, 0xab, 0x9e, 0x1b, 0x86, 0x92, 0x6a, 0x58, + 0xeb, 0xa1, 0x87, 0x71, 0x40, 0xfb, 0x9d, 0x8f, 0x2c, 0x82, 0x32, 0xe1, + 0x7f, 0xfc, 0xe9, 0xd1, 0x76, 0xa3, 0x56, 0xdf, 0x38, 0xdb, 0xe2, 0x8a, + 0xd3, 0x7e, 0xb4, 0xe2, 0xc9, 0x6a, 0xb2, 0x02, 0xe8, 0xf6, 0x34, 0xde, + 0x51, 0x36, 0xd7, 0x3a, 0xba, 0x0f, 0x51, 0x3d, 0xb0, 0xe8, 0x8e, 0x58, + 0x72, 0x1c, 0x89, 0xac, 0x68, 0xa5, 0x03, 0xb1, 0xd6, 0x5d, 0x32, 0x2f, + 0x3c, 0x71, 0xcc, 0xc2, 0xd7, 0xf9, 0x51, 0xb1, 0xc8, 0x07, 0x07, 0x63, + 0xe7, 0xa9, 0x9b, 0x9f, 0xdb, 0xc5, 0xb5, 0x68, 0xfd, 0xed, 0x11, 0x0c, + 0xa7, 0xfa, 0x08, 0x59, 0xa8, 0x84, 0xcd, 0x36, 0x6b, 0xa5, 0xfe, 0xf9, + 0xd3, 0xe1, 0x36, 0xaf, 0x71, 0x47, 0x39, 0x1e, 0xb7, 0xbc, 0x06, 0x66, + 0xb8, 0xd7, 0x6d, 0x37, 0x6d, 0x52, 0x85, 0x34, 0x2b, 0x05, 0x62, 0x2e, + 0xbe, 0x6d, 0xa3, 0x76, 0xcd, 0xe0, 0xd6, 0x3e, 0x9d, 0xcf, 0x74, 0xf9, + 0xb4, 0x6b, 0xc0, 0x20, 0xe9, 0xd7, 0x19, 0x2d, 0xe6, 0x8a, 0xfd, 0xa2, + 0xa4, 0x4a, 0xea, 0x01, 0x91, 0xf5, 0xb5, 0x29, 0x7a, 0xda, 0x68, 0xc6, + 0x6c, 0xa0, 0x99, 0x5b, 0x79, 0x18, 0x96, 0xb1, 0xbe, 0x38, 0x74, 0x66, + 0x4b, 0x47, 0x46, 0x89, 0xea, 0x25, 0x2a, 0x9e, 0x3a, 0xdc, 0x49, 0x6b, + 0xba, 0xcb, 0xe4, 0x7a, 0x8f, 0x60, 0x35, 0xf3, 0x9f, 0x9d, 0xeb, 0x9d, + 0xfa, 0x0c, 0xaf, 0x6e, 0x47, 0x65, 0xaf, 0x17, 0x18, 0x56, 0x16, 0xe8, + 0x01, 0xd5, 0x55, 0xdf, 0xca, 0x41, 0x63, 0xd0, 0x48, 0x9b, 0x08, 0xdb, + 0xdd, 0x73, 0x4a, 0xa5, +}; + +static const gnutls_datum_t rsa2342_sha1_sig = { + .data = (unsigned char *)rsa2342_sha1_sig_data, + .size = sizeof(rsa2342_sha1_sig_data), +}; + +static void +rsa_import_keypair(gnutls_privkey_t *privkey, gnutls_pubkey_t *pubkey, + const char *filename) +{ + const char *srcdir; + char path[256]; + gnutls_datum_t tmp; + gnutls_x509_privkey_t xprivkey; + int ret; + + ret = gnutls_x509_privkey_init(&xprivkey); + if (ret < 0) { + fail("gnutls_x509_privkey_init failed\n"); + } + srcdir = getenv("srcdir"); + if (!srcdir) { + srcdir = "."; + } + snprintf(path, sizeof(path), "%s/certs/%s", srcdir, filename); + ret = gnutls_load_file(path, &tmp); + if (ret < 0) { + fail("gnutls_load_file failed\n"); + } + ret = gnutls_x509_privkey_import(xprivkey, &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + fail("gnutls_x509_privkey_import failed\n"); + } + gnutls_free(tmp.data); + + ret = gnutls_privkey_init(privkey); + if (ret < 0) { + fail("gnutls_privkey_init failed\n"); + } + ret = gnutls_privkey_import_x509(*privkey, xprivkey, + GNUTLS_PRIVKEY_IMPORT_COPY); + if (ret < 0) { + fail("gnutls_privkey_import_x509 failed\n"); + } + gnutls_x509_privkey_deinit(xprivkey); + + ret = gnutls_pubkey_init(pubkey); + if (ret < 0) { + fail("gnutls_pubkey_init failed\n"); + } + ret = gnutls_pubkey_import_privkey(*pubkey, *privkey, + GNUTLS_KEY_DIGITAL_SIGNATURE, 0); + if (ret < 0) { + fail("gnutls_pubkey_import_privkey failed\n"); + } + +} + void doit(void) { int ret; @@ -34,6 +121,11 @@ void doit(void) gnutls_privkey_t privkey; gnutls_datum_t key = { key16, sizeof(key16) }; gnutls_datum_t iv = { iv16, sizeof(iv16) }; + gnutls_fips140_context_t fips_context; + gnutls_fips140_operation_state_t fips_state; + gnutls_datum_t signature; + unsigned int bits; + uint8_t hmac[64]; fprintf(stderr, "Please note that if in FIPS140 mode, you need to assure the library's integrity prior to running this test\n"); @@ -53,6 +145,38 @@ void doit(void) fail("Cannot initialize library\n"); } + ret = gnutls_fips140_context_init(&fips_context); + if (ret < 0) { + fail("Cannot initialize FIPS context\n"); + } + fips_state = gnutls_fips140_get_operation_state(fips_context); + if (fips_state != GNUTLS_FIPS140_OP_INITIAL) { + fail("operation state is not initial\n"); + } + ret = gnutls_fips140_pop_context(); + if (ret != GNUTLS_E_INVALID_REQUEST) { + fail("gnutls_fips140_pop_context succeeded while not pushed\n"); + } + +#define FIPS_PUSH_CONTEXT() do { \ + ret = gnutls_fips140_push_context(fips_context); \ + if (ret < 0) { \ + fail("gnutls_fips140_push_context failed\n"); \ + } \ +} while (0) + +#define FIPS_POP_CONTEXT(state) do { \ + ret = gnutls_fips140_pop_context(); \ + if (ret < 0) { \ + fail("gnutls_fips140_context_pop failed\n"); \ + } \ + fips_state = gnutls_fips140_get_operation_state(fips_context); \ + if (fips_state != GNUTLS_FIPS140_OP_ ## state) { \ + fail("operation state is not " # state " (%d)\n", \ + fips_state); \ + } \ +} while (0) + /* Try crypto.h functionality */ ret = gnutls_cipher_init(&ch, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); @@ -78,6 +202,42 @@ void doit(void) fail("gnutls_hmac_init succeeded for md5\n"); } + /* HMAC with key equal to or longer than 112 bits: approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_hmac_init(&mh, GNUTLS_MAC_SHA256, key.data, key.size); + if (ret < 0) { + fail("gnutls_hmac_init failed\n"); + } + gnutls_hmac_deinit(mh, NULL); + FIPS_POP_CONTEXT(APPROVED); + + /* HMAC with key shorter than 112 bits: not approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_hmac_init(&mh, GNUTLS_MAC_SHA256, key.data, 13); + if (ret < 0) { + fail("gnutls_hmac_init failed\n"); + } + gnutls_hmac_deinit(mh, NULL); + FIPS_POP_CONTEXT(NOT_APPROVED); + + /* HMAC with key equal to or longer than 112 bits: approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_hmac_fast(GNUTLS_MAC_SHA256, key.data, key.size, + data.data, data.size, hmac); + if (ret < 0) { + fail("gnutls_hmac_fast failed\n"); + } + FIPS_POP_CONTEXT(APPROVED); + + /* HMAC with key shorter than 112 bits: not approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_hmac_fast(GNUTLS_MAC_SHA256, key.data, 13, + data.data, data.size, hmac); + if (ret < 0) { + fail("gnutls_hmac_fast failed\n"); + } + FIPS_POP_CONTEXT(NOT_APPROVED); + ret = gnutls_rnd(GNUTLS_RND_NONCE, key16, sizeof(key16)); if (ret < 0) { fail("gnutls_rnd failed\n"); @@ -101,6 +261,23 @@ void doit(void) } gnutls_deinit(session); + /* Generate 2048-bit RSA key */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_x509_privkey_init(&xprivkey); + if (ret < 0) { + fail("gnutls_privkey_init failed\n"); + } + bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_MEDIUM); + ret = gnutls_x509_privkey_generate(xprivkey, GNUTLS_PK_RSA, bits, 0); + if (ret < 0) { + fail("gnutls_x509_privkey_generate failed (%d) for %u-bit key\n", + ret, bits); + } + gnutls_x509_privkey_deinit(xprivkey); + FIPS_POP_CONTEXT(APPROVED); + + /* Generate 512-bit RSA key */ + FIPS_PUSH_CONTEXT(); ret = gnutls_x509_privkey_init(&xprivkey); if (ret < 0) { fail("gnutls_privkey_init failed\n"); @@ -110,6 +287,60 @@ void doit(void) fail("gnutls_x509_privkey_generate succeeded (%d) for 512-bit key\n", ret); } gnutls_x509_privkey_deinit(xprivkey); + FIPS_POP_CONTEXT(ERROR); + + /* Import 2432-bit RSA key; not a security function */ + FIPS_PUSH_CONTEXT(); + rsa_import_keypair(&privkey, &pubkey, "rsa-2432.pem"); + FIPS_POP_CONTEXT(INITIAL); + + /* Create a signature with SHA256; approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_privkey_sign_data(privkey, GNUTLS_DIG_SHA256, 0, + &data, &signature); + if (ret < 0) { + fail("gnutls_privkey_sign_data failed\n"); + } + gnutls_free(signature.data); + FIPS_POP_CONTEXT(APPROVED); + + /* Create a signature with SHA-1; not approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_privkey_sign_data(privkey, GNUTLS_DIG_SHA1, 0, + &data, &signature); + if (ret < 0) { + fail("gnutls_privkey_sign_data failed\n"); + } + gnutls_free(signature.data); + FIPS_POP_CONTEXT(NOT_APPROVED); + + /* Verify a signature created with SHA-1; approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_pubkey_verify_data2(pubkey, GNUTLS_SIGN_RSA_SHA1, 0, &data, + &rsa2342_sha1_sig); + if (ret < 0) { + fail("gnutls_pubkey_verify_data2 failed\n"); + } + FIPS_POP_CONTEXT(APPROVED); + gnutls_pubkey_deinit(pubkey); + gnutls_privkey_deinit(privkey); + + /* Import 512-bit RSA key; not a security function */ + FIPS_PUSH_CONTEXT(); + rsa_import_keypair(&privkey, &pubkey, "rsa-512.pem"); + FIPS_POP_CONTEXT(INITIAL); + + /* Create a signature; not approved */ + FIPS_PUSH_CONTEXT(); + ret = gnutls_privkey_sign_data(privkey, GNUTLS_DIG_SHA256, 0, + &data, &signature); + if (ret < 0) { + fail("gnutls_privkey_sign_data failed\n"); + } + gnutls_free(signature.data); + FIPS_POP_CONTEXT(NOT_APPROVED); + gnutls_pubkey_deinit(pubkey); + gnutls_privkey_deinit(privkey); /* Test when FIPS140 is set to error state */ _gnutls_lib_simulate_error(); @@ -152,6 +383,7 @@ void doit(void) fail("gnutls_init succeeded when in FIPS140 error state\n"); } + gnutls_fips140_context_deinit(fips_context); gnutls_global_deinit(); return; } diff --git a/tests/kdf-api.c b/tests/kdf-api.c index ec74f44ce8..25fbc6a81f 100644 --- a/tests/kdf-api.c +++ b/tests/kdf-api.c @@ -32,6 +32,32 @@ #define MAX_BUF 1024 +static gnutls_fips140_context_t fips_context; +static gnutls_fips140_operation_state_t fips_state; + +#define FIPS_PUSH_CONTEXT() do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_push_context(fips_context); \ + if (ret < 0) { \ + fail("gnutls_fips140_push_context failed\n"); \ + } \ + } \ +} while (0) + +#define FIPS_POP_CONTEXT(state) do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_pop_context(); \ + if (ret < 0) { \ + fail("gnutls_fips140_context_pop failed\n"); \ + } \ + fips_state = gnutls_fips140_get_operation_state(fips_context); \ + if (fips_state != GNUTLS_FIPS140_OP_ ## state) { \ + fail("operation state is not " # state " (%d)\n", \ + fips_state); \ + } \ + } \ +} while (0) + static void test_hkdf(gnutls_mac_algorithm_t mac, const char *ikm_hex, @@ -48,6 +74,7 @@ test_hkdf(gnutls_mac_algorithm_t mac, gnutls_datum_t prk; gnutls_datum_t okm; uint8_t buf[MAX_BUF]; + int ret; success("HKDF test with %s\n", gnutls_mac_get_name(mac)); @@ -60,7 +87,9 @@ test_hkdf(gnutls_mac_algorithm_t mac, hex.size = strlen(salt_hex); assert(gnutls_hex_decode2(&hex, &salt) >= 0); + FIPS_PUSH_CONTEXT(); assert(gnutls_hkdf_extract(mac, &ikm, &salt, buf) >= 0); + FIPS_POP_CONTEXT(NOT_APPROVED); gnutls_free(ikm.data); gnutls_free(salt.data); @@ -79,7 +108,9 @@ test_hkdf(gnutls_mac_algorithm_t mac, hex.size = strlen(info_hex); assert(gnutls_hex_decode2(&hex, &info) >= 0); + FIPS_PUSH_CONTEXT(); assert(gnutls_hkdf_expand(mac, &prk, &info, buf, length) >= 0); + FIPS_POP_CONTEXT(NOT_APPROVED); gnutls_free(info.data); okm.data = buf; @@ -106,6 +137,7 @@ test_pbkdf2(gnutls_mac_algorithm_t mac, gnutls_datum_t salt; gnutls_datum_t okm; uint8_t buf[MAX_BUF]; + int ret; success("PBKDF2 test with %s\n", gnutls_mac_get_name(mac)); @@ -117,7 +149,9 @@ test_pbkdf2(gnutls_mac_algorithm_t mac, hex.size = strlen(salt_hex); assert(gnutls_hex_decode2(&hex, &salt) >= 0); + FIPS_PUSH_CONTEXT(); assert(gnutls_pbkdf2(mac, &ikm, &salt, iter_count, buf, length) >= 0); + FIPS_POP_CONTEXT(APPROVED); gnutls_free(ikm.data); gnutls_free(salt.data); @@ -135,6 +169,8 @@ test_pbkdf2(gnutls_mac_algorithm_t mac, void doit(void) { + assert(gnutls_fips140_context_init(&fips_context) >= 0); + /* Test vector from RFC 5869. More thorough testing is done * in nettle. */ test_hkdf(GNUTLS_MAC_SHA256, @@ -157,4 +193,6 @@ doit(void) 4096, 20, "4b007901b765489abead49d926f721d065a429c1"); + + gnutls_fips140_context_deinit(fips_context); } diff --git a/tests/privkey-keygen.c b/tests/privkey-keygen.c index 565beccb20..cac530fc43 100644 --- a/tests/privkey-keygen.c +++ b/tests/privkey-keygen.c @@ -49,7 +49,7 @@ static int sec_param[MAX_TRIES] = static void tls_log_func(int level, const char *str) { - fprintf(stderr, "%s |<%d>| %s", "crq_key_id", level, str); + fprintf(stderr, "%s |<%d>| %s", "privkey-keygen", level, str); } const gnutls_datum_t raw_data = { @@ -102,10 +102,47 @@ static void sign_verify_data(gnutls_pk_algorithm_t algorithm, gnutls_x509_privke gnutls_free(signature.data); } +static unsigned int +is_approved_pk_algo(gnutls_pk_algorithm_t algo) { + switch (algo) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + case GNUTLS_PK_EC: + return 1; + default: + return 0; + } +} + void doit(void) { gnutls_x509_privkey_t pkey, dst; int ret, algorithm, i; + gnutls_fips140_context_t fips_context; + gnutls_fips140_operation_state_t fips_state; + +#define FIPS_PUSH_CONTEXT() do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_push_context(fips_context); \ + if (ret < 0) { \ + fail("gnutls_fips140_push_context failed\n"); \ + } \ + } \ +} while (0) + +#define FIPS_POP_CONTEXT(state) do { \ + if (gnutls_fips140_mode_enabled()) { \ + ret = gnutls_fips140_pop_context(); \ + if (ret < 0) { \ + fail("gnutls_fips140_context_pop failed\n"); \ + } \ + fips_state = gnutls_fips140_get_operation_state(fips_context); \ + if (fips_state != GNUTLS_FIPS140_OP_ ## state) { \ + fail("operation state is not " # state " (%d)\n", \ + fips_state); \ + } \ + } \ +} while (0) ret = global_init(); if (ret < 0) @@ -115,6 +152,11 @@ void doit(void) if (debug) gnutls_global_set_log_level(4711); + ret = gnutls_fips140_context_init(&fips_context); + if (ret < 0) { + fail("Cannot initialize FIPS context\n"); + } + for (i = 0; i < MAX_TRIES; i++) { for (algorithm = GNUTLS_PK_RSA; algorithm <= GNUTLS_PK_MAX; algorithm++) { @@ -150,6 +192,7 @@ void doit(void) ret); } + FIPS_PUSH_CONTEXT(); ret = gnutls_x509_privkey_generate(pkey, algorithm, gnutls_sec_param_to_pk_bits @@ -164,6 +207,11 @@ void doit(void) gnutls_pk_algorithm_get_name (algorithm), ret); } + if (is_approved_pk_algo(algorithm)) { + FIPS_POP_CONTEXT(APPROVED); + } else { + FIPS_POP_CONTEXT(NOT_APPROVED); + } ret = gnutls_x509_privkey_verify_params(pkey); if (ret < 0) { @@ -181,8 +229,21 @@ void doit(void) fail("gnutls_x509_privkey_generate after cpy (%s): %s (%d)\n", gnutls_pk_algorithm_get_name(algorithm), gnutls_strerror(ret), ret); } + FIPS_PUSH_CONTEXT(); sign_verify_data(algorithm, pkey); + if (is_approved_pk_algo(algorithm)) { + FIPS_POP_CONTEXT(APPROVED); + } else { + FIPS_POP_CONTEXT(NOT_APPROVED); + } + + FIPS_PUSH_CONTEXT(); sign_verify_data(algorithm, dst); + if (is_approved_pk_algo(algorithm)) { + FIPS_POP_CONTEXT(APPROVED); + } else { + FIPS_POP_CONTEXT(NOT_APPROVED); + } gnutls_x509_privkey_deinit(pkey); gnutls_x509_privkey_deinit(dst); diff --git a/tests/slow/cipher-api-test.c b/tests/slow/cipher-api-test.c index a8e4bbf90a..b58c60d596 100644 --- a/tests/slow/cipher-api-test.c +++ b/tests/slow/cipher-api-test.c @@ -30,6 +30,11 @@ #include <gnutls/abstract.h> #include <gnutls/x509.h> +/* Test illegal use of gnutls_cipher_* and gnutls_aead_cipher_* + * API. This test is written using fork, because some of the test + * cases may hit assertion failure in Nettle and crash the process. + */ + #if defined(WIN32) int main(int argc, char **argv) { |