From 7d8d8feb502ddb20a0d115fa3f63403c849a7168 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Thu, 10 Feb 2022 16:43:08 +0100 Subject: [PATCH 1/2] pkcs12: mark MAC generation and verification as FIPS non-approved Signed-off-by: Daiki Ueno --- lib/x509/pkcs12.c | 39 +++++++++++++++++++++++++--- tests/pkcs12_encode.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c index a8f7d8f956..11b9da3ac9 100644 --- a/lib/x509/pkcs12.c +++ b/lib/x509/pkcs12.c @@ -286,13 +286,26 @@ gnutls_pkcs12_export(gnutls_pkcs12_t pkcs12, gnutls_x509_crt_fmt_t format, void *output_data, size_t * output_data_size) { + int ret; + if (pkcs12 == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } - return _gnutls_x509_export_int(pkcs12->pkcs12, format, PEM_PKCS12, - output_data, output_data_size); + ret = _gnutls_x509_export_int(pkcs12->pkcs12, format, PEM_PKCS12, + output_data, output_data_size); + + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + /* PKCS#12 export is always non-approved, because the MAC + * calculation involves non-approved KDF (PKCS#12 KDF) and + * without MAC the protection is insufficient. + */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; } /** @@ -317,13 +330,25 @@ int gnutls_pkcs12_export2(gnutls_pkcs12_t pkcs12, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) { + int ret; + if (pkcs12 == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } - return _gnutls_x509_export_int2(pkcs12->pkcs12, format, PEM_PKCS12, - out); + ret = _gnutls_x509_export_int2(pkcs12->pkcs12, format, PEM_PKCS12, + out); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + /* PKCS#12 export is always non-approved, because the MAC + * calculation involves non-approved KDF (PKCS#12 KDF) and + * without MAC the protection is insufficient. + */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; } static int oid2bag(const char *oid) @@ -1025,9 +1050,12 @@ int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12, gnutls_mac_algorithm_t m goto cleanup; } + /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); return 0; cleanup: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); _gnutls_free_datum(&tmp); return result; } @@ -1203,8 +1231,11 @@ pkcs12_try_gost: goto cleanup; } + /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); result = 0; cleanup: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); _gnutls_free_datum(&tmp); _gnutls_free_datum(&salt); return result; diff --git a/tests/pkcs12_encode.c b/tests/pkcs12_encode.c index 3b0e84ef13..b8f7d17267 100644 --- a/tests/pkcs12_encode.c +++ b/tests/pkcs12_encode.c @@ -70,6 +70,29 @@ static void tls_log_func(int level, const char *str) fprintf(stderr, "|<%d>| %s", level, str); } +#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) + void doit(void) { gnutls_pkcs12_t pkcs12; @@ -82,6 +105,8 @@ void doit(void) char outbuf[10240]; size_t size; unsigned tests, i; + gnutls_fips140_context_t fips_context; + gnutls_fips140_operation_state_t fips_state; ret = global_init(); if (ret < 0) { @@ -93,6 +118,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"); + } + /* Read certs. */ ret = gnutls_x509_crt_init(&client); if (ret < 0) { @@ -196,6 +226,8 @@ void doit(void) gnutls_pkcs12_bag_deinit(bag); } + FIPS_PUSH_CONTEXT(); + /* MAC the structure, export and print. */ ret = gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA1, "pass"); if (ret < 0) { @@ -203,36 +235,60 @@ void doit(void) exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + ret = gnutls_pkcs12_verify_mac(pkcs12, "pass"); if (ret < 0) { fprintf(stderr, "verify_mac: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + ret = gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, "passwd"); if (ret < 0) { fprintf(stderr, "generate_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + ret = gnutls_pkcs12_verify_mac(pkcs12, "passwd"); if (ret < 0) { fprintf(stderr, "verify_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + ret = gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA512, "passwd1"); if (ret < 0) { fprintf(stderr, "generate_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + ret = gnutls_pkcs12_verify_mac(pkcs12, "passwd1"); if (ret < 0) { fprintf(stderr, "verify_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + + FIPS_PUSH_CONTEXT(); + size = sizeof(outbuf); ret = gnutls_pkcs12_export(pkcs12, GNUTLS_X509_FMT_PEM, outbuf, @@ -242,10 +298,13 @@ void doit(void) exit(1); } + FIPS_POP_CONTEXT(NOT_APPROVED); + if (debug) fwrite(outbuf, size, 1, stdout); /* Cleanup. */ + gnutls_fips140_context_deinit(fips_context); gnutls_pkcs12_deinit(pkcs12); gnutls_x509_crt_deinit(client); gnutls_x509_crt_deinit(ca); -- 2.34.1 From e7f9267342bc2231149a640163c82b63c86f1dfd Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Thu, 10 Feb 2022 17:35:13 +0100 Subject: [PATCH 2/2] _gnutls_pkcs_raw_{decrypt,encrypt}_data: use public crypto API These functions previously used the internal crypto API (_gnutls_cipher_*) which does not have algorithm checks for FIPS. This change switches the code to use the public crypto API (gnutls_cipher_*) to trigger proper state transitions under FIPS mode. Signed-off-by: Daiki Ueno --- lib/x509/pkcs7-crypt.c | 36 +++++++++++----------------- tests/pkcs12_encode.c | 54 +++++++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/x509/pkcs7-crypt.c b/lib/x509/pkcs7-crypt.c index 4cce52ecf0..2dc5bc4df0 100644 --- a/lib/x509/pkcs7-crypt.c +++ b/lib/x509/pkcs7-crypt.c @@ -1130,8 +1130,7 @@ _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, gnutls_datum_t enc = { NULL, 0 }; uint8_t *key = NULL; gnutls_datum_t dkey, d_iv; - cipher_hd_st ch; - int ch_init = 0; + gnutls_cipher_hd_t ch = NULL; int key_size, ret; unsigned int pass_len = 0; const struct pkcs_cipher_schema_st *p; @@ -1237,8 +1236,7 @@ _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, d_iv.data = (uint8_t *) enc_params->iv; d_iv.size = enc_params->iv_size; - ret = - _gnutls_cipher_init(&ch, ce, &dkey, &d_iv, 0); + ret = gnutls_cipher_init(&ch, ce->id, &dkey, &d_iv); gnutls_free(key); @@ -1247,9 +1245,7 @@ _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, goto error; } - ch_init = 1; - - ret = _gnutls_cipher_decrypt(&ch, enc.data, enc.size); + ret = gnutls_cipher_decrypt(ch, enc.data, enc.size); if (ret < 0) { gnutls_assert(); ret = GNUTLS_E_DECRYPTION_FAILED; @@ -1281,7 +1277,7 @@ _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, decrypted_data->size = enc.size; } - _gnutls_cipher_deinit(&ch); + gnutls_cipher_deinit(ch); ret = 0; @@ -1294,8 +1290,9 @@ _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, gnutls_free(password); gnutls_free(enc.data); gnutls_free(key); - if (ch_init != 0) - _gnutls_cipher_deinit(&ch); + if (ch) { + gnutls_cipher_deinit(ch); + } return ret; } @@ -1725,8 +1722,7 @@ _gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain, int data_size; uint8_t *data = NULL; gnutls_datum_t d_iv; - cipher_hd_st ch; - int ch_init = 0; + gnutls_cipher_hd_t ch = NULL; uint8_t pad, pad_size; const cipher_entry_st *ce; @@ -1756,18 +1752,13 @@ _gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain, d_iv.data = (uint8_t *) enc_params->iv; d_iv.size = enc_params->iv_size; - result = - _gnutls_cipher_init(&ch, cipher_to_entry(enc_params->cipher), - key, &d_iv, 1); - + result = gnutls_cipher_init(&ch, enc_params->cipher, key, &d_iv); if (result < 0) { gnutls_assert(); goto error; } - ch_init = 1; - - result = _gnutls_cipher_encrypt(&ch, data, data_size); + result = gnutls_cipher_encrypt(ch, data, data_size); if (result < 0) { gnutls_assert(); goto error; @@ -1776,13 +1767,14 @@ _gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain, encrypted->data = data; encrypted->size = data_size; - _gnutls_cipher_deinit(&ch); + gnutls_cipher_deinit(ch); return 0; error: gnutls_free(data); - if (ch_init != 0) - _gnutls_cipher_deinit(&ch); + if (ch) { + gnutls_cipher_deinit(ch); + } return result; } diff --git a/tests/pkcs12_encode.c b/tests/pkcs12_encode.c index b8f7d17267..78f6f41b48 100644 --- a/tests/pkcs12_encode.c +++ b/tests/pkcs12_encode.c @@ -104,9 +104,17 @@ void doit(void) int ret, indx; char outbuf[10240]; size_t size; - unsigned tests, i; + unsigned i; gnutls_fips140_context_t fips_context; gnutls_fips140_operation_state_t fips_state; + size_t n_tests = 0; + struct tests { + const char *name; + gnutls_x509_crt_t crt; + const char *friendly_name; + unsigned bag_encrypt_flags; + int bag_encrypt_expected; + } tests[2]; ret = global_init(); if (ret < 0) { @@ -157,21 +165,34 @@ void doit(void) exit(1); } - /* Generate and add PKCS#12 cert bags. */ - if (!gnutls_fips140_mode_enabled()) { - tests = 2; /* include RC2 */ + tests[n_tests].name = "3DES"; + tests[n_tests].crt = client; + tests[n_tests].friendly_name = "client"; + tests[n_tests].bag_encrypt_flags = GNUTLS_PKCS8_USE_PKCS12_3DES; + tests[n_tests].bag_encrypt_expected = 0; + n_tests++; + + tests[n_tests].name = "RC2-40"; + tests[n_tests].crt = ca; + tests[n_tests].friendly_name = "ca"; + tests[n_tests].bag_encrypt_flags = GNUTLS_PKCS_USE_PKCS12_RC2_40; + if (gnutls_fips140_mode_enabled()) { + tests[n_tests].bag_encrypt_expected = + GNUTLS_E_UNWANTED_ALGORITHM; } else { - tests = 1; + tests[n_tests].bag_encrypt_expected = 0; } + n_tests++; - for (i = 0; i < tests; i++) { + /* Generate and add PKCS#12 cert bags. */ + for (i = 0; i < n_tests; i++) { ret = gnutls_pkcs12_bag_init(&bag); if (ret < 0) { fprintf(stderr, "bag_init: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } - ret = gnutls_pkcs12_bag_set_crt(bag, i == 0 ? client : ca); + ret = gnutls_pkcs12_bag_set_crt(bag, tests[i].crt); if (ret < 0) { fprintf(stderr, "set_crt: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); @@ -180,16 +201,14 @@ void doit(void) indx = ret; ret = gnutls_pkcs12_bag_set_friendly_name(bag, indx, - i == - 0 ? "client" : - "ca"); + tests[i].friendly_name); if (ret < 0) { fprintf(stderr, "set_friendly_name: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } size = sizeof(key_id_buf); - ret = gnutls_x509_crt_get_key_id(i == 0 ? client : ca, 0, + ret = gnutls_x509_crt_get_key_id(tests[i].crt, 0, key_id_buf, &size); if (ret < 0) { fprintf(stderr, "get_key_id: %s (%d)\n", gnutls_strerror(ret), ret); @@ -206,14 +225,11 @@ void doit(void) } ret = gnutls_pkcs12_bag_encrypt(bag, "pass", - i == - 0 ? - GNUTLS_PKCS8_USE_PKCS12_3DES - : - GNUTLS_PKCS_USE_PKCS12_RC2_40); - if (ret < 0) { - fprintf(stderr, "bag_encrypt: %d: %s", ret, - i == 0 ? "3DES" : "RC2-40"); + tests[i].bag_encrypt_flags); + if (ret != tests[i].bag_encrypt_expected) { + fprintf(stderr, "bag_encrypt: returned %d, expected %d: %s", ret, + tests[i].bag_encrypt_expected, + tests[i].name); exit(1); } -- 2.34.1