Botan 3.13.0
Crypto and TLS for C&
poly1305.cpp
Go to the documentation of this file.
1/*
2* Derived from poly1305-donna-64.h by Andrew Moon <liquidsun@gmail.com>
3* in https://github.com/floodyberry/poly1305-donna
4*
5* (C) 2014 Andrew Moon
6* (C) 2014,2025,2026 Jack Lloyd
7*
8* Botan is released under the Simplified BSD License (see license.txt)
9*/
10
11#include <botan/internal/poly1305.h>
12
13#include <botan/exceptn.h>
14#include <botan/internal/buffer_slicer.h>
15#include <botan/internal/ct_utils.h>
16#include <botan/internal/donna128.h>
17#include <botan/internal/loadstor.h>
18
19#if defined(BOTAN_HAS_POLY1305_AVX2) || defined(BOTAN_HAS_POLY1305_AVX512)
20 #include <botan/internal/cpuid.h>
21#endif
22
23namespace Botan {
24
25namespace {
26
27// State layout: pad || accum || r || r^2 || r^3 || ... || r^n
28// This ordering allows extending with more powers of r at the end
29constexpr size_t PAD_BASE = 0; // pad[0..1]
30constexpr size_t H_BASE = 2; // h[0..2] (accumulator)
31constexpr size_t R_BASE = 5; // r^1[0..2], r^2[3..5], r^3[6..8], etc.
32
33// Multiply two values in radix 2^44 representation mod (2^130 - 5)
34// h = a * b mod p
35BOTAN_FORCE_INLINE void poly1305_mul_44(uint64_t& h0,
36 uint64_t& h1,
37 uint64_t& h2,
38 uint64_t a0,
39 uint64_t a1,
40 uint64_t a2,
41 uint64_t b0,
42 uint64_t b1,
43 uint64_t b2) {
44 constexpr uint64_t M44 = 0xFFFFFFFFFFF;
45 constexpr uint64_t M42 = 0x3FFFFFFFFFF;
46
47#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
48 typedef donna128 uint128_t;
49#endif
50
51 const uint64_t s1 = b1 * 20;
52 const uint64_t s2 = b2 * 20;
53
54 const uint128_t d0 = uint128_t(a0) * b0 + uint128_t(a1) * s2 + uint128_t(a2) * s1;
55 const uint64_t c0 = carry_shift(d0, 44);
56
57 const uint128_t d1 = uint128_t(a0) * b1 + uint128_t(a1) * b0 + uint128_t(a2) * s2 + c0;
58 const uint64_t c1 = carry_shift(d1, 44);
59
60 const uint128_t d2 = uint128_t(a0) * b2 + uint128_t(a1) * b1 + uint128_t(a2) * b0 + c1;
61 const uint64_t c2 = carry_shift(d2, 42);
62
63 h0 = (d0 & M44) + c2 * 5;
64 h1 = (d1 & M44) + (h0 >> 44);
65 h0 &= M44;
66 h2 = d2 & M42;
67}
68
69// Extend powers of r from current max to target
70void poly1305_extend_powers(secure_vector<uint64_t>& X, size_t target_powers) {
71 const size_t current_powers = (X.size() - 5) / 3;
72
73 if(current_powers >= target_powers) {
74 return;
75 }
76
77 // Load r^1 for multiplication
78 const uint64_t r0 = X[R_BASE + 0];
79 const uint64_t r1 = X[R_BASE + 1];
80 const uint64_t r2 = X[R_BASE + 2];
81
82 X.resize(5 + target_powers * 3);
83
84 // Compute r^(current+1) through r^target
85 for(size_t i = current_powers + 1; i <= target_powers; ++i) {
86 const size_t offset = R_BASE + (i - 1) * 3;
87 poly1305_mul_44(
88 X[offset + 0], X[offset + 1], X[offset + 2], X[offset - 3], X[offset - 2], X[offset - 1], r0, r1, r2);
89 }
90}
91
92// Initialize Poly1305 state and precompute powers of r
93void poly1305_init(secure_vector<uint64_t>& X, const uint8_t key[32]) {
94 X.clear();
95 X.reserve(2 + 3 + 2 * 3);
96 X.resize(2 + 3 + 3);
97
98 /* Save pad for later (first 2 slots) */
99 X[PAD_BASE + 0] = load_le<uint64_t>(key, 2);
100 X[PAD_BASE + 1] = load_le<uint64_t>(key, 3);
101
102 /* h = 0 (accumulator, next 3 slots) */
103 X[H_BASE + 0] = 0;
104 X[H_BASE + 1] = 0;
105 X[H_BASE + 2] = 0;
106
107 /* r &= 0xffffffc0ffffffc0ffffffc0fffffff (clamping) */
108 const uint64_t t0 = load_le<uint64_t>(key, 0);
109 const uint64_t t1 = load_le<uint64_t>(key, 1);
110
111 const uint64_t r0 = (t0) & 0xffc0fffffff;
112 const uint64_t r1 = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
113 const uint64_t r2 = ((t1 >> 24)) & 0x00ffffffc0f;
114
115 // Store r^1
116 X[R_BASE + 0] = r0;
117 X[R_BASE + 1] = r1;
118 X[R_BASE + 2] = r2;
119
120 poly1305_extend_powers(X, 2);
121}
122
123// Process a single block: h = (h + m) * r mod p
124BOTAN_FORCE_INLINE void poly1305_block_single(uint64_t& h0,
125 uint64_t& h1,
126 uint64_t& h2,
127 uint64_t r0,
128 uint64_t r1,
129 uint64_t r2,
130 uint64_t s1,
131 uint64_t s2,
132 const uint8_t* m,
133 uint64_t hibit) {
134 constexpr uint64_t M44 = 0xFFFFFFFFFFF;
135 constexpr uint64_t M42 = 0x3FFFFFFFFFF;
136
137#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
138 typedef donna128 uint128_t;
139#endif
140
141 const uint64_t t0 = load_le<uint64_t>(m, 0);
142 const uint64_t t1 = load_le<uint64_t>(m, 1);
143
144 h0 += (t0 & M44);
145 h1 += ((t0 >> 44) | (t1 << 20)) & M44;
146 h2 += ((t1 >> 24) & M42) | hibit;
147
148 const uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1;
149 const uint64_t c0 = carry_shift(d0, 44);
150
151 const uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2 + c0;
152 const uint64_t c1 = carry_shift(d1, 44);
153
154 const uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0 + c1;
155 const uint64_t c2 = carry_shift(d2, 42);
156
157 h0 = (d0 & M44) + c2 * 5;
158 h1 = (d1 & M44) + (h0 >> 44);
159 h0 &= M44;
160 h2 = d2 & M42;
161}
162
163// Process two blocks in parallel: h = ((h + m0) * r + m1) * r = (h + m0) * r^2 + m1 * r
164// The multiplications by r^2 and r are independent, enabling ILP
165BOTAN_FORCE_INLINE void poly1305_block_pair(uint64_t& h0,
166 uint64_t& h1,
167 uint64_t& h2,
168 uint64_t r0,
169 uint64_t r1,
170 uint64_t r2,
171 uint64_t s1,
172 uint64_t s2,
173 uint64_t rr0,
174 uint64_t rr1,
175 uint64_t rr2,
176 uint64_t ss1,
177 uint64_t ss2,
178 const uint8_t* m,
179 uint64_t hibit) {
180 constexpr uint64_t M44 = 0xFFFFFFFFFFF;
181 constexpr uint64_t M42 = 0x3FFFFFFFFFF;
182
183#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
184 typedef donna128 uint128_t;
185#endif
186
187 // Load first block (will be multiplied by r^2)
188 const uint64_t m0_t0 = load_le<uint64_t>(m, 0);
189 const uint64_t m0_t1 = load_le<uint64_t>(m, 1);
190
191 // Load second block (will be multiplied by r)
192 const uint64_t m1_t0 = load_le<uint64_t>(m + 16, 0);
193 const uint64_t m1_t1 = load_le<uint64_t>(m + 16, 1);
194
195 // Add first block to h
196 h0 += (m0_t0 & M44);
197 h1 += ((m0_t0 >> 44) | (m0_t1 << 20)) & M44;
198 h2 += ((m0_t1 >> 24) & M42) | hibit;
199
200 // Convert second block to limbs
201 const uint64_t b0 = (m1_t0 & M44);
202 const uint64_t b1 = ((m1_t0 >> 44) | (m1_t1 << 20)) & M44;
203 const uint64_t b2 = ((m1_t1 >> 24) & M42) | hibit;
204
205 // Compute (h + m0) * r^2 + m1 * r
206 const uint128_t d0 = uint128_t(h0) * rr0 + uint128_t(h1) * ss2 + uint128_t(h2) * ss1 + uint128_t(b0) * r0 +
207 uint128_t(b1) * s2 + uint128_t(b2) * s1;
208 const uint64_t c0 = carry_shift(d0, 44);
209
210 const uint128_t d1 = uint128_t(h0) * rr1 + uint128_t(h1) * rr0 + uint128_t(h2) * ss2 + uint128_t(b0) * r1 +
211 uint128_t(b1) * r0 + uint128_t(b2) * s2 + c0;
212 const uint64_t c1 = carry_shift(d1, 44);
213
214 const uint128_t d2 = uint128_t(h0) * rr2 + uint128_t(h1) * rr1 + uint128_t(h2) * rr0 + uint128_t(b0) * r2 +
215 uint128_t(b1) * r1 + uint128_t(b2) * r0 + c1;
216 const uint64_t c2 = carry_shift(d2, 42);
217
218 h0 = (d0 & M44) + c2 * 5;
219 h1 = (d1 & M44) + (h0 >> 44);
220 h0 &= M44;
221 h2 = d2 & M42;
222}
223
224void poly1305_blocks(secure_vector<uint64_t>& X, const uint8_t* m, size_t blocks, bool is_final = false) {
225 const uint64_t hibit = is_final ? 0 : (static_cast<uint64_t>(1) << 40);
226
227 // Load r (at R_BASE + 0)
228 const uint64_t r0 = X[R_BASE + 0];
229 const uint64_t r1 = X[R_BASE + 1];
230 const uint64_t r2 = X[R_BASE + 2];
231 const uint64_t s1 = r1 * 20;
232 const uint64_t s2 = r2 * 20;
233
234 // Load r^2 (at R_BASE + 3)
235 const uint64_t rr0 = X[R_BASE + 3];
236 const uint64_t rr1 = X[R_BASE + 4];
237 const uint64_t rr2 = X[R_BASE + 5];
238
239 // Precompute
240 const uint64_t ss1 = rr1 * 20;
241 const uint64_t ss2 = rr2 * 20;
242
243 // Load accumulator
244 uint64_t h0 = X[H_BASE + 0];
245 uint64_t h1 = X[H_BASE + 1];
246 uint64_t h2 = X[H_BASE + 2];
247
248 while(blocks >= 2) {
249 poly1305_block_pair(h0, h1, h2, r0, r1, r2, s1, s2, rr0, rr1, rr2, ss1, ss2, m, hibit);
250 m += 32;
251 blocks -= 2;
252 }
253
254 // Final block?
255 if(blocks > 0) {
256 poly1305_block_single(h0, h1, h2, r0, r1, r2, s1, s2, m, hibit);
257 }
258
259 // Store accumulator
260 X[H_BASE + 0] = h0;
261 X[H_BASE + 1] = h1;
262 X[H_BASE + 2] = h2;
263}
264
265void poly1305_finish(secure_vector<uint64_t>& X, uint8_t mac[16]) {
266 constexpr uint64_t M44 = 0xFFFFFFFFFFF;
267 constexpr uint64_t M42 = 0x3FFFFFFFFFF;
268
269 /* fully carry h */
270 uint64_t h0 = X[H_BASE + 0];
271 uint64_t h1 = X[H_BASE + 1];
272 uint64_t h2 = X[H_BASE + 2];
273
274 uint64_t c = (h1 >> 44);
275 h1 &= M44;
276 h2 += c;
277 c = (h2 >> 42);
278 h2 &= M42;
279 h0 += c * 5;
280 c = (h0 >> 44);
281 h0 &= M44;
282 h1 += c;
283 c = (h1 >> 44);
284 h1 &= M44;
285 h2 += c;
286 c = (h2 >> 42);
287 h2 &= M42;
288 h0 += c * 5;
289 c = (h0 >> 44);
290 h0 &= M44;
291 h1 += c;
292
293 /* compute h + -p */
294 uint64_t g0 = h0 + 5;
295 c = (g0 >> 44);
296 g0 &= M44;
297 uint64_t g1 = h1 + c;
298 c = (g1 >> 44);
299 g1 &= M44;
300 const uint64_t g2 = h2 + c - (static_cast<uint64_t>(1) << 42);
301
302 /* select h if h < p, or h + -p if h >= p */
303 const auto h_mask = CT::Mask<uint64_t>::expand_top_bit(g2);
304 h0 = h_mask.select(h0, g0);
305 h1 = h_mask.select(h1, g1);
306 h2 = h_mask.select(h2, g2);
307
308 /* h = (h + pad) */
309 const uint64_t t0 = X[PAD_BASE + 0];
310 const uint64_t t1 = X[PAD_BASE + 1];
311
312 h0 += ((t0)&M44);
313 c = (h0 >> 44);
314 h0 &= M44;
315 h1 += (((t0 >> 44) | (t1 << 20)) & M44) + c;
316 c = (h1 >> 44);
317 h1 &= M44;
318 h2 += (((t1 >> 24)) & M42) + c;
319 h2 &= M42;
320
321 /* mac = h % (2^128) */
322 h0 = ((h0) | (h1 << 44));
323 h1 = ((h1 >> 20) | (h2 << 24));
324
325 store_le(mac, h0, h1);
326
327 /* zero out the state */
328 clear_mem(X.data(), X.size());
329}
330
331} // namespace
332
334 zap(m_poly);
335 m_buffer.clear();
336}
337
339 // Minimum size: pad(2) + accum(3) + r(3) + r^2(3) = 11
340 return m_poly.size() >= 11;
341}
342
343void Poly1305::start_msg(std::span<const uint8_t> nonce) {
344 if(!nonce.empty()) {
345 throw Invalid_IV_Length(name(), nonce.size());
346 }
348
349 m_buffer.clear();
350 m_poly[H_BASE + 0] = 0;
351 m_poly[H_BASE + 1] = 0;
352 m_poly[H_BASE + 2] = 0;
353}
354
355void Poly1305::key_schedule(std::span<const uint8_t> key) {
356 m_buffer.clear();
357
358 poly1305_init(m_poly, key.data());
359}
360
361std::string Poly1305::provider() const {
362#if defined(BOTAN_HAS_POLY1305_AVX512)
363 if(auto feat = CPUID::check(CPUID::Feature::AVX512)) {
364 return *feat;
365 }
366#endif
367
368#if defined(BOTAN_HAS_POLY1305_AVX2)
369 if(auto feat = CPUID::check(CPUID::Feature::AVX2)) {
370 return *feat;
371 }
372#endif
373
374 return "base";
375}
376
377void Poly1305::add_data(std::span<const uint8_t> input) {
379
380 BufferSlicer in(input);
381
382 while(!in.empty()) {
383 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
384 poly1305_blocks(m_poly, one_block->data(), 1);
385 }
386
387 if(m_buffer.in_alignment()) {
388 const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
389 if(full_blocks > 0) {
390 const uint8_t* data_ptr = aligned_data.data();
391 size_t blocks_remaining = full_blocks;
392
393#if defined(BOTAN_HAS_POLY1305_AVX512)
394 if(blocks_remaining >= 8 * 3 && CPUID::has(CPUID::Feature::AVX512)) {
395 // Lazily compute r^3 through r^8 on first AVX512 use
396 poly1305_extend_powers(m_poly, 8);
397 const size_t processed = poly1305_avx512_blocks(m_poly, data_ptr, blocks_remaining);
398 data_ptr += processed * 16;
399 blocks_remaining -= processed;
400 }
401#endif
402
403#if defined(BOTAN_HAS_POLY1305_AVX2)
404 if(blocks_remaining >= 4 * 6 && CPUID::has(CPUID::Feature::AVX2)) {
405 // Lazily compute r^3 and r^4 on first AVX2 use
406 poly1305_extend_powers(m_poly, 4);
407 const size_t processed = poly1305_avx2_blocks(m_poly, data_ptr, blocks_remaining);
408 data_ptr += processed * 16;
409 blocks_remaining -= processed;
410 }
411#endif
412
413 if(blocks_remaining > 0) {
414 poly1305_blocks(m_poly, data_ptr, blocks_remaining);
415 }
416 }
417 }
418 }
419}
420
421void Poly1305::final_result(std::span<uint8_t> out) {
423
424 if(!m_buffer.in_alignment()) {
425 const uint8_t final_byte = 0x01;
426 m_buffer.append({&final_byte, 1});
427 m_buffer.fill_up_with_zeros();
428 poly1305_blocks(m_poly, m_buffer.consume().data(), 1, true);
429 }
430
431 poly1305_finish(m_poly, out.data());
432
433 m_poly.clear();
434 m_buffer.clear();
435}
436
437} // namespace Botan
std::tuple< std::span< const uint8_t >, size_t > aligned_data_to_process(BufferSlicer &slicer) const
std::optional< std::span< const T > > handle_unaligned_data(BufferSlicer &slicer)
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_top_bit(T v)
Definition ct_utils.h:415
void clear() override
Definition poly1305.cpp:333
std::string name() const override
Definition poly1305.h:23
std::string provider() const override
Definition poly1305.cpp:361
bool has_keying_material() const override
Definition poly1305.cpp:338
void assert_key_material_set() const
Definition sym_algo.h:180
#define BOTAN_FORCE_INLINE
Definition compiler.h:87
constexpr uint64_t carry_shift(const donna128 &a, size_t shift)
Definition donna128.h:129
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:261
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:736
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:495
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118