Botan 3.5.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/exceptn.h>
14#include <botan/mem_ops.h>
15#include <botan/types.h>
16#include <botan/internal/ct_utils.h>
17#include <botan/internal/mp_asmi.h>
18#include <algorithm>
19#include <array>
20#include <span>
21
22namespace Botan {
23
24/*
25* If cond == 0, does nothing.
26* If cond > 0, swaps x[0:size] with y[0:size]
27* Runs in constant time
28*/
29template <WordType W>
30inline constexpr void bigint_cnd_swap(W cnd, W x[], W y[], size_t size) {
31 const auto mask = CT::Mask<W>::expand(cnd);
32
33 for(size_t i = 0; i != size; ++i) {
34 const W a = x[i];
35 const W b = y[i];
36 x[i] = mask.select(b, a);
37 y[i] = mask.select(a, b);
38 }
39}
40
41template <WordType W>
42inline constexpr W bigint_cnd_add(W cnd, W x[], size_t x_size, const W y[], size_t y_size) {
43 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
44
45 const auto mask = CT::Mask<W>::expand(cnd);
46
47 W carry = 0;
48
49 const size_t blocks = y_size - (y_size % 8);
50 W z[8] = {0};
51
52 for(size_t i = 0; i != blocks; i += 8) {
53 carry = word8_add3(z, x + i, y + i, carry);
54 mask.select_n(x + i, z, x + i, 8);
55 }
56
57 for(size_t i = blocks; i != y_size; ++i) {
58 z[0] = word_add(x[i], y[i], &carry);
59 x[i] = mask.select(z[0], x[i]);
60 }
61
62 for(size_t i = y_size; i != x_size; ++i) {
63 z[0] = word_add(x[i], static_cast<W>(0), &carry);
64 x[i] = mask.select(z[0], x[i]);
65 }
66
67 return mask.if_set_return(carry);
68}
69
70/*
71* If cond > 0 adds x[0:size] and y[0:size] and returns carry
72* Runs in constant time
73*/
74template <WordType W>
75inline constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size) {
76 return bigint_cnd_add(cnd, x, size, y, size);
77}
78
79/*
80* If cond > 0 subtracts x[0:size] and y[0:size] and returns borrow
81* Runs in constant time
82*/
83template <WordType W>
84inline constexpr auto bigint_cnd_sub(W cnd, W x[], size_t x_size, const W y[], size_t y_size) -> W {
85 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
86
87 const auto mask = CT::Mask<W>::expand(cnd);
88
89 W carry = 0;
90
91 const size_t blocks = y_size - (y_size % 8);
92 W z[8] = {0};
93
94 for(size_t i = 0; i != blocks; i += 8) {
95 carry = word8_sub3(z, x + i, y + i, carry);
96 mask.select_n(x + i, z, x + i, 8);
97 }
98
99 for(size_t i = blocks; i != y_size; ++i) {
100 z[0] = word_sub(x[i], y[i], &carry);
101 x[i] = mask.select(z[0], x[i]);
102 }
103
104 for(size_t i = y_size; i != x_size; ++i) {
105 z[0] = word_sub(x[i], static_cast<W>(0), &carry);
106 x[i] = mask.select(z[0], x[i]);
107 }
108
109 return mask.if_set_return(carry);
110}
111
112/*
113* If cond > 0 adds x[0:size] and y[0:size] and returns carry
114* Runs in constant time
115*/
116template <WordType W>
117inline constexpr auto bigint_cnd_sub(W cnd, W x[], const W y[], size_t size) -> W {
118 return bigint_cnd_sub(cnd, x, size, y, size);
119}
120
121/*
122* Equivalent to
123* bigint_cnd_add( mask, x, y, size);
124* bigint_cnd_sub(~mask, x, y, size);
125*
126* Mask must be either 0 or all 1 bits
127*/
128template <WordType W>
129inline constexpr void bigint_cnd_add_or_sub(CT::Mask<W> mask, W x[], const W y[], size_t size) {
130 const size_t blocks = size - (size % 8);
131
132 W carry = 0;
133 W borrow = 0;
134
135 W t0[8] = {0};
136 W t1[8] = {0};
137
138 for(size_t i = 0; i != blocks; i += 8) {
139 carry = word8_add3(t0, x + i, y + i, carry);
140 borrow = word8_sub3(t1, x + i, y + i, borrow);
141 mask.select_n(x + i, t0, t1, 8);
142 }
143
144 for(size_t i = blocks; i != size; ++i) {
145 const W a = word_add(x[i], y[i], &carry);
146 const W s = word_sub(x[i], y[i], &borrow);
147
148 x[i] = mask.select(a, s);
149 }
150}
151
152/*
153* Equivalent to
154* bigint_cnd_add( mask, x, size, y, size);
155* bigint_cnd_sub(~mask, x, size, z, size);
156*
157* Mask must be either 0 or all 1 bits
158*
159* Returns the carry or borrow resp
160*/
161template <WordType W>
162inline constexpr auto bigint_cnd_addsub(CT::Mask<W> mask, W x[], const W y[], const W z[], size_t size) -> W {
163 const size_t blocks = size - (size % 8);
164
165 W carry = 0;
166 W borrow = 0;
167
168 W t0[8] = {0};
169 W t1[8] = {0};
170
171 for(size_t i = 0; i != blocks; i += 8) {
172 carry = word8_add3(t0, x + i, y + i, carry);
173 borrow = word8_sub3(t1, x + i, z + i, borrow);
174 mask.select_n(x + i, t0, t1, 8);
175 }
176
177 for(size_t i = blocks; i != size; ++i) {
178 t0[0] = word_add(x[i], y[i], &carry);
179 t1[0] = word_sub(x[i], z[i], &borrow);
180 x[i] = mask.select(t0[0], t1[0]);
181 }
182
183 return mask.select(carry, borrow);
184}
185
186/*
187* 2s complement absolute value
188* If cond > 0 sets x to ~x + 1
189* Runs in constant time
190*/
191template <WordType W>
192inline constexpr void bigint_cnd_abs(W cnd, W x[], size_t size) {
193 const auto mask = CT::Mask<W>::expand(cnd);
194
195 W carry = mask.if_set_return(1);
196 for(size_t i = 0; i != size; ++i) {
197 const W z = word_add(~x[i], static_cast<W>(0), &carry);
198 x[i] = mask.select(z, x[i]);
199 }
200}
201
202/**
203* Two operand addition with carry out
204*/
205template <WordType W>
206inline constexpr auto bigint_add2_nc(W x[], size_t x_size, const W y[], size_t y_size) -> W {
207 W carry = 0;
208
209 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
210
211 const size_t blocks = y_size - (y_size % 8);
212
213 for(size_t i = 0; i != blocks; i += 8) {
214 carry = word8_add2(x + i, y + i, carry);
215 }
216
217 for(size_t i = blocks; i != y_size; ++i) {
218 x[i] = word_add(x[i], y[i], &carry);
219 }
220
221 for(size_t i = y_size; i != x_size; ++i) {
222 x[i] = word_add(x[i], static_cast<W>(0), &carry);
223 }
224
225 return carry;
226}
227
228/**
229* Three operand addition with carry out
230*/
231template <WordType W>
232inline constexpr auto bigint_add3_nc(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
233 if(x_size < y_size) {
234 return bigint_add3_nc(z, y, y_size, x, x_size);
235 }
236
237 W carry = 0;
238
239 const size_t blocks = y_size - (y_size % 8);
240
241 for(size_t i = 0; i != blocks; i += 8) {
242 carry = word8_add3(z + i, x + i, y + i, carry);
243 }
244
245 for(size_t i = blocks; i != y_size; ++i) {
246 z[i] = word_add(x[i], y[i], &carry);
247 }
248
249 for(size_t i = y_size; i != x_size; ++i) {
250 z[i] = word_add(x[i], static_cast<W>(0), &carry);
251 }
252
253 return carry;
254}
255
256template <WordType W, size_t N>
257inline constexpr auto bigint_add(std::span<W, N> z, std::span<const W, N> x, std::span<const W, N> y) -> W {
258 if constexpr(N == 4) {
259 return word4_add3<W>(z.data(), x.data(), y.data(), 0);
260 } else if constexpr(N == 8) {
261 return word8_add3<W>(z.data(), x.data(), y.data(), 0);
262 } else {
263 return bigint_add3_nc(z.data(), x.data(), N, y.data(), N);
264 }
265}
266
267/**
268* Two operand addition
269* @param x the first operand (and output)
270* @param x_size size of x
271* @param y the second operand
272* @param y_size size of y (must be <= x_size)
273*/
274template <WordType W>
275inline constexpr void bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) {
276 x[x_size] += bigint_add2_nc(x, x_size, y, y_size);
277}
278
279/**
280* Three operand addition
281*/
282template <WordType W>
283inline constexpr void bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) {
284 z[x_size > y_size ? x_size : y_size] += bigint_add3_nc(z, x, x_size, y, y_size);
285}
286
287/**
288* Two operand subtraction
289*/
290template <WordType W>
291inline constexpr auto bigint_sub2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
292 W borrow = 0;
293
294 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
295
296 const size_t blocks = y_size - (y_size % 8);
297
298 for(size_t i = 0; i != blocks; i += 8) {
299 borrow = word8_sub2(x + i, y + i, borrow);
300 }
301
302 for(size_t i = blocks; i != y_size; ++i) {
303 x[i] = word_sub(x[i], y[i], &borrow);
304 }
305
306 for(size_t i = y_size; i != x_size; ++i) {
307 x[i] = word_sub(x[i], static_cast<W>(0), &borrow);
308 }
309
310 return borrow;
311}
312
313/**
314* Two operand subtraction, x = y - x; assumes y >= x
315*/
316template <WordType W>
317inline constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size) {
318 W borrow = 0;
319
320 const size_t blocks = y_size - (y_size % 8);
321
322 for(size_t i = 0; i != blocks; i += 8) {
323 borrow = word8_sub2_rev(x + i, y + i, borrow);
324 }
325
326 for(size_t i = blocks; i != y_size; ++i) {
327 x[i] = word_sub(y[i], x[i], &borrow);
328 }
329
330 BOTAN_ASSERT(borrow == 0, "y must be greater than x");
331}
332
333/**
334* Three operand subtraction
335*
336* Expects that x_size >= y_size
337*
338* Writes to z[0:x_size] and returns borrow
339*/
340template <WordType W>
341inline constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
342 W borrow = 0;
343
344 BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
345
346 const size_t blocks = y_size - (y_size % 8);
347
348 for(size_t i = 0; i != blocks; i += 8) {
349 borrow = word8_sub3(z + i, x + i, y + i, borrow);
350 }
351
352 for(size_t i = blocks; i != y_size; ++i) {
353 z[i] = word_sub(x[i], y[i], &borrow);
354 }
355
356 for(size_t i = y_size; i != x_size; ++i) {
357 z[i] = word_sub(x[i], static_cast<W>(0), &borrow);
358 }
359
360 return borrow;
361}
362
363/**
364* Conditional subtraction for Montgomery reduction
365*
366* This function assumes that (x0 || x) is less than 2*p
367*
368* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
369*
370* If z would be positive, returns z[0:N]
371* Otherwise returns original input x
372*/
373template <WordType W>
374inline constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[]) {
375 W borrow = 0;
376
377 const size_t blocks = N - (N % 8);
378
379 for(size_t i = 0; i != blocks; i += 8) {
380 borrow = word8_sub3(z + i, x + i, p + i, borrow);
381 }
382
383 for(size_t i = blocks; i != N; ++i) {
384 z[i] = word_sub(x[i], p[i], &borrow);
385 }
386
387 word_sub(x0, static_cast<W>(0), &borrow);
388
389 CT::conditional_assign_mem(borrow, z, x, N);
390}
391
392/**
393* Conditional subtraction for Montgomery reduction
394*
395* This function assumes that (x0 || x) is less than 2*p
396*
397* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
398*
399* If z would be positive, returns z[0:N]
400* Otherwise returns original input x
401*/
402template <size_t N, WordType W>
403inline constexpr void bigint_monty_maybe_sub(W z[N], W x0, const W x[N], const W y[N]) {
404 W borrow = 0;
405
406 if constexpr(N == 4) {
407 borrow = word4_sub3(z, x, y, borrow);
408 } else if constexpr(N == 8) {
409 borrow = word8_sub3(z, x, y, borrow);
410 } else {
411 const constexpr size_t blocks = N - (N % 8);
412 for(size_t i = 0; i != blocks; i += 8) {
413 borrow = word8_sub3(z + i, x + i, y + i, borrow);
414 }
415
416 for(size_t i = blocks; i != N; ++i) {
417 z[i] = word_sub(x[i], y[i], &borrow);
418 }
419 }
420
421 word_sub(x0, static_cast<W>(0), &borrow);
422
423 CT::conditional_assign_mem(borrow, z, x, N);
424}
425
426/**
427* Return abs(x-y), ie if x >= y, then compute z = x - y
428* Otherwise compute z = y - x
429* No borrow is possible since the result is always >= 0
430*
431* Returns ~0 if x >= y or 0 if x < y
432* @param z output array of at least N words
433* @param x input array of N words
434* @param y input array of N words
435* @param N length of x and y
436* @param ws array of at least 2*N words
437*/
438template <WordType W>
439inline constexpr auto bigint_sub_abs(W z[], const W x[], const W y[], size_t N, W ws[]) -> CT::Mask<W> {
440 // Subtract in both direction then conditional copy out the result
441
442 W* ws0 = ws;
443 W* ws1 = ws + N;
444
445 W borrow0 = 0;
446 W borrow1 = 0;
447
448 const size_t blocks = N - (N % 8);
449
450 for(size_t i = 0; i != blocks; i += 8) {
451 borrow0 = word8_sub3(ws0 + i, x + i, y + i, borrow0);
452 borrow1 = word8_sub3(ws1 + i, y + i, x + i, borrow1);
453 }
454
455 for(size_t i = blocks; i != N; ++i) {
456 ws0[i] = word_sub(x[i], y[i], &borrow0);
457 ws1[i] = word_sub(y[i], x[i], &borrow1);
458 }
459
460 return CT::conditional_copy_mem(borrow0, z, ws1, ws0, N);
461}
462
463/*
464* Shift Operations
465*/
466template <WordType W>
467inline constexpr void bigint_shl1(W x[], size_t x_size, size_t x_words, size_t shift) {
468 const size_t word_shift = shift / WordInfo<W>::bits;
469 const size_t bit_shift = shift % WordInfo<W>::bits;
470
471 copy_mem(x + word_shift, x, x_words);
472 clear_mem(x, word_shift);
473
474 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
475 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
476
477 W carry = 0;
478 for(size_t i = word_shift; i != x_size; ++i) {
479 const W w = x[i];
480 x[i] = (w << bit_shift) | carry;
481 carry = carry_mask.if_set_return(w >> carry_shift);
482 }
483}
484
485template <WordType W>
486inline constexpr void bigint_shr1(W x[], size_t x_size, size_t shift) {
487 const size_t word_shift = shift / WordInfo<W>::bits;
488 const size_t bit_shift = shift % WordInfo<W>::bits;
489
490 const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0;
491
492 if(top > 0) {
493 copy_mem(x, x + word_shift, top);
494 }
495 clear_mem(x + top, std::min(word_shift, x_size));
496
497 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
498 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
499
500 W carry = 0;
501
502 for(size_t i = 0; i != top; ++i) {
503 const W w = x[top - i - 1];
504 x[top - i - 1] = (w >> bit_shift) | carry;
505 carry = carry_mask.if_set_return(w << carry_shift);
506 }
507}
508
509template <WordType W>
510inline constexpr void bigint_shl2(W y[], const W x[], size_t x_size, size_t shift) {
511 const size_t word_shift = shift / WordInfo<W>::bits;
512 const size_t bit_shift = shift % WordInfo<W>::bits;
513
514 copy_mem(y + word_shift, x, x_size);
515
516 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
517 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
518
519 W carry = 0;
520 for(size_t i = word_shift; i != x_size + word_shift + 1; ++i) {
521 const W w = y[i];
522 y[i] = (w << bit_shift) | carry;
523 carry = carry_mask.if_set_return(w >> carry_shift);
524 }
525}
526
527template <WordType W>
528inline constexpr void bigint_shr2(W y[], const W x[], size_t x_size, size_t shift) {
529 const size_t word_shift = shift / WordInfo<W>::bits;
530 const size_t bit_shift = shift % WordInfo<W>::bits;
531 const size_t new_size = x_size < word_shift ? 0 : (x_size - word_shift);
532
533 if(new_size > 0) {
534 copy_mem(y, x + word_shift, new_size);
535 }
536
537 const auto carry_mask = CT::Mask<W>::expand(bit_shift);
538 const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);
539
540 W carry = 0;
541 for(size_t i = new_size; i > 0; --i) {
542 W w = y[i - 1];
543 y[i - 1] = (w >> bit_shift) | carry;
544 carry = carry_mask.if_set_return(w << carry_shift);
545 }
546}
547
548/*
549* Linear Multiply - returns the carry
550*/
551template <WordType W>
552[[nodiscard]] inline constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W {
553 const size_t blocks = x_size - (x_size % 8);
554
555 W carry = 0;
556
557 for(size_t i = 0; i != blocks; i += 8) {
558 carry = word8_linmul2(x + i, y, carry);
559 }
560
561 for(size_t i = blocks; i != x_size; ++i) {
562 x[i] = word_madd2(x[i], y, &carry);
563 }
564
565 return carry;
566}
567
568template <WordType W>
569inline constexpr void bigint_linmul3(W z[], const W x[], size_t x_size, W y) {
570 const size_t blocks = x_size - (x_size % 8);
571
572 W carry = 0;
573
574 for(size_t i = 0; i != blocks; i += 8) {
575 carry = word8_linmul3(z + i, x + i, y, carry);
576 }
577
578 for(size_t i = blocks; i != x_size; ++i) {
579 z[i] = word_madd2(x[i], y, &carry);
580 }
581
582 z[x_size] = carry;
583}
584
585/**
586* Compare x and y
587* Return -1 if x < y
588* Return 0 if x == y
589* Return 1 if x > y
590*/
591template <WordType W>
592inline constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size) {
593 static_assert(sizeof(W) >= sizeof(uint32_t), "Size assumption");
594
595 const W LT = static_cast<W>(-1);
596 const W EQ = 0;
597 const W GT = 1;
598
599 const size_t common_elems = std::min(x_size, y_size);
600
601 W result = EQ; // until found otherwise
602
603 for(size_t i = 0; i != common_elems; i++) {
604 const auto is_eq = CT::Mask<W>::is_equal(x[i], y[i]);
605 const auto is_lt = CT::Mask<W>::is_lt(x[i], y[i]);
606
607 result = is_eq.select(result, is_lt.select(LT, GT));
608 }
609
610 if(x_size < y_size) {
611 W mask = 0;
612 for(size_t i = x_size; i != y_size; i++) {
613 mask |= y[i];
614 }
615
616 // If any bits were set in high part of y, then x < y
617 result = CT::Mask<W>::is_zero(mask).select(result, LT);
618 } else if(y_size < x_size) {
619 W mask = 0;
620 for(size_t i = y_size; i != x_size; i++) {
621 mask |= x[i];
622 }
623
624 // If any bits were set in high part of x, then x > y
625 result = CT::Mask<W>::is_zero(mask).select(result, GT);
626 }
627
628 CT::unpoison(result);
629 BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
630 return static_cast<int32_t>(result);
631}
632
633/**
634* Compare x and y
635* Return ~0 if x[0:x_size] < y[0:y_size] or 0 otherwise
636* If lt_or_equal is true, returns ~0 also for x == y
637*/
638template <WordType W>
639inline 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)
640 -> CT::Mask<W> {
641 const size_t common_elems = std::min(x_size, y_size);
642
643 auto is_lt = CT::Mask<W>::expand(lt_or_equal);
644
645 for(size_t i = 0; i != common_elems; i++) {
646 const auto eq = CT::Mask<W>::is_equal(x[i], y[i]);
647 const auto lt = CT::Mask<W>::is_lt(x[i], y[i]);
648 is_lt = eq.select_mask(is_lt, lt);
649 }
650
651 if(x_size < y_size) {
652 W mask = 0;
653 for(size_t i = x_size; i != y_size; i++) {
654 mask |= y[i];
655 }
656 // If any bits were set in high part of y, then is_lt should be forced true
657 is_lt |= CT::Mask<W>::expand(mask);
658 } else if(y_size < x_size) {
659 W mask = 0;
660 for(size_t i = y_size; i != x_size; i++) {
661 mask |= x[i];
662 }
663
664 // If any bits were set in high part of x, then is_lt should be false
665 is_lt &= CT::Mask<W>::is_zero(mask);
666 }
667
668 return is_lt;
669}
670
671template <WordType W>
672inline constexpr auto bigint_ct_is_eq(const W x[], size_t x_size, const W y[], size_t y_size) -> CT::Mask<W> {
673 const size_t common_elems = std::min(x_size, y_size);
674
675 W diff = 0;
676
677 for(size_t i = 0; i != common_elems; i++) {
678 diff |= (x[i] ^ y[i]);
679 }
680
681 // If any bits were set in high part of x/y, then they are not equal
682 if(x_size < y_size) {
683 for(size_t i = x_size; i != y_size; i++) {
684 diff |= y[i];
685 }
686 } else if(y_size < x_size) {
687 for(size_t i = y_size; i != x_size; i++) {
688 diff |= x[i];
689 }
690 }
691
692 return CT::Mask<W>::is_zero(diff);
693}
694
695/**
696* Set z to abs(x-y), ie if x >= y, then compute z = x - y
697* Otherwise compute z = y - x
698* No borrow is possible since the result is always >= 0
699*
700* Return the relative size of x vs y (-1, 0, 1)
701*
702* @param z output array of max(x_size,y_size) words
703* @param x input param
704* @param x_size length of x
705* @param y input param
706* @param y_size length of y
707*/
708template <WordType W>
709inline constexpr int32_t bigint_sub_abs(W z[], const W x[], size_t x_size, const W y[], size_t y_size) {
710 const int32_t relative_size = bigint_cmp(x, x_size, y, y_size);
711
712 // Swap if relative_size == -1
713 const bool need_swap = relative_size < 0;
714 CT::conditional_swap_ptr(need_swap, x, y);
715 CT::conditional_swap(need_swap, x_size, y_size);
716
717 /*
718 * We know at this point that x >= y so if y_size is larger than
719 * x_size, we are guaranteed they are just leading zeros which can
720 * be ignored
721 */
722 y_size = std::min(x_size, y_size);
723
724 bigint_sub3(z, x, x_size, y, y_size);
725
726 return relative_size;
727}
728
729/**
730* Set t to t-s modulo mod
731*
732* @param t first integer
733* @param s second integer
734* @param mod the modulus
735* @param mod_sw size of t, s, and mod
736* @param ws workspace of size mod_sw
737*/
738template <WordType W>
739inline constexpr void bigint_mod_sub(W t[], const W s[], const W mod[], size_t mod_sw, W ws[]) {
740 // is t < s or not?
741 const auto is_lt = bigint_ct_is_lt(t, mod_sw, s, mod_sw);
742
743 // ws = p - s
744 const W borrow = bigint_sub3(ws, mod, mod_sw, s, mod_sw);
745
746 // Compute either (t - s) or (t + (p - s)) depending on mask
747 const W carry = bigint_cnd_addsub(is_lt, t, ws, s, mod_sw);
748
749 if(!std::is_constant_evaluated()) {
750 BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
751 }
752
753 BOTAN_UNUSED(carry, borrow);
754}
755
756template <size_t N, WordType W>
757inline constexpr void bigint_mod_sub_n(W t[], const W s[], const W mod[], W ws[]) {
758 // is t < s or not?
759 const auto is_lt = bigint_ct_is_lt(t, N, s, N);
760
761 // ws = p - s
762 const W borrow = bigint_sub3(ws, mod, N, s, N);
763
764 // Compute either (t - s) or (t + (p - s)) depending on mask
765 const W carry = bigint_cnd_addsub(is_lt, t, ws, s, N);
766
767 if(!std::is_constant_evaluated()) {
768 BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
769 }
770
771 BOTAN_UNUSED(carry, borrow);
772}
773
774/**
775* Compute ((n1<<bits) + n0) / d
776*/
777template <WordType W>
778inline constexpr auto bigint_divop_vartime(W n1, W n0, W d) -> W {
779 if(d == 0) {
780 throw Invalid_Argument("bigint_divop_vartime divide by zero");
781 }
782
783 if constexpr(WordInfo<W>::dword_is_native) {
784 typename WordInfo<W>::dword n = n1;
785 n <<= WordInfo<W>::bits;
786 n |= n0;
787 return static_cast<W>(n / d);
788 } else {
789 W high = n1 % d;
790 W quotient = 0;
791
792 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
793 const W high_top_bit = high >> (WordInfo<W>::bits - 1);
794
795 high <<= 1;
796 high |= (n0 >> (WordInfo<W>::bits - 1 - i)) & 1;
797 quotient <<= 1;
798
799 if(high_top_bit || high >= d) {
800 high -= d;
801 quotient |= 1;
802 }
803 }
804
805 return quotient;
806 }
807}
808
809/**
810* Compute ((n1<<bits) + n0) % d
811*/
812template <WordType W>
813inline constexpr auto bigint_modop_vartime(W n1, W n0, W d) -> W {
814 if(d == 0) {
815 throw Invalid_Argument("bigint_modop_vartime divide by zero");
816 }
817
818 W z = bigint_divop_vartime(n1, n0, d);
819 W carry = 0;
820 z = word_madd2(z, d, &carry);
821 return (n0 - z);
822}
823
824/*
825* Compute an integer x such that (a*x) == -1 (mod 2^n)
826*
827* Throws an exception if input is even, since in that case no inverse
828* exists. If input is odd, then input and 2^n are relatively prime and
829* the inverse exists.
830*/
831template <WordType W>
832inline constexpr auto monty_inverse(W a) -> W {
833 if(a % 2 == 0) {
834 throw Invalid_Argument("monty_inverse only valid for odd integers");
835 }
836
837 /*
838 * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
839 * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
840 */
841
842 W b = 1;
843 W r = 0;
844
845 for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
846 const W bi = b % 2;
847 r >>= 1;
848 r += bi << (WordInfo<W>::bits - 1);
849
850 b -= a * bi;
851 b >>= 1;
852 }
853
854 // Now invert in addition space
855 r = (WordInfo<W>::max - r) + 1;
856
857 return r;
858}
859
860template <size_t S, WordType W, size_t N>
861inline constexpr W shift_left(std::array<W, N>& x) {
862 static_assert(S < WordInfo<W>::bits, "Shift too large");
863
864 W carry = 0;
865 for(size_t i = 0; i != N; ++i) {
866 const W w = x[i];
867 x[i] = (w << S) | carry;
868 carry = w >> (WordInfo<W>::bits - S);
869 }
870
871 return carry;
872}
873
874template <size_t S, WordType W, size_t N>
875inline constexpr W shift_right(std::array<W, N>& x) {
876 static_assert(S < WordInfo<W>::bits, "Shift too large");
877
878 W carry = 0;
879 for(size_t i = 0; i != N; ++i) {
880 const W w = x[N - 1 - i];
881 x[N - 1 - i] = (w >> S) | carry;
882 carry = w << (WordInfo<W>::bits - S);
883 }
884
885 return carry;
886}
887
888// Should be consteval but this triggers a bug in Clang 14
889template <WordType W, size_t N>
890constexpr auto hex_to_words(const char (&s)[N]) {
891 // Char count includes null terminator which we ignore
892 const constexpr size_t C = N - 1;
893
894 // Number of nibbles that a word can hold
895 const constexpr size_t NPW = (WordInfo<W>::bits / 4);
896
897 // Round up to the next number of words that will fit the input
898 const constexpr size_t S = (C + NPW - 1) / NPW;
899
900 auto hex2int = [](char c) -> int8_t {
901 if(c >= '0' && c <= '9') {
902 return static_cast<int8_t>(c - '0');
903 } else if(c >= 'a' && c <= 'f') {
904 return static_cast<int8_t>(c - 'a' + 10);
905 } else if(c >= 'A' && c <= 'F') {
906 return static_cast<int8_t>(c - 'A' + 10);
907 } else {
908 return -1;
909 }
910 };
911
912 std::array<W, S> r = {0};
913
914 for(size_t i = 0; i != C; ++i) {
915 const int8_t c = hex2int(s[i]);
916 if(c >= 0) {
917 shift_left<4>(r);
918 r[0] += c;
919 }
920 }
921
922 return r;
923}
924
925/*
926* Comba Multiplication / Squaring
927*/
928BOTAN_FUZZER_API void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
929BOTAN_FUZZER_API void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
930BOTAN_FUZZER_API void bigint_comba_mul7(word z[14], const word x[7], const word y[7]);
931BOTAN_FUZZER_API void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
932BOTAN_FUZZER_API void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
933BOTAN_FUZZER_API void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
934BOTAN_FUZZER_API void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);
935
936BOTAN_FUZZER_API void bigint_comba_sqr4(word out[8], const word in[4]);
937BOTAN_FUZZER_API void bigint_comba_sqr6(word out[12], const word in[6]);
938BOTAN_FUZZER_API void bigint_comba_sqr7(word out[14], const word x[7]);
939BOTAN_FUZZER_API void bigint_comba_sqr8(word out[16], const word in[8]);
940BOTAN_FUZZER_API void bigint_comba_sqr9(word out[18], const word in[9]);
941BOTAN_FUZZER_API void bigint_comba_sqr16(word out[32], const word in[16]);
942BOTAN_FUZZER_API void bigint_comba_sqr24(word out[48], const word in[24]);
943
944/*
945* Comba Fixed Length Multiplication
946*/
947template <size_t N, WordType W>
948constexpr inline void comba_mul(W z[2 * N], const W x[N], const W y[N]) {
949 if(!std::is_constant_evaluated()) {
950 if constexpr(std::same_as<W, word> && N == 4) {
951 return bigint_comba_mul4(z, x, y);
952 }
953 if constexpr(std::same_as<W, word> && N == 6) {
954 return bigint_comba_mul6(z, x, y);
955 }
956 if constexpr(std::same_as<W, word> && N == 7) {
957 return bigint_comba_mul7(z, x, y);
958 }
959 if constexpr(std::same_as<W, word> && N == 8) {
960 return bigint_comba_mul8(z, x, y);
961 }
962 if constexpr(std::same_as<W, word> && N == 9) {
963 return bigint_comba_mul9(z, x, y);
964 }
965 if constexpr(std::same_as<W, word> && N == 16) {
966 return bigint_comba_mul16(z, x, y);
967 }
968 }
969
970 word3<W> accum;
971
972 for(size_t i = 0; i != 2 * N; ++i) {
973 const size_t start = i + 1 < N ? 0 : i + 1 - N;
974 const size_t end = std::min(N, i + 1);
975
976 for(size_t j = start; j != end; ++j) {
977 accum.mul(x[j], y[i - j]);
978 }
979 z[i] = accum.extract();
980 }
981}
982
983template <size_t N, WordType W>
984constexpr inline void comba_sqr(W z[2 * N], const W x[N]) {
985 if(!std::is_constant_evaluated()) {
986 if constexpr(std::same_as<W, word> && N == 4) {
987 return bigint_comba_sqr4(z, x);
988 }
989 if constexpr(std::same_as<W, word> && N == 6) {
990 return bigint_comba_sqr6(z, x);
991 }
992 if constexpr(std::same_as<W, word> && N == 7) {
993 return bigint_comba_sqr7(z, x);
994 }
995 if constexpr(std::same_as<W, word> && N == 8) {
996 return bigint_comba_sqr8(z, x);
997 }
998 if constexpr(std::same_as<W, word> && N == 9) {
999 return bigint_comba_sqr9(z, x);
1000 }
1001 if constexpr(std::same_as<W, word> && N == 16) {
1002 return bigint_comba_sqr16(z, x);
1003 }
1004 }
1005
1006 word3<W> accum;
1007
1008 for(size_t i = 0; i != 2 * N; ++i) {
1009 const size_t start = i + 1 < N ? 0 : i + 1 - N;
1010 const size_t end = std::min(N, i + 1);
1011
1012 for(size_t j = start; j != end; ++j) {
1013 accum.mul(x[j], x[i - j]);
1014 }
1015 z[i] = accum.extract();
1016 }
1017}
1018
1019/*
1020* Montgomery reduction
1021*
1022* Each of these functions makes the following assumptions:
1023*
1024* z_size == 2*p_size
1025* ws_size >= p_size
1026*/
1027BOTAN_FUZZER_API void bigint_monty_redc_4(word z[8], const word p[4], word p_dash, word ws[]);
1028BOTAN_FUZZER_API void bigint_monty_redc_6(word z[12], const word p[6], word p_dash, word ws[]);
1029BOTAN_FUZZER_API void bigint_monty_redc_8(word z[16], const word p[8], word p_dash, word ws[]);
1030BOTAN_FUZZER_API void bigint_monty_redc_16(word z[32], const word p[16], word p_dash, word ws[]);
1031BOTAN_FUZZER_API void bigint_monty_redc_24(word z[48], const word p[24], word p_dash, word ws[]);
1032BOTAN_FUZZER_API void bigint_monty_redc_32(word z[64], const word p[32], word p_dash, word ws[]);
1033
1035void bigint_monty_redc_generic(word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]);
1036
1037/**
1038* Montgomery Reduction
1039* @param z integer to reduce, of size exactly 2*p_size. Output is in
1040* the first p_size words, higher words are set to zero.
1041* @param p modulus
1042* @param p_size size of p
1043* @param p_dash Montgomery value
1044* @param ws array of at least p_size words
1045* @param ws_size size of ws in words
1046*/
1047inline void bigint_monty_redc(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
1048 const size_t z_size = 2 * p_size;
1049
1050 BOTAN_ARG_CHECK(ws_size >= p_size, "Montgomery reduction workspace too small");
1051
1052 if(p_size == 4) {
1053 bigint_monty_redc_4(z, p, p_dash, ws);
1054 } else if(p_size == 6) {
1055 bigint_monty_redc_6(z, p, p_dash, ws);
1056 } else if(p_size == 8) {
1057 bigint_monty_redc_8(z, p, p_dash, ws);
1058 } else if(p_size == 16) {
1059 bigint_monty_redc_16(z, p, p_dash, ws);
1060 } else if(p_size == 24) {
1061 bigint_monty_redc_24(z, p, p_dash, ws);
1062 } else if(p_size == 32) {
1063 bigint_monty_redc_32(z, p, p_dash, ws);
1064 } else {
1065 bigint_monty_redc_generic(z, z_size, p, p_size, p_dash, ws);
1066 }
1067}
1068
1069/**
1070* Basecase O(N^2) multiplication
1071*/
1073void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size);
1074
1075/**
1076* Basecase O(N^2) squaring
1077*/
1079void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size);
1080
1081/*
1082* High Level Multiplication/Squaring Interfaces
1083*/
1084void bigint_mul(word z[],
1085 size_t z_size,
1086 const word x[],
1087 size_t x_size,
1088 size_t x_sw,
1089 const word y[],
1090 size_t y_size,
1091 size_t y_sw,
1092 word workspace[],
1093 size_t ws_size);
1094
1095void 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);
1096
1097} // namespace Botan
1098
1099#endif
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
Definition ct_utils.h:373
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:213
constexpr T select(T x, T y) const
Definition ct_utils.h:356
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:250
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:258
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:245
constexpr W extract()
Definition mp_asmi.h:647
constexpr void mul(W x, W y)
Definition mp_asmi.h:641
#define BOTAN_FUZZER_API
Definition compiler.h:63
constexpr void conditional_swap_ptr(bool cnd, T &x, T &y)
Definition ct_utils.h:462
constexpr void conditional_swap(bool cnd, T &x, T &y)
Definition ct_utils.h:452
constexpr Mask< T > conditional_assign_mem(T cnd, T *sink, const T *src, size_t elems)
Definition ct_utils.h:438
constexpr Mask< T > conditional_copy_mem(Mask< T > mask, T *to, const T *from0, const T *from1, size_t elems)
Definition ct_utils.h:426
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:57
constexpr void bigint_cnd_abs(W cnd, W x[], size_t size)
Definition mp_core.h:192
constexpr void bigint_linmul3(W z[], const W x[], size_t x_size, W y)
Definition mp_core.h:569
constexpr auto bigint_add(std::span< W, N > z, std::span< const W, N > x, std::span< const W, N > y) -> W
Definition mp_core.h:257
constexpr void bigint_cnd_swap(W cnd, W x[], W y[], size_t size)
Definition mp_core.h:30
constexpr auto word8_sub3(W z[8], const W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:366
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:861
constexpr auto word_sub(W x, W y, W *carry) -> W
Definition mp_asmi.h:281
void bigint_monty_redc(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size)
Definition mp_core.h:1047
constexpr auto word_add(W x, W y, W *carry) -> W
Definition mp_asmi.h:177
constexpr void comba_sqr(W z[2 *N], const W x[N])
Definition mp_core.h:984
constexpr uint64_t carry_shift(const donna128 &a, size_t shift)
Definition donna128.h:132
constexpr void bigint_shr2(W y[], const W x[], size_t x_size, size_t shift)
Definition mp_core.h:528
BOTAN_FUZZER_API void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size)
Definition mp_karat.cpp:46
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
BOTAN_FUZZER_API void bigint_monty_redc_24(word z[48], const word p[24], word p_dash, word ws[])
constexpr void bigint_shr1(W x[], size_t x_size, size_t shift)
Definition mp_core.h:486
constexpr auto word8_add3(W z[8], const W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:236
constexpr void bigint_mod_sub_n(W t[], const W s[], const W mod[], W ws[])
Definition mp_core.h:757
constexpr auto word4_sub3(W z[4], const W x[4], const W y[4], W carry) -> W
Definition mp_asmi.h:389
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
Definition mp_core.h:948
constexpr auto word8_sub2(W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:314
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:66
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:326
void bigint_comba_mul16(word z[32], const word x[16], const word y[16])
Definition mp_comba.cpp:794
constexpr auto word8_sub2_rev(W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:340
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:341
constexpr auto monty_inverse(W a) -> W
Definition mp_core.h:832
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:282
constexpr auto bigint_cnd_addsub(CT::Mask< W > mask, W x[], const W y[], const W z[], size_t size) -> W
Definition mp_core.h:162
void bigint_comba_mul6(word z[12], const word x[6], const word y[6])
Definition mp_comba.cpp:115
BOTAN_FUZZER_API void bigint_monty_redc_4(word z[8], const word p[4], word p_dash, word ws[])
constexpr void bigint_shl1(W x[], size_t x_size, size_t x_words, size_t shift)
Definition mp_core.h:467
constexpr auto word4_add3(W z[4], const W x[4], const W y[4], W carry) -> W
Definition mp_asmi.h:259
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:672
constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:592
void bigint_comba_mul7(word z[14], const word x[7], const word y[7])
Definition mp_comba.cpp:221
constexpr auto bigint_cnd_sub(W cnd, W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:84
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:374
constexpr void bigint_shl2(W y[], const W x[], size_t x_size, size_t shift)
Definition mp_core.h:510
void bigint_comba_mul9(word z[18], const word x[9], const word y[9])
Definition mp_comba.cpp:511
constexpr W bigint_cnd_add(W cnd, W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:42
void carry(int64_t &h0, int64_t &h1)
constexpr auto word8_linmul2(W x[8], W y, W carry) -> W
Definition mp_asmi.h:411
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:813
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:439
BOTAN_FUZZER_API void bigint_monty_redc_6(word z[12], const word p[6], word p_dash, word ws[])
constexpr void bigint_add2(W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:275
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:639
constexpr auto word8_add2(W x[8], const W y[8], W carry) -> W
Definition mp_asmi.h:210
constexpr auto bigint_sub2(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:291
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:890
void bigint_comba_sqr16(word z[32], const word x[16])
Definition mp_comba.cpp:618
constexpr void bigint_cnd_add_or_sub(CT::Mask< W > mask, W x[], const W y[], size_t size)
Definition mp_core.h:129
constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size)
Definition mp_core.h:317
void bigint_comba_sqr9(word z[18], const word x[9])
Definition mp_comba.cpp:440
constexpr auto bigint_add2_nc(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:206
constexpr auto word8_linmul3(W z[8], const W x[8], W y, W carry) -> W
Definition mp_asmi.h:437
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
constexpr void bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:283
BOTAN_FUZZER_API void bigint_monty_redc_generic(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_comba_sqr24(word z[48], const word x[24])
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146
BOTAN_FUZZER_API void bigint_monty_redc_32(word z[64], const word p[32], word p_dash, word ws[])
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_16(word z[32], const word p[16], word p_dash, word ws[])
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:120
constexpr auto bigint_divop_vartime(W n1, W n0, W d) -> W
Definition mp_core.h:778
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:875
BOTAN_FUZZER_API void bigint_monty_redc_8(word z[16], const word p[8], word p_dash, word ws[])
constexpr void bigint_mod_sub(W t[], const W s[], const W mod[], size_t mod_sw, W ws[])
Definition mp_core.h:739
constexpr auto bigint_add3_nc(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:232
constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W
Definition mp_core.h:552