diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan/src/lib/utils/ghash/ghash.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/botan/src/lib/utils/ghash/ghash.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/utils/ghash/ghash.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp new file mode 100644 index 0000000000..e24f5e02ca --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp @@ -0,0 +1,236 @@ +/* +* GCM GHASH +* (C) 2013,2015,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ghash.h> +#include <botan/internal/ct_utils.h> +#include <botan/loadstor.h> +#include <botan/cpuid.h> +#include <botan/exceptn.h> + +namespace Botan { + +std::string GHASH::provider() const + { +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + return "clmul"; +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) + if(CPUID::has_vperm()) + return "vperm"; +#endif + + return "base"; + } + +void GHASH::ghash_multiply(secure_vector<uint8_t>& x, + const uint8_t input[], + size_t blocks) + { +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + { + return ghash_multiply_cpu(x.data(), m_H_pow.data(), input, blocks); + } +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) + if(CPUID::has_vperm()) + { + return ghash_multiply_vperm(x.data(), m_HM.data(), input, blocks); + } +#endif + + CT::poison(x.data(), x.size()); + + const uint64_t ALL_BITS = 0xFFFFFFFFFFFFFFFF; + + uint64_t X[2] = { + load_be<uint64_t>(x.data(), 0), + load_be<uint64_t>(x.data(), 1) + }; + + for(size_t b = 0; b != blocks; ++b) + { + X[0] ^= load_be<uint64_t>(input, 2*b); + X[1] ^= load_be<uint64_t>(input, 2*b+1); + + uint64_t Z[2] = { 0, 0 }; + + for(size_t i = 0; i != 64; ++i) + { + const uint64_t X0MASK = (ALL_BITS + (X[0] >> 63)) ^ ALL_BITS; + const uint64_t X1MASK = (ALL_BITS + (X[1] >> 63)) ^ ALL_BITS; + + X[0] <<= 1; + X[1] <<= 1; + + Z[0] ^= m_HM[4*i ] & X0MASK; + Z[1] ^= m_HM[4*i+1] & X0MASK; + Z[0] ^= m_HM[4*i+2] & X1MASK; + Z[1] ^= m_HM[4*i+3] & X1MASK; + } + + X[0] = Z[0]; + X[1] = Z[1]; + } + + store_be<uint64_t>(x.data(), X[0], X[1]); + CT::unpoison(x.data(), x.size()); + } + +void GHASH::ghash_update(secure_vector<uint8_t>& ghash, + const uint8_t input[], size_t length) + { + verify_key_set(!m_HM.empty()); + + /* + This assumes if less than block size input then we're just on the + final block and should pad with zeros + */ + + const size_t full_blocks = length / GCM_BS; + const size_t final_bytes = length - (full_blocks * GCM_BS); + + if(full_blocks > 0) + { + ghash_multiply(ghash, input, full_blocks); + } + + if(final_bytes) + { + uint8_t last_block[GCM_BS] = { 0 }; + copy_mem(last_block, input + full_blocks * GCM_BS, final_bytes); + ghash_multiply(ghash, last_block, 1); + secure_scrub_memory(last_block, final_bytes); + } + } + +void GHASH::key_schedule(const uint8_t key[], size_t length) + { + m_H.assign(key, key+length); + m_H_ad.resize(GCM_BS); + m_ad_len = 0; + m_text_len = 0; + + uint64_t H0 = load_be<uint64_t>(m_H.data(), 0); + uint64_t H1 = load_be<uint64_t>(m_H.data(), 1); + + const uint64_t R = 0xE100000000000000; + + m_HM.resize(256); + + // precompute the multiples of H + for(size_t i = 0; i != 2; ++i) + { + for(size_t j = 0; j != 64; ++j) + { + /* + we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68 + to make indexing nicer in the multiplication code + */ + m_HM[4*j+2*i] = H0; + m_HM[4*j+2*i+1] = H1; + + // GCM's bit ops are reversed so we carry out of the bottom + const uint64_t carry = R * (H1 & 1); + H1 = (H1 >> 1) | (H0 << 63); + H0 = (H0 >> 1) ^ carry; + } + } + +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + { + m_H_pow.resize(8); + ghash_precompute_cpu(m_H.data(), m_H_pow.data()); + } +#endif + } + +void GHASH::start(const uint8_t nonce[], size_t len) + { + BOTAN_ARG_CHECK(len == 16, "GHASH requires a 128-bit nonce"); + m_nonce.assign(nonce, nonce + len); + m_ghash = m_H_ad; + } + +void GHASH::set_associated_data(const uint8_t input[], size_t length) + { + if(m_ghash.empty() == false) + throw Invalid_State("Too late to set AD in GHASH"); + + zeroise(m_H_ad); + + ghash_update(m_H_ad, input, length); + m_ad_len = length; + } + +void GHASH::update_associated_data(const uint8_t ad[], size_t length) + { + verify_key_set(m_ghash.size() == GCM_BS); + m_ad_len += length; + ghash_update(m_ghash, ad, length); + } + +void GHASH::update(const uint8_t input[], size_t length) + { + verify_key_set(m_ghash.size() == GCM_BS); + m_text_len += length; + ghash_update(m_ghash, input, length); + } + +void GHASH::add_final_block(secure_vector<uint8_t>& hash, + size_t ad_len, size_t text_len) + { + /* + * stack buffer is fine here since the text len is public + * and the length of the AD is probably not sensitive either. + */ + uint8_t final_block[GCM_BS]; + store_be<uint64_t>(final_block, 8*ad_len, 8*text_len); + ghash_update(hash, final_block, GCM_BS); + } + +void GHASH::final(uint8_t mac[], size_t mac_len) + { + BOTAN_ARG_CHECK(mac_len > 0 && mac_len <= 16, "GHASH output length"); + add_final_block(m_ghash, m_ad_len, m_text_len); + + for(size_t i = 0; i != mac_len; ++i) + mac[i] = m_ghash[i] ^ m_nonce[i]; + + m_ghash.clear(); + m_text_len = 0; + } + +void GHASH::nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t nonce_len) + { + BOTAN_ASSERT(m_ghash.size() == 0, "nonce_hash called during wrong time"); + + ghash_update(y0, nonce, nonce_len); + add_final_block(y0, 0, nonce_len); + } + +void GHASH::clear() + { + zap(m_H); + zap(m_HM); + reset(); + } + +void GHASH::reset() + { + zeroise(m_H_ad); + m_ghash.clear(); + m_nonce.clear(); + m_text_len = m_ad_len = 0; + } + +} |