From e49e1db1c275b202a6734967ddb783aad3ce949b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 1 Sep 2026 12:57:16 +0100 Subject: [PATCH] [crypto] Reject zero-length IVs for GCM ciphers A zero-length IV is not permitted by the NIST GCM specification, since it would lead to leaking the authentication key. The only existing code path that can currently lead to the use of a GCM cipher with a zero-length initialisation vector is CMS decryption. Modifying a CMS encrypted message to include a zero-length IV would leak information required to obtain the authentication key into the transient decrypted image, but this transient image would then fail the GCM authentication tag check and so the decrypted plaintext would be immediately overwritten (with the re-encrypted ciphertext). Improve robustness by rejecting a zero-length initialisation vector for a GCM cipher, and add a test case to ensure that this rejection remains in place in future. Signed-off-by: Michael Brown --- src/crypto/gcm.c | 8 +++++++- src/include/ipxe/errfile.h | 1 + src/tests/gcm_test.c | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c index 3948e0a07..f569be0ad 100644 --- a/src/crypto/gcm.c +++ b/src/crypto/gcm.c @@ -37,6 +37,7 @@ FILE_SECBOOT ( PERMITTED ); #include #include +#include #include #include #include @@ -473,7 +474,7 @@ int gcm_setiv ( struct cipher_algorithm *cipher, void *ctx, /* Initialisation vector is exactly 96 bits, use it as-is */ memcpy ( context->gcm.ctr.ctr.iv, iv, ivlen ); - } else { + } else if ( ivlen ) { /* Calculate hash over initialisation vector */ context->gcm.flags = GCM_FL_IV; @@ -486,6 +487,11 @@ int gcm_setiv ( struct cipher_algorithm *cipher, void *ctx, build_assert ( gcm_offset ( ctr ) > gcm_offset ( hash ) ); build_assert ( gcm_offset ( ctr ) > gcm_offset ( len ) ); build_assert ( gcm_offset ( ctr ) < gcm_offset ( key ) ); + + } else { + + /* Zero-length IVs are not permitted */ + return -ENOTSUP; } DBGC2 ( context, "GCM %p Y[0]:\n", context ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 0e908d957..31175d33c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -457,6 +457,7 @@ FILE_SECBOOT ( PERMITTED ); #define ERRFILE_cbc ( ERRFILE_OTHER | 0x006c0000 ) #define ERRFILE_channel ( ERRFILE_OTHER | 0x006d0000 ) #define ERRFILE_tlskey ( ERRFILE_OTHER | 0x006e0000 ) +#define ERRFILE_gcm ( ERRFILE_OTHER | 0x006f0000 ) /** @} */ diff --git a/src/tests/gcm_test.c b/src/tests/gcm_test.c index 04a42b5c9..2246a28f5 100644 --- a/src/tests/gcm_test.c +++ b/src/tests/gcm_test.c @@ -357,6 +357,10 @@ CIPHER_TEST ( gcm_test_18, &aes_gcm_algorithm, GCM_KEY_256, AUTH ( 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, 0xc8, 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a ) ); +/** Zero-length IV */ +CIPHER_TEST ( gcm_empty_iv, &aes_gcm_algorithm, GCM_KEY_256, IV(), + ADDITIONAL(), PLAINTEXT(), CIPHERTEXT(), AUTH() ); + /** * Perform Galois/Counter Mode self-test * @@ -384,6 +388,7 @@ static void gcm_test_exec ( void ) { cipher_ok ( &gcm_test_16 ); cipher_ok ( &gcm_test_17 ); cipher_ok ( &gcm_test_18 ); + cipher_iv_fail_ok ( &gcm_empty_iv ); /* Speed tests */ for ( keylen = 128 ; keylen <= 256 ; keylen += 64 ) {