Botan 3.9.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* Compute ((n1<<bits) + n0) / d
533*/
534template <WordType W>
535inline constexpr auto bigint_divop_vartime(W n1, W n0, W d) -> W {
536 BOTAN_ARG_CHECK(d != 0, "Division by zero");
537
538 if constexpr(WordInfo<W>::dword_is_native) {
539 typename WordInfo<W>::dword n = n1;
540 n <<= WordInfo<W>::bits;
541 n |= n0;
542 return static_cast<W>(n / d);
543 } else {
544 W high = n1 % d;
545 W quotient = 0;
546
547 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
548 const W high_top_bit = high >> (WordInfo<W>::bits - 1);
549
550 high <<= 1;
551 high |= (n0 >> (WordInfo<W>::bits - 1 - i)) & 1;
552 quotient <<= 1;
553
554 if(high_top_bit || high >= d) {
555 high -= d;
556 quotient |= 1;
557 }
558 }
559
560 return quotient;
561 }
562}
563
564/**
565* Compute ((n1<<bits) + n0) % d
566*/
567template <WordType W>
568inline constexpr auto bigint_modop_vartime(W n1, W n0, W d) -> W {
569 BOTAN_ARG_CHECK(d != 0, "Division by zero");
570
571 W z = bigint_divop_vartime(n1, n0, d);
572 W carry = 0;
573 z = word_madd2(z, d, &carry);
574 return (n0 - z);
575}
576
577/*
578* Compute an integer x such that (a*x) == -1 (mod 2^n)
579*
580* Throws an exception if input is even, since in that case no inverse
581* exists. If input is odd, then input and 2^n are relatively prime and
582* the inverse exists.
583*/
584template <WordType W>
585inline constexpr auto monty_inverse(W a) -> W {
586 BOTAN_ARG_CHECK(a % 2 == 1, "Cannot compute Montgomery inverse of an even integer");
587
588 /*
589 * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
590 * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
591 */
592
593 W b = 1;
594 W r = 0;
595
596 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
597 const W bi = b % 2;
598 r >>= 1;
599 r += bi << (WordInfo<W>::bits - 1);
600
601 b -= a * bi;
602 b >>= 1;
603 }
604
605 // Now invert in addition space
606 r = (WordInfo<W>::max - r) + 1;
607
608 return r;
609}
610
611template <size_t S, WordType W, size_t N>
612inline constexpr W shift_left(std::array<W, N>& x) {
613 static_assert(S < WordInfo<W>::bits, "Shift too large");
614
615 W carry = 0;
616 for(size_t i = 0; i != N; ++i) {
617 const W w = x[i];
618 x[i] = (w << S) | carry;
619 carry = w >> (WordInfo<W>::bits - S);
620 }
621
622 return carry;
623}
624
625template <size_t S, WordType W, size_t N>
626inline constexpr W shift_right(std::array<W, N>& x) {
627 static_assert(S < WordInfo<W>::bits, "Shift too large");
628
629 W carry = 0;
630 for(size_t i = 0; i != N; ++i) {
631 const W w = x[N - 1 - i];
632 x[N - 1 - i] = (w >> S) | carry;
633 carry = w << (WordInfo<W>::bits - S);
634 }
635
636 return carry;
637}
638
639// Should be consteval but this triggers a bug in Clang 14
640template <WordType W, size_t N>
641constexpr auto hex_to_words(const char (&s)[N]) {
642 // Char count includes null terminator which we ignore
643 const constexpr size_t C = N - 1;
644
645 // Number of nibbles that a word can hold
646 const constexpr size_t NPW = (WordInfo<W>::bits / 4);
647
648 // Round up to the next number of words that will fit the input
649 const constexpr size_t S = (C + NPW - 1) / NPW;
650
651 auto hex2int = [](char c) -> int8_t {
652 if(c >= '0' && c <= '9') {
653 return static_cast<int8_t>(c - '0');
654 } else if(c >= 'a' && c <= 'f') {
655 return static_cast<int8_t>(c - 'a' + 10);
656 } else if(c >= 'A' && c <= 'F') {
657 return static_cast<int8_t>(c - 'A' + 10);
658 } else {
659 return -1;
660 }
661 };
662
663 std::array<W, S> r = {0};
664
665 for(size_t i = 0; i != C; ++i) {
666 const int8_t c = hex2int(s[i]);
667 if(c >= 0) {
668 shift_left<4>(r);
669 r[0] += c;
670 }
671 }
672
673 return r;
674}
675
676/*
677* Comba Multiplication / Squaring
678*/
679BOTAN_FUZZER_API void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
680BOTAN_FUZZER_API void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
681BOTAN_FUZZER_API void bigint_comba_mul7(word z[14], const word x[7], const word y[7]);
682BOTAN_FUZZER_API void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
683BOTAN_FUZZER_API void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
684BOTAN_FUZZER_API void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
685BOTAN_FUZZER_API void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);
686
687BOTAN_FUZZER_API void bigint_comba_sqr4(word out[8], const word in[4]);
688BOTAN_FUZZER_API void bigint_comba_sqr6(word out[12], const word in[6]);
689BOTAN_FUZZER_API void bigint_comba_sqr7(word out[14], const word x[7]);
690BOTAN_FUZZER_API void bigint_comba_sqr8(word out[16], const word in[8]);
691BOTAN_FUZZER_API void bigint_comba_sqr9(word out[18], const word in[9]);
692BOTAN_FUZZER_API void bigint_comba_sqr16(word out[32], const word in[16]);
693BOTAN_FUZZER_API void bigint_comba_sqr24(word out[48], const word in[24]);
694
695/*
696* Comba Fixed Length Multiplication
697*/
698template <size_t N, WordType W>
699constexpr inline void comba_mul(W z[2 * N], const W x[N], const W y[N]) {
700 if(!std::is_constant_evaluated()) {
701 if constexpr(std::same_as<W, word> && N == 4) {
702 return bigint_comba_mul4(z, x, y);
703 }
704 if constexpr(std::same_as<W, word> && N == 6) {
705 return bigint_comba_mul6(z, x, y);
706 }
707 if constexpr(std::same_as<W, word> && N == 7) {
708 return bigint_comba_mul7(z, x, y);
709 }
710 if constexpr(std::same_as<W, word> && N == 8) {
711 return bigint_comba_mul8(z, x, y);
712 }
713 if constexpr(std::same_as<W, word> && N == 9) {
714 return bigint_comba_mul9(z, x, y);
715 }
716 if constexpr(std::same_as<W, word> && N == 16) {
717 return bigint_comba_mul16(z, x, y);
718 }
719 }
720
721 word3<W> accum;
722
723 for(size_t i = 0; i != 2 * N; ++i) {
724 const size_t start = i + 1 < N ? 0 : i + 1 - N;
725 const size_t end = std::min(N, i + 1);
726
727 for(size_t j = start; j != end; ++j) {
728 accum.mul(x[j], y[i - j]);
729 }
730 z[i] = accum.extract();
731 }
732}
733
734template <size_t N, WordType W>
735constexpr inline void comba_sqr(W z[2 * N], const W x[N]) {
736 if(!std::is_constant_evaluated()) {
737 if constexpr(std::same_as<W, word> && N == 4) {
738 return bigint_comba_sqr4(z, x);
739 }
740 if constexpr(std::same_as<W, word> && N == 6) {
741 return bigint_comba_sqr6(z, x);
742 }
743 if constexpr(std::same_as<W, word> && N == 7) {
744 return bigint_comba_sqr7(z, x);
745 }
746 if constexpr(std::same_as<W, word> && N == 8) {
747 return bigint_comba_sqr8(z, x);
748 }
749 if constexpr(std::same_as<W, word> && N == 9) {
750 return bigint_comba_sqr9(z, x);
751 }
752 if constexpr(std::same_as<W, word> && N == 16) {
753 return bigint_comba_sqr16(z, x);
754 }
755 }
756
757 word3<W> accum;
758
759 for(size_t i = 0; i != 2 * N; ++i) {
760 const size_t start = i + 1 < N ? 0 : i + 1 - N;
761 const size_t end = std::min(N, i + 1);
762
763 for(size_t j = start; j != end; ++j) {
764 accum.mul(x[j], x[i - j]);
765 }
766 z[i] = accum.extract();
767 }
768}
769
770/*
771* Montgomery reduction
772*
773* Sets r to the Montgomery reduction of z using parameters p / p_dash
774*
775* The workspace should be of size equal to the prime
776*/
777BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]);
778BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]);
779BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]);
780BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]);
781BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]);
782BOTAN_FUZZER_API void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]);
783BOTAN_FUZZER_API void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]);
784
787 word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]);
788
789/**
790* Montgomery Reduction
791* @param r result of exactly p_size words
792* @param z integer to reduce, of size exactly 2*p_size.
793* @param p modulus
794* @param p_size size of p
795* @param p_dash Montgomery value
796* @param ws array of at least p_size words
797* @param ws_size size of ws in words
798*
799* It is allowed to set &r[0] == &z[0] however in this case note that only the
800* first p_size words of r will be written to and the high p_size words of r/z
801* will still hold the original inputs, these must be cleared after use.
802* See bigint_monty_redc_inplace
803*/
805 word r[], const word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
806 const size_t z_size = 2 * p_size;
807
808 BOTAN_ARG_CHECK(ws_size >= p_size, "Montgomery reduction workspace too small");
809
810 if(p_size == 4) {
811 bigint_monty_redc_4(r, z, p, p_dash, ws);
812 } else if(p_size == 6) {
813 bigint_monty_redc_6(r, z, p, p_dash, ws);
814 } else if(p_size == 8) {
815 bigint_monty_redc_8(r, z, p, p_dash, ws);
816 } else if(p_size == 12) {
817 bigint_monty_redc_12(r, z, p, p_dash, ws);
818 } else if(p_size == 16) {
819 bigint_monty_redc_16(r, z, p, p_dash, ws);
820 } else if(p_size == 24) {
821 bigint_monty_redc_24(r, z, p, p_dash, ws);
822 } else if(p_size == 32) {
823 bigint_monty_redc_32(r, z, p, p_dash, ws);
824 } else {
825 bigint_monty_redc_generic(r, z, z_size, p, p_size, p_dash, ws);
826 }
827}
828
829inline void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
830 bigint_monty_redc(z, z, p, p_size, p_dash, ws, ws_size);
831 clear_mem(z + p_size, p_size);
832}
833
834/**
835* Basecase O(N^2) multiplication
836*/
838void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size);
839
840/**
841* Basecase O(N^2) squaring
842*/
844void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size);
845
846/*
847* High Level Multiplication/Squaring Interfaces
848*/
849void bigint_mul(word z[],
850 size_t z_size,
851 const word x[],
852 size_t x_size,
853 size_t x_sw,
854 const word y[],
855 size_t y_size,
856 size_t y_sw,
857 word workspace[],
858 size_t ws_size);
859
860void 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);
861
862/**
863* Reduce z modulo p = 2**B - C where C is small
864*
865* z is assumed to be at most (p-1)**2
866*
867* For details on the algorithm see
868* - Handbook of Applied Cryptography, Algorithm 14.47
869* - Guide to Elliptic Curve Cryptography, Algorithm 2.54 and Note 2.55
870*
871*/
872template <WordType W, size_t N, W C>
873constexpr std::array<W, N> redc_crandall(std::span<const W, 2 * N> z) {
874 static_assert(N >= 2);
875
876 std::array<W, N> hi = {};
877
878 // hi = hi * c + lo
879
880 W carry = 0;
881 for(size_t i = 0; i != N; ++i) {
882 hi[i] = word_madd3(z[i + N], C, z[i], &carry);
883 }
884
885 // hi += carry * C
886 word carry_c[2] = {0};
887 carry_c[0] = word_madd2(carry, C, &carry_c[1]);
888
889 carry = bigint_add2(hi.data(), N, carry_c, 2);
890
891 constexpr W P0 = WordInfo<W>::max - (C - 1);
892
893 std::array<W, N> r = {};
894
895 W borrow = 0;
896
897 /*
898 * For undetermined reasons, on GCC (only) removing this asm block causes
899 * massive (up to 20%) performance regressions in secp256k1.
900 *
901 * The generated code without the asm seems quite reasonable, and timing
902 * repeated calls to redc_crandall with the cycle counter show that GCC
903 * computes it in about the same number of cycles with or without the asm.
904 *
905 * So the cause of the regression is unclear. But it is reproducible across
906 * machines and GCC versions.
907 */
908#if defined(BOTAN_MP_USE_X86_64_ASM) && defined(__GNUC__) && !defined(__clang__)
909 if constexpr(N == 4 && std::same_as<W, uint64_t>) {
910 if(!std::is_constant_evaluated()) {
911 asm volatile(R"(
912 movq 0(%[x]), %[borrow]
913 subq %[p0], %[borrow]
914 movq %[borrow], 0(%[r])
915 movq 16(%[x]), %[borrow]
916 sbbq $-1, %[borrow]
917 movq %[borrow], 8(%[r])
918 movq 16(%[x]), %[borrow]
919 sbbq $-1, %[borrow]
920 movq %[borrow], 16(%[r])
921 movq 24(%[x]), %[borrow]
922 sbbq $-1, %[borrow]
923 movq %[borrow], 24(%[r])
924 sbbq %[borrow],%[borrow]
925 negq %[borrow]
926 )"
927 : [borrow] "=r"(borrow)
928 : [x] "r"(hi.data()), [p0] "r"(P0), [r] "r"(r.data()), "0"(borrow)
929 : "cc", "memory");
930 }
931
932 borrow = (carry - borrow) > carry;
933 CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
934 return r;
935 }
936#endif
937
938 r[0] = word_sub(hi[0], P0, &borrow);
939 for(size_t i = 1; i != N; ++i) {
940 r[i] = word_sub(hi[i], WordInfo<W>::max, &borrow);
941 }
942
943 borrow = (carry - borrow) > carry;
944
945 CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
946
947 return r;
948}
949
950// Extract a WindowBits sized window out of s, depending on offset.
951template <size_t WindowBits, typename W, size_t N>
952constexpr size_t read_window_bits(std::span<const W, N> words, size_t offset) {
953 static_assert(WindowBits >= 1 && WindowBits <= 7);
954
955 constexpr uint8_t WindowMask = static_cast<uint8_t>(1 << WindowBits) - 1;
956
957 constexpr size_t W_bits = sizeof(W) * 8;
958 const auto bit_shift = offset % W_bits;
959 const auto word_offset = words.size() - 1 - (offset / W_bits);
960
961 const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0;
962
963 const auto w0 = words[word_offset];
964
965 if(single_byte_window) {
966 return (w0 >> bit_shift) & WindowMask;
967 } else {
968 // Otherwise we must join two words and extract the result
969 const auto w1 = words[word_offset - 1];
970 const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift)));
971 return combined & WindowMask;
972 }
973}
974
975} // namespace Botan
976
977#endif
#define BOTAN_FUZZER_API
Definition api.h:65
#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 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:612
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:735
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:952
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:699
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:585
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:31
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:829
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:804
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_modop_vartime(W n1, W n0, W d) -> W
Definition mp_core.h:568
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:873
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:641
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 auto bigint_divop_vartime(W n1, W n0, W d) -> W
Definition mp_core.h:535
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:626
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