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 */
 }
openSUSE Build Service is sponsored by