From fbf82276d8389436861c504505339814c6b2f3c2 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 1 Dec 2024 14:31:23 +0100 Subject: [PATCH] Remove heap allocations from aes::calcJ0 --- CLI/cli_test.cpp | 19 +++++++++++++++++++ soup/aes.cpp | 41 +++++++++++++++++++++++++++++++---------- soup/aes.hpp | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/CLI/cli_test.cpp b/CLI/cli_test.cpp index 8375ed91..a8f3303c 100644 --- a/CLI/cli_test.cpp +++ b/CLI/cli_test.cpp @@ -193,6 +193,25 @@ static void unit_crypto() aes::ecbDecrypt(reinterpret_cast(data.data()), data.size(), reinterpret_cast(key), 32); assert(data == "The quick brown fox jumps over the lazy dog."); }); + test("calcJ0", [] + { + const uint8_t bytes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + { + uint8_t res[16]; + aes::calcJ0(res, bytes, bytes, 15); + assert(string::bin2hex((const char*)res, 16) == "C53CC6D5A2EEBC3F3FF12D420285FC8C"); + } + { + uint8_t res[16]; + aes::calcJ0(res, bytes, bytes, 16); + assert(string::bin2hex((const char*)res, 16) == "67144FBCDC0E6262B92502D7DA99CE72"); + } + { + uint8_t res[16]; + aes::calcJ0(res, bytes, bytes, 17); + assert(string::bin2hex((const char*)res, 16) == "B85388BE5704F782153B4FDCC1F16FF7"); + } + }); } test("SegWitAddress", [] diff --git a/soup/aes.cpp b/soup/aes.cpp index 0de378e9..71a4da64 100644 --- a/soup/aes.cpp +++ b/soup/aes.cpp @@ -950,7 +950,7 @@ NAMESPACE_SOUP aes::encryptBlock(h, h, roundKeys, Nr); } - void aes::calcJ0(uint8_t j0[16], const uint8_t h[16], const uint8_t* iv, size_t iv_len) SOUP_EXCAL + void aes::calcJ0(uint8_t j0[16], const uint8_t h[16], const uint8_t* iv, size_t iv_len) noexcept { if (iv_len == 12) { @@ -962,15 +962,21 @@ NAMESPACE_SOUP } else { - const auto len_iv = iv_len * 8; - const auto s = 128 * plusaes::detail::gcm::ceil(len_iv / 128.0) - len_iv; - std::vector ghash_in; - ghash_in.reserve(32); - plusaes::detail::gcm::push_back(ghash_in, iv, iv_len); - plusaes::detail::gcm::push_back_zero_bits(ghash_in, s + 64); - plusaes::detail::gcm::push_back(ghash_in, std::bitset<64>(len_iv)); - - return ghash(j0, h, ghash_in.data(), ghash_in.size()); + GhashState state(j0, h); + state.append(iv, iv_len); + + const auto iv_bits = iv_len * 8; + auto padding = (128 * static_cast(std::ceil(iv_bits / 128.0) + 0.5) - iv_bits) / 8; + while (padding--) + { + state.appendByte(0); + } + + memset(state.buffer, 0, 16); + reinterpret_cast(state.buffer)[1] = Endianness::invert(iv_bits); static_assert(ENDIAN_NATIVE == ENDIAN_LITTLE); + state.transform(); + + SOUP_DEBUG_ASSERT(state.buffer_counter == 0); } } @@ -1029,4 +1035,19 @@ NAMESPACE_SOUP ghash(tag, h, ghash_in.data(), ghash_in.size()); gctr(tag, 16, roundKeys, Nr, j0); } + + aes::GhashState::GhashState(uint8_t res[16], const uint8_t h[16]) noexcept + : res(res), h(h), buffer_counter(0) + { + memset(res, 0, 16); + } + + void aes::GhashState::transform() noexcept + { + xorBlocks(res, buffer); + + uint8_t tmp[16]; + memcpy(tmp, res, 16); + mulBlocks(res, tmp, h); + } } diff --git a/soup/aes.hpp b/soup/aes.hpp index 2062137f..5dd20086 100644 --- a/soup/aes.hpp +++ b/soup/aes.hpp @@ -7,9 +7,8 @@ NAMESPACE_SOUP { - class aes + struct aes { - public: // Input size should be a multiple of 16 bytes. // GCM can deal with unaligned data, other methods will simply ignore the trailing bytes -> they will not be encrypted. // You may use a padding scheme such as PKCS#7 for padding. @@ -27,7 +26,7 @@ NAMESPACE_SOUP static void gcmEncrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, uint8_t tag[16]) SOUP_EXCAL; static bool gcmDecrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, const uint8_t tag[16]) SOUP_EXCAL; - private: + static void expandKey(uint8_t w[240], const uint8_t* key, size_t key_len) noexcept; static void expandKeyForDecryption(uint8_t w[240], const uint8_t* key, size_t key_len) noexcept; [[nodiscard]] static int getNk(size_t key_len) noexcept; @@ -56,9 +55,39 @@ NAMESPACE_SOUP static void mulBlocks(uint8_t res[16], const uint8_t x[16], const uint8_t y[16]) noexcept; static void ghash(uint8_t res[16], const uint8_t h[16], const uint8_t x[], size_t x_bytes) noexcept; static void calcH(uint8_t h[16], uint8_t roundKeys[240], const int Nr) noexcept; - static void calcJ0(uint8_t j0[16], const uint8_t h[16], const uint8_t* iv, size_t iv_len) SOUP_EXCAL; + static void calcJ0(uint8_t j0[16], const uint8_t h[16], const uint8_t* iv, size_t iv_len) noexcept; static void inc32(uint8_t block[16]) noexcept; static void gctr(uint8_t* data, size_t data_len, const uint8_t roundKeys[240], const int Nr, const uint8_t icb[8]) noexcept; static void calcGcmTag(uint8_t tag[16], uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t roundKeys[16], const int Nr, const uint8_t h[16], const uint8_t j0[16]) SOUP_EXCAL; + + struct GhashState + { + uint8_t* const res; + const uint8_t* const h; + uint8_t buffer[16]; + uint8_t buffer_counter; + + GhashState(uint8_t res[16], const uint8_t h[16]) noexcept; + + void append(const uint8_t data[], size_t size) noexcept + { + for (size_t i = 0; i != size; ++i) + { + appendByte(data[i]); + } + } + + void appendByte(uint8_t byte) noexcept + { + buffer[buffer_counter] = byte; + if (++buffer_counter == 16) + { + buffer_counter = 0; + transform(); + } + } + + void transform() noexcept; + }; }; }