mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-22 09:12:13 -05:00
LibCrypto: Accept correct IV sizes for AES-GCM
AES-GCM should accept 96-bits keys as is. Any other key should be preprocessed with GHASH.
This commit is contained in:
parent
3167d4f06b
commit
1ae28324bd
Notes:
github-actions[bot]
2024-12-16 12:28:59 +00:00
Author: https://github.com/devgianlu Commit: https://github.com/LadybirdBrowser/ladybird/commit/1ae28324bd2 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2935 Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/gmta ✅
3 changed files with 51 additions and 37 deletions
|
@ -67,19 +67,44 @@ public:
|
|||
encrypt(in, out, ivec);
|
||||
}
|
||||
|
||||
void encrypt(ReadonlyBytes in, Bytes out, ReadonlyBytes iv_in, ReadonlyBytes aad, Bytes tag)
|
||||
ByteBuffer process_iv(ReadonlyBytes iv_in)
|
||||
{
|
||||
auto iv_buf_result = ByteBuffer::copy(iv_in);
|
||||
// Not enough memory to figure out :shrug:
|
||||
if (iv_buf_result.is_error()) {
|
||||
dbgln("GCM::encrypt: Not enough memory to allocate {} bytes for IV", iv_in.size());
|
||||
return;
|
||||
if (iv_in.size() == 12) {
|
||||
auto buf = MUST(ByteBuffer::create_zeroed(16));
|
||||
buf.overwrite(0, iv_in.data(), iv_in.size());
|
||||
|
||||
// Increment the IV for block 0
|
||||
auto iv = buf.bytes();
|
||||
CTR<T>::increment(iv);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
auto iv = iv_buf_result.value().bytes();
|
||||
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
// Otherwise, the IV is padded with the minimum number of '0' bits, possibly none,
|
||||
// so that the length of the resulting string is a multiple of 128 bits (the block size);
|
||||
// this string in turn is appended with 64 additional '0' bits, followed by
|
||||
// the 64-bit representation of the length of the IV, and the GHASH function
|
||||
// is applied to the resulting string to form the precounter block.
|
||||
auto iv_pad = iv_in.size() % 16 == 0 ? 0 : 16 - (iv_in.size() % 16);
|
||||
auto data = MUST(ByteBuffer::create_zeroed(iv_in.size() + iv_pad + 8 + 8));
|
||||
data.overwrite(0, iv_in.data(), iv_in.size());
|
||||
ByteReader::store(data.data() + iv_in.size() + iv_pad + 8, AK::convert_between_host_and_big_endian<u64>(iv_in.size() * 8));
|
||||
|
||||
u32 out[4] { 0, 0, 0, 0 };
|
||||
m_ghash->process_one(out, data);
|
||||
|
||||
auto buf = MUST(ByteBuffer::create_uninitialized(16));
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
ByteReader::store(buf.data() + (i * 4), AK::convert_between_host_and_big_endian(out[i]));
|
||||
return buf;
|
||||
}
|
||||
|
||||
void encrypt(ReadonlyBytes in, Bytes out, ReadonlyBytes iv_in, ReadonlyBytes aad, Bytes tag)
|
||||
{
|
||||
auto iv_buf = process_iv(iv_in);
|
||||
auto iv = iv_buf.bytes();
|
||||
|
||||
// Increment the IV for block 0
|
||||
CTR<T>::increment(iv);
|
||||
typename T::BlockType block0;
|
||||
block0.overwrite(iv);
|
||||
this->cipher().encrypt_block(block0, block0);
|
||||
|
@ -94,20 +119,14 @@ public:
|
|||
|
||||
auto auth_tag = m_ghash->process(aad, out);
|
||||
block0.apply_initialization_vector({ auth_tag.data, array_size(auth_tag.data) });
|
||||
block0.bytes().copy_to(tag);
|
||||
(void)block0.bytes().copy_trimmed_to(tag);
|
||||
}
|
||||
|
||||
VerificationConsistency decrypt(ReadonlyBytes in, Bytes out, ReadonlyBytes iv_in, ReadonlyBytes aad, ReadonlyBytes tag)
|
||||
{
|
||||
auto iv_buf_result = ByteBuffer::copy(iv_in);
|
||||
// Not enough memory to figure out :shrug:
|
||||
if (iv_buf_result.is_error())
|
||||
return VerificationConsistency::Inconsistent;
|
||||
auto iv_buf = process_iv(iv_in);
|
||||
auto iv = iv_buf.bytes();
|
||||
|
||||
auto iv = iv_buf_result.value().bytes();
|
||||
|
||||
// Increment the IV for block 0
|
||||
CTR<T>::increment(iv);
|
||||
typename T::BlockType block0;
|
||||
block0.overwrite(iv);
|
||||
this->cipher().encrypt_block(block0, block0);
|
||||
|
@ -119,7 +138,8 @@ public:
|
|||
block0.apply_initialization_vector({ auth_tag.data, array_size(auth_tag.data) });
|
||||
|
||||
auto test_consistency = [&] {
|
||||
if (block0.block_size() != tag.size() || !timing_safe_compare(block0.bytes().data(), tag.data(), tag.size()))
|
||||
VERIFY(block0.block_size() >= tag.size());
|
||||
if (block0.block_size() < tag.size() || !timing_safe_compare(block0.bytes().data(), tag.data(), tag.size()))
|
||||
return VerificationConsistency::Inconsistent;
|
||||
|
||||
return VerificationConsistency::Consistent;
|
||||
|
|
|
@ -154,13 +154,10 @@ void TLSv12::update_packet(ByteBuffer& packet)
|
|||
// AEAD IV (12)
|
||||
// IV (4)
|
||||
// (Nonce) (8)
|
||||
// -- Our GCM impl takes 16 bytes
|
||||
// zero (4)
|
||||
u8 iv[16];
|
||||
Bytes iv_bytes { iv, 16 };
|
||||
u8 iv[12];
|
||||
Bytes iv_bytes { iv, 12 };
|
||||
Bytes { m_context.crypto.local_aead_iv, 4 }.copy_to(iv_bytes);
|
||||
fill_with_random(iv_bytes.slice(4, 8));
|
||||
memset(iv_bytes.offset(12), 0, 4);
|
||||
|
||||
// write the random part of the iv out
|
||||
iv_bytes.slice(4, 8).copy_to(ct.bytes().slice(header_size));
|
||||
|
@ -400,13 +397,10 @@ ssize_t TLSv12::handle_message(ReadonlyBytes buffer)
|
|||
// AEAD IV (12)
|
||||
// IV (4)
|
||||
// (Nonce) (8)
|
||||
// -- Our GCM impl takes 16 bytes
|
||||
// zero (4)
|
||||
u8 iv[16];
|
||||
Bytes iv_bytes { iv, 16 };
|
||||
u8 iv[12];
|
||||
Bytes iv_bytes { iv, 12 };
|
||||
Bytes { m_context.crypto.remote_aead_iv, 4 }.copy_to(iv_bytes);
|
||||
nonce.copy_to(iv_bytes.slice(4));
|
||||
memset(iv_bytes.offset(12), 0, 4);
|
||||
|
||||
auto ciphertext = payload.slice(0, payload.size() - 16);
|
||||
auto tag = payload.slice(ciphertext.size());
|
||||
|
|
|
@ -466,7 +466,7 @@ TEST_CASE(test_AES_GCM_128bit_encrypt_empty)
|
|||
u8 result_tag[] { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a };
|
||||
Bytes out;
|
||||
auto tag = ByteBuffer::create_uninitialized(16).release_value();
|
||||
cipher.encrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, tag);
|
||||
cipher.encrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, tag);
|
||||
EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0);
|
||||
}
|
||||
|
||||
|
@ -478,7 +478,7 @@ TEST_CASE(test_AES_GCM_128bit_encrypt_zeros)
|
|||
auto tag = ByteBuffer::create_uninitialized(16).release_value();
|
||||
auto out = ByteBuffer::create_uninitialized(16).release_value();
|
||||
auto out_bytes = out.bytes();
|
||||
cipher.encrypt("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, tag);
|
||||
cipher.encrypt("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, tag);
|
||||
EXPECT(memcmp(result_ct, out.data(), out.size()) == 0);
|
||||
EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0);
|
||||
}
|
||||
|
@ -494,7 +494,7 @@ TEST_CASE(test_AES_GCM_128bit_encrypt_multiple_blocks_with_iv)
|
|||
cipher.encrypt(
|
||||
"\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55"_b,
|
||||
out_bytes,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88"_b,
|
||||
{},
|
||||
tag);
|
||||
EXPECT(memcmp(result_ct, out.data(), out.size()) == 0);
|
||||
|
@ -512,7 +512,7 @@ TEST_CASE(test_AES_GCM_128bit_encrypt_with_aad)
|
|||
cipher.encrypt(
|
||||
"\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55"_b,
|
||||
out_bytes,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88"_b,
|
||||
{},
|
||||
tag);
|
||||
EXPECT(memcmp(result_ct, out.data(), out.size()) == 0);
|
||||
|
@ -524,7 +524,7 @@ TEST_CASE(test_AES_GCM_128bit_decrypt_empty)
|
|||
Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption);
|
||||
u8 input_tag[] { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a };
|
||||
Bytes out;
|
||||
auto consistency = cipher.decrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
auto consistency = cipher.decrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent);
|
||||
EXPECT_EQ(out.size(), 0u);
|
||||
}
|
||||
|
@ -537,7 +537,7 @@ TEST_CASE(test_AES_GCM_128bit_decrypt_zeros)
|
|||
u8 result_pt[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
auto out = ByteBuffer::create_uninitialized(16).release_value();
|
||||
auto out_bytes = out.bytes();
|
||||
auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent);
|
||||
EXPECT(memcmp(result_pt, out.data(), out.size()) == 0);
|
||||
}
|
||||
|
@ -550,7 +550,7 @@ TEST_CASE(test_AES_GCM_128bit_decrypt_multiple_blocks_with_iv)
|
|||
u8 result_pt[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
auto out = ByteBuffer::create_uninitialized(16).release_value();
|
||||
auto out_bytes = out.bytes();
|
||||
auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, {}, { input_tag, 16 });
|
||||
EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent);
|
||||
EXPECT(memcmp(result_pt, out.data(), out.size()) == 0);
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ TEST_CASE(test_AES_GCM_128bit_decrypt_multiple_blocks_with_aad)
|
|||
auto consistency = cipher.decrypt(
|
||||
{ input_ct, 64 },
|
||||
out_bytes,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b,
|
||||
"\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88"_b,
|
||||
"\xde\xad\xbe\xef\xfa\xaf\x11\xcc"_b,
|
||||
{ input_tag, 16 });
|
||||
EXPECT(memcmp(result_pt, out.data(), out.size()) == 0);
|
||||
|
|
Loading…
Reference in a new issue