1*0ed42a6fSJason A. Donenfeld // SPDX-License-Identifier: GPL-2.0 OR MIT
2*0ed42a6fSJason A. Donenfeld /*
3*0ed42a6fSJason A. Donenfeld * Copyright (C) 2016-2017 INRIA and Microsoft Corporation.
4*0ed42a6fSJason A. Donenfeld * Copyright (C) 2018-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5*0ed42a6fSJason A. Donenfeld *
6*0ed42a6fSJason A. Donenfeld * This is a machine-generated formally verified implementation of Curve25519
7*0ed42a6fSJason A. Donenfeld * ECDH from: <https://github.com/mitls/hacl-star>. Though originally machine
8*0ed42a6fSJason A. Donenfeld * generated, it has been tweaked to be suitable for use in the kernel. It is
9*0ed42a6fSJason A. Donenfeld * optimized for 64-bit machines that can efficiently work with 128-bit
10*0ed42a6fSJason A. Donenfeld * integer types.
11*0ed42a6fSJason A. Donenfeld */
12*0ed42a6fSJason A. Donenfeld
13*0ed42a6fSJason A. Donenfeld #include <asm/unaligned.h>
14*0ed42a6fSJason A. Donenfeld #include <crypto/curve25519.h>
15*0ed42a6fSJason A. Donenfeld #include <linux/string.h>
16*0ed42a6fSJason A. Donenfeld
u64_eq_mask(u64 a,u64 b)17*0ed42a6fSJason A. Donenfeld static __always_inline u64 u64_eq_mask(u64 a, u64 b)
18*0ed42a6fSJason A. Donenfeld {
19*0ed42a6fSJason A. Donenfeld u64 x = a ^ b;
20*0ed42a6fSJason A. Donenfeld u64 minus_x = ~x + (u64)1U;
21*0ed42a6fSJason A. Donenfeld u64 x_or_minus_x = x | minus_x;
22*0ed42a6fSJason A. Donenfeld u64 xnx = x_or_minus_x >> (u32)63U;
23*0ed42a6fSJason A. Donenfeld u64 c = xnx - (u64)1U;
24*0ed42a6fSJason A. Donenfeld return c;
25*0ed42a6fSJason A. Donenfeld }
26*0ed42a6fSJason A. Donenfeld
u64_gte_mask(u64 a,u64 b)27*0ed42a6fSJason A. Donenfeld static __always_inline u64 u64_gte_mask(u64 a, u64 b)
28*0ed42a6fSJason A. Donenfeld {
29*0ed42a6fSJason A. Donenfeld u64 x = a;
30*0ed42a6fSJason A. Donenfeld u64 y = b;
31*0ed42a6fSJason A. Donenfeld u64 x_xor_y = x ^ y;
32*0ed42a6fSJason A. Donenfeld u64 x_sub_y = x - y;
33*0ed42a6fSJason A. Donenfeld u64 x_sub_y_xor_y = x_sub_y ^ y;
34*0ed42a6fSJason A. Donenfeld u64 q = x_xor_y | x_sub_y_xor_y;
35*0ed42a6fSJason A. Donenfeld u64 x_xor_q = x ^ q;
36*0ed42a6fSJason A. Donenfeld u64 x_xor_q_ = x_xor_q >> (u32)63U;
37*0ed42a6fSJason A. Donenfeld u64 c = x_xor_q_ - (u64)1U;
38*0ed42a6fSJason A. Donenfeld return c;
39*0ed42a6fSJason A. Donenfeld }
40*0ed42a6fSJason A. Donenfeld
modulo_carry_top(u64 * b)41*0ed42a6fSJason A. Donenfeld static __always_inline void modulo_carry_top(u64 *b)
42*0ed42a6fSJason A. Donenfeld {
43*0ed42a6fSJason A. Donenfeld u64 b4 = b[4];
44*0ed42a6fSJason A. Donenfeld u64 b0 = b[0];
45*0ed42a6fSJason A. Donenfeld u64 b4_ = b4 & 0x7ffffffffffffLLU;
46*0ed42a6fSJason A. Donenfeld u64 b0_ = b0 + 19 * (b4 >> 51);
47*0ed42a6fSJason A. Donenfeld b[4] = b4_;
48*0ed42a6fSJason A. Donenfeld b[0] = b0_;
49*0ed42a6fSJason A. Donenfeld }
50*0ed42a6fSJason A. Donenfeld
fproduct_copy_from_wide_(u64 * output,u128 * input)51*0ed42a6fSJason A. Donenfeld static __always_inline void fproduct_copy_from_wide_(u64 *output, u128 *input)
52*0ed42a6fSJason A. Donenfeld {
53*0ed42a6fSJason A. Donenfeld {
54*0ed42a6fSJason A. Donenfeld u128 xi = input[0];
55*0ed42a6fSJason A. Donenfeld output[0] = ((u64)(xi));
56*0ed42a6fSJason A. Donenfeld }
57*0ed42a6fSJason A. Donenfeld {
58*0ed42a6fSJason A. Donenfeld u128 xi = input[1];
59*0ed42a6fSJason A. Donenfeld output[1] = ((u64)(xi));
60*0ed42a6fSJason A. Donenfeld }
61*0ed42a6fSJason A. Donenfeld {
62*0ed42a6fSJason A. Donenfeld u128 xi = input[2];
63*0ed42a6fSJason A. Donenfeld output[2] = ((u64)(xi));
64*0ed42a6fSJason A. Donenfeld }
65*0ed42a6fSJason A. Donenfeld {
66*0ed42a6fSJason A. Donenfeld u128 xi = input[3];
67*0ed42a6fSJason A. Donenfeld output[3] = ((u64)(xi));
68*0ed42a6fSJason A. Donenfeld }
69*0ed42a6fSJason A. Donenfeld {
70*0ed42a6fSJason A. Donenfeld u128 xi = input[4];
71*0ed42a6fSJason A. Donenfeld output[4] = ((u64)(xi));
72*0ed42a6fSJason A. Donenfeld }
73*0ed42a6fSJason A. Donenfeld }
74*0ed42a6fSJason A. Donenfeld
75*0ed42a6fSJason A. Donenfeld static __always_inline void
fproduct_sum_scalar_multiplication_(u128 * output,u64 * input,u64 s)76*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(u128 *output, u64 *input, u64 s)
77*0ed42a6fSJason A. Donenfeld {
78*0ed42a6fSJason A. Donenfeld output[0] += (u128)input[0] * s;
79*0ed42a6fSJason A. Donenfeld output[1] += (u128)input[1] * s;
80*0ed42a6fSJason A. Donenfeld output[2] += (u128)input[2] * s;
81*0ed42a6fSJason A. Donenfeld output[3] += (u128)input[3] * s;
82*0ed42a6fSJason A. Donenfeld output[4] += (u128)input[4] * s;
83*0ed42a6fSJason A. Donenfeld }
84*0ed42a6fSJason A. Donenfeld
fproduct_carry_wide_(u128 * tmp)85*0ed42a6fSJason A. Donenfeld static __always_inline void fproduct_carry_wide_(u128 *tmp)
86*0ed42a6fSJason A. Donenfeld {
87*0ed42a6fSJason A. Donenfeld {
88*0ed42a6fSJason A. Donenfeld u32 ctr = 0;
89*0ed42a6fSJason A. Donenfeld u128 tctr = tmp[ctr];
90*0ed42a6fSJason A. Donenfeld u128 tctrp1 = tmp[ctr + 1];
91*0ed42a6fSJason A. Donenfeld u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
92*0ed42a6fSJason A. Donenfeld u128 c = ((tctr) >> (51));
93*0ed42a6fSJason A. Donenfeld tmp[ctr] = ((u128)(r0));
94*0ed42a6fSJason A. Donenfeld tmp[ctr + 1] = ((tctrp1) + (c));
95*0ed42a6fSJason A. Donenfeld }
96*0ed42a6fSJason A. Donenfeld {
97*0ed42a6fSJason A. Donenfeld u32 ctr = 1;
98*0ed42a6fSJason A. Donenfeld u128 tctr = tmp[ctr];
99*0ed42a6fSJason A. Donenfeld u128 tctrp1 = tmp[ctr + 1];
100*0ed42a6fSJason A. Donenfeld u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
101*0ed42a6fSJason A. Donenfeld u128 c = ((tctr) >> (51));
102*0ed42a6fSJason A. Donenfeld tmp[ctr] = ((u128)(r0));
103*0ed42a6fSJason A. Donenfeld tmp[ctr + 1] = ((tctrp1) + (c));
104*0ed42a6fSJason A. Donenfeld }
105*0ed42a6fSJason A. Donenfeld
106*0ed42a6fSJason A. Donenfeld {
107*0ed42a6fSJason A. Donenfeld u32 ctr = 2;
108*0ed42a6fSJason A. Donenfeld u128 tctr = tmp[ctr];
109*0ed42a6fSJason A. Donenfeld u128 tctrp1 = tmp[ctr + 1];
110*0ed42a6fSJason A. Donenfeld u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
111*0ed42a6fSJason A. Donenfeld u128 c = ((tctr) >> (51));
112*0ed42a6fSJason A. Donenfeld tmp[ctr] = ((u128)(r0));
113*0ed42a6fSJason A. Donenfeld tmp[ctr + 1] = ((tctrp1) + (c));
114*0ed42a6fSJason A. Donenfeld }
115*0ed42a6fSJason A. Donenfeld {
116*0ed42a6fSJason A. Donenfeld u32 ctr = 3;
117*0ed42a6fSJason A. Donenfeld u128 tctr = tmp[ctr];
118*0ed42a6fSJason A. Donenfeld u128 tctrp1 = tmp[ctr + 1];
119*0ed42a6fSJason A. Donenfeld u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
120*0ed42a6fSJason A. Donenfeld u128 c = ((tctr) >> (51));
121*0ed42a6fSJason A. Donenfeld tmp[ctr] = ((u128)(r0));
122*0ed42a6fSJason A. Donenfeld tmp[ctr + 1] = ((tctrp1) + (c));
123*0ed42a6fSJason A. Donenfeld }
124*0ed42a6fSJason A. Donenfeld }
125*0ed42a6fSJason A. Donenfeld
fmul_shift_reduce(u64 * output)126*0ed42a6fSJason A. Donenfeld static __always_inline void fmul_shift_reduce(u64 *output)
127*0ed42a6fSJason A. Donenfeld {
128*0ed42a6fSJason A. Donenfeld u64 tmp = output[4];
129*0ed42a6fSJason A. Donenfeld u64 b0;
130*0ed42a6fSJason A. Donenfeld {
131*0ed42a6fSJason A. Donenfeld u32 ctr = 5 - 0 - 1;
132*0ed42a6fSJason A. Donenfeld u64 z = output[ctr - 1];
133*0ed42a6fSJason A. Donenfeld output[ctr] = z;
134*0ed42a6fSJason A. Donenfeld }
135*0ed42a6fSJason A. Donenfeld {
136*0ed42a6fSJason A. Donenfeld u32 ctr = 5 - 1 - 1;
137*0ed42a6fSJason A. Donenfeld u64 z = output[ctr - 1];
138*0ed42a6fSJason A. Donenfeld output[ctr] = z;
139*0ed42a6fSJason A. Donenfeld }
140*0ed42a6fSJason A. Donenfeld {
141*0ed42a6fSJason A. Donenfeld u32 ctr = 5 - 2 - 1;
142*0ed42a6fSJason A. Donenfeld u64 z = output[ctr - 1];
143*0ed42a6fSJason A. Donenfeld output[ctr] = z;
144*0ed42a6fSJason A. Donenfeld }
145*0ed42a6fSJason A. Donenfeld {
146*0ed42a6fSJason A. Donenfeld u32 ctr = 5 - 3 - 1;
147*0ed42a6fSJason A. Donenfeld u64 z = output[ctr - 1];
148*0ed42a6fSJason A. Donenfeld output[ctr] = z;
149*0ed42a6fSJason A. Donenfeld }
150*0ed42a6fSJason A. Donenfeld output[0] = tmp;
151*0ed42a6fSJason A. Donenfeld b0 = output[0];
152*0ed42a6fSJason A. Donenfeld output[0] = 19 * b0;
153*0ed42a6fSJason A. Donenfeld }
154*0ed42a6fSJason A. Donenfeld
fmul_mul_shift_reduce_(u128 * output,u64 * input,u64 * input21)155*0ed42a6fSJason A. Donenfeld static __always_inline void fmul_mul_shift_reduce_(u128 *output, u64 *input,
156*0ed42a6fSJason A. Donenfeld u64 *input21)
157*0ed42a6fSJason A. Donenfeld {
158*0ed42a6fSJason A. Donenfeld u32 i;
159*0ed42a6fSJason A. Donenfeld u64 input2i;
160*0ed42a6fSJason A. Donenfeld {
161*0ed42a6fSJason A. Donenfeld u64 input2i = input21[0];
162*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(output, input, input2i);
163*0ed42a6fSJason A. Donenfeld fmul_shift_reduce(input);
164*0ed42a6fSJason A. Donenfeld }
165*0ed42a6fSJason A. Donenfeld {
166*0ed42a6fSJason A. Donenfeld u64 input2i = input21[1];
167*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(output, input, input2i);
168*0ed42a6fSJason A. Donenfeld fmul_shift_reduce(input);
169*0ed42a6fSJason A. Donenfeld }
170*0ed42a6fSJason A. Donenfeld {
171*0ed42a6fSJason A. Donenfeld u64 input2i = input21[2];
172*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(output, input, input2i);
173*0ed42a6fSJason A. Donenfeld fmul_shift_reduce(input);
174*0ed42a6fSJason A. Donenfeld }
175*0ed42a6fSJason A. Donenfeld {
176*0ed42a6fSJason A. Donenfeld u64 input2i = input21[3];
177*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(output, input, input2i);
178*0ed42a6fSJason A. Donenfeld fmul_shift_reduce(input);
179*0ed42a6fSJason A. Donenfeld }
180*0ed42a6fSJason A. Donenfeld i = 4;
181*0ed42a6fSJason A. Donenfeld input2i = input21[i];
182*0ed42a6fSJason A. Donenfeld fproduct_sum_scalar_multiplication_(output, input, input2i);
183*0ed42a6fSJason A. Donenfeld }
184*0ed42a6fSJason A. Donenfeld
fmul_fmul(u64 * output,u64 * input,u64 * input21)185*0ed42a6fSJason A. Donenfeld static __always_inline void fmul_fmul(u64 *output, u64 *input, u64 *input21)
186*0ed42a6fSJason A. Donenfeld {
187*0ed42a6fSJason A. Donenfeld u64 tmp[5] = { input[0], input[1], input[2], input[3], input[4] };
188*0ed42a6fSJason A. Donenfeld {
189*0ed42a6fSJason A. Donenfeld u128 b4;
190*0ed42a6fSJason A. Donenfeld u128 b0;
191*0ed42a6fSJason A. Donenfeld u128 b4_;
192*0ed42a6fSJason A. Donenfeld u128 b0_;
193*0ed42a6fSJason A. Donenfeld u64 i0;
194*0ed42a6fSJason A. Donenfeld u64 i1;
195*0ed42a6fSJason A. Donenfeld u64 i0_;
196*0ed42a6fSJason A. Donenfeld u64 i1_;
197*0ed42a6fSJason A. Donenfeld u128 t[5] = { 0 };
198*0ed42a6fSJason A. Donenfeld fmul_mul_shift_reduce_(t, tmp, input21);
199*0ed42a6fSJason A. Donenfeld fproduct_carry_wide_(t);
200*0ed42a6fSJason A. Donenfeld b4 = t[4];
201*0ed42a6fSJason A. Donenfeld b0 = t[0];
202*0ed42a6fSJason A. Donenfeld b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
203*0ed42a6fSJason A. Donenfeld b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
204*0ed42a6fSJason A. Donenfeld t[4] = b4_;
205*0ed42a6fSJason A. Donenfeld t[0] = b0_;
206*0ed42a6fSJason A. Donenfeld fproduct_copy_from_wide_(output, t);
207*0ed42a6fSJason A. Donenfeld i0 = output[0];
208*0ed42a6fSJason A. Donenfeld i1 = output[1];
209*0ed42a6fSJason A. Donenfeld i0_ = i0 & 0x7ffffffffffffLLU;
210*0ed42a6fSJason A. Donenfeld i1_ = i1 + (i0 >> 51);
211*0ed42a6fSJason A. Donenfeld output[0] = i0_;
212*0ed42a6fSJason A. Donenfeld output[1] = i1_;
213*0ed42a6fSJason A. Donenfeld }
214*0ed42a6fSJason A. Donenfeld }
215*0ed42a6fSJason A. Donenfeld
fsquare_fsquare__(u128 * tmp,u64 * output)216*0ed42a6fSJason A. Donenfeld static __always_inline void fsquare_fsquare__(u128 *tmp, u64 *output)
217*0ed42a6fSJason A. Donenfeld {
218*0ed42a6fSJason A. Donenfeld u64 r0 = output[0];
219*0ed42a6fSJason A. Donenfeld u64 r1 = output[1];
220*0ed42a6fSJason A. Donenfeld u64 r2 = output[2];
221*0ed42a6fSJason A. Donenfeld u64 r3 = output[3];
222*0ed42a6fSJason A. Donenfeld u64 r4 = output[4];
223*0ed42a6fSJason A. Donenfeld u64 d0 = r0 * 2;
224*0ed42a6fSJason A. Donenfeld u64 d1 = r1 * 2;
225*0ed42a6fSJason A. Donenfeld u64 d2 = r2 * 2 * 19;
226*0ed42a6fSJason A. Donenfeld u64 d419 = r4 * 19;
227*0ed42a6fSJason A. Donenfeld u64 d4 = d419 * 2;
228*0ed42a6fSJason A. Donenfeld u128 s0 = ((((((u128)(r0) * (r0))) + (((u128)(d4) * (r1))))) +
229*0ed42a6fSJason A. Donenfeld (((u128)(d2) * (r3))));
230*0ed42a6fSJason A. Donenfeld u128 s1 = ((((((u128)(d0) * (r1))) + (((u128)(d4) * (r2))))) +
231*0ed42a6fSJason A. Donenfeld (((u128)(r3 * 19) * (r3))));
232*0ed42a6fSJason A. Donenfeld u128 s2 = ((((((u128)(d0) * (r2))) + (((u128)(r1) * (r1))))) +
233*0ed42a6fSJason A. Donenfeld (((u128)(d4) * (r3))));
234*0ed42a6fSJason A. Donenfeld u128 s3 = ((((((u128)(d0) * (r3))) + (((u128)(d1) * (r2))))) +
235*0ed42a6fSJason A. Donenfeld (((u128)(r4) * (d419))));
236*0ed42a6fSJason A. Donenfeld u128 s4 = ((((((u128)(d0) * (r4))) + (((u128)(d1) * (r3))))) +
237*0ed42a6fSJason A. Donenfeld (((u128)(r2) * (r2))));
238*0ed42a6fSJason A. Donenfeld tmp[0] = s0;
239*0ed42a6fSJason A. Donenfeld tmp[1] = s1;
240*0ed42a6fSJason A. Donenfeld tmp[2] = s2;
241*0ed42a6fSJason A. Donenfeld tmp[3] = s3;
242*0ed42a6fSJason A. Donenfeld tmp[4] = s4;
243*0ed42a6fSJason A. Donenfeld }
244*0ed42a6fSJason A. Donenfeld
fsquare_fsquare_(u128 * tmp,u64 * output)245*0ed42a6fSJason A. Donenfeld static __always_inline void fsquare_fsquare_(u128 *tmp, u64 *output)
246*0ed42a6fSJason A. Donenfeld {
247*0ed42a6fSJason A. Donenfeld u128 b4;
248*0ed42a6fSJason A. Donenfeld u128 b0;
249*0ed42a6fSJason A. Donenfeld u128 b4_;
250*0ed42a6fSJason A. Donenfeld u128 b0_;
251*0ed42a6fSJason A. Donenfeld u64 i0;
252*0ed42a6fSJason A. Donenfeld u64 i1;
253*0ed42a6fSJason A. Donenfeld u64 i0_;
254*0ed42a6fSJason A. Donenfeld u64 i1_;
255*0ed42a6fSJason A. Donenfeld fsquare_fsquare__(tmp, output);
256*0ed42a6fSJason A. Donenfeld fproduct_carry_wide_(tmp);
257*0ed42a6fSJason A. Donenfeld b4 = tmp[4];
258*0ed42a6fSJason A. Donenfeld b0 = tmp[0];
259*0ed42a6fSJason A. Donenfeld b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
260*0ed42a6fSJason A. Donenfeld b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
261*0ed42a6fSJason A. Donenfeld tmp[4] = b4_;
262*0ed42a6fSJason A. Donenfeld tmp[0] = b0_;
263*0ed42a6fSJason A. Donenfeld fproduct_copy_from_wide_(output, tmp);
264*0ed42a6fSJason A. Donenfeld i0 = output[0];
265*0ed42a6fSJason A. Donenfeld i1 = output[1];
266*0ed42a6fSJason A. Donenfeld i0_ = i0 & 0x7ffffffffffffLLU;
267*0ed42a6fSJason A. Donenfeld i1_ = i1 + (i0 >> 51);
268*0ed42a6fSJason A. Donenfeld output[0] = i0_;
269*0ed42a6fSJason A. Donenfeld output[1] = i1_;
270*0ed42a6fSJason A. Donenfeld }
271*0ed42a6fSJason A. Donenfeld
fsquare_fsquare_times_(u64 * output,u128 * tmp,u32 count1)272*0ed42a6fSJason A. Donenfeld static __always_inline void fsquare_fsquare_times_(u64 *output, u128 *tmp,
273*0ed42a6fSJason A. Donenfeld u32 count1)
274*0ed42a6fSJason A. Donenfeld {
275*0ed42a6fSJason A. Donenfeld u32 i;
276*0ed42a6fSJason A. Donenfeld fsquare_fsquare_(tmp, output);
277*0ed42a6fSJason A. Donenfeld for (i = 1; i < count1; ++i)
278*0ed42a6fSJason A. Donenfeld fsquare_fsquare_(tmp, output);
279*0ed42a6fSJason A. Donenfeld }
280*0ed42a6fSJason A. Donenfeld
fsquare_fsquare_times(u64 * output,u64 * input,u32 count1)281*0ed42a6fSJason A. Donenfeld static __always_inline void fsquare_fsquare_times(u64 *output, u64 *input,
282*0ed42a6fSJason A. Donenfeld u32 count1)
283*0ed42a6fSJason A. Donenfeld {
284*0ed42a6fSJason A. Donenfeld u128 t[5];
285*0ed42a6fSJason A. Donenfeld memcpy(output, input, 5 * sizeof(*input));
286*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times_(output, t, count1);
287*0ed42a6fSJason A. Donenfeld }
288*0ed42a6fSJason A. Donenfeld
fsquare_fsquare_times_inplace(u64 * output,u32 count1)289*0ed42a6fSJason A. Donenfeld static __always_inline void fsquare_fsquare_times_inplace(u64 *output,
290*0ed42a6fSJason A. Donenfeld u32 count1)
291*0ed42a6fSJason A. Donenfeld {
292*0ed42a6fSJason A. Donenfeld u128 t[5];
293*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times_(output, t, count1);
294*0ed42a6fSJason A. Donenfeld }
295*0ed42a6fSJason A. Donenfeld
crecip_crecip(u64 * out,u64 * z)296*0ed42a6fSJason A. Donenfeld static __always_inline void crecip_crecip(u64 *out, u64 *z)
297*0ed42a6fSJason A. Donenfeld {
298*0ed42a6fSJason A. Donenfeld u64 buf[20] = { 0 };
299*0ed42a6fSJason A. Donenfeld u64 *a0 = buf;
300*0ed42a6fSJason A. Donenfeld u64 *t00 = buf + 5;
301*0ed42a6fSJason A. Donenfeld u64 *b0 = buf + 10;
302*0ed42a6fSJason A. Donenfeld u64 *t01;
303*0ed42a6fSJason A. Donenfeld u64 *b1;
304*0ed42a6fSJason A. Donenfeld u64 *c0;
305*0ed42a6fSJason A. Donenfeld u64 *a;
306*0ed42a6fSJason A. Donenfeld u64 *t0;
307*0ed42a6fSJason A. Donenfeld u64 *b;
308*0ed42a6fSJason A. Donenfeld u64 *c;
309*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(a0, z, 1);
310*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t00, a0, 2);
311*0ed42a6fSJason A. Donenfeld fmul_fmul(b0, t00, z);
312*0ed42a6fSJason A. Donenfeld fmul_fmul(a0, b0, a0);
313*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t00, a0, 1);
314*0ed42a6fSJason A. Donenfeld fmul_fmul(b0, t00, b0);
315*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t00, b0, 5);
316*0ed42a6fSJason A. Donenfeld t01 = buf + 5;
317*0ed42a6fSJason A. Donenfeld b1 = buf + 10;
318*0ed42a6fSJason A. Donenfeld c0 = buf + 15;
319*0ed42a6fSJason A. Donenfeld fmul_fmul(b1, t01, b1);
320*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t01, b1, 10);
321*0ed42a6fSJason A. Donenfeld fmul_fmul(c0, t01, b1);
322*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t01, c0, 20);
323*0ed42a6fSJason A. Donenfeld fmul_fmul(t01, t01, c0);
324*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times_inplace(t01, 10);
325*0ed42a6fSJason A. Donenfeld fmul_fmul(b1, t01, b1);
326*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t01, b1, 50);
327*0ed42a6fSJason A. Donenfeld a = buf;
328*0ed42a6fSJason A. Donenfeld t0 = buf + 5;
329*0ed42a6fSJason A. Donenfeld b = buf + 10;
330*0ed42a6fSJason A. Donenfeld c = buf + 15;
331*0ed42a6fSJason A. Donenfeld fmul_fmul(c, t0, b);
332*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(t0, c, 100);
333*0ed42a6fSJason A. Donenfeld fmul_fmul(t0, t0, c);
334*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times_inplace(t0, 50);
335*0ed42a6fSJason A. Donenfeld fmul_fmul(t0, t0, b);
336*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times_inplace(t0, 5);
337*0ed42a6fSJason A. Donenfeld fmul_fmul(out, t0, a);
338*0ed42a6fSJason A. Donenfeld }
339*0ed42a6fSJason A. Donenfeld
fsum(u64 * a,u64 * b)340*0ed42a6fSJason A. Donenfeld static __always_inline void fsum(u64 *a, u64 *b)
341*0ed42a6fSJason A. Donenfeld {
342*0ed42a6fSJason A. Donenfeld a[0] += b[0];
343*0ed42a6fSJason A. Donenfeld a[1] += b[1];
344*0ed42a6fSJason A. Donenfeld a[2] += b[2];
345*0ed42a6fSJason A. Donenfeld a[3] += b[3];
346*0ed42a6fSJason A. Donenfeld a[4] += b[4];
347*0ed42a6fSJason A. Donenfeld }
348*0ed42a6fSJason A. Donenfeld
fdifference(u64 * a,u64 * b)349*0ed42a6fSJason A. Donenfeld static __always_inline void fdifference(u64 *a, u64 *b)
350*0ed42a6fSJason A. Donenfeld {
351*0ed42a6fSJason A. Donenfeld u64 tmp[5] = { 0 };
352*0ed42a6fSJason A. Donenfeld u64 b0;
353*0ed42a6fSJason A. Donenfeld u64 b1;
354*0ed42a6fSJason A. Donenfeld u64 b2;
355*0ed42a6fSJason A. Donenfeld u64 b3;
356*0ed42a6fSJason A. Donenfeld u64 b4;
357*0ed42a6fSJason A. Donenfeld memcpy(tmp, b, 5 * sizeof(*b));
358*0ed42a6fSJason A. Donenfeld b0 = tmp[0];
359*0ed42a6fSJason A. Donenfeld b1 = tmp[1];
360*0ed42a6fSJason A. Donenfeld b2 = tmp[2];
361*0ed42a6fSJason A. Donenfeld b3 = tmp[3];
362*0ed42a6fSJason A. Donenfeld b4 = tmp[4];
363*0ed42a6fSJason A. Donenfeld tmp[0] = b0 + 0x3fffffffffff68LLU;
364*0ed42a6fSJason A. Donenfeld tmp[1] = b1 + 0x3ffffffffffff8LLU;
365*0ed42a6fSJason A. Donenfeld tmp[2] = b2 + 0x3ffffffffffff8LLU;
366*0ed42a6fSJason A. Donenfeld tmp[3] = b3 + 0x3ffffffffffff8LLU;
367*0ed42a6fSJason A. Donenfeld tmp[4] = b4 + 0x3ffffffffffff8LLU;
368*0ed42a6fSJason A. Donenfeld {
369*0ed42a6fSJason A. Donenfeld u64 xi = a[0];
370*0ed42a6fSJason A. Donenfeld u64 yi = tmp[0];
371*0ed42a6fSJason A. Donenfeld a[0] = yi - xi;
372*0ed42a6fSJason A. Donenfeld }
373*0ed42a6fSJason A. Donenfeld {
374*0ed42a6fSJason A. Donenfeld u64 xi = a[1];
375*0ed42a6fSJason A. Donenfeld u64 yi = tmp[1];
376*0ed42a6fSJason A. Donenfeld a[1] = yi - xi;
377*0ed42a6fSJason A. Donenfeld }
378*0ed42a6fSJason A. Donenfeld {
379*0ed42a6fSJason A. Donenfeld u64 xi = a[2];
380*0ed42a6fSJason A. Donenfeld u64 yi = tmp[2];
381*0ed42a6fSJason A. Donenfeld a[2] = yi - xi;
382*0ed42a6fSJason A. Donenfeld }
383*0ed42a6fSJason A. Donenfeld {
384*0ed42a6fSJason A. Donenfeld u64 xi = a[3];
385*0ed42a6fSJason A. Donenfeld u64 yi = tmp[3];
386*0ed42a6fSJason A. Donenfeld a[3] = yi - xi;
387*0ed42a6fSJason A. Donenfeld }
388*0ed42a6fSJason A. Donenfeld {
389*0ed42a6fSJason A. Donenfeld u64 xi = a[4];
390*0ed42a6fSJason A. Donenfeld u64 yi = tmp[4];
391*0ed42a6fSJason A. Donenfeld a[4] = yi - xi;
392*0ed42a6fSJason A. Donenfeld }
393*0ed42a6fSJason A. Donenfeld }
394*0ed42a6fSJason A. Donenfeld
fscalar(u64 * output,u64 * b,u64 s)395*0ed42a6fSJason A. Donenfeld static __always_inline void fscalar(u64 *output, u64 *b, u64 s)
396*0ed42a6fSJason A. Donenfeld {
397*0ed42a6fSJason A. Donenfeld u128 tmp[5];
398*0ed42a6fSJason A. Donenfeld u128 b4;
399*0ed42a6fSJason A. Donenfeld u128 b0;
400*0ed42a6fSJason A. Donenfeld u128 b4_;
401*0ed42a6fSJason A. Donenfeld u128 b0_;
402*0ed42a6fSJason A. Donenfeld {
403*0ed42a6fSJason A. Donenfeld u64 xi = b[0];
404*0ed42a6fSJason A. Donenfeld tmp[0] = ((u128)(xi) * (s));
405*0ed42a6fSJason A. Donenfeld }
406*0ed42a6fSJason A. Donenfeld {
407*0ed42a6fSJason A. Donenfeld u64 xi = b[1];
408*0ed42a6fSJason A. Donenfeld tmp[1] = ((u128)(xi) * (s));
409*0ed42a6fSJason A. Donenfeld }
410*0ed42a6fSJason A. Donenfeld {
411*0ed42a6fSJason A. Donenfeld u64 xi = b[2];
412*0ed42a6fSJason A. Donenfeld tmp[2] = ((u128)(xi) * (s));
413*0ed42a6fSJason A. Donenfeld }
414*0ed42a6fSJason A. Donenfeld {
415*0ed42a6fSJason A. Donenfeld u64 xi = b[3];
416*0ed42a6fSJason A. Donenfeld tmp[3] = ((u128)(xi) * (s));
417*0ed42a6fSJason A. Donenfeld }
418*0ed42a6fSJason A. Donenfeld {
419*0ed42a6fSJason A. Donenfeld u64 xi = b[4];
420*0ed42a6fSJason A. Donenfeld tmp[4] = ((u128)(xi) * (s));
421*0ed42a6fSJason A. Donenfeld }
422*0ed42a6fSJason A. Donenfeld fproduct_carry_wide_(tmp);
423*0ed42a6fSJason A. Donenfeld b4 = tmp[4];
424*0ed42a6fSJason A. Donenfeld b0 = tmp[0];
425*0ed42a6fSJason A. Donenfeld b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
426*0ed42a6fSJason A. Donenfeld b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
427*0ed42a6fSJason A. Donenfeld tmp[4] = b4_;
428*0ed42a6fSJason A. Donenfeld tmp[0] = b0_;
429*0ed42a6fSJason A. Donenfeld fproduct_copy_from_wide_(output, tmp);
430*0ed42a6fSJason A. Donenfeld }
431*0ed42a6fSJason A. Donenfeld
fmul(u64 * output,u64 * a,u64 * b)432*0ed42a6fSJason A. Donenfeld static __always_inline void fmul(u64 *output, u64 *a, u64 *b)
433*0ed42a6fSJason A. Donenfeld {
434*0ed42a6fSJason A. Donenfeld fmul_fmul(output, a, b);
435*0ed42a6fSJason A. Donenfeld }
436*0ed42a6fSJason A. Donenfeld
crecip(u64 * output,u64 * input)437*0ed42a6fSJason A. Donenfeld static __always_inline void crecip(u64 *output, u64 *input)
438*0ed42a6fSJason A. Donenfeld {
439*0ed42a6fSJason A. Donenfeld crecip_crecip(output, input);
440*0ed42a6fSJason A. Donenfeld }
441*0ed42a6fSJason A. Donenfeld
point_swap_conditional_step(u64 * a,u64 * b,u64 swap1,u32 ctr)442*0ed42a6fSJason A. Donenfeld static __always_inline void point_swap_conditional_step(u64 *a, u64 *b,
443*0ed42a6fSJason A. Donenfeld u64 swap1, u32 ctr)
444*0ed42a6fSJason A. Donenfeld {
445*0ed42a6fSJason A. Donenfeld u32 i = ctr - 1;
446*0ed42a6fSJason A. Donenfeld u64 ai = a[i];
447*0ed42a6fSJason A. Donenfeld u64 bi = b[i];
448*0ed42a6fSJason A. Donenfeld u64 x = swap1 & (ai ^ bi);
449*0ed42a6fSJason A. Donenfeld u64 ai1 = ai ^ x;
450*0ed42a6fSJason A. Donenfeld u64 bi1 = bi ^ x;
451*0ed42a6fSJason A. Donenfeld a[i] = ai1;
452*0ed42a6fSJason A. Donenfeld b[i] = bi1;
453*0ed42a6fSJason A. Donenfeld }
454*0ed42a6fSJason A. Donenfeld
point_swap_conditional5(u64 * a,u64 * b,u64 swap1)455*0ed42a6fSJason A. Donenfeld static __always_inline void point_swap_conditional5(u64 *a, u64 *b, u64 swap1)
456*0ed42a6fSJason A. Donenfeld {
457*0ed42a6fSJason A. Donenfeld point_swap_conditional_step(a, b, swap1, 5);
458*0ed42a6fSJason A. Donenfeld point_swap_conditional_step(a, b, swap1, 4);
459*0ed42a6fSJason A. Donenfeld point_swap_conditional_step(a, b, swap1, 3);
460*0ed42a6fSJason A. Donenfeld point_swap_conditional_step(a, b, swap1, 2);
461*0ed42a6fSJason A. Donenfeld point_swap_conditional_step(a, b, swap1, 1);
462*0ed42a6fSJason A. Donenfeld }
463*0ed42a6fSJason A. Donenfeld
point_swap_conditional(u64 * a,u64 * b,u64 iswap)464*0ed42a6fSJason A. Donenfeld static __always_inline void point_swap_conditional(u64 *a, u64 *b, u64 iswap)
465*0ed42a6fSJason A. Donenfeld {
466*0ed42a6fSJason A. Donenfeld u64 swap1 = 0 - iswap;
467*0ed42a6fSJason A. Donenfeld point_swap_conditional5(a, b, swap1);
468*0ed42a6fSJason A. Donenfeld point_swap_conditional5(a + 5, b + 5, swap1);
469*0ed42a6fSJason A. Donenfeld }
470*0ed42a6fSJason A. Donenfeld
point_copy(u64 * output,u64 * input)471*0ed42a6fSJason A. Donenfeld static __always_inline void point_copy(u64 *output, u64 *input)
472*0ed42a6fSJason A. Donenfeld {
473*0ed42a6fSJason A. Donenfeld memcpy(output, input, 5 * sizeof(*input));
474*0ed42a6fSJason A. Donenfeld memcpy(output + 5, input + 5, 5 * sizeof(*input));
475*0ed42a6fSJason A. Donenfeld }
476*0ed42a6fSJason A. Donenfeld
addanddouble_fmonty(u64 * pp,u64 * ppq,u64 * p,u64 * pq,u64 * qmqp)477*0ed42a6fSJason A. Donenfeld static __always_inline void addanddouble_fmonty(u64 *pp, u64 *ppq, u64 *p,
478*0ed42a6fSJason A. Donenfeld u64 *pq, u64 *qmqp)
479*0ed42a6fSJason A. Donenfeld {
480*0ed42a6fSJason A. Donenfeld u64 *qx = qmqp;
481*0ed42a6fSJason A. Donenfeld u64 *x2 = pp;
482*0ed42a6fSJason A. Donenfeld u64 *z2 = pp + 5;
483*0ed42a6fSJason A. Donenfeld u64 *x3 = ppq;
484*0ed42a6fSJason A. Donenfeld u64 *z3 = ppq + 5;
485*0ed42a6fSJason A. Donenfeld u64 *x = p;
486*0ed42a6fSJason A. Donenfeld u64 *z = p + 5;
487*0ed42a6fSJason A. Donenfeld u64 *xprime = pq;
488*0ed42a6fSJason A. Donenfeld u64 *zprime = pq + 5;
489*0ed42a6fSJason A. Donenfeld u64 buf[40] = { 0 };
490*0ed42a6fSJason A. Donenfeld u64 *origx = buf;
491*0ed42a6fSJason A. Donenfeld u64 *origxprime0 = buf + 5;
492*0ed42a6fSJason A. Donenfeld u64 *xxprime0;
493*0ed42a6fSJason A. Donenfeld u64 *zzprime0;
494*0ed42a6fSJason A. Donenfeld u64 *origxprime;
495*0ed42a6fSJason A. Donenfeld xxprime0 = buf + 25;
496*0ed42a6fSJason A. Donenfeld zzprime0 = buf + 30;
497*0ed42a6fSJason A. Donenfeld memcpy(origx, x, 5 * sizeof(*x));
498*0ed42a6fSJason A. Donenfeld fsum(x, z);
499*0ed42a6fSJason A. Donenfeld fdifference(z, origx);
500*0ed42a6fSJason A. Donenfeld memcpy(origxprime0, xprime, 5 * sizeof(*xprime));
501*0ed42a6fSJason A. Donenfeld fsum(xprime, zprime);
502*0ed42a6fSJason A. Donenfeld fdifference(zprime, origxprime0);
503*0ed42a6fSJason A. Donenfeld fmul(xxprime0, xprime, z);
504*0ed42a6fSJason A. Donenfeld fmul(zzprime0, x, zprime);
505*0ed42a6fSJason A. Donenfeld origxprime = buf + 5;
506*0ed42a6fSJason A. Donenfeld {
507*0ed42a6fSJason A. Donenfeld u64 *xx0;
508*0ed42a6fSJason A. Donenfeld u64 *zz0;
509*0ed42a6fSJason A. Donenfeld u64 *xxprime;
510*0ed42a6fSJason A. Donenfeld u64 *zzprime;
511*0ed42a6fSJason A. Donenfeld u64 *zzzprime;
512*0ed42a6fSJason A. Donenfeld xx0 = buf + 15;
513*0ed42a6fSJason A. Donenfeld zz0 = buf + 20;
514*0ed42a6fSJason A. Donenfeld xxprime = buf + 25;
515*0ed42a6fSJason A. Donenfeld zzprime = buf + 30;
516*0ed42a6fSJason A. Donenfeld zzzprime = buf + 35;
517*0ed42a6fSJason A. Donenfeld memcpy(origxprime, xxprime, 5 * sizeof(*xxprime));
518*0ed42a6fSJason A. Donenfeld fsum(xxprime, zzprime);
519*0ed42a6fSJason A. Donenfeld fdifference(zzprime, origxprime);
520*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(x3, xxprime, 1);
521*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(zzzprime, zzprime, 1);
522*0ed42a6fSJason A. Donenfeld fmul(z3, zzzprime, qx);
523*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(xx0, x, 1);
524*0ed42a6fSJason A. Donenfeld fsquare_fsquare_times(zz0, z, 1);
525*0ed42a6fSJason A. Donenfeld {
526*0ed42a6fSJason A. Donenfeld u64 *zzz;
527*0ed42a6fSJason A. Donenfeld u64 *xx;
528*0ed42a6fSJason A. Donenfeld u64 *zz;
529*0ed42a6fSJason A. Donenfeld u64 scalar;
530*0ed42a6fSJason A. Donenfeld zzz = buf + 10;
531*0ed42a6fSJason A. Donenfeld xx = buf + 15;
532*0ed42a6fSJason A. Donenfeld zz = buf + 20;
533*0ed42a6fSJason A. Donenfeld fmul(x2, xx, zz);
534*0ed42a6fSJason A. Donenfeld fdifference(zz, xx);
535*0ed42a6fSJason A. Donenfeld scalar = 121665;
536*0ed42a6fSJason A. Donenfeld fscalar(zzz, zz, scalar);
537*0ed42a6fSJason A. Donenfeld fsum(zzz, xx);
538*0ed42a6fSJason A. Donenfeld fmul(z2, zzz, zz);
539*0ed42a6fSJason A. Donenfeld }
540*0ed42a6fSJason A. Donenfeld }
541*0ed42a6fSJason A. Donenfeld }
542*0ed42a6fSJason A. Donenfeld
543*0ed42a6fSJason A. Donenfeld static __always_inline void
ladder_smallloop_cmult_small_loop_step(u64 * nq,u64 * nqpq,u64 * nq2,u64 * nqpq2,u64 * q,u8 byt)544*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop_step(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
545*0ed42a6fSJason A. Donenfeld u64 *q, u8 byt)
546*0ed42a6fSJason A. Donenfeld {
547*0ed42a6fSJason A. Donenfeld u64 bit0 = (u64)(byt >> 7);
548*0ed42a6fSJason A. Donenfeld u64 bit;
549*0ed42a6fSJason A. Donenfeld point_swap_conditional(nq, nqpq, bit0);
550*0ed42a6fSJason A. Donenfeld addanddouble_fmonty(nq2, nqpq2, nq, nqpq, q);
551*0ed42a6fSJason A. Donenfeld bit = (u64)(byt >> 7);
552*0ed42a6fSJason A. Donenfeld point_swap_conditional(nq2, nqpq2, bit);
553*0ed42a6fSJason A. Donenfeld }
554*0ed42a6fSJason A. Donenfeld
555*0ed42a6fSJason A. Donenfeld static __always_inline void
ladder_smallloop_cmult_small_loop_double_step(u64 * nq,u64 * nqpq,u64 * nq2,u64 * nqpq2,u64 * q,u8 byt)556*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop_double_step(u64 *nq, u64 *nqpq, u64 *nq2,
557*0ed42a6fSJason A. Donenfeld u64 *nqpq2, u64 *q, u8 byt)
558*0ed42a6fSJason A. Donenfeld {
559*0ed42a6fSJason A. Donenfeld u8 byt1;
560*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt);
561*0ed42a6fSJason A. Donenfeld byt1 = byt << 1;
562*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1);
563*0ed42a6fSJason A. Donenfeld }
564*0ed42a6fSJason A. Donenfeld
565*0ed42a6fSJason A. Donenfeld static __always_inline void
ladder_smallloop_cmult_small_loop(u64 * nq,u64 * nqpq,u64 * nq2,u64 * nqpq2,u64 * q,u8 byt,u32 i)566*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
567*0ed42a6fSJason A. Donenfeld u64 *q, u8 byt, u32 i)
568*0ed42a6fSJason A. Donenfeld {
569*0ed42a6fSJason A. Donenfeld while (i--) {
570*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop_double_step(nq, nqpq, nq2,
571*0ed42a6fSJason A. Donenfeld nqpq2, q, byt);
572*0ed42a6fSJason A. Donenfeld byt <<= 2;
573*0ed42a6fSJason A. Donenfeld }
574*0ed42a6fSJason A. Donenfeld }
575*0ed42a6fSJason A. Donenfeld
ladder_bigloop_cmult_big_loop(u8 * n1,u64 * nq,u64 * nqpq,u64 * nq2,u64 * nqpq2,u64 * q,u32 i)576*0ed42a6fSJason A. Donenfeld static __always_inline void ladder_bigloop_cmult_big_loop(u8 *n1, u64 *nq,
577*0ed42a6fSJason A. Donenfeld u64 *nqpq, u64 *nq2,
578*0ed42a6fSJason A. Donenfeld u64 *nqpq2, u64 *q,
579*0ed42a6fSJason A. Donenfeld u32 i)
580*0ed42a6fSJason A. Donenfeld {
581*0ed42a6fSJason A. Donenfeld while (i--) {
582*0ed42a6fSJason A. Donenfeld u8 byte = n1[i];
583*0ed42a6fSJason A. Donenfeld ladder_smallloop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q,
584*0ed42a6fSJason A. Donenfeld byte, 4);
585*0ed42a6fSJason A. Donenfeld }
586*0ed42a6fSJason A. Donenfeld }
587*0ed42a6fSJason A. Donenfeld
ladder_cmult(u64 * result,u8 * n1,u64 * q)588*0ed42a6fSJason A. Donenfeld static void ladder_cmult(u64 *result, u8 *n1, u64 *q)
589*0ed42a6fSJason A. Donenfeld {
590*0ed42a6fSJason A. Donenfeld u64 point_buf[40] = { 0 };
591*0ed42a6fSJason A. Donenfeld u64 *nq = point_buf;
592*0ed42a6fSJason A. Donenfeld u64 *nqpq = point_buf + 10;
593*0ed42a6fSJason A. Donenfeld u64 *nq2 = point_buf + 20;
594*0ed42a6fSJason A. Donenfeld u64 *nqpq2 = point_buf + 30;
595*0ed42a6fSJason A. Donenfeld point_copy(nqpq, q);
596*0ed42a6fSJason A. Donenfeld nq[0] = 1;
597*0ed42a6fSJason A. Donenfeld ladder_bigloop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, 32);
598*0ed42a6fSJason A. Donenfeld point_copy(result, nq);
599*0ed42a6fSJason A. Donenfeld }
600*0ed42a6fSJason A. Donenfeld
format_fexpand(u64 * output,const u8 * input)601*0ed42a6fSJason A. Donenfeld static __always_inline void format_fexpand(u64 *output, const u8 *input)
602*0ed42a6fSJason A. Donenfeld {
603*0ed42a6fSJason A. Donenfeld const u8 *x00 = input + 6;
604*0ed42a6fSJason A. Donenfeld const u8 *x01 = input + 12;
605*0ed42a6fSJason A. Donenfeld const u8 *x02 = input + 19;
606*0ed42a6fSJason A. Donenfeld const u8 *x0 = input + 24;
607*0ed42a6fSJason A. Donenfeld u64 i0, i1, i2, i3, i4, output0, output1, output2, output3, output4;
608*0ed42a6fSJason A. Donenfeld i0 = get_unaligned_le64(input);
609*0ed42a6fSJason A. Donenfeld i1 = get_unaligned_le64(x00);
610*0ed42a6fSJason A. Donenfeld i2 = get_unaligned_le64(x01);
611*0ed42a6fSJason A. Donenfeld i3 = get_unaligned_le64(x02);
612*0ed42a6fSJason A. Donenfeld i4 = get_unaligned_le64(x0);
613*0ed42a6fSJason A. Donenfeld output0 = i0 & 0x7ffffffffffffLLU;
614*0ed42a6fSJason A. Donenfeld output1 = i1 >> 3 & 0x7ffffffffffffLLU;
615*0ed42a6fSJason A. Donenfeld output2 = i2 >> 6 & 0x7ffffffffffffLLU;
616*0ed42a6fSJason A. Donenfeld output3 = i3 >> 1 & 0x7ffffffffffffLLU;
617*0ed42a6fSJason A. Donenfeld output4 = i4 >> 12 & 0x7ffffffffffffLLU;
618*0ed42a6fSJason A. Donenfeld output[0] = output0;
619*0ed42a6fSJason A. Donenfeld output[1] = output1;
620*0ed42a6fSJason A. Donenfeld output[2] = output2;
621*0ed42a6fSJason A. Donenfeld output[3] = output3;
622*0ed42a6fSJason A. Donenfeld output[4] = output4;
623*0ed42a6fSJason A. Donenfeld }
624*0ed42a6fSJason A. Donenfeld
format_fcontract_first_carry_pass(u64 * input)625*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_first_carry_pass(u64 *input)
626*0ed42a6fSJason A. Donenfeld {
627*0ed42a6fSJason A. Donenfeld u64 t0 = input[0];
628*0ed42a6fSJason A. Donenfeld u64 t1 = input[1];
629*0ed42a6fSJason A. Donenfeld u64 t2 = input[2];
630*0ed42a6fSJason A. Donenfeld u64 t3 = input[3];
631*0ed42a6fSJason A. Donenfeld u64 t4 = input[4];
632*0ed42a6fSJason A. Donenfeld u64 t1_ = t1 + (t0 >> 51);
633*0ed42a6fSJason A. Donenfeld u64 t0_ = t0 & 0x7ffffffffffffLLU;
634*0ed42a6fSJason A. Donenfeld u64 t2_ = t2 + (t1_ >> 51);
635*0ed42a6fSJason A. Donenfeld u64 t1__ = t1_ & 0x7ffffffffffffLLU;
636*0ed42a6fSJason A. Donenfeld u64 t3_ = t3 + (t2_ >> 51);
637*0ed42a6fSJason A. Donenfeld u64 t2__ = t2_ & 0x7ffffffffffffLLU;
638*0ed42a6fSJason A. Donenfeld u64 t4_ = t4 + (t3_ >> 51);
639*0ed42a6fSJason A. Donenfeld u64 t3__ = t3_ & 0x7ffffffffffffLLU;
640*0ed42a6fSJason A. Donenfeld input[0] = t0_;
641*0ed42a6fSJason A. Donenfeld input[1] = t1__;
642*0ed42a6fSJason A. Donenfeld input[2] = t2__;
643*0ed42a6fSJason A. Donenfeld input[3] = t3__;
644*0ed42a6fSJason A. Donenfeld input[4] = t4_;
645*0ed42a6fSJason A. Donenfeld }
646*0ed42a6fSJason A. Donenfeld
format_fcontract_first_carry_full(u64 * input)647*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_first_carry_full(u64 *input)
648*0ed42a6fSJason A. Donenfeld {
649*0ed42a6fSJason A. Donenfeld format_fcontract_first_carry_pass(input);
650*0ed42a6fSJason A. Donenfeld modulo_carry_top(input);
651*0ed42a6fSJason A. Donenfeld }
652*0ed42a6fSJason A. Donenfeld
format_fcontract_second_carry_pass(u64 * input)653*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_second_carry_pass(u64 *input)
654*0ed42a6fSJason A. Donenfeld {
655*0ed42a6fSJason A. Donenfeld u64 t0 = input[0];
656*0ed42a6fSJason A. Donenfeld u64 t1 = input[1];
657*0ed42a6fSJason A. Donenfeld u64 t2 = input[2];
658*0ed42a6fSJason A. Donenfeld u64 t3 = input[3];
659*0ed42a6fSJason A. Donenfeld u64 t4 = input[4];
660*0ed42a6fSJason A. Donenfeld u64 t1_ = t1 + (t0 >> 51);
661*0ed42a6fSJason A. Donenfeld u64 t0_ = t0 & 0x7ffffffffffffLLU;
662*0ed42a6fSJason A. Donenfeld u64 t2_ = t2 + (t1_ >> 51);
663*0ed42a6fSJason A. Donenfeld u64 t1__ = t1_ & 0x7ffffffffffffLLU;
664*0ed42a6fSJason A. Donenfeld u64 t3_ = t3 + (t2_ >> 51);
665*0ed42a6fSJason A. Donenfeld u64 t2__ = t2_ & 0x7ffffffffffffLLU;
666*0ed42a6fSJason A. Donenfeld u64 t4_ = t4 + (t3_ >> 51);
667*0ed42a6fSJason A. Donenfeld u64 t3__ = t3_ & 0x7ffffffffffffLLU;
668*0ed42a6fSJason A. Donenfeld input[0] = t0_;
669*0ed42a6fSJason A. Donenfeld input[1] = t1__;
670*0ed42a6fSJason A. Donenfeld input[2] = t2__;
671*0ed42a6fSJason A. Donenfeld input[3] = t3__;
672*0ed42a6fSJason A. Donenfeld input[4] = t4_;
673*0ed42a6fSJason A. Donenfeld }
674*0ed42a6fSJason A. Donenfeld
format_fcontract_second_carry_full(u64 * input)675*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_second_carry_full(u64 *input)
676*0ed42a6fSJason A. Donenfeld {
677*0ed42a6fSJason A. Donenfeld u64 i0;
678*0ed42a6fSJason A. Donenfeld u64 i1;
679*0ed42a6fSJason A. Donenfeld u64 i0_;
680*0ed42a6fSJason A. Donenfeld u64 i1_;
681*0ed42a6fSJason A. Donenfeld format_fcontract_second_carry_pass(input);
682*0ed42a6fSJason A. Donenfeld modulo_carry_top(input);
683*0ed42a6fSJason A. Donenfeld i0 = input[0];
684*0ed42a6fSJason A. Donenfeld i1 = input[1];
685*0ed42a6fSJason A. Donenfeld i0_ = i0 & 0x7ffffffffffffLLU;
686*0ed42a6fSJason A. Donenfeld i1_ = i1 + (i0 >> 51);
687*0ed42a6fSJason A. Donenfeld input[0] = i0_;
688*0ed42a6fSJason A. Donenfeld input[1] = i1_;
689*0ed42a6fSJason A. Donenfeld }
690*0ed42a6fSJason A. Donenfeld
format_fcontract_trim(u64 * input)691*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_trim(u64 *input)
692*0ed42a6fSJason A. Donenfeld {
693*0ed42a6fSJason A. Donenfeld u64 a0 = input[0];
694*0ed42a6fSJason A. Donenfeld u64 a1 = input[1];
695*0ed42a6fSJason A. Donenfeld u64 a2 = input[2];
696*0ed42a6fSJason A. Donenfeld u64 a3 = input[3];
697*0ed42a6fSJason A. Donenfeld u64 a4 = input[4];
698*0ed42a6fSJason A. Donenfeld u64 mask0 = u64_gte_mask(a0, 0x7ffffffffffedLLU);
699*0ed42a6fSJason A. Donenfeld u64 mask1 = u64_eq_mask(a1, 0x7ffffffffffffLLU);
700*0ed42a6fSJason A. Donenfeld u64 mask2 = u64_eq_mask(a2, 0x7ffffffffffffLLU);
701*0ed42a6fSJason A. Donenfeld u64 mask3 = u64_eq_mask(a3, 0x7ffffffffffffLLU);
702*0ed42a6fSJason A. Donenfeld u64 mask4 = u64_eq_mask(a4, 0x7ffffffffffffLLU);
703*0ed42a6fSJason A. Donenfeld u64 mask = (((mask0 & mask1) & mask2) & mask3) & mask4;
704*0ed42a6fSJason A. Donenfeld u64 a0_ = a0 - (0x7ffffffffffedLLU & mask);
705*0ed42a6fSJason A. Donenfeld u64 a1_ = a1 - (0x7ffffffffffffLLU & mask);
706*0ed42a6fSJason A. Donenfeld u64 a2_ = a2 - (0x7ffffffffffffLLU & mask);
707*0ed42a6fSJason A. Donenfeld u64 a3_ = a3 - (0x7ffffffffffffLLU & mask);
708*0ed42a6fSJason A. Donenfeld u64 a4_ = a4 - (0x7ffffffffffffLLU & mask);
709*0ed42a6fSJason A. Donenfeld input[0] = a0_;
710*0ed42a6fSJason A. Donenfeld input[1] = a1_;
711*0ed42a6fSJason A. Donenfeld input[2] = a2_;
712*0ed42a6fSJason A. Donenfeld input[3] = a3_;
713*0ed42a6fSJason A. Donenfeld input[4] = a4_;
714*0ed42a6fSJason A. Donenfeld }
715*0ed42a6fSJason A. Donenfeld
format_fcontract_store(u8 * output,u64 * input)716*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract_store(u8 *output, u64 *input)
717*0ed42a6fSJason A. Donenfeld {
718*0ed42a6fSJason A. Donenfeld u64 t0 = input[0];
719*0ed42a6fSJason A. Donenfeld u64 t1 = input[1];
720*0ed42a6fSJason A. Donenfeld u64 t2 = input[2];
721*0ed42a6fSJason A. Donenfeld u64 t3 = input[3];
722*0ed42a6fSJason A. Donenfeld u64 t4 = input[4];
723*0ed42a6fSJason A. Donenfeld u64 o0 = t1 << 51 | t0;
724*0ed42a6fSJason A. Donenfeld u64 o1 = t2 << 38 | t1 >> 13;
725*0ed42a6fSJason A. Donenfeld u64 o2 = t3 << 25 | t2 >> 26;
726*0ed42a6fSJason A. Donenfeld u64 o3 = t4 << 12 | t3 >> 39;
727*0ed42a6fSJason A. Donenfeld u8 *b0 = output;
728*0ed42a6fSJason A. Donenfeld u8 *b1 = output + 8;
729*0ed42a6fSJason A. Donenfeld u8 *b2 = output + 16;
730*0ed42a6fSJason A. Donenfeld u8 *b3 = output + 24;
731*0ed42a6fSJason A. Donenfeld put_unaligned_le64(o0, b0);
732*0ed42a6fSJason A. Donenfeld put_unaligned_le64(o1, b1);
733*0ed42a6fSJason A. Donenfeld put_unaligned_le64(o2, b2);
734*0ed42a6fSJason A. Donenfeld put_unaligned_le64(o3, b3);
735*0ed42a6fSJason A. Donenfeld }
736*0ed42a6fSJason A. Donenfeld
format_fcontract(u8 * output,u64 * input)737*0ed42a6fSJason A. Donenfeld static __always_inline void format_fcontract(u8 *output, u64 *input)
738*0ed42a6fSJason A. Donenfeld {
739*0ed42a6fSJason A. Donenfeld format_fcontract_first_carry_full(input);
740*0ed42a6fSJason A. Donenfeld format_fcontract_second_carry_full(input);
741*0ed42a6fSJason A. Donenfeld format_fcontract_trim(input);
742*0ed42a6fSJason A. Donenfeld format_fcontract_store(output, input);
743*0ed42a6fSJason A. Donenfeld }
744*0ed42a6fSJason A. Donenfeld
format_scalar_of_point(u8 * scalar,u64 * point)745*0ed42a6fSJason A. Donenfeld static __always_inline void format_scalar_of_point(u8 *scalar, u64 *point)
746*0ed42a6fSJason A. Donenfeld {
747*0ed42a6fSJason A. Donenfeld u64 *x = point;
748*0ed42a6fSJason A. Donenfeld u64 *z = point + 5;
749*0ed42a6fSJason A. Donenfeld u64 buf[10] __aligned(32) = { 0 };
750*0ed42a6fSJason A. Donenfeld u64 *zmone = buf;
751*0ed42a6fSJason A. Donenfeld u64 *sc = buf + 5;
752*0ed42a6fSJason A. Donenfeld crecip(zmone, z);
753*0ed42a6fSJason A. Donenfeld fmul(sc, x, zmone);
754*0ed42a6fSJason A. Donenfeld format_fcontract(scalar, sc);
755*0ed42a6fSJason A. Donenfeld }
756*0ed42a6fSJason A. Donenfeld
curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE],const u8 secret[CURVE25519_KEY_SIZE],const u8 basepoint[CURVE25519_KEY_SIZE])757*0ed42a6fSJason A. Donenfeld void curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE],
758*0ed42a6fSJason A. Donenfeld const u8 secret[CURVE25519_KEY_SIZE],
759*0ed42a6fSJason A. Donenfeld const u8 basepoint[CURVE25519_KEY_SIZE])
760*0ed42a6fSJason A. Donenfeld {
761*0ed42a6fSJason A. Donenfeld u64 buf0[10] __aligned(32) = { 0 };
762*0ed42a6fSJason A. Donenfeld u64 *x0 = buf0;
763*0ed42a6fSJason A. Donenfeld u64 *z = buf0 + 5;
764*0ed42a6fSJason A. Donenfeld u64 *q;
765*0ed42a6fSJason A. Donenfeld format_fexpand(x0, basepoint);
766*0ed42a6fSJason A. Donenfeld z[0] = 1;
767*0ed42a6fSJason A. Donenfeld q = buf0;
768*0ed42a6fSJason A. Donenfeld {
769*0ed42a6fSJason A. Donenfeld u8 e[32] __aligned(32) = { 0 };
770*0ed42a6fSJason A. Donenfeld u8 *scalar;
771*0ed42a6fSJason A. Donenfeld memcpy(e, secret, 32);
772*0ed42a6fSJason A. Donenfeld curve25519_clamp_secret(e);
773*0ed42a6fSJason A. Donenfeld scalar = e;
774*0ed42a6fSJason A. Donenfeld {
775*0ed42a6fSJason A. Donenfeld u64 buf[15] = { 0 };
776*0ed42a6fSJason A. Donenfeld u64 *nq = buf;
777*0ed42a6fSJason A. Donenfeld u64 *x = nq;
778*0ed42a6fSJason A. Donenfeld x[0] = 1;
779*0ed42a6fSJason A. Donenfeld ladder_cmult(nq, scalar, q);
780*0ed42a6fSJason A. Donenfeld format_scalar_of_point(mypublic, nq);
781*0ed42a6fSJason A. Donenfeld memzero_explicit(buf, sizeof(buf));
782*0ed42a6fSJason A. Donenfeld }
783*0ed42a6fSJason A. Donenfeld memzero_explicit(e, sizeof(e));
784*0ed42a6fSJason A. Donenfeld }
785*0ed42a6fSJason A. Donenfeld memzero_explicit(buf0, sizeof(buf0));
786*0ed42a6fSJason A. Donenfeld }
787