summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2022-01-08 10:04:40 +0000
committerDaiki Ueno <ueno@gnu.org>2022-01-08 10:04:40 +0000
commit451002bd7db940038fd3a82308fbb5a8f0f76634 (patch)
tree90d161365301fd01bcde9477320b2d0099f2eb6b
parent527f2f799fa18f80941470ce89706c33e671b5ef (diff)
parentdb74acb11bc807d108425a16146182e93d6a73fe (diff)
downloadgnutls-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--NEWS6
-rw-r--r--devel/libgnutls.abignore18
-rw-r--r--devel/symbols.last5
-rw-r--r--doc/Makefile.am11
-rw-r--r--doc/cha-internals.texi24
-rw-r--r--doc/manpages/Makefile.am5
-rw-r--r--lib/cipher_int.c2
-rw-r--r--lib/crypto-api.c473
-rw-r--r--lib/fips.c182
-rw-r--r--lib/fips.h175
-rw-r--r--lib/hash_int.c4
-rw-r--r--lib/includes/gnutls/gnutls.h.in39
-rw-r--r--lib/libgnutls.map5
-rw-r--r--lib/nettle/pk.c654
-rw-r--r--lib/pk.c12
-rw-r--r--lib/pubkey.c13
-rw-r--r--tests/dh-compute.c72
-rw-r--r--tests/fips-test.c232
-rw-r--r--tests/kdf-api.c38
-rw-r--r--tests/privkey-keygen.c63
-rw-r--r--tests/slow/cipher-api-test.c5
21 files changed, 1683 insertions, 355 deletions
diff --git a/NEWS b/NEWS
index 638b3b51c2..adb04a155e 100644
--- a/NEWS
+++ b/NEWS
@@ -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, &params->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(&params->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(&params->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;
diff --git a/lib/pk.c b/lib/pk.c
index 1904f59453..c5600a32a3 100644
--- a/lib/pk.c
+++ b/lib/pk.c
@@ -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)
{