Skip to content

Commit

Permalink
LibCrypto: Accept correct IV sizes for AES-GCM
Browse files Browse the repository at this point in the history
AES-GCM should accept 96-bits keys as is. Any other key should be
preprocessed with GHASH.
  • Loading branch information
devgianlu committed Dec 16, 2024
1 parent c1e631d commit e4614e2
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 37 deletions.
58 changes: 39 additions & 19 deletions Libraries/LibCrypto/Cipher/Mode/GCM.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,44 @@ class GCM : public CTR<T, IncrementFunction> {
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);
Expand All @@ -94,20 +119,14 @@ class GCM : public CTR<T, IncrementFunction> {

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);
Expand All @@ -119,7 +138,8 @@ class GCM : public CTR<T, IncrementFunction> {
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;
Expand Down
14 changes: 4 additions & 10 deletions Libraries/LibTLS/Record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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());
Expand Down
16 changes: 8 additions & 8 deletions Tests/LibCrypto/TestAES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
Expand Down

0 comments on commit e4614e2

Please sign in to comment.