|
|
a74aed |
From 26b2caef673aba8bfd10db3b1b8117f941c18e58 Mon Sep 17 00:00:00 2001
|
|
|
a74aed |
From: Daiki Ueno <ueno@gnu.org>
|
|
|
a74aed |
Date: Fri, 21 Oct 2022 15:48:39 +0900
|
|
|
a74aed |
Subject: [PATCH] cipher: add restriction on CCM tag length under FIPS mode
|
|
|
a74aed |
|
|
|
a74aed |
This change prohibits any use of tag length other than 4, 6, 8, 10,
|
|
|
a74aed |
12, 14, and 16 bytes in CCM used under FIPS mode, in accordance with
|
|
|
a74aed |
SP800-38C A.1. While use of tag lengths smaller than 8 bytes is not
|
|
|
a74aed |
recommended, we simply allow 4 and 6 bytes tags for now.
|
|
|
a74aed |
|
|
|
a74aed |
Signed-off-by: Daiki Ueno <ueno@gnu.org>
|
|
|
a74aed |
---
|
|
|
a74aed |
lib/accelerated/aarch64/aes-ccm-aarch64.c | 39 ++++++++++
|
|
|
a74aed |
lib/accelerated/x86/aes-ccm-x86-aesni.c | 39 ++++++++++
|
|
|
a74aed |
lib/nettle/cipher.c | 55 ++++++++++++++
|
|
|
a74aed |
tests/fips-test.c | 87 ++++++++++++++++++++++-
|
|
|
a74aed |
4 files changed, 218 insertions(+), 2 deletions(-)
|
|
|
a74aed |
|
|
|
a74aed |
diff --git a/lib/accelerated/aarch64/aes-ccm-aarch64.c b/lib/accelerated/aarch64/aes-ccm-aarch64.c
|
|
|
a74aed |
index a2ba259e99..b415d4ddfb 100644
|
|
|
a74aed |
--- a/lib/accelerated/aarch64/aes-ccm-aarch64.c
|
|
|
a74aed |
+++ b/lib/accelerated/aarch64/aes-ccm-aarch64.c
|
|
|
a74aed |
@@ -36,6 +36,7 @@
|
|
|
a74aed |
#include <byteswap.h>
|
|
|
a74aed |
#include <nettle/ccm.h>
|
|
|
a74aed |
#include <aes-aarch64.h>
|
|
|
a74aed |
+#include <fips.h>
|
|
|
a74aed |
|
|
|
a74aed |
typedef struct ccm_aarch64_aes_ctx {
|
|
|
a74aed |
AES_KEY key;
|
|
|
a74aed |
@@ -103,6 +104,25 @@ aes_ccm_aead_encrypt(void *_ctx,
|
|
|
a74aed |
if (unlikely(encr_size < plain_size + tag_size))
|
|
|
a74aed |
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
|
|
|
a74aed |
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16 between 32
|
|
|
a74aed |
+ * and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64 should not be used
|
|
|
a74aed |
+ * under sufficient restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ccm_encrypt_message(&ctx->key, aarch64_aes_encrypt,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
@@ -129,6 +149,25 @@ aes_ccm_aead_decrypt(void *_ctx,
|
|
|
a74aed |
if (unlikely(plain_size < encr_size - tag_size))
|
|
|
a74aed |
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
|
|
|
a74aed |
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16 between 32
|
|
|
a74aed |
+ * and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64 should not be used
|
|
|
a74aed |
+ * under sufficient restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ret = ccm_decrypt_message(&ctx->key, aarch64_aes_encrypt,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
diff --git a/lib/accelerated/x86/aes-ccm-x86-aesni.c b/lib/accelerated/x86/aes-ccm-x86-aesni.c
|
|
|
a74aed |
index 701c0f992a..9ebbdd7b2a 100644
|
|
|
a74aed |
--- a/lib/accelerated/x86/aes-ccm-x86-aesni.c
|
|
|
a74aed |
+++ b/lib/accelerated/x86/aes-ccm-x86-aesni.c
|
|
|
a74aed |
@@ -37,6 +37,7 @@
|
|
|
a74aed |
#include <byteswap.h>
|
|
|
a74aed |
#include <nettle/ccm.h>
|
|
|
a74aed |
#include <aes-x86.h>
|
|
|
a74aed |
+#include <fips.h>
|
|
|
a74aed |
|
|
|
a74aed |
typedef struct ccm_x86_aes_ctx {
|
|
|
a74aed |
AES_KEY key;
|
|
|
a74aed |
@@ -95,6 +96,25 @@ aes_ccm_aead_encrypt(void *_ctx,
|
|
|
a74aed |
if (unlikely(encr_size < plain_size + tag_size))
|
|
|
a74aed |
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
|
|
|
a74aed |
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16 between 32
|
|
|
a74aed |
+ * and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64 should not be used
|
|
|
a74aed |
+ * under sufficient restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ccm_encrypt_message(&ctx->key, x86_aes_encrypt,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
@@ -121,6 +141,25 @@ aes_ccm_aead_decrypt(void *_ctx,
|
|
|
a74aed |
if (unlikely(plain_size < encr_size - tag_size))
|
|
|
a74aed |
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
|
|
|
a74aed |
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16 between 32
|
|
|
a74aed |
+ * and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64 should not be used
|
|
|
a74aed |
+ * under sufficient restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ret = ccm_decrypt_message(&ctx->key, x86_aes_encrypt,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
diff --git a/lib/nettle/cipher.c b/lib/nettle/cipher.c
|
|
|
a74aed |
index 9c2ce19e7e..8c23d11252 100644
|
|
|
a74aed |
--- a/lib/nettle/cipher.c
|
|
|
a74aed |
+++ b/lib/nettle/cipher.c
|
|
|
a74aed |
@@ -1253,6 +1253,34 @@ wrap_nettle_cipher_aead_encrypt(void *_ctx,
|
|
|
a74aed |
ctx->cipher->tag(ctx->ctx_ptr, tag_size, ((uint8_t*)encr) + plain_size);
|
|
|
a74aed |
} else {
|
|
|
a74aed |
/* CCM-style cipher */
|
|
|
a74aed |
+
|
|
|
a74aed |
+ switch (ctx->cipher->algo) {
|
|
|
a74aed |
+ case GNUTLS_CIPHER_AES_128_CCM:
|
|
|
a74aed |
+ case GNUTLS_CIPHER_AES_256_CCM:
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16
|
|
|
a74aed |
+ * between 32 and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64
|
|
|
a74aed |
+ * should not be used under sufficient
|
|
|
a74aed |
+ * restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ctx->cipher->aead_encrypt(ctx,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
@@ -1302,6 +1330,33 @@ wrap_nettle_cipher_aead_decrypt(void *_ctx,
|
|
|
a74aed |
if (unlikely(plain_size < encr_size))
|
|
|
a74aed |
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
|
|
|
a74aed |
|
|
|
a74aed |
+ switch (ctx->cipher->algo) {
|
|
|
a74aed |
+ case GNUTLS_CIPHER_AES_128_CCM:
|
|
|
a74aed |
+ case GNUTLS_CIPHER_AES_256_CCM:
|
|
|
a74aed |
+ /* SP800-38C A.1 says Tlen must be a multiple of 16
|
|
|
a74aed |
+ * between 32 and 128.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ switch (tag_size) {
|
|
|
a74aed |
+ case 4: case 6:
|
|
|
a74aed |
+ /* SP800-38C B.2 says Tlen smaller than 64
|
|
|
a74aed |
+ * should not be used under sufficient
|
|
|
a74aed |
+ * restriction. We simply allow those for now.
|
|
|
a74aed |
+ */
|
|
|
a74aed |
+ FALLTHROUGH;
|
|
|
a74aed |
+ case 8: case 10: case 12: case 14: case 16:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ if (_gnutls_fips_mode_enabled()) {
|
|
|
a74aed |
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ default:
|
|
|
a74aed |
+ break;
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+
|
|
|
a74aed |
ret = ctx->cipher->aead_decrypt(ctx,
|
|
|
a74aed |
nonce_size, nonce,
|
|
|
a74aed |
auth_size, auth,
|
|
|
a74aed |
diff --git a/tests/fips-test.c b/tests/fips-test.c
|
|
|
a74aed |
index f7556d7bbb..c43503fba0 100644
|
|
|
a74aed |
--- a/tests/fips-test.c
|
|
|
a74aed |
+++ b/tests/fips-test.c
|
|
|
a74aed |
@@ -1,4 +1,5 @@
|
|
|
a74aed |
#include <config.h>
|
|
|
a74aed |
+#include <stdbool.h>
|
|
|
a74aed |
#include <stdint.h>
|
|
|
a74aed |
#include <stdio.h>
|
|
|
a74aed |
#include <string.h>
|
|
|
a74aed |
@@ -213,14 +214,96 @@ test_cipher_disallowed(gnutls_cipher_algorithm_t cipher)
|
|
|
a74aed |
FIPS_POP_CONTEXT(ERROR);
|
|
|
a74aed |
}
|
|
|
a74aed |
|
|
|
a74aed |
+static void
|
|
|
a74aed |
+test_ccm_cipher(gnutls_cipher_algorithm_t cipher, size_t tag_length,
|
|
|
a74aed |
+ bool expect_encryption_fail,
|
|
|
a74aed |
+ gnutls_fips140_operation_state_t expected_state)
|
|
|
a74aed |
+{
|
|
|
a74aed |
+ int ret;
|
|
|
a74aed |
+ unsigned key_size = gnutls_cipher_get_key_size(cipher);
|
|
|
a74aed |
+ gnutls_aead_cipher_hd_t h;
|
|
|
a74aed |
+ gnutls_datum_t key = { key_data, key_size };
|
|
|
a74aed |
+ unsigned char buffer[256];
|
|
|
a74aed |
+ size_t length;
|
|
|
a74aed |
+ gnutls_memset(key_data, 0, key_size);
|
|
|
a74aed |
+
|
|
|
a74aed |
+ FIPS_PUSH_CONTEXT();
|
|
|
a74aed |
+ ret = gnutls_aead_cipher_init(&h, cipher, &key);
|
|
|
a74aed |
+ if (ret < 0) {
|
|
|
a74aed |
+ fail("gnutls_aead_cipher_init failed for %s\n",
|
|
|
a74aed |
+ gnutls_cipher_get_name(cipher));
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ FIPS_POP_CONTEXT(APPROVED);
|
|
|
a74aed |
+
|
|
|
a74aed |
+ fips_push_context(fips_context);
|
|
|
a74aed |
+ memset(buffer, 0, sizeof(buffer));
|
|
|
a74aed |
+ length = sizeof(buffer);
|
|
|
a74aed |
+ ret = gnutls_aead_cipher_encrypt(h, iv_data,
|
|
|
a74aed |
+ gnutls_cipher_get_iv_size(cipher),
|
|
|
a74aed |
+ NULL, 0, tag_length,
|
|
|
a74aed |
+ buffer, length - tag_length,
|
|
|
a74aed |
+ buffer, &length);
|
|
|
a74aed |
+ if (expect_encryption_fail) {
|
|
|
a74aed |
+ if (ret != GNUTLS_E_INVALID_REQUEST) {
|
|
|
a74aed |
+ fail("gnutls_aead_cipher_encrypt(%s) returned %d "
|
|
|
a74aed |
+ "while %d is expected\n",
|
|
|
a74aed |
+ gnutls_cipher_get_name(cipher),
|
|
|
a74aed |
+ ret, GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ } else if (ret < 0) {
|
|
|
a74aed |
+ fail("gnutls_aead_cipher_encrypt failed for %s\n",
|
|
|
a74aed |
+ gnutls_cipher_get_name(cipher));
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ fips_pop_context(fips_context, expected_state);
|
|
|
a74aed |
+
|
|
|
a74aed |
+ fips_push_context(fips_context);
|
|
|
a74aed |
+ length = sizeof(buffer);
|
|
|
a74aed |
+ ret = gnutls_aead_cipher_decrypt(h, iv_data,
|
|
|
a74aed |
+ gnutls_cipher_get_iv_size(cipher),
|
|
|
a74aed |
+ NULL, 0, tag_length,
|
|
|
a74aed |
+ buffer, length,
|
|
|
a74aed |
+ buffer, &length);
|
|
|
a74aed |
+ if (expect_encryption_fail) {
|
|
|
a74aed |
+ if (ret != GNUTLS_E_INVALID_REQUEST) {
|
|
|
a74aed |
+ fail("gnutls_aead_cipher_decrypt(%s) returned %d "
|
|
|
a74aed |
+ "while %d is expected\n",
|
|
|
a74aed |
+ gnutls_cipher_get_name(cipher),
|
|
|
a74aed |
+ ret, GNUTLS_E_INVALID_REQUEST);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ } else if (ret < 0) {
|
|
|
a74aed |
+ fail("gnutls_aead_cipher_decrypt failed for %s\n",
|
|
|
a74aed |
+ gnutls_cipher_get_name(cipher));
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ fips_pop_context(fips_context, expected_state);
|
|
|
a74aed |
+
|
|
|
a74aed |
+ gnutls_aead_cipher_deinit(h);
|
|
|
a74aed |
+}
|
|
|
a74aed |
+
|
|
|
a74aed |
static inline void
|
|
|
a74aed |
test_ciphers(void)
|
|
|
a74aed |
{
|
|
|
a74aed |
+ size_t i;
|
|
|
a74aed |
+
|
|
|
a74aed |
test_cipher_approved(GNUTLS_CIPHER_AES_128_CBC);
|
|
|
a74aed |
test_cipher_approved(GNUTLS_CIPHER_AES_192_CBC);
|
|
|
a74aed |
test_cipher_approved(GNUTLS_CIPHER_AES_256_CBC);
|
|
|
a74aed |
- test_aead_cipher_approved(GNUTLS_CIPHER_AES_128_CCM);
|
|
|
a74aed |
- test_aead_cipher_approved(GNUTLS_CIPHER_AES_256_CCM);
|
|
|
a74aed |
+
|
|
|
a74aed |
+ /* Check for all allowed Tlen */
|
|
|
a74aed |
+ for (i = 4; i <= 16; i += 2) {
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_128_CCM, i,
|
|
|
a74aed |
+ false, GNUTLS_FIPS140_OP_APPROVED);
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_256_CCM, i,
|
|
|
a74aed |
+ false, GNUTLS_FIPS140_OP_APPROVED);
|
|
|
a74aed |
+ }
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_128_CCM, 3,
|
|
|
a74aed |
+ true, GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_256_CCM, 3,
|
|
|
a74aed |
+ true, GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_128_CCM, 5,
|
|
|
a74aed |
+ true, GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+ test_ccm_cipher(GNUTLS_CIPHER_AES_256_CCM, 5,
|
|
|
a74aed |
+ true, GNUTLS_FIPS140_OP_ERROR);
|
|
|
a74aed |
+
|
|
|
a74aed |
test_aead_cipher_approved(GNUTLS_CIPHER_AES_128_CCM_8);
|
|
|
a74aed |
test_aead_cipher_approved(GNUTLS_CIPHER_AES_256_CCM_8);
|
|
|
a74aed |
test_cipher_approved(GNUTLS_CIPHER_AES_128_CFB8);
|
|
|
a74aed |
--
|
|
|
a74aed |
2.38.1
|
|
|
a74aed |
|