diff --git a/crypto/cipher_extra/aead_test.cc b/crypto/cipher_extra/aead_test.cc index 2300d4a6a27..c6ab4c1827b 100644 --- a/crypto/cipher_extra/aead_test.cc +++ b/crypto/cipher_extra/aead_test.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include "../fipsmodule/cipher/internal.h" #include "../internal.h" @@ -91,7 +92,13 @@ static const struct KnownAEAD kAEADs[] = { {"AES_256_GCM", EVP_aead_aes_256_gcm, "aes_256_gcm_tests.txt", kCanTruncateTags | kVariableNonce}, + + {"XAES_256_GCM", EVP_aead_xaes_256_gcm, "xaes_256_gcm_tests.txt", + kCanTruncateTags | kVariableNonce}, + {"XAES_256_GCM_KC", EVP_aead_xaes_256_gcm_kc, "xaes_256_gcm_kc_tests.txt", + kCanTruncateTags | kVariableNonce}, + {"AES_256_GCM_NIST", EVP_aead_aes_256_gcm, "nist_cavp/aes_256_gcm.txt", kCanTruncateTags | kVariableNonce}, @@ -1437,6 +1444,10 @@ static const EvpAeadCtxSerdeTestParams kEvpAeadCtxSerde[] = { 17}, {"EVP_aead_aes_256_gcm", EVP_aead_aes_256_gcm(), kEvpAeadCtxKey, 32, 16, 18}, + {"EVP_aead_xaes_256_gcm", EVP_aead_xaes_256_gcm(), kEvpAeadCtxKey, 32, 16, + 29}, + {"EVP_aead_xaes_256_gcm_kc", EVP_aead_xaes_256_gcm_kc(), kEvpAeadCtxKey, 32, 16, + 30}, {"EVP_aead_chacha20_poly1305", EVP_aead_chacha20_poly1305(), kEvpAeadCtxKey, 32, 16, 5}, {"EVP_aead_xchacha20_poly1305", EVP_aead_xchacha20_poly1305(), @@ -1603,3 +1614,230 @@ TEST(EvpAeadCtxSerdeTest, ID) { ASSERT_TRUE(identifiers[id]); } } + +TEST(CipherTest, XAES_256_GCM_EVP_AEAD_INVALID_NONCE_KEY_LENGTH) { + // Test invalid nonce sizes and key length + std::vector key(32), nonce(24), plaintext, ciphertext(16); + const size_t tag_size = 16; + bssl::ScopedEVP_AEAD_CTX ctx; + // Invalid key length + int key_len = 23; + ASSERT_FALSE(EVP_AEAD_CTX_init(ctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key_len, tag_size, nullptr)); + // Use 256-bit key length + key_len = 32; + // Encryption + ASSERT_TRUE(EVP_AEAD_CTX_init(ctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key_len, tag_size, nullptr)); + + int nonce_len = 24; + size_t plaintext_len = 0, ciphertext_len = 0; + // Invalid nonce and nonce size + ASSERT_FALSE(EVP_AEAD_CTX_seal(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + nullptr, nonce_len, plaintext.data(), plaintext_len, nullptr, 0)); + nonce_len = 19; + ASSERT_FALSE(EVP_AEAD_CTX_seal(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + nonce.data(), nonce_len, plaintext.data(), plaintext_len, nullptr, 0)); + nonce_len = 25; + ASSERT_FALSE(EVP_AEAD_CTX_seal(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + nonce.data(), nonce_len, plaintext.data(), plaintext_len, nullptr, 0)); + + nonce_len = 24; + // Invalid max output size + ASSERT_FALSE(EVP_AEAD_CTX_seal(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len, nonce.data(), nonce_len, plaintext.data(), + plaintext_len, nullptr, 0)); + + // Decryption + bssl::ScopedEVP_AEAD_CTX dctx; + ASSERT_TRUE(EVP_AEAD_CTX_init(dctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key_len, tag_size, nullptr)); + // Invalid nonce and nonce size + nonce_len = 24; + ASSERT_FALSE(EVP_AEAD_CTX_open(dctx.get(), plaintext.data(), &plaintext_len, ciphertext.size() - tag_size, + nullptr, nonce_len, ciphertext.data(), ciphertext.size(), nullptr, 0)); + nonce_len = 19; + ASSERT_FALSE(EVP_AEAD_CTX_open(dctx.get(), plaintext.data(), &plaintext_len, ciphertext.size() - tag_size, + nonce.data(), nonce_len, ciphertext.data(), ciphertext.size(), nullptr, 0)); + nonce_len = 25; + ASSERT_FALSE(EVP_AEAD_CTX_open(dctx.get(), plaintext.data(), &plaintext_len, ciphertext.size() - tag_size, + nonce.data(), nonce_len, ciphertext.data(), ciphertext.size(), nullptr, 0)); +} + +TEST(CipherTest, XAES_256_GCM_EVP_AEAD_DERIVING_SUBKEYS_DIFFERENT_NONCES) { + /* + * Test deriving many subkeys from a main key but with different nonces + * Source of test vectors: + * https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md + */ + std::vector key; + + /* ============ INITIALIZE ENCRYPTION CONTEXT ============ */ + bssl::ScopedEVP_AEAD_CTX ectx; + size_t tag_size = 16; + // Initialize the main key + DecodeHex(&key, "0101010101010101010101010101010101010101010101010101010101010101"); + ASSERT_TRUE(EVP_AEAD_CTX_init(ectx.get(), EVP_aead_xaes_256_gcm(), key.data(), key.size(), tag_size, nullptr)); + + /* ============ INITIALIZE DECRYPTION CONTEXT ============ */ + bssl::ScopedEVP_AEAD_CTX dctx; + // Initialize the main key + ASSERT_TRUE(EVP_AEAD_CTX_init(dctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key.size(), tag_size, nullptr)); + + // Test encryption and decryption + const auto test = [&ectx, &dctx, &tag_size](std::vector &iv, const uint8_t *plaintext, size_t plaintext_len, + std::vector &expected_ciphertext_and_tag) { + // Encrypt + std::vector ciphertext; + ciphertext.resize(plaintext_len + tag_size); + size_t ciphertext_len = 0; + + // Initiaze IV, derive a subkey and encrypt + ASSERT_TRUE(EVP_AEAD_CTX_seal(ectx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + iv.data(), iv.size(), plaintext, plaintext_len, nullptr, 0)); + ASSERT_EQ(Bytes(ciphertext), Bytes(expected_ciphertext_and_tag)); + + // Decrypt + std::vector decrypted; + decrypted.resize(ciphertext_len - tag_size); + size_t decrypted_len = 0; + + // Initiaze IV, derive a subkey and decrypt + ASSERT_TRUE(EVP_AEAD_CTX_open(dctx.get(), decrypted.data(), &decrypted_len, ciphertext_len - tag_size, + iv.data(), iv.size(), ciphertext.data(), ciphertext_len, nullptr, 0)); + + ASSERT_EQ((size_t)decrypted_len, plaintext_len); + ASSERT_EQ(Bytes(decrypted), Bytes(plaintext, plaintext_len)); + }; + + // Test with an IV + std::vector iv; + DecodeHex(&iv, "424242424242424242424242424242424242424242424242"); + + // Test encryption and decryption with a plaintext + const uint8_t *plaintext = (const uint8_t *)"Hello, XAES-256-GCM!"; + std::vector ciphertext_and_tag; + DecodeHex(&ciphertext_and_tag, "01e5f78bc99de880bd2eeff2870d361f0eab5b2fc55268f34b14045878fe3668db980319"); + test(iv, plaintext, strlen((const char *)plaintext), ciphertext_and_tag); + + // Test with another IV + DecodeHex(&iv, "4142434445464748494a4b4c4d4e4f505152535455565758"); + + // Test encryption and decryption again with another plaintext + plaintext = (const uint8_t *)"XAES-256-GCM"; + DecodeHex(&ciphertext_and_tag, "ce546ef63c9cc60765923609b33a9a1974e96e52daf2fcf7075e2271"); + test(iv, plaintext, strlen((const char *)plaintext), ciphertext_and_tag); +} + +TEST(CipherTest, XAES_256_GCM_EVP_AEAD_MULTI_LOOP_TEST) { + // Source of multi-loop tests: + // https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM/go/XAES-256-GCM_test.go + const auto test = [](int n, const char *output) { + bssl::ScopedEVP_MD_CTX s; + ASSERT_TRUE(EVP_DigestInit(s.get(), EVP_shake128())); + bssl::ScopedEVP_MD_CTX d; + ASSERT_TRUE(EVP_DigestInit(d.get(), EVP_shake128())); + + std::vector key(32), nonce(24), plaintext(256); + std::vector aad(256), ciphertext(272), decrypted(256); + uint8_t plaintext_len = 0, aad_len = 0; + + int key_len = 32, nonce_len = 24, tag_size = 16; + + for(int i = 0; i < n; ++i) { + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), key.data(), key_len)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), nonce.data(), nonce_len)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), &plaintext_len, 1)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), plaintext.data(), plaintext_len)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), &aad_len, 1)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), aad.data(), aad_len)); + + // XAES-256-GCM Encryption + bssl::ScopedEVP_AEAD_CTX ctx; + ASSERT_TRUE(EVP_AEAD_CTX_init(ctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key_len, tag_size, nullptr)); + + size_t ciphertext_len = 0; + ASSERT_TRUE(EVP_AEAD_CTX_seal(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + nonce.data(), nonce_len, plaintext.data(), plaintext_len, aad.data(), aad_len)); + ASSERT_TRUE(EVP_DigestUpdate(d.get(), ciphertext.data(), ciphertext_len)); + + // XAES-256-GCM Decryption + bssl::ScopedEVP_AEAD_CTX dctx; + ASSERT_TRUE(EVP_AEAD_CTX_init(dctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key_len, tag_size, nullptr)); + size_t len = 0; + ASSERT_TRUE(EVP_AEAD_CTX_open(dctx.get(), decrypted.data(), &len, ciphertext_len - tag_size, + nonce.data(), nonce_len, ciphertext.data(), ciphertext_len, aad.data(), aad_len)); + ASSERT_EQ(Bytes(decrypted.data(), len), Bytes(plaintext.data(), plaintext_len)); + } + std::vector expected; + DecodeHex(&expected, output); + uint8_t got[32] = {0}; + ASSERT_TRUE(EVP_DigestFinalXOF(d.get(), got, 32)); + ASSERT_EQ(Bytes(got, 32), Bytes(expected)); + }; + + test(10000, "e6b9edf2df6cec60c8cbd864e2211b597fb69a529160cd040d56c0c210081939"); + test(1000000, "2163ae1445985a30b60585ee67daa55674df06901b890593e824b8a7c885ab15"); +} + +TEST(CipherTest, XAES_256_GCM_EVP_AEAD_SHORTER_NONCE) { + std::vector key; + + /* ============ INITIALIZE ENCRYPTION CONTEXT ============ */ + bssl::ScopedEVP_AEAD_CTX ectx; + size_t tag_size = 16; + // Initialize the main key + DecodeHex(&key, "0101010101010101010101010101010101010101010101010101010101010101"); + ASSERT_TRUE(EVP_AEAD_CTX_init(ectx.get(), EVP_aead_xaes_256_gcm(), key.data(), key.size(), tag_size, nullptr)); + + /* ============ INITIALIZE DECRYPTION CONTEXT ============ */ + bssl::ScopedEVP_AEAD_CTX dctx; + // Initialize the main key + ASSERT_TRUE(EVP_AEAD_CTX_init(dctx.get(), EVP_aead_xaes_256_gcm(), key.data(), key.size(), tag_size, nullptr)); + + // Test encryption and decryption + const auto test = [&ectx, &dctx, &tag_size](std::vector &iv, + const uint8_t *plaintext, size_t plaintext_len) { + // Encrypt + std::vector ciphertext; + ciphertext.resize(plaintext_len + tag_size); + size_t ciphertext_len = 0; + + // Initiaze IV, derive a subkey and encrypt + ASSERT_TRUE(EVP_AEAD_CTX_seal(ectx.get(), ciphertext.data(), &ciphertext_len, + plaintext_len + EVP_AEAD_max_overhead(EVP_aead_xaes_256_gcm()), + iv.data(), iv.size(), plaintext, plaintext_len, nullptr, 0)); + + // Decrypt + std::vector decrypted; + decrypted.resize(ciphertext_len - tag_size); + size_t decrypted_len = 0; + + // Initiaze IV, derive a subkey and decrypt + ASSERT_TRUE(EVP_AEAD_CTX_open(dctx.get(), decrypted.data(), &decrypted_len, ciphertext_len - tag_size, + iv.data(), iv.size(), ciphertext.data(), ciphertext_len, nullptr, 0)); + + ASSERT_EQ((size_t)decrypted_len, plaintext_len); + ASSERT_EQ(Bytes(decrypted), Bytes(plaintext, plaintext_len)); + }; + + // Test with a 20-byte IV + std::vector iv; + DecodeHex(&iv, "4242424242424242424242424242424242424242"); + + // Test encryption and decryption with a plaintext + const uint8_t *plaintext = (const uint8_t *)"Hello, XAES-256-GCM!"; + std::vector ciphertext_and_tag; + DecodeHex(&ciphertext_and_tag, "01e5f78bc99de880bd2eeff2870d361f0eab5b2fc55268f34b14045878fe3668db980319"); + test(iv, plaintext, strlen((const char *)plaintext)); + + // Test with a 23-byte IV + DecodeHex(&iv, "4142434445464748494a4b4c4d4e4f5051525354555657"); + + // Test encryption and decryption again with another plaintext + plaintext = (const uint8_t *)"XAES-256-GCM"; + DecodeHex(&ciphertext_and_tag, "ce546ef63c9cc60765923609b33a9a1974e96e52daf2fcf7075e2271"); + test(iv, plaintext, strlen((const char *)plaintext)); +} diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c index f67cb233fe9..f4f8316e8cc 100644 --- a/crypto/cipher_extra/cipher_extra.c +++ b/crypto/cipher_extra/cipher_extra.c @@ -89,6 +89,8 @@ static const struct { {NID_aes_256_ctr, "aes-256-ctr", EVP_aes_256_ctr}, {NID_aes_256_ecb, "aes-256-ecb", EVP_aes_256_ecb}, {NID_aes_256_gcm, "aes-256-gcm", EVP_aes_256_gcm}, + {NID_xaes_256_gcm, "xaes-256-gcm", EVP_xaes_256_gcm}, + {NID_xaes_256_gcm_kc, "xaes-256-gcm-kc", EVP_xaes_256_gcm_kc}, {NID_aes_256_ofb128, "aes-256-ofb", EVP_aes_256_ofb}, {NID_aes_256_xts, "aes-256-xts", EVP_aes_256_xts}, {NID_chacha20_poly1305, "chacha20-poly1305", EVP_chacha20_poly1305}, @@ -114,7 +116,9 @@ static const struct { {"aes128", "aes-128-cbc"}, {"id-aes128-gcm", "aes-128-gcm"}, {"id-aes192-gcm", "aes-192-gcm"}, - {"id-aes256-gcm", "aes-256-gcm"} + {"id-aes256-gcm", "aes-256-gcm"}, + {"id-xaes256-gcm", "xaes-256-gcm"}, + {"id-xaes256-gcm-kc", "xaes-256-gcm-kc"} }; const EVP_CIPHER *EVP_get_cipherbynid(int nid) { diff --git a/crypto/cipher_extra/cipher_test.cc b/crypto/cipher_extra/cipher_test.cc index 5290a69f23e..023dee39442 100644 --- a/crypto/cipher_extra/cipher_test.cc +++ b/crypto/cipher_extra/cipher_test.cc @@ -68,6 +68,7 @@ #include #include #include +#include #include "../internal.h" #include "../test/file_test.h" @@ -127,6 +128,10 @@ static const EVP_CIPHER *GetCipher(const std::string &name) { return EVP_aes_192_ccm(); } else if (name == "AES-256-CCM") { return EVP_aes_256_ccm(); + } else if (name == "XAES-256-GCM") { + return EVP_xaes_256_gcm(); + } else if (name == "XAES-256-GCM-KC") { + return EVP_xaes_256_gcm_kc(); } return nullptr; } @@ -183,7 +188,8 @@ static void TestCipherAPI(const EVP_CIPHER *cipher, Operation op, bool padding, bssl::Span plaintext, bssl::Span ciphertext, bssl::Span aad, - bssl::Span tag) { + bssl::Span tag, + bssl::Span kc = {}) { bool encrypt = op == Operation::kEncrypt; bool is_custom_cipher = EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_CIPHER; @@ -191,6 +197,7 @@ static void TestCipherAPI(const EVP_CIPHER *cipher, Operation op, bool padding, bssl::Span expected = encrypt ? ciphertext : plaintext; bool is_aead = EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER; bool is_ccm = EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE; + bool is_kc = EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_KC_CIPHER; // Some |EVP_CIPHER|s take a variable-length key, and need to first be // configured with the key length, which requires configuring the cipher. @@ -229,10 +236,17 @@ static void TestCipherAPI(const EVP_CIPHER *cipher, Operation op, bool padding, tag.size(), encrypt ? nullptr : const_cast(tag.data()))); } + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), /*cipher=*/nullptr, /*engine=*/nullptr, /*key=*/nullptr, iv.data(), /*enc=*/-1)); + // Verify key commitment + if(is_kc && !encrypt) { + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_VERIFY_KC, + kc.size(), const_cast(kc.data()))); + } + // CCM requires the full length of the plaintext to be known ahead of time. if (is_ccm) { int len; @@ -356,6 +370,15 @@ static void TestCipherAPI(const EVP_CIPHER *cipher, Operation op, bool padding, tag.size(), rtag)); EXPECT_EQ(Bytes(tag), Bytes(rtag, tag.size())); } + + if(encrypt & is_kc) { + uint8_t rkc[32]; + ASSERT_LE(kc.size(), sizeof(rkc)); + ASSERT_TRUE(MaybeCopyCipherContext(copy, &ctx)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_KC, + kc.size(), rkc)); + EXPECT_EQ(Bytes(kc), Bytes(rkc, kc.size())); + } } } @@ -454,7 +477,8 @@ static void TestCipher(const EVP_CIPHER *cipher, Operation input_op, bssl::Span plaintext, bssl::Span ciphertext, bssl::Span aad, - bssl::Span tag) { + bssl::Span tag, + bssl::Span kc = {}) { size_t block_size = EVP_CIPHER_block_size(cipher); bool is_ccm = EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE; std::vector ops; @@ -484,11 +508,11 @@ static void TestCipher(const EVP_CIPHER *cipher, Operation input_op, SCOPED_TRACE(copy); TestCipherAPI(cipher, op, padding, copy, in_place, /*use_evp_cipher=*/false, chunk_size, key, iv, - plaintext, ciphertext, aad, tag); + plaintext, ciphertext, aad, tag, kc); if (!padding && chunk_size % block_size == 0) { TestCipherAPI(cipher, op, padding, copy, in_place, /*use_evp_cipher=*/true, chunk_size, key, iv, - plaintext, ciphertext, aad, tag); + plaintext, ciphertext, aad, tag, kc); } } if (!padding) { @@ -506,7 +530,7 @@ static void CipherFileTest(FileTest *t) { const EVP_CIPHER *cipher = GetCipher(cipher_str); ASSERT_TRUE(cipher); - std::vector key, iv, plaintext, ciphertext, aad, tag; + std::vector key, iv, plaintext, ciphertext, aad, tag, kc; // Force an allocation of the underlying data-store so that v.data() is // non-NULL even for empty test vectors. plaintext.reserve(1); @@ -521,7 +545,9 @@ static void CipherFileTest(FileTest *t) { ASSERT_TRUE(t->GetBytes(&aad, "AAD")); ASSERT_TRUE(t->GetBytes(&tag, "Tag")); } - + if(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_KC_CIPHER) { + ASSERT_TRUE(t->GetBytes(&kc, "KC")); + } Operation op = Operation::kBoth; if (t->HasAttribute("Operation")) { const std::string &str = t->GetAttributeOrDie("Operation"); @@ -535,9 +561,9 @@ static void CipherFileTest(FileTest *t) { FAIL() << "Unknown operation: " << str; } } - + TestCipher(cipher, op, /*padding=*/false, key, iv, plaintext, ciphertext, aad, - tag); + tag, kc); } TEST(CipherTest, TestVectors) { @@ -1080,6 +1106,7 @@ TEST(CipherTest, GetCipher) { test_get_cipher(NID_aes_256_ctr, "aes-256-ctr"); test_get_cipher(NID_aes_256_ecb, "aes-256-ecb"); test_get_cipher(NID_aes_256_gcm, "aes-256-gcm"); + test_get_cipher(NID_xaes_256_gcm, "xaes-256-gcm"); test_get_cipher(NID_aes_256_ofb128, "aes-256-ofb"); test_get_cipher(NID_aes_256_xts, "aes-256-xts"); test_get_cipher(NID_chacha20_poly1305, "chacha20-poly1305"); @@ -1102,6 +1129,8 @@ TEST(CipherTest, GetCipher) { test_get_cipher(NID_aes_128_gcm, "id-aes128-gcm"); test_get_cipher(NID_aes_192_gcm, "id-aes192-gcm"); test_get_cipher(NID_aes_256_gcm, "id-aes256-gcm"); + test_get_cipher(NID_xaes_256_gcm, "id-xaes256-gcm"); + test_get_cipher(NID_xaes_256_gcm_kc, "id-xaes256-gcm-kc"); // error case EXPECT_FALSE(EVP_get_cipherbyname(nullptr)); @@ -1454,3 +1483,187 @@ TEST(CipherTest, Empty_EVP_CIPHER_CTX_V1187459157) { CHECK_ERROR(EVP_DecryptUpdate(ctx.get(), out_vec.data(), &out_len, in_vec.data(), in_len), ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); CHECK_ERROR(EVP_DecryptFinal(ctx.get(), out_vec.data(), &out_len), ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); } + +TEST(CipherTest, XAES_256_GCM_EVP_CIPHER_INVALID_NONCE_KEY_LENGTH) { + std::vector key(32), nonce(24); + + // XAES-256-GCM Encryption + bssl::UniquePtr ctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), EVP_xaes_256_gcm(), nullptr, nullptr, nullptr, 1)); + + // Valid nonce size: 20 bytes <= |N| <= 24 bytes + // Test invalid nonce size + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 19, nullptr)); + ASSERT_FALSE(EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), nonce.data(), -1)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 25, nullptr)); + ASSERT_FALSE(EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), nonce.data(), -1)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 24, nullptr)); + + // Valid key length: 32 bytes + // Test invalid key length + ctx.get()->key_len = 24; + ASSERT_FALSE(EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), nonce.data(), -1)); +} + +TEST(CipherTest, XAES_256_GCM_EVP_CIPHER_DERIVING_SUBKEYS_DIFFERENT_NONCES) { + /* + * Test deriving many subkeys from a main key but with different nonces + * Source of test vectors: + * https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md + */ + std::vector key; + + /* ============ INITIALIZE ENCRYPTION CONTEXT ============ */ + bssl::UniquePtr ectx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(ectx); + ASSERT_TRUE(EVP_CipherInit_ex(ectx.get(), EVP_xaes_256_gcm(), nullptr, nullptr, nullptr, 1)); + size_t iv_len = 24; + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ectx.get(), EVP_CTRL_AEAD_SET_IVLEN, iv_len, nullptr)); + + // Initialize the main key + ConvertToBytes(&key, "0101010101010101010101010101010101010101010101010101010101010101"); + ASSERT_TRUE(EVP_CipherInit_ex(ectx.get(), nullptr, nullptr, key.data(), nullptr, -1)); + + /* ============ INITIALIZE DECRYPTION CONTEXT ============ */ + bssl::UniquePtr dctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(dctx); + ASSERT_TRUE(EVP_DecryptInit_ex(dctx.get(), EVP_xaes_256_gcm(), nullptr, nullptr, nullptr)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(dctx.get(), EVP_CTRL_AEAD_SET_IVLEN, iv_len, nullptr)); + + // Initialize the main key + ASSERT_TRUE(EVP_DecryptInit_ex(dctx.get(), nullptr, nullptr, key.data(), nullptr)); + + // Test encryption and decryption + const auto test = [&ectx, &dctx](std::vector &iv, const uint8_t *plaintext, size_t plaintext_len, + std::vector &expected_ciphertext, std::vector &expected_tag) { + // Encrypt + // Initiaze IV and derive a subkey + ASSERT_TRUE(EVP_CipherInit_ex(ectx.get(), nullptr, nullptr, nullptr, iv.data(), -1)); + + std::vector ciphertext, tag; + ciphertext.resize(plaintext_len); + int ciphertext_len = 0; + + ASSERT_TRUE(EVP_CipherUpdate(ectx.get(), ciphertext.data(), &ciphertext_len, + plaintext, plaintext_len)); + int len = 0; + ASSERT_TRUE(EVP_CipherFinal_ex(ectx.get(), ciphertext.data() + ciphertext_len, &len)); + ciphertext_len += len; + ASSERT_EQ(Bytes(ciphertext), Bytes(expected_ciphertext)); + + size_t tag_size = 16; + tag.resize(tag_size); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ectx.get(), EVP_CTRL_AEAD_GET_TAG, tag.size(), (void*)tag.data())); + ASSERT_EQ(Bytes(tag), Bytes(expected_tag)); + + // Decrypt + // Initiaze IV and derive a subkey + ASSERT_TRUE(EVP_DecryptInit_ex(dctx.get(), nullptr, nullptr, nullptr, iv.data())); + + std::vector decrypted; + decrypted.resize(ciphertext_len); + int decrypted_len = 0; + + ASSERT_TRUE(EVP_DecryptUpdate(dctx.get(), decrypted.data(), &decrypted_len, ciphertext.data(), ciphertext_len)); + + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(dctx.get(), EVP_CTRL_AEAD_SET_TAG, tag.size(), tag.data())); + ASSERT_TRUE(EVP_DecryptFinal(dctx.get(), decrypted.data() + decrypted_len, &len)); + decrypted_len += len; + + ASSERT_EQ((size_t)decrypted_len, plaintext_len); + ASSERT_EQ(Bytes(decrypted), Bytes(plaintext, plaintext_len)); + }; + + // Test with an IV + std::vector iv; + ConvertToBytes(&iv, "424242424242424242424242424242424242424242424242"); + + // Test encryption and decryption with a plaintext + const uint8_t *plaintext = (const uint8_t *)"Hello, XAES-256-GCM!"; + std::vector ciphertext, tag; + ConvertToBytes(&ciphertext, "01e5f78bc99de880bd2eeff2870d361f0eab5b2f"); + ConvertToBytes(&tag, "c55268f34b14045878fe3668db980319"); + test(iv, plaintext, strlen((const char *)plaintext), ciphertext, tag); + + // Test with another IV + ConvertToBytes(&iv, "4142434445464748494a4b4c4d4e4f505152535455565758"); + + // Test encryption and decryption again with another plaintext + plaintext = (const uint8_t *)"XAES-256-GCM"; + ConvertToBytes(&ciphertext, "ce546ef63c9cc60765923609"); + ConvertToBytes(&tag, "b33a9a1974e96e52daf2fcf7075e2271"); + test(iv, plaintext, strlen((const char *)plaintext), ciphertext, tag); +} + +TEST(CipherTest, XAES_256_GCM_EVP_CIPHER_MULTI_LOOP_TEST) { + // Source of multi-loop tests: + // https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM/go/XAES-256-GCM_test.go + const auto test = [](int n, const char *output) { + bssl::ScopedEVP_MD_CTX s; + ASSERT_TRUE(EVP_DigestInit(s.get(), EVP_shake128())); + bssl::ScopedEVP_MD_CTX d; + ASSERT_TRUE(EVP_DigestInit(d.get(), EVP_shake128())); + + bssl::UniquePtr ctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), EVP_xaes_256_gcm(), nullptr, nullptr, nullptr, 1)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 24, nullptr)); + + bssl::UniquePtr dctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(dctx); + ASSERT_TRUE(EVP_DecryptInit_ex(dctx.get(), EVP_xaes_256_gcm(), nullptr, nullptr, nullptr)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(dctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 24, nullptr)); + + std::vector key(32), nonce(24), plaintext(256); + std::vector aad(256), ciphertext(256), tag(16); + uint8_t plaintext_len = 0, aad_len = 0; + int tag_size = 16; + + for(int i = 0; i < n; ++i) { + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), key.data(), 32)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), nonce.data(), 24)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), &plaintext_len, 1)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), plaintext.data(), plaintext_len)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), &aad_len, 1)); + ASSERT_TRUE(EVP_DigestSqueeze(s.get(), aad.data(), aad_len)); + + // XAES-256-GCM Encryption + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), nonce.data(), -1)); + ASSERT_EQ(aad_len, EVP_Cipher(ctx.get(), nullptr, aad.data(), aad_len)); + int ciphertext_len = 0; + ASSERT_TRUE(EVP_CipherUpdate(ctx.get(), ciphertext.data(), &ciphertext_len, + plaintext.data(), plaintext_len)); + + int len = 0; + ASSERT_TRUE(EVP_CipherFinal_ex(ctx.get(), ciphertext.data() + ciphertext_len, &len)); + ciphertext_len += len; + ASSERT_TRUE(EVP_DigestUpdate(d.get(), ciphertext.data(), ciphertext_len)); + + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, tag_size, tag.data())); + ASSERT_TRUE(EVP_DigestUpdate(d.get(), tag.data(), tag_size)); + + // XAES-256-GCM Decryption + ASSERT_TRUE(EVP_DecryptInit_ex(dctx.get(), nullptr, nullptr, key.data(), nonce.data())); + + std::vector decrypted; + decrypted.resize(plaintext_len); + len = 0; + EVP_DecryptUpdate(dctx.get(), nullptr, &len, aad.data(), aad_len); + ASSERT_TRUE(EVP_DecryptUpdate(dctx.get(), decrypted.data(), &len, ciphertext.data(), ciphertext_len)); + + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(dctx.get(), EVP_CTRL_AEAD_SET_TAG, tag_size, tag.data())); + ASSERT_TRUE(EVP_DecryptFinal(dctx.get(), decrypted.data() + len, &len)); + + ASSERT_EQ(Bytes(decrypted), Bytes(plaintext.data(), plaintext_len)); + } + std::vector expected; + ASSERT_TRUE(ConvertToBytes(&expected, output)); + uint8_t got[32] = {0}; + ASSERT_TRUE(EVP_DigestFinalXOF(d.get(), got, 32)); + ASSERT_EQ(Bytes(got, 32), Bytes(expected)); + }; + + test(10000, "e6b9edf2df6cec60c8cbd864e2211b597fb69a529160cd040d56c0c210081939"); + test(1000000, "2163ae1445985a30b60585ee67daa55674df06901b890593e824b8a7c885ab15"); +} diff --git a/crypto/cipher_extra/test/cipher_tests.txt b/crypto/cipher_extra/test/cipher_tests.txt index 1622a88dd14..be0607e6a07 100644 --- a/crypto/cipher_extra/test/cipher_tests.txt +++ b/crypto/cipher_extra/test/cipher_tests.txt @@ -506,6 +506,60 @@ AAD = feedfacedeadbeeffeedfacedeadbeefabaddad2 Tag = a44a8266ee1c8eb0c8b5d4cf5ae9f19b Operation = InvalidDecrypt +# Source of test vectors: +# https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md +Cipher = XAES-256-GCM +Key = 0101010101010101010101010101010101010101010101010101010101010101 +IV = 424242424242424242424242424242424242424242424242 +Plaintext = 48656c6c6f2c20584145532d3235362d47434d21 +Ciphertext = 01e5f78bc99de880bd2eeff2870d361f0eab5b2f +AAD = +Tag = c55268f34b14045878fe3668db980319 + +Cipher = XAES-256-GCM +Key = 0101010101010101010101010101010101010101010101010101010101010101 +IV = 4142434445464748494a4b4c4d4e4f505152535455565758 +Plaintext = 584145532d3235362d47434d +Ciphertext = ce546ef63c9cc60765923609 +AAD = +Tag = b33a9a1974e96e52daf2fcf7075e2271 + +Cipher = XAES-256-GCM +Key = 0303030303030303030303030303030303030303030303030303030303030303 +IV = 4142434445464748494a4b4c4d4e4f505152535455565758 +Plaintext = 584145532d3235362d47434d +Ciphertext = 986ec1832593df5443a17943 +AAD = 633273702e6f72672f584145532d3235362d47434d +Tag = 7fd083bf3fdb41abd740a21f71eb769d + +# Note: KC are our own test values +Cipher = XAES-256-GCM-KC +Key = 0101010101010101010101010101010101010101010101010101010101010101 +IV = 424242424242424242424242424242424242424242424242 +Plaintext = 48656c6c6f2c20584145532d3235362d47434d21 +Ciphertext = 01e5f78bc99de880bd2eeff2870d361f0eab5b2f +AAD = +Tag = c55268f34b14045878fe3668db980319 +KC = bf37571be1b43aeca64a95d99a2f162e24f8bfd79bbb71fa7d943e6fc060a8ae + +Cipher = XAES-256-GCM-KC +Key = 0101010101010101010101010101010101010101010101010101010101010101 +IV = 4142434445464748494a4b4c4d4e4f505152535455565758 +Plaintext = 584145532d3235362d47434d +Ciphertext = ce546ef63c9cc60765923609 +AAD = +Tag = b33a9a1974e96e52daf2fcf7075e2271 +KC = 04076b6085eebab138855fe57811c04112eff989d44120dfff662d5475a383c3 + +Cipher = XAES-256-GCM-KC +Key = 0303030303030303030303030303030303030303030303030303030303030303 +IV = 4142434445464748494a4b4c4d4e4f505152535455565758 +Plaintext = 584145532d3235362d47434d +Ciphertext = 986ec1832593df5443a17943 +AAD = 633273702e6f72672f584145532d3235362d47434d +Tag = 7fd083bf3fdb41abd740a21f71eb769d +KC = 5553cd21d1592b422e3129632a3187eee8a658cdca5c5b32ce86308dcc18e9d1 + # local add-ons, primarily streaming ghash tests # 128 bytes aad Cipher = AES-128-GCM diff --git a/crypto/cipher_extra/test/xaes_256_gcm_kc_tests.txt b/crypto/cipher_extra/test/xaes_256_gcm_kc_tests.txt new file mode 100644 index 00000000000..750dccb8746 --- /dev/null +++ b/crypto/cipher_extra/test/xaes_256_gcm_kc_tests.txt @@ -0,0 +1,23 @@ +# The XAES-256-GCM-KC test cases from cipher_tests.txt have been merged into this +# file. + +KEY: 0101010101010101010101010101010101010101010101010101010101010101 +NONCE: 424242424242424242424242424242424242424242424242 +IN: 48656c6c6f2c20584145532d3235362d47434d21 +AD: +CT: 01e5f78bc99de880bd2eeff2870d361f0eab5b2f +TAG: c55268f34b14045878fe3668db980319bf37571be1b43aeca64a95d99a2f162e24f8bfd79bbb71fa7d943e6fc060a8ae + +KEY: 0101010101010101010101010101010101010101010101010101010101010101 +NONCE: 4142434445464748494a4b4c4d4e4f505152535455565758 +IN: 584145532d3235362d47434d +AD: +CT: ce546ef63c9cc60765923609 +TAG: b33a9a1974e96e52daf2fcf7075e227104076b6085eebab138855fe57811c04112eff989d44120dfff662d5475a383c3 + +KEY: 0303030303030303030303030303030303030303030303030303030303030303 +NONCE: 4142434445464748494a4b4c4d4e4f505152535455565758 +IN: 584145532d3235362d47434d +AD: 633273702e6f72672f584145532d3235362d47434d +CT: 986ec1832593df5443a17943 +TAG: 7fd083bf3fdb41abd740a21f71eb769d5553cd21d1592b422e3129632a3187eee8a658cdca5c5b32ce86308dcc18e9d1 diff --git a/crypto/cipher_extra/test/xaes_256_gcm_tests.txt b/crypto/cipher_extra/test/xaes_256_gcm_tests.txt new file mode 100644 index 00000000000..0b2714414ae --- /dev/null +++ b/crypto/cipher_extra/test/xaes_256_gcm_tests.txt @@ -0,0 +1,23 @@ +# The XAES-256-GCM test cases from cipher_tests.txt have been merged into this +# file. + +KEY: 0101010101010101010101010101010101010101010101010101010101010101 +NONCE: 424242424242424242424242424242424242424242424242 +IN: 48656c6c6f2c20584145532d3235362d47434d21 +AD: +CT: 01e5f78bc99de880bd2eeff2870d361f0eab5b2f +TAG: c55268f34b14045878fe3668db980319 + +KEY: 0101010101010101010101010101010101010101010101010101010101010101 +NONCE: 4142434445464748494a4b4c4d4e4f505152535455565758 +IN: 584145532d3235362d47434d +AD: +CT: ce546ef63c9cc60765923609 +TAG: b33a9a1974e96e52daf2fcf7075e2271 + +KEY: 0303030303030303030303030303030303030303030303030303030303030303 +NONCE: 4142434445464748494a4b4c4d4e4f505152535455565758 +IN: 584145532d3235362d47434d +AD: 633273702e6f72672f584145532d3235362d47434d +CT: 986ec1832593df5443a17943 +TAG: 7fd083bf3fdb41abd740a21f71eb769d diff --git a/crypto/fipsmodule/cipher/e_aes.c b/crypto/fipsmodule/cipher/e_aes.c index 60921abaa07..68689426219 100644 --- a/crypto/fipsmodule/cipher/e_aes.c +++ b/crypto/fipsmodule/cipher/e_aes.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include "../../internal.h" @@ -346,6 +347,21 @@ ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_KEY *gcm_key, #define EVP_AES_GCM_CTX_PADDING 8 #endif +typedef struct { + EVP_AES_GCM_CTX aes_gcm_ctx; + AES_KEY xaes_key; + uint8_t k1[AES_BLOCK_SIZE]; +} XAES_256_GCM_CTX; + +#define XAES_256_GCM_KEY_COMMIT_SIZE (AES_BLOCK_SIZE * 2) + +typedef struct { + EVP_AES_GCM_CTX aes_gcm_ctx; + AES_KEY xaes_key; + uint8_t k1[AES_BLOCK_SIZE]; + uint8_t kc[XAES_256_GCM_KEY_COMMIT_SIZE]; +} XAES_256_GCM_KC_CTX; + static EVP_AES_GCM_CTX *aes_gcm_from_cipher_ctx(EVP_CIPHER_CTX *ctx) { OPENSSL_STATIC_ASSERT( alignof(EVP_AES_GCM_CTX) <= 16, @@ -353,17 +369,48 @@ static EVP_AES_GCM_CTX *aes_gcm_from_cipher_ctx(EVP_CIPHER_CTX *ctx) { // |malloc| guarantees up to 4-byte alignment on 32-bit and 8-byte alignment // on 64-bit systems, so we need to adjust to reach 16-byte alignment. - assert(ctx->cipher->ctx_size == - sizeof(EVP_AES_GCM_CTX) + EVP_AES_GCM_CTX_PADDING); - - char *ptr = ctx->cipher_data; + char *ptr = NULL; + + switch(ctx->cipher->nid) { + // AES-GCM + case NID_aes_128_gcm: + case NID_aes_192_gcm: + case NID_aes_256_gcm: + assert(ctx->cipher->ctx_size == + sizeof(EVP_AES_GCM_CTX) + EVP_AES_GCM_CTX_PADDING); + ptr = ctx->cipher_data; +#if defined(OPENSSL_32_BIT) + assert((uintptr_t)ptr % 4 == 0); + ptr += (uintptr_t)ptr & 4; +#endif + assert((uintptr_t)ptr % 8 == 0); + ptr += (uintptr_t)ptr & 8; + return (EVP_AES_GCM_CTX *)ptr; + // XAES-256-GCM + case NID_xaes_256_gcm: + assert(ctx->cipher->ctx_size == sizeof(XAES_256_GCM_CTX)); + ptr = ctx->cipher_data; +#if defined(OPENSSL_32_BIT) + assert((uintptr_t)ptr % 4 == 0); + ptr += (uintptr_t)ptr & 4; +#endif + assert((uintptr_t)ptr % 8 == 0); + ptr += (uintptr_t)ptr & 8; + return &((XAES_256_GCM_CTX *)ptr)->aes_gcm_ctx; + case NID_xaes_256_gcm_kc: + assert(ctx->cipher->ctx_size == sizeof(XAES_256_GCM_KC_CTX)); + ptr = ctx->cipher_data; #if defined(OPENSSL_32_BIT) - assert((uintptr_t)ptr % 4 == 0); - ptr += (uintptr_t)ptr & 4; + assert((uintptr_t)ptr % 4 == 0); + ptr += (uintptr_t)ptr & 4; #endif - assert((uintptr_t)ptr % 8 == 0); - ptr += (uintptr_t)ptr & 8; - return (EVP_AES_GCM_CTX *)ptr; + assert((uintptr_t)ptr % 8 == 0); + ptr += (uintptr_t)ptr & 8; + return &((XAES_256_GCM_KC_CTX *)ptr)->aes_gcm_ctx; + default: + break; + } + return NULL; } static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key, @@ -1745,3 +1792,596 @@ int EVP_has_aes_hardware(void) { } OPENSSL_MSVC_PRAGMA(warning(pop)) + +/* ---------------------------- XAES-256-GCM ---------------------------- +Specification: https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md +-----------------------------------------------------------------------*/ +#ifdef __AVX512F__ +#include + +__attribute__((target("vaes,avx512vl,aes,sse2"))) +static inline __m128i aes128_keyexpand(__m128i key) { + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + return _mm_xor_si128(key, _mm_slli_si128(key, 4)); +} + +#define KEYEXP128_H(K1, K2, I, S) _mm_xor_si128(aes128_keyexpand(K1), \ + _mm_shuffle_epi32(_mm_aeskeygenassist_si128(K2, I), S)) +#define KEYEXP256(K1, K2, I) KEYEXP128_H(K1, K2, I, 0xff) +#define KEYEXP256_2(K1, K2) KEYEXP128_H(K1, K2, 0x00, 0xaa) + +__attribute__((target("vaes,avx512vl,aes,sse2"))) +static inline void aes_key_setup_enc(__m128i rk[], const __m256i cipherKey) { + /* 256 bit key setup */ + _mm256_storeu2_m128i(rk + 1, rk, cipherKey); + // rk[0] = _mm_loadu_si128((const block128*) cipherKey); + // rk[1] = _mm_loadu_si128((const block128*) (cipherKey+16)); + rk[2] = KEYEXP256(rk[0], rk[1], 0x01); + rk[3] = KEYEXP256_2(rk[1], rk[2]); + rk[4] = KEYEXP256(rk[2], rk[3], 0x02); + rk[5] = KEYEXP256_2(rk[3], rk[4]); + rk[6] = KEYEXP256(rk[4], rk[5], 0x04); + rk[7] = KEYEXP256_2(rk[5], rk[6]); + rk[8] = KEYEXP256(rk[6], rk[7], 0x08); + rk[9] = KEYEXP256_2(rk[7], rk[8]); + rk[10] = KEYEXP256(rk[8], rk[9], 0x10); + rk[11] = KEYEXP256_2(rk[9], rk[10]); + rk[12] = KEYEXP256(rk[10], rk[11], 0x20); + rk[13] = KEYEXP256_2(rk[11], rk[12]); + rk[14] = KEYEXP256(rk[12], rk[13], 0x40); +} + +__attribute__((target("vaes,avx512vl,aes,sse2"))) +static int CMAC_KDF_x2(uint8_t *in, uint8_t *out, __m128i *rk) { + __m256i derived_key; + derived_key = _mm256_loadu_si256((__m256i*)in); + derived_key = _mm256_xor_si256(derived_key, _mm256_broadcast_i32x4(rk[0])); + + for(size_t r = 1; r < 14; ++r) { + derived_key = _mm256_aesenc_epi128(derived_key, + _mm256_broadcast_i32x4(rk[r])); + } + + derived_key = _mm256_aesenclast_epi128(derived_key, + _mm256_broadcast_i32x4(rk[14])); + + _mm256_storeu_si256((__m256i*)out, derived_key); + + return 1; +} + +__attribute__((target("vaes,avx512f,aes,sse2"))) +static int CMAC_KDF_x4(uint8_t *in, uint8_t *out, __m128i *rk) { + __m512i derived_key; + derived_key = _mm512_loadu_si512((__m512i*)in); + derived_key = _mm512_xor_si512(derived_key, _mm512_broadcast_i32x4(rk[0])); + + for(size_t r = 1; r < 14; ++r) { + derived_key = _mm512_aesenc_epi128(derived_key, + _mm512_broadcast_i32x4(rk[r])); + } + + derived_key = _mm512_aesenclast_epi128(derived_key, + _mm512_broadcast_i32x4(rk[14])); + + _mm512_storeu_si512((__m512i*)out, derived_key); + + return 1; +} +#endif + +/* ---------------------------- XAES-256-GCM ---------------------------- +Specification: +https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md +Extension to support nonce size less than 24 bytes: +https://eprint.iacr.org/2025/758.pdf#page=24 +-----------------------------------------------------------------------*/ +#define XAES_256_GCM_KEY_LENGTH (AES_BLOCK_SIZE * 2) +#define XAES_256_GCM_MAX_NONCE_SIZE (AES_GCM_NONCE_LENGTH * 2) +#define XAES_256_GCM_MIN_NONCE_SIZE (20) + +/* +The following function performs the step #2 of CMAC specified in: +https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf#page=14 +Only K1 is needed as the CMAC input is always a complete block. +Also note that K1 only depends on the input main key. +Pseudocode: + If MSB(L) = 0: K1 = L << 1; + Else: K1 = (L << 1) ^ (0x00, ..., 0x00, 0x87) +*/ +#define BINARY_FIELD_MUL_X_128(out, in) \ +do { \ + unsigned i; \ + /* Shift |in| to left, including carry. */ \ + for (i = 0; i < 15; i++) { \ + out[i] = (in[i] << 1) | (in[i+1] >> 7); \ + } \ + /* If MSB set fixup with R. */ \ + const uint8_t carry = in[0] >> 7; \ + out[i] = (in[i] << 1) ^ ((0 - carry) & 0x87); \ +} while(0); + +static int xaes_256_gcm_CMAC_derive_key(AES_KEY *xaes_key, uint8_t *k1, + const uint8_t* nonce, uint8_t *derived_key) { + uint8_t M[AES_BLOCK_SIZE] = {0x00, 0x01, 0x58, 0x00}; + OPENSSL_memcpy(M + 4, nonce, 12); + + for (size_t i = 0; i < AES_BLOCK_SIZE; i++) { + M[i] ^= k1[i]; + } + + AES_encrypt(M, derived_key, xaes_key); + M[1] ^= 0x03; + AES_encrypt(M, derived_key + AES_BLOCK_SIZE, xaes_key); + + return 1; +} + +static XAES_256_GCM_CTX *xaes_256_gcm_from_cipher_ctx(EVP_CIPHER_CTX *ctx) { + // alignment to be consistent with aes_gcm_from_cipher_ctx() + char *ptr = ctx->cipher_data; +#if defined(OPENSSL_32_BIT) + assert((uintptr_t)ptr % 4 == 0); + ptr += (uintptr_t)ptr & 4; +#endif + assert((uintptr_t)ptr % 8 == 0); + ptr += (uintptr_t)ptr & 8; + return (XAES_256_GCM_CTX *)ptr; +} + +static int xaes_256_gcm_set_gcm_key(EVP_CIPHER_CTX *ctx, const uint8_t *nonce, int enc) { + XAES_256_GCM_CTX *xaes_ctx = xaes_256_gcm_from_cipher_ctx(ctx); + EVP_AES_GCM_CTX *gctx = &xaes_ctx->aes_gcm_ctx; + + // Nonce size: 20 bytes <= |N| <= 24 bytes + if(gctx->ivlen < XAES_256_GCM_MIN_NONCE_SIZE || + gctx->ivlen > XAES_256_GCM_MAX_NONCE_SIZE) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_NONCE_SIZE); + return 0; + } + + uint8_t derived_key[XAES_256_GCM_KEY_LENGTH]; + + xaes_256_gcm_CMAC_derive_key(&xaes_ctx->xaes_key, xaes_ctx->k1, nonce, derived_key); + + int ivlen = gctx->ivlen; + + // AES-GCM uses a different size nonce than XAES-GCM, + // so to be able to call aes_gcm_init_key with ctx we temporarily + // set the nonce (iv) length to AES_GCM_NONCE_LENGTH. + gctx->ivlen = AES_GCM_NONCE_LENGTH; + + // For nonce size < 24 bytes + // Reference: https://eprint.iacr.org/2025/758.pdf#page=24 + aes_gcm_init_key(ctx, derived_key, nonce + ivlen - AES_GCM_NONCE_LENGTH, enc); + + // Re-assign the original nonce size of XAES-256-GCM (20 <= |N| <= 24) + gctx->ivlen = ivlen; + + return 1; +} + +static int xaes_256_gcm_ctx_init(AES_KEY *xaes_key, uint8_t *k1, const uint8_t *key) { + static const uint8_t kZeroIn[AES_BLOCK_SIZE] = {0}; + uint8_t L[AES_BLOCK_SIZE]; + AES_set_encrypt_key(key, XAES_256_GCM_KEY_LENGTH << 3, xaes_key); + AES_encrypt(kZeroIn, L, xaes_key); + BINARY_FIELD_MUL_X_128(k1, L); + return 1; +} + +// ------------------------------------------------------------------------------ +// --------------- EVP_CIPHER XAES-256-GCM Without Key Commitment --------------- +// ------------------------------------------------------------------------------ +static int xaes_256_gcm_init(EVP_CIPHER_CTX *ctx, const uint8_t *key, + const uint8_t *iv, int enc) { + // Key length: 32 bytes + if (ctx->key_len != XAES_256_GCM_KEY_LENGTH) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); + return 0; + } + + XAES_256_GCM_CTX *xaes_ctx = xaes_256_gcm_from_cipher_ctx(ctx); + + // When main key is provided, initialize the xaes-256-gcm context + if(key != NULL) { + xaes_256_gcm_ctx_init(&xaes_ctx->xaes_key, xaes_ctx->k1, key); + } + + // If iv is provided, even if main key is not, derive a subkey + if(iv != NULL) { + return xaes_256_gcm_set_gcm_key(ctx, iv, enc); + } + + return 1; +} + +DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_xaes_256_gcm) { + OPENSSL_memset(out, 0, sizeof(EVP_CIPHER)); + out->nid = NID_xaes_256_gcm; + out->block_size = AES_BLOCK_SIZE; + out->key_len = XAES_256_GCM_KEY_LENGTH; + out->iv_len = XAES_256_GCM_MAX_NONCE_SIZE; + out->ctx_size = sizeof(XAES_256_GCM_CTX); + out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_CUSTOM_COPY | + EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | + EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER; + out->init = xaes_256_gcm_init; + out->cipher = aes_gcm_cipher; + out->cleanup = aes_gcm_cleanup; + out->ctrl = aes_gcm_ctrl; +} + +// ------------------------------------------------------------------------------ +// ---------------- EVP_AEAD XAES-256-GCM Without Key Commitment ---------------- +// ------------------------------------------------------------------------------ +/* Since ctx->state capacity is 568 bytes, it does not have enough space + * to contain both xaes_256_gcm_ctx data and aead_aes_gcm_ctx data which + * require 804 bytes, so we store a pointer to aead_aes_gcm_ctx instead */ +typedef struct { + struct aead_aes_gcm_ctx *gcm_ctx; + AES_KEY xaes_key; + uint8_t k1[AES_BLOCK_SIZE]; +} AEAD_XAES_256_GCM_CTX; + +static int aead_xaes_256_gcm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, + size_t key_len, size_t requested_tag_len) { + // Max tag length: 16 bytes + if(requested_tag_len > EVP_AEAD_AES_GCM_TAG_LEN) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_TAG_SIZE); + return 0; + } + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + // Allocate memory for xaes_ctx->gcm_ctx + xaes_ctx->gcm_ctx = OPENSSL_malloc(sizeof(struct aead_aes_gcm_ctx)); + + xaes_256_gcm_ctx_init(&xaes_ctx->xaes_key, xaes_ctx->k1, key); + + // requested_tag_len = 0 means using the default tag length of AES_GCM + ctx->tag_len = (requested_tag_len > 0) ? requested_tag_len : EVP_AEAD_AES_GCM_TAG_LEN; + + return 1; +} + +static int aead_xaes_256_gcm_set_gcm_key(AEAD_XAES_256_GCM_CTX *xaes_ctx, + const uint8_t *nonce, const size_t nonce_len) { + if(!nonce || nonce_len < 20 || nonce_len > 24) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_NONCE_SIZE); + return 0; + } + + uint8_t gcm_key[XAES_256_GCM_KEY_LENGTH]; + + xaes_256_gcm_CMAC_derive_key(&xaes_ctx->xaes_key, xaes_ctx->k1, nonce, gcm_key); + + struct aead_aes_gcm_ctx *gcm_ctx = xaes_ctx->gcm_ctx; + + gcm_ctx->ctr = aes_ctr_set_key(&gcm_ctx->ks.ks, &gcm_ctx->gcm_key, NULL, + gcm_key, XAES_256_GCM_KEY_LENGTH); + + return 1; +} + +static int aead_xaes_256_gcm_seal_scatter( + const EVP_AEAD_CTX *ctx, uint8_t *out, + uint8_t *out_tag, size_t *out_tag_len, + const size_t max_out_tag_len, + const uint8_t *nonce, const size_t nonce_len, + const uint8_t *in, const size_t in_len, + const uint8_t *extra_in, + const size_t extra_in_len, const uint8_t *ad, + const size_t ad_len) { + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + if(!aead_xaes_256_gcm_set_gcm_key(xaes_ctx, nonce, nonce_len)) { + return 0; + } + + // Reference for nonce size < 24 bytes: + // https://eprint.iacr.org/2025/758.pdf#page=24 + return aead_aes_gcm_seal_scatter_impl( + xaes_ctx->gcm_ctx, out, out_tag, out_tag_len, max_out_tag_len, + nonce + nonce_len - AES_GCM_NONCE_LENGTH, AES_GCM_NONCE_LENGTH, + in, in_len, extra_in, extra_in_len, ad, ad_len, ctx->tag_len); +} + +static int aead_xaes_256_gcm_open_gather(const EVP_AEAD_CTX *ctx, uint8_t *out, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *in_tag, size_t in_tag_len, + const uint8_t *ad, size_t ad_len) { + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + if(!aead_xaes_256_gcm_set_gcm_key(xaes_ctx, nonce, nonce_len)) { + return 0; + } + + // Reference for nonce size < 24 bytes: + // https://eprint.iacr.org/2025/758.pdf#page=24 + return aead_aes_gcm_open_gather_impl( + xaes_ctx->gcm_ctx, out, nonce + nonce_len - AES_GCM_NONCE_LENGTH, + AES_GCM_NONCE_LENGTH, in, in_len, in_tag, in_tag_len, + ad, ad_len, ctx->tag_len); +} + +static void aead_xaes_256_gcm_cleanup(EVP_AEAD_CTX *ctx) { + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + OPENSSL_free(xaes_ctx->gcm_ctx); + aead_aes_gcm_cleanup(ctx); +} + +DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_xaes_256_gcm) { + OPENSSL_memset(out, 0, sizeof(EVP_AEAD)); + out->key_len = XAES_256_GCM_KEY_LENGTH; + out->nonce_len = XAES_256_GCM_MAX_NONCE_SIZE; + out->overhead = EVP_AEAD_AES_GCM_TAG_LEN; + out->max_tag_len = EVP_AEAD_AES_GCM_TAG_LEN; + out->aead_id = AEAD_XAES_256_GCM_ID; + + out->init = aead_xaes_256_gcm_init; + out->cleanup = aead_xaes_256_gcm_cleanup; + out->seal_scatter = aead_xaes_256_gcm_seal_scatter; + out->open_gather = aead_xaes_256_gcm_open_gather; +} + +// ------------------------------------------------------------------------------ +// ---------------- EVP_CIPHER XAES-256-GCM With Key Commitment ----------------- +// ----------- Reference: https://eprint.iacr.org/2025/758.pdf#page=6 ----------- +// ------------------------------------------------------------------------------ +static XAES_256_GCM_KC_CTX *xaes_256_gcm_kc_from_cipher_ctx(EVP_CIPHER_CTX *ctx) { + // alignment to be consistent with aes_gcm_from_cipher_ctx() + char *ptr = ctx->cipher_data; +#if defined(OPENSSL_32_BIT) + assert((uintptr_t)ptr % 4 == 0); + ptr += (uintptr_t)ptr & 4; +#endif + assert((uintptr_t)ptr % 8 == 0); + ptr += (uintptr_t)ptr & 8; + return (XAES_256_GCM_KC_CTX *)ptr; +} + +static int xaes_256_gcm_CMAC_extract_key_commitment(AES_KEY *xaes_key, uint8_t *k1, + uint8_t *key_commitment, const uint8_t* nonce, const unsigned nonce_len) { + uint8_t W[AES_BLOCK_SIZE]; + + uint8_t kc_prefix[4] = {0x58, 0x43, 0x4D, 0x54}; + uint8_t X1[AES_BLOCK_SIZE]; + OPENSSL_memcpy(W, kc_prefix, 4); + OPENSSL_memcpy(W + 4, nonce, AES_GCM_NONCE_LENGTH); + + AES_encrypt(W, X1, xaes_key); + // For nonce size < 24 + // Reference: https://eprint.iacr.org/2025/758.pdf#page=24 + OPENSSL_memcpy(W, nonce + nonce_len - AES_GCM_NONCE_LENGTH, AES_GCM_NONCE_LENGTH); + W[AES_BLOCK_SIZE-4] = XAES_256_GCM_MAX_NONCE_SIZE - nonce_len; + W[AES_BLOCK_SIZE-3] = 0x01; + W[AES_BLOCK_SIZE-2] = 0x00; + W[AES_BLOCK_SIZE-1] = 0x01; + + for (size_t i = 0; i < AES_BLOCK_SIZE; i++) { + W[i] ^= X1[i] ^ k1[i]; + } + + AES_encrypt(W, key_commitment, xaes_key); + W[AES_BLOCK_SIZE-1] ^= 0x03; + AES_encrypt(W, key_commitment + AES_BLOCK_SIZE, xaes_key); + + return 1; +} + +static int xaes_256_gcm_init_key_commit(EVP_CIPHER_CTX *ctx, const uint8_t *key, + const uint8_t *iv, int enc) { + // Key length: 32 bytes + if (ctx->key_len != XAES_256_GCM_KEY_LENGTH) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); + return 0; + } + + XAES_256_GCM_KC_CTX *xaes_ctx = xaes_256_gcm_kc_from_cipher_ctx(ctx); + + // When main key is provided, initialize the xaes-256-gcm context + if(key != NULL) { + xaes_256_gcm_ctx_init(&xaes_ctx->xaes_key, xaes_ctx->k1, key); + } + + // If iv is provided, even if main key is not, derive a subkey and its key commitment + /* Note: Unlike the MAC tag, key commitment only depends on the main key + * and iv, not on plaintext and aad, therefore we extract it in init when + * deriving subkey instead of at the end of encrytion like the MAC tag */ + if(iv != NULL) { + // Derive subkey + xaes_256_gcm_set_gcm_key(ctx, iv, enc); + // Extract key commitment + EVP_AES_GCM_CTX *gctx = &xaes_ctx->aes_gcm_ctx; + xaes_256_gcm_CMAC_extract_key_commitment(&xaes_ctx->xaes_key, xaes_ctx->k1, xaes_ctx->kc, iv, gctx->ivlen); + } + + return 1; +} + +static int xaes_256_gcm_key_commit_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) { + + XAES_256_GCM_KC_CTX *xaes_ctx = xaes_256_gcm_kc_from_cipher_ctx(ctx); + + switch(type) { + case EVP_CTRL_AEAD_GET_KC: + if(arg < XAES_256_GCM_KEY_COMMIT_SIZE) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + OPENSSL_memcpy(ptr, xaes_ctx->kc, arg); + return 1; + /* Since the verification of key commitment (KC) can happen at any time, + * not only at the end of decryption like for MAC tag, we create a control + * command for KC verification, rather than a command to set KC, then + * verifying it at the end of decryption as for checking the MAC tag */ + case EVP_CTRL_AEAD_VERIFY_KC: + if(arg < XAES_256_GCM_KEY_COMMIT_SIZE) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + if(OPENSSL_memcmp(xaes_ctx->kc, ptr, XAES_256_GCM_KEY_COMMIT_SIZE)) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_KEY_COMMITMENT_INVALID); + return 0; + } + return 1; + default: + return aes_gcm_ctrl(ctx, type, arg, ptr); + } +} + +DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_xaes_256_gcm_kc) { + OPENSSL_memset(out, 0, sizeof(EVP_CIPHER)); + out->nid = NID_xaes_256_gcm_kc; + out->block_size = 1; + out->key_len = XAES_256_GCM_KEY_LENGTH; + out->iv_len = XAES_256_GCM_MAX_NONCE_SIZE; + out->ctx_size = sizeof(XAES_256_GCM_KC_CTX); + out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_CUSTOM_COPY | + EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | + EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_KC_CIPHER; + out->init = xaes_256_gcm_init_key_commit; + out->cipher = aes_gcm_cipher; + out->cleanup = aes_gcm_cleanup; + out->ctrl = xaes_256_gcm_key_commit_ctrl; +} + +// ----------------------------------------------------------------------------- +// ------------------ EVP_AEAD XAES-256-GCM With Key Commitment ---------------- +// ----------------------------------------------------------------------------- +static int aead_xaes_256_gcm_kc_init(EVP_AEAD_CTX *ctx, const uint8_t *key, + size_t key_len, size_t requested_tag_len) { + // Max tag and key commitment length: 16 + 32 bytes + if(requested_tag_len > EVP_AEAD_AES_GCM_TAG_LEN + XAES_256_GCM_KEY_COMMIT_SIZE) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_TAG_SIZE); + return 0; + } + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + // Allocate memory for xaes_ctx->gcm_ctx + xaes_ctx->gcm_ctx = OPENSSL_malloc(sizeof(struct aead_aes_gcm_ctx)); + + xaes_256_gcm_ctx_init(&xaes_ctx->xaes_key, xaes_ctx->k1, key); + + // requested_tag_len = 0 means using the default tag length of XAES_256_GCM_KC + ctx->tag_len = (requested_tag_len > 0) ? requested_tag_len : + (EVP_AEAD_AES_GCM_TAG_LEN + XAES_256_GCM_KEY_COMMIT_SIZE); + + return 1; +} + +static int aead_xaes_256_gcm_key_commit_seal_scatter( + const EVP_AEAD_CTX *ctx, uint8_t *out, + uint8_t *out_tag, size_t *out_tag_len, + const size_t max_out_tag_len, + const uint8_t *nonce, const size_t nonce_len, + const uint8_t *in, const size_t in_len, + const uint8_t *extra_in, + const size_t extra_in_len, const uint8_t *ad, + const size_t ad_len) { + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + if(!aead_xaes_256_gcm_set_gcm_key(xaes_ctx, nonce, nonce_len)) { + return 0; + } + + /* The tag is constituted by two components: the MAC part concatenated with the + * key commitment part. Truncating the key commitment is currently unsupported. We + * take into account the cases where there is not enough space for key commitment */ + size_t tag_len = ctx->tag_len; + size_t key_commitment_len = XAES_256_GCM_KEY_COMMIT_SIZE; + if(ctx->tag_len > XAES_256_GCM_KEY_COMMIT_SIZE) { + // Evaluate the remaning space size for MAC + tag_len = ctx->tag_len - XAES_256_GCM_KEY_COMMIT_SIZE; + } + else { + // If there is not enough space for at least 1-byte tag and key commitment + key_commitment_len = 0; + } + + aead_aes_gcm_seal_scatter_impl(xaes_ctx->gcm_ctx, out, out_tag, out_tag_len, + max_out_tag_len - key_commitment_len, + nonce + nonce_len - AES_GCM_NONCE_LENGTH, AES_GCM_NONCE_LENGTH, + in, in_len, extra_in, extra_in_len, ad, ad_len, tag_len); + + // We extract key commitment only if it is reserved enough space + if(key_commitment_len > 0) { + uint8_t key_commitment[XAES_256_GCM_KEY_COMMIT_SIZE]; + + xaes_256_gcm_CMAC_extract_key_commitment(&xaes_ctx->xaes_key, xaes_ctx->k1, + key_commitment, nonce, nonce_len); + + OPENSSL_memcpy(out_tag + *out_tag_len, key_commitment, key_commitment_len); + *out_tag_len += key_commitment_len; + } + + return 1; +} + +static int aead_xaes_256_gcm_key_commit_open_gather( + const EVP_AEAD_CTX *ctx, uint8_t *out, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *in_tag, size_t in_tag_len, + const uint8_t *ad, size_t ad_len) { + + AEAD_XAES_256_GCM_CTX *xaes_ctx = (AEAD_XAES_256_GCM_CTX*)&ctx->state; + + if (!aead_xaes_256_gcm_set_gcm_key(xaes_ctx, nonce, nonce_len)) { + return 0; + } + + /* The tag is constituted by two components: the MAC part concatenated with the + * key commitment part. Truncating the key commitment is currently unsupported. We + * take into account the cases where there is not enough space for key commitment */ + size_t tag_len = ctx->tag_len; + size_t key_commitment_len = XAES_256_GCM_KEY_COMMIT_SIZE; + if(ctx->tag_len > XAES_256_GCM_KEY_COMMIT_SIZE) { + // The tag is truncated but leave enough space for key commitment + // We need to evaluate the remaning space size for MAC + tag_len = ctx->tag_len - XAES_256_GCM_KEY_COMMIT_SIZE; + } + else { + // If there is not enough space for at least 1-byte tag and key commitment + key_commitment_len = 0; + } + + // We verify key commitment only if it is present + if(key_commitment_len > 0) { + uint8_t key_commitment[XAES_256_GCM_KEY_COMMIT_SIZE]; + + xaes_256_gcm_CMAC_extract_key_commitment(&xaes_ctx->xaes_key, xaes_ctx->k1, + key_commitment, nonce, nonce_len); + + if(OPENSSL_memcmp(in_tag + (in_tag_len - key_commitment_len), + key_commitment, key_commitment_len)) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_KEY_COMMITMENT_INVALID); + return 0; + } + } + + return aead_aes_gcm_open_gather_impl( + xaes_ctx->gcm_ctx, out, nonce + nonce_len - AES_GCM_NONCE_LENGTH, AES_GCM_NONCE_LENGTH, + in, in_len, in_tag, in_tag_len - key_commitment_len, ad, ad_len, tag_len); +} + +DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_xaes_256_gcm_kc) { + OPENSSL_memset(out, 0, sizeof(EVP_AEAD)); + out->key_len = XAES_256_GCM_KEY_LENGTH; + out->nonce_len = XAES_256_GCM_MAX_NONCE_SIZE; + out->overhead = EVP_AEAD_AES_GCM_TAG_LEN + XAES_256_GCM_KEY_COMMIT_SIZE; + out->max_tag_len = EVP_AEAD_AES_GCM_TAG_LEN + XAES_256_GCM_KEY_COMMIT_SIZE; + out->aead_id = AEAD_XAES_256_GCM_KC_ID; + + out->init = aead_xaes_256_gcm_kc_init; + out->cleanup = aead_xaes_256_gcm_cleanup; + out->seal_scatter = aead_xaes_256_gcm_key_commit_seal_scatter; + out->open_gather = aead_xaes_256_gcm_key_commit_open_gather; +} diff --git a/crypto/fipsmodule/cipher/internal.h b/crypto/fipsmodule/cipher/internal.h index a2170f06919..3ff4fe3c3ba 100644 --- a/crypto/fipsmodule/cipher/internal.h +++ b/crypto/fipsmodule/cipher/internal.h @@ -107,7 +107,9 @@ extern "C" { #define AEAD_AES_128_CCM_BLUETOOTH_8_ID 26 #define AEAD_AES_128_CCM_MATTER_ID 27 #define AEAD_AES_256_CBC_SHA384_TLS_ID 28 -#define AEAD_MAX_ID 28 +#define AEAD_XAES_256_GCM_ID 29 +#define AEAD_XAES_256_GCM_KC_ID 30 +#define AEAD_MAX_ID 30 // EVP_AEAD represents a specific AEAD algorithm. struct evp_aead_st { diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h index 6834ec79562..248198071d7 100644 --- a/crypto/obj/obj_dat.h +++ b/crypto/obj/obj_dat.h @@ -56,7 +56,7 @@ /* This file is generated by crypto/obj/objects.go. */ -#define NUM_NID 999 +#define NUM_NID 1001 static const uint8_t kObjectData[] = { /* NID_rsadsi */ @@ -7330,6 +7330,26 @@ static const uint8_t kObjectData[] = { 0xc9, 0x7b, 0x06, + /* NID_xaes_256_gcm */ + 0x60, + 0x86, + 0x48, + 0x01, + 0x65, + 0x03, + 0x04, + 0x01, + 0x31, + /* NID_xaes_256_gcm_kc */ + 0x60, + 0x86, + 0x48, + 0x01, + 0x65, + 0x03, + 0x04, + 0x01, + 0x32, }; static const ASN1_OBJECT kObjects[NUM_NID] = { @@ -9017,6 +9037,10 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"ED25519ph", "ED25519ph", NID_ED25519ph, 0, NULL, 0}, {"SecP384r1MLKEM1024", "SecP384r1MLKEM1024", NID_SecP384r1MLKEM1024, 9, &kObjectData[6360], 0}, + {"id-xaes256-GCM", "xaes-256-gcm", NID_xaes_256_gcm, 9, &kObjectData[6369], + 0}, + {"id-xaes256-GCM-KC", "xaes-256-gcm-kc", NID_xaes_256_gcm_kc, 9, + &kObjectData[6378], 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { @@ -9657,6 +9681,8 @@ static const uint16_t kNIDsInShortNameOrder[] = { 194 /* id-smime-spq */, 250 /* id-smime-spq-ets-sqt-unotice */, 249 /* id-smime-spq-ets-sqt-uri */, + 999 /* id-xaes256-GCM */, + 1000 /* id-xaes256-GCM-KC */, 676 /* identified-organization */, 461 /* info */, 748 /* inhibitAnyPolicy */, @@ -10996,14 +11022,16 @@ static const uint16_t kNIDsInLongNameOrder[] = { 503 /* x500UniqueIdentifier */, 158 /* x509Certificate */, 160 /* x509Crl */, + 999 /* xaes-256-gcm */, + 1000 /* xaes-256-gcm-kc */, 125 /* zlib compression */, }; static const uint16_t kNIDsInOIDOrder[] = { 434 /* 0.9 (OBJ_data) */, 182 /* 1.2 (OBJ_member_body) */, - 379 /* 1.3 (OBJ_org) */, 676 /* 1.3 (OBJ_identified_organization) */, + 379 /* 1.3 (OBJ_org) */, 11 /* 2.5 (OBJ_X500) */, 647 /* 2.23 (OBJ_international_organizations) */, 380 /* 1.3.6 (OBJ_dod) */, @@ -11711,6 +11739,8 @@ static const uint16_t kNIDsInOIDOrder[] = { 901 /* 2.16.840.1.101.3.4.1.46 (OBJ_aes_256_gcm) */, 902 /* 2.16.840.1.101.3.4.1.47 (OBJ_aes_256_ccm) */, 903 /* 2.16.840.1.101.3.4.1.48 (OBJ_id_aes256_wrap_pad) */, + 999 /* 2.16.840.1.101.3.4.1.49 (OBJ_xaes_256_gcm) */, + 1000 /* 2.16.840.1.101.3.4.1.50 (OBJ_xaes_256_gcm_kc) */, 672 /* 2.16.840.1.101.3.4.2.1 (OBJ_sha256) */, 673 /* 2.16.840.1.101.3.4.2.2 (OBJ_sha384) */, 674 /* 2.16.840.1.101.3.4.2.3 (OBJ_sha512) */, diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num index d46689d3866..597acd8b3c6 100644 --- a/crypto/obj/obj_mac.num +++ b/crypto/obj/obj_mac.num @@ -986,3 +986,5 @@ MLDSA65 995 MLDSA87 996 ED25519ph 997 SecP384r1MLKEM1024 998 +xaes_256_gcm 999 +xaes_256_gcm_kc 1000 diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt index d52dfcb614a..3260e1e268e 100644 --- a/crypto/obj/objects.txt +++ b/crypto/obj/objects.txt @@ -893,7 +893,9 @@ aes 44 : AES-256-CFB : aes-256-cfb aes 45 : id-aes256-wrap aes 46 : id-aes256-GCM : aes-256-gcm aes 47 : id-aes256-CCM : aes-256-ccm -aes 48 : id-aes256-wrap-pad +aes 48 : id-aes256-wrap-pad +aes 49 : id-xaes256-GCM : xaes-256-gcm +aes 50 : id-xaes256-GCM-KC : xaes-256-gcm-kc # There are no OIDs for these modes... diff --git a/crypto/test/test_util.cc b/crypto/test/test_util.cc index 363b147ff3a..1c50e19e056 100644 --- a/crypto/test/test_util.cc +++ b/crypto/test/test_util.cc @@ -34,6 +34,17 @@ #include "openssl/pem.h" #include "openssl/rand.h" +bool ConvertToBytes(std::vector *out, const std::string &value) { + if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') { + out->assign(value.begin() + 1, value.end() - 1); + return true; + } + + if (!DecodeHex(out, value)) { + return false; + } + return true; +} void hexdump(FILE *fp, const char *msg, const void *in, size_t len) { const uint8_t *data = reinterpret_cast(in); diff --git a/crypto/test/test_util.h b/crypto/test/test_util.h index 4f13fedc3e7..932f9a38ad8 100644 --- a/crypto/test/test_util.h +++ b/crypto/test/test_util.h @@ -34,6 +34,7 @@ // hexdump writes |msg| to |fp| followed by the hex encoding of |len| bytes // from |in|. void hexdump(FILE *fp, const char *msg, const void *in, size_t len); +bool ConvertToBytes(std::vector *out, const std::string &value); // Bytes is a wrapper over a byte slice which may be compared for equality. This // allows it to be used in EXPECT_EQ macros. diff --git a/generated-src/crypto_test_data.cc.tar.bz2 b/generated-src/crypto_test_data.cc.tar.bz2 index af724d46742..545a8120d66 100644 Binary files a/generated-src/crypto_test_data.cc.tar.bz2 and b/generated-src/crypto_test_data.cc.tar.bz2 differ diff --git a/include/openssl/aead.h b/include/openssl/aead.h index 64df91ecfee..5896312b5fb 100644 --- a/include/openssl/aead.h +++ b/include/openssl/aead.h @@ -431,6 +431,9 @@ OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_gcm_tls13(void); // 1.3 nonce construction. OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_gcm_tls13(void); +// EVP_aead_xaes_256_gcm is AES-256 in Galois Counter Mode with CMAC-based KDF +OPENSSL_EXPORT const EVP_AEAD *EVP_aead_xaes_256_gcm(void); +OPENSSL_EXPORT const EVP_AEAD *EVP_aead_xaes_256_gcm_kc(void); // Obscure functions. diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h index cf6132061e5..336b626eefa 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h @@ -365,7 +365,6 @@ OPENSSL_EXPORT int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md, #define EVP_CIPH_OCB_MODE 0x9 #define EVP_CIPH_WRAP_MODE 0xa - // Cipher flags (for |EVP_CIPHER_flags|). // EVP_CIPH_VARIABLE_LENGTH indicates that the cipher takes a variable length @@ -394,6 +393,11 @@ OPENSSL_EXPORT int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md, // one. #define EVP_CIPH_FLAG_AEAD_CIPHER 0x800 +// EVP_CIPH_FLAG_KC_CIPHER specifies that the cipher supports key commitment. +// Currently, this flag is only used for test cases, i.e., to check whether +// the cipher supports key commitment or not +#define EVP_CIPH_FLAG_KC_CIPHER 0x4000 + // EVP_CIPH_CUSTOM_COPY indicates that the |ctrl| callback should be called // with |EVP_CTRL_COPY| at the end of normal |EVP_CIPHER_CTX_copy| // processing. @@ -492,6 +496,8 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_get_cipherbyname(const char *name); // not act on it until the entire operation is complete. OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_128_gcm(void); OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_256_gcm(void); +OPENSSL_EXPORT const EVP_CIPHER *EVP_xaes_256_gcm(void); +OPENSSL_EXPORT const EVP_CIPHER *EVP_xaes_256_gcm_kc(void); OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_128_ccm(void); OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_192_ccm(void); @@ -601,6 +607,9 @@ OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_add_cipher_alias(const char *a, // EVP_CTRL_GCM_SET_IV_INV sets the GCM invocation field, decrypt only #define EVP_CTRL_GCM_SET_IV_INV 0x18 #define EVP_CTRL_GET_IVLEN 0x19 +// For extracting and verifying key commitment +#define EVP_CTRL_AEAD_GET_KC 0x20 +#define EVP_CTRL_AEAD_VERIFY_KC 0x21 // The following constants are unused. #define EVP_GCM_TLS_FIXED_IV_LEN 4 @@ -735,5 +744,7 @@ BSSL_NAMESPACE_END #define CIPHER_R_ALIGNMENT_CHANGED 142 #define CIPHER_R_SERIALIZATION_INVALID_SERDE_VERSION 143 #define CIPHER_R_SERIALIZATION_INVALID_CIPHER_ID 144 +// We're introducing a new error code for invalid key commitment +#define CIPHER_R_KEY_COMMITMENT_INVALID 145 #endif // OPENSSL_HEADER_CIPHER_H diff --git a/include/openssl/nid.h b/include/openssl/nid.h index 77f925f52df..026f5ea890e 100644 --- a/include/openssl/nid.h +++ b/include/openssl/nid.h @@ -4386,6 +4386,16 @@ extern "C" { #define NID_SecP384r1MLKEM1024 998 #define OBJ_SecP384r1MLKEM1024 1L, 3L, 6L, 1L, 4L, 1L, 42235L, 6L +#define SN_xaes_256_gcm "id-xaes256-GCM" +#define LN_xaes_256_gcm "xaes-256-gcm" +#define NID_xaes_256_gcm 999 +#define OBJ_xaes_256_gcm 2L, 16L, 840L, 1L, 101L, 3L, 4L, 1L, 49L + +#define SN_xaes_256_gcm_kc "id-xaes256-GCM-KC" +#define LN_xaes_256_gcm_kc "xaes-256-gcm-kc" +#define NID_xaes_256_gcm_kc 1000 +#define OBJ_xaes_256_gcm_kc 2L, 16L, 840L, 1L, 101L, 3L, 4L, 1L, 50L + #if defined(__cplusplus) } /* extern C */ #endif diff --git a/sources.cmake b/sources.cmake index aedc37f0e1f..462e71fb5ad 100644 --- a/sources.cmake +++ b/sources.cmake @@ -33,6 +33,8 @@ set( crypto/cipher_extra/test/aes_256_gcm_randnonce_tests.txt crypto/cipher_extra/test/aes_256_gcm_siv_tests.txt crypto/cipher_extra/test/aes_256_gcm_tests.txt + crypto/cipher_extra/test/xaes_256_gcm_tests.txt + crypto/cipher_extra/test/xaes_256_gcm_kc_tests.txt crypto/cipher_extra/test/aes_ccm_test.txt crypto/cipher_extra/test/chacha20_poly1305_tests.txt crypto/cipher_extra/test/xchacha20_poly1305_tests.txt diff --git a/tool/speed.cc b/tool/speed.cc index a1160082cec..c8104704bbb 100644 --- a/tool/speed.cc +++ b/tool/speed.cc @@ -2950,6 +2950,9 @@ bool Speed(const std::vector &args) { !SpeedEvpCipherGeneric(EVP_aes_128_gcm(), "EVP-AES-128-GCM", kTLSADLen, selected) || !SpeedEvpCipherGeneric(EVP_aes_192_gcm(), "EVP-AES-192-GCM", kTLSADLen, selected) || !SpeedEvpCipherGeneric(EVP_aes_256_gcm(), "EVP-AES-256-GCM", kTLSADLen, selected) || +#if AWSLC_API_VERSION > 31 + !SpeedEvpCipherGeneric(EVP_xaes_256_gcm(), "EVP-XAES-256-GCM", kTLSADLen, selected) || +#endif !SpeedEvpCipherGeneric(EVP_aes_128_ctr(), "EVP-AES-128-CTR", kTLSADLen, selected) || !SpeedEvpCipherGeneric(EVP_aes_192_ctr(), "EVP-AES-192-CTR", kTLSADLen, selected) || !SpeedEvpCipherGeneric(EVP_aes_256_ctr(), "EVP-AES-256-CTR", kTLSADLen, selected) || @@ -3053,6 +3056,8 @@ bool Speed(const std::vector &args) { #endif #if AWSLC_API_VERSION > 31 !SpeedDigestSign(selected) || + !SpeedAEADSeal(EVP_aead_xaes_256_gcm(), "AEAD-XAES-256-GCM", kTLSADLen, selected) || + !SpeedAEADOpen(EVP_aead_xaes_256_gcm(), "AEAD-XAES-256-GCM", kTLSADLen, selected) || #endif !SpeedAEADSeal(EVP_aead_aes_128_gcm(), "AEAD-AES-128-GCM", kTLSADLen, selected) || !SpeedAEADOpen(EVP_aead_aes_128_gcm(), "AEAD-AES-128-GCM", kTLSADLen, selected) ||