diff --git a/Libraries/LibCrypto/Cipher/Mode/GCM.h b/Libraries/LibCrypto/Cipher/Mode/GCM.h index 20ad8e390a3..21186667378 100644 --- a/Libraries/LibCrypto/Cipher/Mode/GCM.h +++ b/Libraries/LibCrypto/Cipher/Mode/GCM.h @@ -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::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(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::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::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; diff --git a/Libraries/LibTLS/Record.cpp b/Libraries/LibTLS/Record.cpp index ad800b1d0bc..907421b10dc 100644 --- a/Libraries/LibTLS/Record.cpp +++ b/Libraries/LibTLS/Record.cpp @@ -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()); diff --git a/Tests/LibCrypto/TestAES.cpp b/Tests/LibCrypto/TestAES.cpp index f84101393e0..cb9f0d14e8a 100644 --- a/Tests/LibCrypto/TestAES.cpp +++ b/Tests/LibCrypto/TestAES.cpp @@ -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);