Botan 3.13.0
Crypto and TLS for C&
polyval.cpp
Go to the documentation of this file.
1/*
2* POLYVAL hash function (RFC 8452)
3* (C) 2026 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/polyval.h>
9
10#include <botan/internal/ct_utils.h>
11#include <botan/internal/ghash.h>
12#include <botan/internal/loadstor.h>
13
14#if defined(BOTAN_HAS_CPUID)
15 #include <botan/internal/cpuid.h>
16#endif
17
18namespace Botan {
19
20namespace {
21
22std::array<uint8_t, 16> byte_reverse(std::span<const uint8_t, 16> x) {
23 const auto w = load_le<std::array<uint64_t, 2>>(x);
24 return store_be(w[1], w[0]);
25}
26
27} // namespace
28
29std::string Polyval::provider() const {
30#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
32 return *feat;
33 }
34#endif
35
36#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
37 if(auto feat = CPUID::check(CPUID::Feature::HW_CLMUL)) {
38 return *feat;
39 }
40#endif
41
42#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
44 return *feat;
45 }
46#endif
47
48 return "base";
49}
50
52 return !m_HM.empty() || !m_H_pow.empty();
53}
54
55void Polyval::key_schedule(std::span<const uint8_t> key) {
56 m_state = {0};
57 m_buffer.clear();
58
59 BOTAN_ASSERT_NOMSG(key.size() == BS);
60
61#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
63 zap(m_HM);
64 if(m_H_pow.size() != 32) {
65 m_H_pow.resize(32);
66 }
67 polyval_precompute_avx512_clmul(key.data(), m_H_pow.data());
68 return;
69 }
70#endif
71
72#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
74 zap(m_HM);
75 polyval_precompute_cpu(key.data(), m_H_pow);
76 return;
77 }
78#endif
79
80 /*
81 The fallback reuses the GHASH tables, relying on RFC 8452 Section 3:
82
83 "We note that POLYVAL(H, X_1, X_2, ...) is equal to
84 ByteReverse(GHASH(ByteReverse(H) * x, ByteReverse(X_1),
85 ByteReverse(X_2), ...)), where ByteReverse is a function that
86 reverses the order of 16 bytes."
87 */
88 zap(m_H_pow);
89 auto H = load_le<std::array<uint64_t, 2>>(key.first<BS>());
90 // Byte reversing the key swaps the words and bswaps each; then multiply
91 // by x in the GHASH convention, carrying out of the bottom bit
92 std::swap(H[0], H[1]);
93 const uint64_t R = 0xE100000000000000;
94 const uint64_t carry = CT::Mask<uint64_t>::expand(H[1] & 1).if_set_return(R);
95 H[1] = (H[1] >> 1) | (H[0] << 63);
96 H[0] = (H[0] >> 1) ^ carry;
97 const auto Hx = store_be(H[0], H[1]);
98 GHASH::ghash_precompute_base(Hx, m_HM);
99}
100
101void Polyval::update(std::span<const uint8_t> input) {
103 BufferSlicer in(input);
104 while(!in.empty()) {
105 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
106 polyval_multiply(m_state, one_block.value(), 1);
107 }
108
109 if(m_buffer.in_alignment()) {
110 const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
111 if(full_blocks > 0) {
112 polyval_multiply(m_state, aligned_data, full_blocks);
113 }
114 }
115 }
117}
118
121 if(!m_buffer.in_alignment()) {
122 m_buffer.fill_up_with_zeros();
123 polyval_multiply(m_state, m_buffer.consume(), 1);
124 }
125}
126
127void Polyval::final(std::span<uint8_t, BS> out) {
129 BOTAN_STATE_CHECK(m_buffer.in_alignment());
130
131 if(m_H_pow.empty()) {
132 // Fallback path; the state is in the GHASH byte order
133 copy_mem(out, byte_reverse(m_state));
134 } else {
135 copy_mem(out, m_state);
136 }
137
138 secure_scrub_memory(m_state);
139}
140
142 zap(m_HM);
143 zap(m_H_pow);
144 secure_scrub_memory(m_state);
145 m_buffer.clear();
146}
147
148void Polyval::polyval_multiply(std::span<uint8_t, BS> x, std::span<const uint8_t> input, size_t blocks) {
149 BOTAN_ASSERT_NOMSG(input.size() % BS == 0);
150
151#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
153 BOTAN_ASSERT_NOMSG(m_H_pow.size() == 32);
154 return polyval_multiply_avx512_clmul(x.data(), m_H_pow.data(), input.data(), blocks);
155 }
156#endif
157
158#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
160 BOTAN_ASSERT_NOMSG(!m_H_pow.empty());
161 return polyval_multiply_cpu(x.data(), m_H_pow, input.data(), blocks);
162 }
163#endif
164
165 // Fallback via GHASH (see key_schedule); each input block is byte
166 // reversed, and the state is maintained in the GHASH byte order
167 BufferSlicer in(input);
168 for(size_t b = 0; b != blocks; ++b) {
169 const auto rev = byte_reverse(in.take<BS>());
170
171#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
173 BOTAN_ASSERT_NOMSG(!m_HM.empty());
174 GHASH::ghash_multiply_vperm(x.data(), m_HM.data(), rev.data(), 1);
175 continue;
176 }
177#endif
178
179 GHASH::ghash_multiply_base(x, m_HM, rev, 1);
180 }
181}
182
183} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
static std::optional< std::string > check(CPUID::Feature feat)
Definition cpuid.h:67
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
void update(std::span< const uint8_t > input)
Definition polyval.cpp:101
std::string provider() const
Definition polyval.cpp:29
void zero_pad()
Zero pad the input to a multiple of the block size.
Definition polyval.cpp:119
bool has_keying_material() const override
Definition polyval.cpp:51
void final(std::span< uint8_t, BS > out)
Write the current state to out, and reset the state.
Definition polyval.cpp:127
void clear() override
Definition polyval.cpp:141
void assert_key_material_set() const
Definition sym_algo.h:180
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:261
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25
void carry(int64_t &h0, int64_t &h1)
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:495
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745