Botan 3.10.0
Crypto and TLS for C&
mp_core.h
Go to the documentation of this file.
1/*
2* MPI Algorithms
3* (C) 1999-2010,2018,2024 Jack Lloyd
4* 2006 Luca Piccarreta
5* 2016 Matthias Gierlings
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#ifndef BOTAN_MP_CORE_OPS_H_
11#define BOTAN_MP_CORE_OPS_H_
12
13#include <botan/assert.h>
14#include <botan/exceptn.h>
15#include <botan/mem_ops.h>
16#include <botan/types.h>
17#include <botan/internal/ct_utils.h>
18#include <botan/internal/mp_asmi.h>
19#include <algorithm>
20#include <array>
21#include <span>
22
23namespace Botan {
24
25/*
26* If cond == 0, does nothing.
27* If cond > 0, swaps x[0:size] with y[0:size]
28* Runs in constant time
29*/
30template <WordType W>
31inline constexpr void bigint_cnd_swap(W cnd, W x[], W y[], size_t size) {
32 const auto mask = CT::Mask<W>::expand(cnd);
33
34 for(size_t i = 0; i != size; ++i) {
35 const W a = x[i];
36 const W b = y[i];
37 x[i] = mask.select(b, a);
38 y[i] = mask.select(a, b);
39 }
40}
41
42/*
43* If cond > 0 adds x[0:size] and y[0:size] and returns carry
44* Runs in constant time
45*/
46template <WordType W>
47inline constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size) {
48 const auto mask = CT::Mask<W>::expand(cnd).value();
49
50 W carry = 0;
51
52 for(size_t i = 0; i != size; ++i) {
53 x[i] = word_add(x[i], y[i] & mask, &carry);
54 }
55
56 return (mask & carry);
57}
58
59/*
60* If cond > 0 subtracts y[0:size] from x[0:size] and returns borrow
61* Runs in constant time
62*/
63template <WordType W>
64inline constexpr auto bigint_cnd_sub(W cnd, W x[], const W y[], size_t size) -> W {
65 const auto mask = CT::Mask<W>::expand(cnd).value();
66
67 W carry = 0;
68
69 for(size_t i = 0; i != size; ++i) {
70 x[i] = word_sub(x[i], y[i] & mask, &carry);
71 }
72
73 return (mask & carry);
74}
75
76/*
77* 2s complement absolute value
78* If cond > 0 sets x to ~x + 1
79* Runs in constant time
80*/
81template <WordType W>
82inline constexpr void bigint_cnd_abs(W cnd, W x[], size_t size) {
83 const auto mask = CT::Mask<W>::expand(cnd);
84
85 W carry = mask.if_set_return(1);
86 for(size_t i = 0; i != size; ++i) {
87 const W z = word_add(~x[i], static_cast<W>(0), &carry);
88 x[i] = mask.select(z, x[i]);
89 }
90}
91
92/**
93* Two operand addition with carry out
94*/
95template <WordType W>
96inline constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
97 W carry = 0;
98
99 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
100
101 const size_t blocks = y_size - (y_size % 8);
102
103 for(size_t i = 0; i != blocks; i += 8) {
104 carry = word8_add2(x + i, y + i, carry);
105 }
106
107 for(size_t i = blocks; i != y_size; ++i) {
108 x[i] = word_add(x[i], y[i], &carry);
109 }
110
111 for(size_t i = y_size; i != x_size; ++i) {
112 x[i] = word_add(x[i], static_cast<W>(0), &carry);
113 }
114
115 return carry;
116}
117
118/**
119* Three operand addition with carry out
120*/
121template <WordType W>
122inline constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
123 if(x_size < y_size) {
124 return bigint_add3(z, y, y_size, x, x_size);
125 }
126
127 W carry = 0;
128
129 const size_t blocks = y_size - (y_size % 8);
130
131 for(size_t i = 0; i != blocks; i += 8) {
132 carry = word8_add3(z + i, x + i, y + i, carry);
133 }
134
135 for(size_t i = blocks; i != y_size; ++i) {
136 z[i] = word_add(x[i], y[i], &carry);
137 }
138
139 for(size_t i = y_size; i != x_size; ++i) {
140 z[i] = word_add(x[i], static_cast<W>(0), &carry);
141 }
142
143 return carry;
144}
145
146/**
147* Two operand subtraction
148*/
149template <WordType W>
150inline constexpr auto bigint_sub2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
151 W borrow = 0;
152
153 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
154
155 const size_t blocks = y_size - (y_size % 8);
156
157 for(size_t i = 0; i != blocks; i += 8) {
158 borrow = word8_sub2(x + i, y + i, borrow);
159 }
160
161 for(size_t i = blocks; i != y_size; ++i) {
162 x[i] = word_sub(x[i], y[i], &borrow);
163 }
164
165 for(size_t i = y_size; i != x_size; ++i) {
166 x[i] = word_sub(x[i], static_cast<W>(0), &borrow);
167 }
168
169 return borrow;
170}
171
172/**
173* Two operand subtraction, x = y - x; assumes y >= x
174*/
175template <WordType W>
176inline constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size) {
177 W borrow = 0;
178
179 for(size_t i = 0; i != y_size; ++i) {
180 x[i] = word_sub(y[i], x[i], &borrow);
181 }
182
183 BOTAN_ASSERT(borrow == 0, "y must be greater than x");
184}
185
186/**
187* Three operand subtraction
188*
189* Expects that x_size >= y_size
190*
191* Writes to z[0:x_size] and returns borrow
192*/
193template <WordType W>
194inline constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
195 W borrow = 0;
196
197 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
198
199 const size_t blocks = y_size - (y_size % 8);
200
201 for(size_t i = 0; i != blocks; i += 8) {
202 borrow = word8_sub3(z + i, x + i, y + i, borrow);
203 }
204
205 for(size_t i = blocks; i != y_size; ++i) {
206 z[i] = word_sub(x[i], y[i], &borrow);
207 }
208
209 for(size_t i = y_size; i != x_size; ++i) {
210 z[i] = word_sub(x[i], static_cast<W>(0), &borrow);
211 }
212
213 return borrow;
214}
215
216/**
217* Conditional subtraction for Montgomery reduction
218*
219* This function assumes that (x0 || x) is less than 2*p
220*
221* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
222*
223* If z would be positive, returns z[0:N]
224* Otherwise returns original input x
225*/
226template <WordType W>
227inline constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[]) {
228 W borrow = 0;
229
230 const size_t blocks = N - (N % 8);
231
232 for(size_t i = 0; i != blocks; i += 8) {
233 borrow = word8_sub3(z + i, x + i, p + i, borrow);
234 }
235
236 for(size_t i = blocks; i != N; ++i) {
237 z[i] = word_sub(x[i], p[i], &borrow);
238 }
239
240 borrow = (x0 - borrow) > x0;
241
242 CT::conditional_assign_mem(borrow, z, x, N);
243}
244
245/**
246* Conditional subtraction for Montgomery reduction
247*
248* This function assumes that (x0 || x) is less than 2*p
249*
250* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
251*
252* If z would be positive, returns z[0:N]
253* Otherwise returns original input x
254*/
255template <size_t N, WordType W>
256inline constexpr void bigint_monty_maybe_sub(W z[N], W x0, const W x[N], const W y[N]) {
257 W borrow = 0;
258
259 for(size_t i = 0; i != N; ++i) {
260 z[i] = word_sub(x[i], y[i], &borrow);
261 }
262
263 borrow = (x0 - borrow) > x0;
264
265 CT::conditional_assign_mem(borrow, z, x, N);
266}
267
268/**
269* Return abs(x-y), ie if x >= y, then compute z = x - y
270* Otherwise compute z = y - x
271* No borrow is possible since the result is always >= 0
272*
273* Returns a Mask: |1| if x >= y or |0| if x < y
274* @param z output array of at least N words
275* @param x input array of N words
276* @param y input array of N words
277* @param N length of x and y
278* @param ws array of at least 2*N words
279*/
280template <WordType W>
281inline constexpr auto bigint_sub_abs(W z[], const W x[], const W y[], size_t N, W ws[]) -> CT::Mask<W> {
282 // Subtract in both direction then conditional copy out the result
283
284 W* ws0 = ws;
285 W* ws1 = ws + N;
286
287 W borrow0 = 0;
288 W borrow1 = 0;
289
290 const size_t blocks = N - (N % 8);
291
292 for(size_t i = 0; i != blocks; i += 8) {
293 borrow0 = word8_sub3(ws0 + i, x + i, y + i, borrow0);
294 borrow1 = word8_sub3(ws1 + i, y + i, x + i, borrow1);
295 }
296
297 for(size_t i = blocks; i != N; ++i) {
298 ws0[i] = word_sub(x[i], y[i], &borrow0);
299 ws1[i] = word_sub(y[i], x[i], &borrow1);
300 }
301
302 return CT::conditional_copy_mem(borrow0, z, ws1, ws0, N);
303}
304
305/*
306* Shift Operations
307*/
308template <WordType W>
309inline constexpr void bigint_shl1(W x[], size_t x_size, size_t x_words, size_t shift) {
310 const size_t word_shift = shift / WordInfo<W>::bits;
311 const size_t bit_shift = shift % WordInfo<W>::bits;
312
313 copy_mem(x + word_shift, x, x_words);
314 clear_mem(x, word_shift);
315
316 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
317 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
318
319 W carry = 0;
320 for(size_t i = word_shift; i != x_size; ++i) {
321 const W w = x[i];
322 x[i] = (w << bit_shift) | carry;
323 carry = carry_mask.if_set_return(w >> carry_shift);
324 }
325}
326
327template <WordType W>
328inline constexpr void bigint_shr1(W x[], size_t x_size, size_t shift) {
329 const size_t word_shift = shift / WordInfo<W>::bits;
330 const size_t bit_shift = shift % WordInfo<W>::bits;
331
332 const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0;
333
334 if(top > 0) {
335 copy_mem(x, x + word_shift, top);
336 }
337 clear_mem(x + top, std::min(word_shift, x_size));
338
339 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
340 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
341
342 W carry = 0;
343
344 for(size_t i = 0; i != top; ++i) {
345 const W w = x[top - i - 1];
346 x[top - i - 1] = (w >> bit_shift) | carry;
347 carry = carry_mask.if_set_return(w << carry_shift);
348 }
349}
350
351template <WordType W>
352inline constexpr void bigint_shl2(W y[], const W x[], size_t x_size, size_t shift) {
353 const size_t word_shift = shift / WordInfo<W>::bits;
354 const size_t bit_shift = shift % WordInfo<W>::bits;
355
356 copy_mem(y + word_shift, x, x_size);
357
358 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
359 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
360
361 W carry = 0;
362 for(size_t i = word_shift; i != x_size + word_shift + 1; ++i) {
363 const W w = y[i];
364 y[i] = (w << bit_shift) | carry;
365 carry = carry_mask.if_set_return(w >> carry_shift);
366 }
367}
368
369template <WordType W>
370inline constexpr void bigint_shr2(W y[], const W x[], size_t x_size, size_t shift) {
371 const size_t word_shift = shift / WordInfo<W>::bits;
372 const size_t bit_shift = shift % WordInfo<W>::bits;
373 const size_t new_size = x_size < word_shift ? 0 : (x_size - word_shift);
374
375 if(new_size > 0) {
376 copy_mem(y, x + word_shift, new_size);
377 }
378
379 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
380 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
381
382 W carry = 0;
383 for(size_t i = new_size; i > 0; --i) {
384 W w = y[i - 1];
385 y[i - 1] = (w >> bit_shift) | carry;
386 carry = carry_mask.if_set_return(w << carry_shift);
387 }
388}
389
390/*
391* Linear Multiply - returns the carry
392*/
393template <WordType W>
394[[nodiscard]] inline constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W {
395 W carry = 0;
396
397 for(size_t i = 0; i != x_size; ++i) {
398 x[i] = word_madd2(x[i], y, &carry);
399 }
400
401 return carry;
402}
403
404template <WordType W>
405inline constexpr void bigint_linmul3(W z[], const W x[], size_t x_size, W y) {
406 const size_t blocks = x_size - (x_size % 8);
407
408 W carry = 0;
409
410 for(size_t i = 0; i != blocks; i += 8) {
411 carry = word8_linmul3(z + i, x + i, y, carry);
412 }
413
414 for(size_t i = blocks; i != x_size; ++i) {
415 z[i] = word_madd2(x[i], y, &carry);
416 }
417
418 z[x_size] = carry;
419}
420
421/**
422* Compare x and y
423* Return -1 if x < y
424* Return 0 if x == y
425* Return 1 if x > y
426*/
427template <WordType W>
428inline constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size) {
429 static_assert(sizeof(W) >= sizeof(uint32_t), "Size assumption");
430
431 const W LT = static_cast<W>(-1);
432 const W EQ = 0;
433 const W GT = 1;
434
435 const size_t common_elems = std::min(x_size, y_size);
436
437 W result = EQ; // until found otherwise
438
439 for(size_t i = 0; i != common_elems; i++) {
440 const auto is_eq = CT::Mask<W>::is_equal(x[i], y[i]);
441 const auto is_lt = CT::Mask<W>::is_lt(x[i], y[i]);
442
443 result = is_eq.select(result, is_lt.select(LT, GT));
444 }
445
446 if(x_size < y_size) {
447 W mask = 0;
448 for(size_t i = x_size; i != y_size; i++) {
449 mask |= y[i];
450 }
451
452 // If any bits were set in high part of y, then x < y
453 result = CT::Mask<W>::is_zero(mask).select(result, LT);
454 } else if(y_size < x_size) {
455 W mask = 0;
456 for(size_t i = y_size; i != x_size; i++) {
457 mask |= x[i];
458 }
459
460 // If any bits were set in high part of x, then x > y
461 result = CT::Mask<W>::is_zero(mask).select(result, GT);
462 }
463
464 CT::unpoison(result);
465 BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
466 return static_cast<int32_t>(result);
467}
468
469/**
470* Compare x and y
471* Returns a Mask: |1| if x[0:x_size] < y[0:y_size] or |0| otherwise
472* If lt_or_equal is true, returns |1| also for x == y
473*/
474template <WordType W>
475inline constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal = false)
476 -> CT::Mask<W> {
477 const size_t common_elems = std::min(x_size, y_size);
478
479 auto is_lt = CT::Mask<W>::expand(lt_or_equal);
480
481 for(size_t i = 0; i != common_elems; i++) {
482 const auto eq = CT::Mask<W>::is_equal(x[i], y[i]);
483 const auto lt = CT::Mask<W>::is_lt(x[i], y[i]);
484 is_lt = eq.select_mask(is_lt, lt);
485 }
486
487 if(x_size < y_size) {
488 W mask = 0;
489 for(size_t i = x_size; i != y_size; i++) {
490 mask |= y[i];
491 }
492 // If any bits were set in high part of y, then is_lt should be forced true
493 is_lt |= CT::Mask<W>::expand(mask);
494 } else if(y_size < x_size) {
495 W mask = 0;
496 for(size_t i = y_size; i != x_size; i++) {
497 mask |= x[i];
498 }
499
500 // If any bits were set in high part of x, then is_lt should be false
501 is_lt &= CT::Mask<W>::is_zero(mask);
502 }
503
504 return is_lt;
505}
506
507template <WordType W>
508inline constexpr auto bigint_ct_is_eq(const W x[], size_t x_size, const W y[], size_t y_size) -> CT::Mask<W> {
509 const size_t common_elems = std::min(x_size, y_size);
510
511 W diff = 0;
512
513 for(size_t i = 0; i != common_elems; i++) {
514 diff |= (x[i] ^ y[i]);
515 }
516
517 // If any bits were set in high part of x/y, then they are not equal
518 if(x_size < y_size) {
519 for(size_t i = x_size; i != y_size; i++) {
520 diff |= y[i];
521 }
522 } else if(y_size < x_size) {
523 for(size_t i = y_size; i != x_size; i++) {
524 diff |= x[i];
525 }
526 }
527
528 return CT::Mask<W>::is_zero(diff);
529}
530
531/**
532* Setup for variable-time word level division/modulo operations
533*
534* Currently this just uses the compiler's support for a 2/1 word division,
535* but likely could be improved by precomputed values based on the divisor,
536* for example using the approaches outlined in Hacker's Delight chapter 10.
537*/
538template <WordType W>
539class divide_precomp final {
540 public:
541 explicit constexpr divide_precomp(W divisor) : m_divisor(divisor) {
542 BOTAN_ARG_CHECK(m_divisor != 0, "Division by zero");
543 }
544
545 // Return floor((n1 || n0) / d)
546 //
547 // This assumes n1 < d so that the quotient fits in a word
548 inline constexpr W vartime_div_2to1(W n1, W n0) const {
549 BOTAN_ASSERT_NOMSG(n1 < m_divisor);
550
551 if(!std::is_constant_evaluated()) {
552#if defined(BOTAN_MP_USE_X86_64_ASM)
553 if constexpr(std::same_as<W, uint64_t>) {
554 W quotient = 0;
555 W remainder = 0;
556 // NOLINTNEXTLINE(*-no-assembler)
557 asm("divq %[v]" : "=a"(quotient), "=d"(remainder) : [v] "r"(m_divisor), "a"(n0), "d"(n1));
558 return quotient;
559 }
560#endif
561
562#if !defined(BOTAN_BUILD_COMPILER_IS_CLANGCL)
563
564 /* clang-cl has a bug where on encountering a 128/64 division it emits
565 * a call to __udivti3() but then fails to link the relevant builtin into
566 * the binary, causing a link failure. Work around this by simply omitting
567 * such code for clang-cl
568 *
569 * See https://github.com/llvm/llvm-project/issues/25679
570 */
571 if constexpr(WordInfo<W>::dword_is_native) {
572 typename WordInfo<W>::dword n = n1;
573 n <<= WordInfo<W>::bits;
574 n |= n0;
575 return static_cast<W>(n / m_divisor);
576 }
577#endif
578 }
579
580 W high = n1;
581 W quotient = 0;
582
583 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
584 const W high_top_bit = high >> (WordInfo<W>::bits - 1);
585
586 high <<= 1;
587 high |= (n0 >> (WordInfo<W>::bits - 1 - i)) & 1;
588 quotient <<= 1;
589
590 if(high_top_bit || high >= m_divisor) {
591 high -= m_divisor;
592 quotient |= 1;
593 }
594 }
595
596 return quotient;
597 }
598
599 // Return floor((n1 || n0) % d)
600 //
601 // This assumes n1 < d so that the quotient fits in a word
602 inline constexpr W vartime_mod_2to1(W n1, W n0) const {
603 BOTAN_ASSERT_NOMSG(n1 < m_divisor);
604 W q = this->vartime_div_2to1(n1, n0);
605 W carry = 0;
606 q = word_madd2(q, m_divisor, &carry);
607 return (n0 - q);
608 }
609
610 private:
611 W m_divisor;
612};
613
614/*
615* Compute an integer x such that (a*x) == -1 (mod 2^n)
616*
617* Throws an exception if input is even, since in that case no inverse
618* exists. If input is odd, then input and 2^n are relatively prime and
619* the inverse exists.
620*/
621template <WordType W>
622inline constexpr auto monty_inverse(W a) -> W {
623 BOTAN_ARG_CHECK(a % 2 == 1, "Cannot compute Montgomery inverse of an even integer");
624
625 /*
626 * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
627 * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
628 */
629
630 W b = 1;
631 W r = 0;
632
633 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
634 const W bi = b % 2;
635 r >>= 1;
636 r += bi << (WordInfo<W>::bits - 1);
637
638 b -= a * bi;
639 b >>= 1;
640 }
641
642 // Now invert in addition space
643 r = (WordInfo<W>::max - r) + 1;
644
645 return r;
646}
647
648template <size_t S, WordType W, size_t N>
649inline constexpr W shift_left(std::array<W, N>& x) {
650 static_assert(S < WordInfo<W>::bits, "Shift too large");
651
652 W carry = 0;
653 for(size_t i = 0; i != N; ++i) {
654 const W w = x[i];
655 x[i] = (w << S) | carry;
656 carry = w >> (WordInfo<W>::bits - S);
657 }
658
659 return carry;
660}
661
662template <size_t S, WordType W, size_t N>
663inline constexpr W shift_right(std::array<W, N>& x) {
664 static_assert(S < WordInfo<W>::bits, "Shift too large");
665
666 W carry = 0;
667 for(size_t i = 0; i != N; ++i) {
668 const W w = x[N - 1 - i];
669 x[N - 1 - i] = (w >> S) | carry;
670 carry = w << (WordInfo<W>::bits - S);
671 }
672
673 return carry;
674}
675
676// Should be consteval but this triggers a bug in Clang 14
677template <WordType W, size_t N>
678constexpr auto hex_to_words(const char (&s)[N]) {
679 // Char count includes null terminator which we ignore
680 const constexpr size_t C = N - 1;
681
682 // Number of nibbles that a word can hold
683 const constexpr size_t NPW = (WordInfo<W>::bits / 4);
684
685 // Round up to the next number of words that will fit the input
686 const constexpr size_t S = (C + NPW - 1) / NPW;
687
688 auto hex2int = [](char c) -> int8_t {
689 if(c >= '0' && c <= '9') {
690 return static_cast<int8_t>(c - '0');
691 } else if(c >= 'a' && c <= 'f') {
692 return static_cast<int8_t>(c - 'a' + 10);
693 } else if(c >= 'A' && c <= 'F') {
694 return static_cast<int8_t>(c - 'A' + 10);
695 } else {
696 return -1;
697 }
698 };
699
700 std::array<W, S> r = {0};
701
702 for(size_t i = 0; i != C; ++i) {
703 const int8_t c = hex2int(s[i]);
704 if(c >= 0) {
705 shift_left<4>(r);
706 r[0] += c;
707 }
708 }
709
710 return r;
711}
712
713/*
714* Comba Multiplication / Squaring
715*/
716BOTAN_FUZZER_API void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
717BOTAN_FUZZER_API void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
718BOTAN_FUZZER_API void bigint_comba_mul7(word z[14], const word x[7], const word y[7]);
719BOTAN_FUZZER_API void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
720BOTAN_FUZZER_API void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
721BOTAN_FUZZER_API void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
722BOTAN_FUZZER_API void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);
723
724BOTAN_FUZZER_API void bigint_comba_sqr4(word out[8], const word in[4]);
725BOTAN_FUZZER_API void bigint_comba_sqr6(word out[12], const word in[6]);
726BOTAN_FUZZER_API void bigint_comba_sqr7(word out[14], const word x[7]);
727BOTAN_FUZZER_API void bigint_comba_sqr8(word out[16], const word in[8]);
728BOTAN_FUZZER_API void bigint_comba_sqr9(word out[18], const word in[9]);
729BOTAN_FUZZER_API void bigint_comba_sqr16(word out[32], const word in[16]);
730BOTAN_FUZZER_API void bigint_comba_sqr24(word out[48], const word in[24]);
731
732/*
733* Comba Fixed Length Multiplication
734*/
735template <size_t N, WordType W>
736constexpr inline void comba_mul(W z[2 * N], const W x[N], const W y[N]) {
737 if(!std::is_constant_evaluated()) {
738 if constexpr(std::same_as<W, word> && N == 4) {
739 return bigint_comba_mul4(z, x, y);
740 }
741 if constexpr(std::same_as<W, word> && N == 6) {
742 return bigint_comba_mul6(z, x, y);
743 }
744 if constexpr(std::same_as<W, word> && N == 7) {
745 return bigint_comba_mul7(z, x, y);
746 }
747 if constexpr(std::same_as<W, word> && N == 8) {
748 return bigint_comba_mul8(z, x, y);
749 }
750 if constexpr(std::same_as<W, word> && N == 9) {
751 return bigint_comba_mul9(z, x, y);
752 }
753 if constexpr(std::same_as<W, word> && N == 16) {
754 return bigint_comba_mul16(z, x, y);
755 }
756 }
757
758 word3<W> accum;
759
760 for(size_t i = 0; i != 2 * N; ++i) {
761 const size_t start = i + 1 < N ? 0 : i + 1 - N;
762 const size_t end = std::min(N, i + 1);
763
764 for(size_t j = start; j != end; ++j) {
765 accum.mul(x[j], y[i - j]);
766 }
767 z[i] = accum.extract();
768 }
769}
770
771template <size_t N, WordType W>
772constexpr inline void comba_sqr(W z[2 * N], const W x[N]) {
773 if(!std::is_constant_evaluated()) {
774 if constexpr(std::same_as<W, word> && N == 4) {
775 return bigint_comba_sqr4(z, x);
776 }
777 if constexpr(std::same_as<W, word> && N == 6) {
778 return bigint_comba_sqr6(z, x);
779 }
780 if constexpr(std::same_as<W, word> && N == 7) {
781 return bigint_comba_sqr7(z, x);
782 }
783 if constexpr(std::same_as<W, word> && N == 8) {
784 return bigint_comba_sqr8(z, x);
785 }
786 if constexpr(std::same_as<W, word> && N == 9) {
787 return bigint_comba_sqr9(z, x);
788 }
789 if constexpr(std::same_as<W, word> && N == 16) {
790 return bigint_comba_sqr16(z, x);
791 }
792 }
793
794 word3<W> accum;
795
796 for(size_t i = 0; i != 2 * N; ++i) {
797 const size_t start = i + 1 < N ? 0 : i + 1 - N;
798 const size_t end = std::min(N, i + 1);
799
800 for(size_t j = start; j != end; ++j) {
801 accum.mul(x[j], x[i - j]);
802 }
803 z[i] = accum.extract();
804 }
805}
806
807/*
808* Montgomery reduction
809*
810* Sets r to the Montgomery reduction of z using parameters p / p_dash
811*
812* The workspace should be of size equal to the prime
813*/
814BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]);
815BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]);
816BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]);
817BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]);
818BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]);
819BOTAN_FUZZER_API void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]);
820BOTAN_FUZZER_API void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]);
821
824 word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]);
825
826/**
827* Montgomery Reduction
828* @param r result of exactly p_size words
829* @param z integer to reduce, of size exactly 2*p_size.
830* @param p modulus
831* @param p_size size of p
832* @param p_dash Montgomery value
833* @param ws array of at least p_size words
834* @param ws_size size of ws in words
835*
836* It is allowed to set &r[0] == &z[0] however in this case note that only the
837* first p_size words of r will be written to and the high p_size words of r/z
838* will still hold the original inputs, these must be cleared after use.
839* See bigint_monty_redc_inplace
840*/
842 word r[], const word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
843 const size_t z_size = 2 * p_size;
844
845 BOTAN_ARG_CHECK(ws_size >= p_size, "Montgomery reduction workspace too small");
846
847 if(p_size == 4) {
848 bigint_monty_redc_4(r, z, p, p_dash, ws);
849 } else if(p_size == 6) {
850 bigint_monty_redc_6(r, z, p, p_dash, ws);
851 } else if(p_size == 8) {
852 bigint_monty_redc_8(r, z, p, p_dash, ws);
853 } else if(p_size == 12) {
854 bigint_monty_redc_12(r, z, p, p_dash, ws);
855 } else if(p_size == 16) {
856 bigint_monty_redc_16(r, z, p, p_dash, ws);
857 } else if(p_size == 24) {
858 bigint_monty_redc_24(r, z, p, p_dash, ws);
859 } else if(p_size == 32) {
860 bigint_monty_redc_32(r, z, p, p_dash, ws);
861 } else {
862 bigint_monty_redc_generic(r, z, z_size, p, p_size, p_dash, ws);
863 }
864}
865
866inline void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
867 bigint_monty_redc(z, z, p, p_size, p_dash, ws, ws_size);
868 clear_mem(z + p_size, p_size);
869}
870
871/**
872* Basecase O(N^2) multiplication
873*/
875void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size);
876
877/**
878* Basecase O(N^2) squaring
879*/
881void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size);
882
883/*
884* High Level Multiplication/Squaring Interfaces
885*/
886void bigint_mul(word z[],
887 size_t z_size,
888 const word x[],
889 size_t x_size,
890 size_t x_sw,
891 const word y[],
892 size_t y_size,
893 size_t y_sw,
894 word workspace[],
895 size_t ws_size);
896
897void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size);
898
899/**
900* Reduce z modulo p = 2**B - C where C is small
901*
902* z is assumed to be at most (p-1)**2
903*
904* For details on the algorithm see
905* - Handbook of Applied Cryptography, Algorithm 14.47
906* - Guide to Elliptic Curve Cryptography, Algorithm 2.54 and Note 2.55
907*
908*/
909template <WordType W, size_t N, W C>
910constexpr std::array<W, N> redc_crandall(std::span<const W, 2 * N> z) {
911 static_assert(N >= 2);
912
913 std::array<W, N> hi = {};
914
915 // hi = hi * c + lo
916
917 W carry = 0;
918 for(size_t i = 0; i != N; ++i) {
919 hi[i] = word_madd3(z[i + N], C, z[i], &carry);
920 }
921
922 // hi += carry * C
923 word carry_c[2] = {0};
924 carry_c[0] = word_madd2(carry, C, &carry_c[1]);
925
926 carry = bigint_add2(hi.data(), N, carry_c, 2);
927
928 constexpr W P0 = WordInfo<W>::max - (C - 1);
929
930 std::array<W, N> r = {};
931
932 W borrow = 0;
933
934 /*
935 * For undetermined reasons, on GCC (only) removing this asm block causes
936 * massive (up to 20%) performance regressions in secp256k1.
937 *
938 * The generated code without the asm seems quite reasonable, and timing
939 * repeated calls to redc_crandall with the cycle counter show that GCC
940 * computes it in about the same number of cycles with or without the asm.
941 *
942 * So the cause of the regression is unclear. But it is reproducible across
943 * machines and GCC versions.
944 */
945#if defined(BOTAN_MP_USE_X86_64_ASM) && defined(__GNUC__) && !defined(__clang__)
946 if constexpr(N == 4 && std::same_as<W, uint64_t>) {
947 if(!std::is_constant_evaluated()) {
948 asm volatile(R"(
949 movq 0(%[x]), %[borrow]
950 subq %[p0], %[borrow]
951 movq %[borrow], 0(%[r])
952 movq 16(%[x]), %[borrow]
953 sbbq $-1, %[borrow]
954 movq %[borrow], 8(%[r])
955 movq 16(%[x]), %[borrow]
956 sbbq $-1, %[borrow]
957 movq %[borrow], 16(%[r])
958 movq 24(%[x]), %[borrow]
959 sbbq $-1, %[borrow]
960 movq %[borrow], 24(%[r])
961 sbbq %[borrow],%[borrow]
962 negq %[borrow]
963 )"
964 : [borrow] "=r"(borrow)
965 : [x] "r"(hi.data()), [p0] "r"(P0), [r] "r"(r.data()), "0"(borrow)
966 : "cc", "memory");
967 }
968
969 borrow = (carry - borrow) > carry;
970 CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
971 return r;
972 }
973#endif
974
975 r[0] = word_sub(hi[0], P0, &borrow);
976 for(size_t i = 1; i != N; ++i) {
977 r[i] = word_sub(hi[i], WordInfo<W>::max, &borrow);
978 }
979
980 borrow = (carry - borrow) > carry;
981
982 CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
983
984 return r;
985}
986
987// Extract a WindowBits sized window out of s, depending on offset.
988template <size_t WindowBits, typename W, size_t N>
989constexpr size_t read_window_bits(std::span<const W, N> words, size_t offset) {
990 static_assert(WindowBits >= 1 && WindowBits <= 7);
991
992 constexpr uint8_t WindowMask = static_cast<uint8_t>(1 << WindowBits) - 1;
993
994 constexpr size_t W_bits = sizeof(W) * 8;
995 const auto bit_shift = offset % W_bits;
996 const auto word_offset = words.size() - 1 - (offset / W_bits);
997
998 const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0;
999
1000 const auto w0 = words[word_offset];
1001
1002 if(single_byte_window) {
1003 return (w0 >> bit_shift) & WindowMask;
1004 } else {
1005 // Otherwise we must join two words and extract the result
1006 const auto w1 = words[word_offset - 1];
1007 const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift)));
1008 return combined & WindowMask;
1009 }
1010}
1011
1012} // namespace Botan
1013
1014#endif
#define BOTAN_FUZZER_API
Definition api.h:65
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:420
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:470
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:478
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:465
constexpr W vartime_mod_2to1(W n1, W n0) const
Definition mp_core.h:602
constexpr W vartime_div_2to1(W n1, W n0) const
Definition mp_core.h:548
constexpr divide_precomp(W divisor)
Definition mp_core.h:541
constexpr W extract()
Definition mp_asmi.h:529
constexpr void mul(W x, W y)
Definition mp_asmi.h:455
constexpr Mask< T > conditional_copy_mem(Mask< T > mask, T *dest, const T *if_set, const T *if_unset, size_t elems)
Definition ct_utils.h:760
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:65
constexpr Mask< T > conditional_assign_mem(T cnd, T *dest, const T *src, size_t elems)
Definition ct_utils.h:777
constexpr void bigint_cnd_abs(W cnd, W x[], size_t size)
Definition mp_core.h:82
constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:96
constexpr void bigint_linmul3(W z[], const W x[], size_t x_size, W y)
Definition mp_core.h:405
constexpr auto bigint_cnd_sub(W cnd, W x[], const W y[], size_t size) -> W
Definition mp_core.h:64
constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:122
constexpr void bigint_cnd_swap(W cnd, W x[], W y[], size_t size)
Definition mp_core.h:31
constexpr auto word8_sub3(W z[8], const W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:331
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:649
constexpr auto word_sub(W x, W y, W *carry) -> W
Definition mp_asmi.h:280
constexpr auto word_add(W x, W y, W *carry) -> W
Definition mp_asmi.h:191
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
constexpr void comba_sqr(W z[2 *N], const W x[N])
Definition mp_core.h:772
constexpr uint64_t carry_shift(const donna128 &a, size_t shift)
Definition donna128.h:129
constexpr void bigint_shr2(W y[], const W x[], size_t x_size, size_t shift)
Definition mp_core.h:370
BOTAN_FUZZER_API void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size)
Definition mp_karat.cpp:46
constexpr size_t read_window_bits(std::span< const W, N > words, size_t offset)
Definition mp_core.h:989
void bigint_comba_sqr4(word z[8], const word x[4])
Definition mp_comba.cpp:16
void bigint_comba_sqr6(word z[12], const word x[6])
Definition mp_comba.cpp:74
constexpr void bigint_shr1(W x[], size_t x_size, size_t shift)
Definition mp_core.h:328
constexpr auto word8_add3(W z[8], const W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:254
BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6])
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
Definition mp_core.h:736
constexpr auto word8_sub2(W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:305
void bigint_comba_sqr7(word z[14], const word x[7])
Definition mp_comba.cpp:171
void bigint_comba_mul4(word z[8], const word x[4], const word y[4])
Definition mp_comba.cpp:42
constexpr auto word_madd2(W a, W b, W *c) -> W
Definition mp_asmi.h:86
void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size)
Definition mp_karat.cpp:327
void bigint_comba_mul16(word z[32], const word x[16], const word y[16])
Definition mp_comba.cpp:794
BOTAN_FUZZER_API void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24])
constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:194
constexpr auto monty_inverse(W a) -> W
Definition mp_core.h:622
BOTAN_FUZZER_API void bigint_monty_redc_generic(word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[])
Definition mp_monty.cpp:28
void bigint_mul(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, const word y[], size_t y_size, size_t y_sw, word workspace[], size_t ws_size)
Definition mp_karat.cpp:283
void bigint_comba_mul6(word z[12], const word x[6], const word y[6])
Definition mp_comba.cpp:115
constexpr void bigint_shl1(W x[], size_t x_size, size_t x_words, size_t shift)
Definition mp_core.h:309
constexpr auto bigint_ct_is_eq(const W x[], size_t x_size, const W y[], size_t y_size) -> CT::Mask< W >
Definition mp_core.h:508
constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:428
BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4])
void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size)
Definition mp_core.h:866
void bigint_monty_redc(word r[], const word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size)
Definition mp_core.h:841
void bigint_comba_mul7(word z[14], const word x[7], const word y[7])
Definition mp_comba.cpp:221
constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size)
Definition mp_core.h:47
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:227
constexpr void bigint_shl2(W y[], const W x[], size_t x_size, size_t shift)
Definition mp_core.h:352
void bigint_comba_mul9(word z[18], const word x[9], const word y[9])
Definition mp_comba.cpp:511
BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12])
void carry(int64_t &h0, int64_t &h1)
BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16])
void bigint_comba_mul24(word z[48], const word x[24], const word y[24])
constexpr auto bigint_sub_abs(W z[], const W x[], const W y[], size_t N, W ws[]) -> CT::Mask< W >
Definition mp_core.h:281
constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal=false) -> CT::Mask< W >
Definition mp_core.h:475
constexpr std::array< W, N > redc_crandall(std::span< const W, 2 *N > z)
Definition mp_core.h:910
constexpr auto word8_add2(W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:228
constexpr auto bigint_sub2(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:150
void bigint_comba_sqr8(word z[16], const word x[8])
Definition mp_comba.cpp:292
constexpr auto hex_to_words(const char(&s)[N])
Definition mp_core.h:678
void bigint_comba_sqr16(word z[32], const word x[16])
Definition mp_comba.cpp:618
constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size)
Definition mp_core.h:176
void bigint_comba_sqr9(word z[18], const word x[9])
Definition mp_comba.cpp:440
constexpr auto word8_linmul3(W z[8], const W x[8], W y, W carry) -> W
Definition mp_asmi.h:357
BOTAN_FUZZER_API void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size)
Definition mp_karat.cpp:20
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
Definition types.h:119
void bigint_comba_sqr24(word z[48], const word x[24])
void bigint_comba_mul8(word z[16], const word x[8], const word y[8])
Definition mp_comba.cpp:352
BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8])
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:119
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:663
BOTAN_FUZZER_API void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32])
constexpr auto word_madd3(W a, W b, W c, W *d) -> W
Definition mp_asmi.h:112
constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W
Definition mp_core.h:394