File openssl-1_1-ossl-sli-021-AES-GCM-external-IV.patch of Package openssl-1_1
--- openssl-1_1-1.1.1w-150600.5.32.1/crypto/evp/e_aes.c 2024-09-24 09:39:10.602383232 +0200
+++ openssl-1_1-1.1.1w-150600.5.32.1-patched-2/crypto/evp/e_aes.c 2024-09-30 12:54:30.230623746 +0200
@@ -2883,6 +2883,8 @@
return 1;
}
+#define IV_SET_INTERNAL 0x80000000
+#define IV_GEN_INTERNAL 0x40000000
static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
{
EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,c);
@@ -2942,6 +2944,7 @@
gctx->iv_gen = 1;
return 1;
}
+
/*
* Fixed field must be at least 4 bytes and invocation field at least
* 8.
@@ -2952,7 +2955,7 @@
memcpy(gctx->iv, ptr, arg);
if (c->encrypt && RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
return 0;
- gctx->iv_gen = 1;
+ gctx->iv_gen = (arg == 4 ? IV_GEN_INTERNAL : 0) | 1;
return 1;
case EVP_CTRL_GCM_IV_GEN:
@@ -2967,7 +2970,10 @@
* to check wrap around or increment more than last 8 bytes.
*/
ctr64_inc(gctx->iv + gctx->ivlen - 8);
- gctx->iv_set = 1;
+ // within EVP_CTRL_GCM_SET_IV_FIXED the caller must provide
+ // exactly 4 bytes (the lower limit for the call to succeed).
+ // Then and only then we consider the IV to be generated internally.
+ gctx->iv_set = (gctx->iv_gen & IV_GEN_INTERNAL ? IV_SET_INTERNAL : 0) | 1;
return 1;
case EVP_CTRL_GCM_SET_IV_INV:
@@ -3272,6 +3278,7 @@
}
#endif
+//
static int aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t len)
{
@@ -3285,6 +3292,23 @@
if (!gctx->iv_set)
return -1;
+
+ // FIPS compliance check
+ //
+ // iv_gen and iv_set must be equal to 1 AND the iv must
+ // have been set internally to be fips compliant.
+ //
+ // This can only be met by calling `*aes_gcm_ctrl` with the following arguments:
+ // 1. `EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, 4, iv)`
+ // 2. `EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, 12, &buffer[0])`
+ //
+ // NOTE: one is required to provide exactly 4 bytes (the lower limit)
+ // during the first call as the remaining bytes are generated
+ // internally.
+ if (ctx->encrypt && (((gctx->iv_gen & IV_GEN_INTERNAL) == 0) || ((gctx->iv_set & IV_SET_INTERNAL) == 0))) {
+ fips_sli_disapprove_EVP_CIPHER_CTX(ctx);
+ }
+
if (in) {
if (out == NULL) {
if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
--- openssl-1_1-1.1.1w-150600.5.32.1/test/fips_slitest.c 2024-09-19 10:27:05.000000000 +0200
+++ openssl-1_1-1.1.1w-150600.5.32.1-patched-2/test/fips_slitest.c 2024-09-30 13:08:14.141439066 +0200
@@ -248,20 +248,24 @@
typedef struct {
int fips_approved;
+ int iv_gen; // The cipher must generate the IV [y=1,n=0]
int cipher_nid;
} SLI_CIPHER_TEST;
static const SLI_CIPHER_TEST cipher_tests[] = {
- {1, NID_aes_128_cfb128},
- {1, NID_aes_256_gcm},
- {0, NID_des_ede3_cbc},
- {0, NID_des_ede3_cfb8},
- {0, NID_des_ofb64},
- {0, NID_des_ede_ecb},
- {0, NID_des_ede_ofb64},
- {0, NID_idea_cbc},
- {1, NID_aes_128_xts},
- {1, NID_aes_256_xts},
+ {1, 0, NID_aes_128_cfb128},
+ // Test if aes_gcm_cipher detects an external IV as not FIPS compliant.
+ {0, 0, NID_aes_256_gcm},
+ // Test if aes_gcm_cipher is fine with a randomly generated IV.
+ {1, 1, NID_aes_256_gcm},
+ {0, 0, NID_des_ede3_cbc},
+ {0, 0, NID_des_ede3_cfb8},
+ {0, 0, NID_des_ofb64},
+ {0, 0, NID_des_ede_ecb},
+ {0, 0, NID_des_ede_ofb64},
+ {0, 0, NID_idea_cbc},
+ {1, 0, NID_aes_128_xts},
+ {1, 0, NID_aes_256_xts},
};
static const size_t cipher_tests_len = sizeof(cipher_tests) / sizeof(cipher_tests[0]);
@@ -318,16 +322,34 @@
|| !TEST_true(RAND_bytes(key, key_len) == 1))
goto end;
- if (iv_len != 0) {
+ if (!cipher_tests[cipher_test_index].iv_gen && iv_len != 0) {
if (!TEST_ptr(iv = OPENSSL_malloc(iv_len))
|| !TEST_true(RAND_bytes(iv, iv_len) == 1))
goto end;
}
- int tmp_len = 0;
if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
- || !TEST_true(EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv) == 1)
- || !TEST_true(EVP_EncryptUpdate(ctx, ctext, &ctext_written_len, ptext, ptext_len) == 1)
+ || !TEST_true(EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) == 1))
+ goto end;
+
+ if (!cipher_tests[cipher_test_index].iv_gen && iv_len != 0) {
+ if (!TEST_ptr(iv = OPENSSL_malloc(iv_len))
+ || !TEST_true(RAND_bytes(iv, iv_len) == 1)
+ || !TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv) == 1))
+ goto end;
+ } else if (cipher_tests[cipher_test_index].iv_gen) {
+ char buffer[12];
+ if (!TEST_ptr(iv = OPENSSL_malloc(4))
+ || !TEST_true(RAND_bytes(iv, 4) == 1)
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, 4, iv) == 1)
+ || !TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) == 1)
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, 12, &buffer[0]) == 1)
+ )
+ goto end;
+ }
+
+ int tmp_len = 0;
+ if (!TEST_true(EVP_EncryptUpdate(ctx, ctext, &ctext_written_len, ptext, ptext_len) == 1)
|| !TEST_true(ctext_written_len <= get_ciphertext_len(ptext_len, cipher))
|| !TEST_true(EVP_EncryptFinal_ex(ctx, ctext + ctext_written_len, &tmp_len) == 1))
goto end;
@@ -644,6 +666,126 @@
return success;
}
+static int fips_evp_aes_gcm_restore_iv_not_fips_compliant() {
+ int success = 0;
+ unsigned char *key = NULL, *iv = NULL, *ctext = NULL, *buffer = NULL;
+ int ctext_written_len = 0;
+ const EVP_CIPHER *cipher = NULL;
+ EVP_CIPHER_CTX *ctx = NULL;
+ const uint8_t* const ptext = get_msg_16();
+ const size_t ptext_len = 16;
+ const int cipher_nid = NID_aes_256_gcm;
+
+ TEST_note("testing if aes_gcm_cipher detects restored IV as not FIPS compliant");
+
+ if (!TEST_ptr(cipher = EVP_get_cipherbynid(cipher_nid))) {
+ goto end;
+ }
+
+ const size_t key_len = EVP_CIPHER_key_length(cipher);
+ const size_t iv_len = EVP_CIPHER_iv_length(cipher);
+ //TEST_note("have keylen = %zd, ivlen = %zd", key_len, iv_len);
+
+ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+ || !TEST_true(EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) == 1))
+ goto end;
+
+ if (!TEST_ptr(key = OPENSSL_malloc(key_len))
+ || !TEST_ptr(ctext = OPENSSL_malloc(get_ciphertext_len(ptext_len, cipher)))
+ || !TEST_true(RAND_bytes(key, key_len) == 1)
+ || !TEST_ptr(iv = OPENSSL_malloc(iv_len))
+ || !TEST_true(RAND_bytes(iv, iv_len) == 1)
+ || !TEST_ptr(buffer = OPENSSL_malloc(iv_len)))
+ goto end;
+
+ if (!TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, -1, iv) == 1)
+ || !TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) == 1)
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, 12, buffer) == 1))
+ goto end;
+
+ int tmp_len = 0;
+ if (!TEST_true(EVP_EncryptUpdate(ctx, ctext, &ctext_written_len, ptext, ptext_len) == 1)
+ || !TEST_true(ctext_written_len <= get_ciphertext_len(ptext_len, cipher))
+ || !TEST_true(EVP_EncryptFinal_ex(ctx, ctext + ctext_written_len, &tmp_len) == 1))
+ goto end;
+
+ if (!TEST_true(ctext_written_len + tmp_len <= get_ciphertext_len(ptext_len, cipher)))
+ goto end;
+
+ if (!TEST_false(fips_sli_is_approved_EVP_CIPHER_CTX(ctx)))
+ goto end;
+
+ success = 1;
+end:
+ EVP_CIPHER_CTX_free(ctx);
+ if (key != NULL)
+ OPENSSL_free(key);
+ if (iv != NULL)
+ OPENSSL_free(iv);
+ if (ctext != NULL)
+ OPENSSL_free(ctext);
+ if (buffer != NULL)
+ OPENSSL_free(buffer);
+ return success;
+}
+
+static int fips_evp_aes_gcm_adding_too_much_iv() {
+ int success = 0;
+ unsigned char *key = NULL, *iv = NULL, *ctext = NULL, *buffer = NULL;
+ int ctext_written_len = 0;
+ const EVP_CIPHER *cipher = NULL;
+ EVP_CIPHER_CTX *ctx = NULL;
+ const uint8_t* const ptext = get_msg_16();
+ const size_t ptext_len = 16;
+ const int cipher_nid = NID_aes_256_gcm;
+
+ // NOTE: with a default of 12 bytes for the iv the maximum is also 4 bytes,
+ // i.e., this test actually doesn't check FIPS compliance directly.
+ TEST_note("testing if aes_gcm_cipher doesn't allow a fixed fieled larger than 4 byets");
+
+ if (!TEST_ptr(cipher = EVP_get_cipherbynid(cipher_nid))) {
+ goto end;
+ }
+
+ const size_t key_len = EVP_CIPHER_key_length(cipher);
+ const size_t iv_len = EVP_CIPHER_iv_length(cipher);
+ //TEST_note("have keylen = %zd, ivlen = %zd", key_len, iv_len);
+
+ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+ || !TEST_true(EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) == 1))
+ goto end;
+
+ if (!TEST_ptr(key = OPENSSL_malloc(key_len))
+ || !TEST_ptr(ctext = OPENSSL_malloc(get_ciphertext_len(ptext_len, cipher)))
+ || !TEST_true(RAND_bytes(key, key_len) == 1)
+ || !TEST_ptr(iv = OPENSSL_malloc(5))
+ || !TEST_true(RAND_bytes(iv, 5) == 1)
+ || !TEST_ptr(buffer = OPENSSL_malloc(iv_len)))
+ goto end;
+
+ if (!TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, 5, iv) == 0)
+ || !TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) == 1)
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, 12, buffer) == 0))
+ goto end;
+
+ int tmp_len = 0;
+ if (!TEST_true(EVP_EncryptUpdate(ctx, ctext, &ctext_written_len, ptext, ptext_len) == 0))
+ goto end;
+
+ success = 1;
+end:
+ EVP_CIPHER_CTX_free(ctx);
+ if (key != NULL)
+ OPENSSL_free(key);
+ if (iv != NULL)
+ OPENSSL_free(iv);
+ if (ctext != NULL)
+ OPENSSL_free(ctext);
+ if (buffer != NULL)
+ OPENSSL_free(buffer);
+ return success;
+}
+
int setup_tests(void) {
ADD_TEST(test_sli_noop);
ADD_TEST(cmac_aes_cbc);
@@ -663,6 +805,9 @@
ADD_ALL_TESTS(test_PKCS5_PBKDF2_HMAC, pbkdf2_tests_len);
ADD_ALL_TESTS(sshkdf, sshkdf_tests_len);
ADD_TEST(rand_bytes);
+
+ ADD_TEST(fips_evp_aes_gcm_restore_iv_not_fips_compliant);
+ ADD_TEST(fips_evp_aes_gcm_adding_too_much_iv);
return 1; /* success */
}