Coverage Report

Created: 2024-06-28 06:19

/src/botan/src/lib/pubkey/x25519/donna.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* Based on curve25519-donna-c64.c from https://github.com/agl/curve25519-donna
3
* revision 80ad9b9930c9baef5829dd2a235b6b7646d32a8e
4
*
5
* Further changes
6
* (C) 2014,2018 Jack Lloyd
7
*
8
* Botan is released under the Simplified BSD License (see license.txt)
9
*/
10
11
/* Copyright 2008, Google Inc.
12
* All rights reserved.
13
*
14
* Code released into the public domain.
15
*
16
* curve25519-donna: Curve25519 elliptic curve, public key function
17
*
18
* https://code.google.com/p/curve25519-donna/
19
*
20
* Adam Langley <agl@imperialviolet.org>
21
*
22
* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
23
*
24
* More information about curve25519 can be found here
25
*   https://cr.yp.to/ecdh.html
26
*
27
* djb's sample implementation of curve25519 is written in a special assembly
28
* language called qhasm and uses the floating point registers.
29
*
30
* This is, almost, a clean room reimplementation from the curve25519 paper. It
31
* uses many of the tricks described therein. Only the crecip function is taken
32
* from the sample implementation.
33
*/
34
35
#include <botan/x25519.h>
36
37
#include <botan/internal/ct_utils.h>
38
#include <botan/internal/donna128.h>
39
#include <botan/internal/loadstor.h>
40
#include <botan/internal/mul128.h>
41
42
namespace Botan {
43
44
namespace {
45
46
#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
47
typedef donna128 uint128_t;
48
#endif
49
50
/* Sum two numbers: output += in */
51
0
inline void fsum(uint64_t out[5], const uint64_t in[5]) {
52
0
   out[0] += in[0];
53
0
   out[1] += in[1];
54
0
   out[2] += in[2];
55
0
   out[3] += in[3];
56
0
   out[4] += in[4];
57
0
}
58
59
/* Find the difference of two numbers: out = in - out
60
* (note the order of the arguments!)
61
*
62
* Assumes that out[i] < 2**52
63
* On return, out[i] < 2**55
64
*/
65
0
inline void fdifference_backwards(uint64_t out[5], const uint64_t in[5]) {
66
   /* 152 is 19 << 3 */
67
0
   const uint64_t two54m152 = (static_cast<uint64_t>(1) << 54) - 152;
68
0
   const uint64_t two54m8 = (static_cast<uint64_t>(1) << 54) - 8;
69
70
0
   out[0] = in[0] + two54m152 - out[0];
71
0
   out[1] = in[1] + two54m8 - out[1];
72
0
   out[2] = in[2] + two54m8 - out[2];
73
0
   out[3] = in[3] + two54m8 - out[3];
74
0
   out[4] = in[4] + two54m8 - out[4];
75
0
}
76
77
0
inline void fadd_sub(uint64_t x[5], uint64_t y[5]) {
78
   // TODO merge these and avoid the tmp array
79
0
   uint64_t tmp[5];
80
0
   copy_mem(tmp, y, 5);
81
0
   fsum(y, x);
82
0
   fdifference_backwards(x, tmp);  // does x - z
83
0
}
84
85
const uint64_t MASK_63 = 0x7ffffffffffff;
86
87
/* Multiply a number by a scalar: out = in * scalar */
88
0
inline void fscalar_product(uint64_t out[5], const uint64_t in[5], const uint64_t scalar) {
89
0
   uint128_t a = uint128_t(in[0]) * scalar;
90
0
   out[0] = a & MASK_63;
91
92
0
   a = uint128_t(in[1]) * scalar + carry_shift(a, 51);
93
0
   out[1] = a & MASK_63;
94
95
0
   a = uint128_t(in[2]) * scalar + carry_shift(a, 51);
96
0
   out[2] = a & MASK_63;
97
98
0
   a = uint128_t(in[3]) * scalar + carry_shift(a, 51);
99
0
   out[3] = a & MASK_63;
100
101
0
   a = uint128_t(in[4]) * scalar + carry_shift(a, 51);
102
0
   out[4] = a & MASK_63;
103
104
0
   out[0] += carry_shift(a, 51) * 19;
105
0
}
106
107
/* Multiply two numbers: out = in2 * in
108
*
109
* out must be distinct to both inputs. The inputs are reduced coefficient
110
* form, the output is not.
111
*
112
* Assumes that in[i] < 2**55 and likewise for in2.
113
* On return, out[i] < 2**52
114
*/
115
0
inline void fmul(uint64_t out[5], const uint64_t in[5], const uint64_t in2[5]) {
116
0
   const uint128_t s0 = in2[0];
117
0
   const uint128_t s1 = in2[1];
118
0
   const uint128_t s2 = in2[2];
119
0
   const uint128_t s3 = in2[3];
120
0
   const uint128_t s4 = in2[4];
121
122
0
   uint64_t r0 = in[0];
123
0
   uint64_t r1 = in[1];
124
0
   uint64_t r2 = in[2];
125
0
   uint64_t r3 = in[3];
126
0
   uint64_t r4 = in[4];
127
128
0
   uint128_t t0 = r0 * s0;
129
0
   uint128_t t1 = r0 * s1 + r1 * s0;
130
0
   uint128_t t2 = r0 * s2 + r2 * s0 + r1 * s1;
131
0
   uint128_t t3 = r0 * s3 + r3 * s0 + r1 * s2 + r2 * s1;
132
0
   uint128_t t4 = r0 * s4 + r4 * s0 + r3 * s1 + r1 * s3 + r2 * s2;
133
134
0
   r4 *= 19;
135
0
   r1 *= 19;
136
0
   r2 *= 19;
137
0
   r3 *= 19;
138
139
0
   t0 += r4 * s1 + r1 * s4 + r2 * s3 + r3 * s2;
140
0
   t1 += r4 * s2 + r2 * s4 + r3 * s3;
141
0
   t2 += r4 * s3 + r3 * s4;
142
0
   t3 += r4 * s4;
143
144
0
   r0 = t0 & MASK_63;
145
0
   t1 += carry_shift(t0, 51);
146
0
   r1 = t1 & MASK_63;
147
0
   t2 += carry_shift(t1, 51);
148
0
   r2 = t2 & MASK_63;
149
0
   t3 += carry_shift(t2, 51);
150
0
   r3 = t3 & MASK_63;
151
0
   t4 += carry_shift(t3, 51);
152
0
   r4 = t4 & MASK_63;
153
0
   uint64_t c = carry_shift(t4, 51);
154
155
0
   r0 += c * 19;
156
0
   c = r0 >> 51;
157
0
   r0 = r0 & MASK_63;
158
0
   r1 += c;
159
0
   c = r1 >> 51;
160
0
   r1 = r1 & MASK_63;
161
0
   r2 += c;
162
163
0
   out[0] = r0;
164
0
   out[1] = r1;
165
0
   out[2] = r2;
166
0
   out[3] = r3;
167
0
   out[4] = r4;
168
0
}
169
170
0
inline void fsquare(uint64_t out[5], const uint64_t in[5], size_t count = 1) {
171
0
   uint64_t r0 = in[0];
172
0
   uint64_t r1 = in[1];
173
0
   uint64_t r2 = in[2];
174
0
   uint64_t r3 = in[3];
175
0
   uint64_t r4 = in[4];
176
177
0
   for(size_t i = 0; i != count; ++i) {
178
0
      const uint64_t d0 = r0 * 2;
179
0
      const uint64_t d1 = r1 * 2;
180
0
      const uint64_t d2 = r2 * 2 * 19;
181
0
      const uint64_t d419 = r4 * 19;
182
0
      const uint64_t d4 = d419 * 2;
183
184
0
      uint128_t t0 = uint128_t(r0) * r0 + uint128_t(d4) * r1 + uint128_t(d2) * (r3);
185
0
      uint128_t t1 = uint128_t(d0) * r1 + uint128_t(d4) * r2 + uint128_t(r3) * (r3 * 19);
186
0
      uint128_t t2 = uint128_t(d0) * r2 + uint128_t(r1) * r1 + uint128_t(d4) * (r3);
187
0
      uint128_t t3 = uint128_t(d0) * r3 + uint128_t(d1) * r2 + uint128_t(r4) * (d419);
188
0
      uint128_t t4 = uint128_t(d0) * r4 + uint128_t(d1) * r3 + uint128_t(r2) * (r2);
189
190
0
      r0 = t0 & MASK_63;
191
0
      t1 += carry_shift(t0, 51);
192
0
      r1 = t1 & MASK_63;
193
0
      t2 += carry_shift(t1, 51);
194
0
      r2 = t2 & MASK_63;
195
0
      t3 += carry_shift(t2, 51);
196
0
      r3 = t3 & MASK_63;
197
0
      t4 += carry_shift(t3, 51);
198
0
      r4 = t4 & MASK_63;
199
0
      uint64_t c = carry_shift(t4, 51);
200
201
0
      r0 += c * 19;
202
0
      c = r0 >> 51;
203
0
      r0 = r0 & MASK_63;
204
0
      r1 += c;
205
0
      c = r1 >> 51;
206
0
      r1 = r1 & MASK_63;
207
0
      r2 += c;
208
0
   }
209
210
0
   out[0] = r0;
211
0
   out[1] = r1;
212
0
   out[2] = r2;
213
0
   out[3] = r3;
214
0
   out[4] = r4;
215
0
}
216
217
/* Take a little-endian, 32-byte number and expand it into polynomial form */
218
0
inline void fexpand(uint64_t* out, const uint8_t* in) {
219
0
   out[0] = load_le<uint64_t>(in, 0) & MASK_63;
220
0
   out[1] = (load_le<uint64_t>(in + 6, 0) >> 3) & MASK_63;
221
0
   out[2] = (load_le<uint64_t>(in + 12, 0) >> 6) & MASK_63;
222
0
   out[3] = (load_le<uint64_t>(in + 19, 0) >> 1) & MASK_63;
223
0
   out[4] = (load_le<uint64_t>(in + 24, 0) >> 12) & MASK_63;
224
0
}
225
226
/* Take a fully reduced polynomial form number and contract it into a
227
* little-endian, 32-byte array
228
*/
229
0
inline void fcontract(uint8_t* out, const uint64_t input[5]) {
230
0
   uint128_t t0 = input[0];
231
0
   uint128_t t1 = input[1];
232
0
   uint128_t t2 = input[2];
233
0
   uint128_t t3 = input[3];
234
0
   uint128_t t4 = input[4];
235
236
0
   for(size_t i = 0; i != 2; ++i) {
237
0
      t1 += t0 >> 51;
238
0
      t0 &= MASK_63;
239
0
      t2 += t1 >> 51;
240
0
      t1 &= MASK_63;
241
0
      t3 += t2 >> 51;
242
0
      t2 &= MASK_63;
243
0
      t4 += t3 >> 51;
244
0
      t3 &= MASK_63;
245
0
      t0 += (t4 >> 51) * 19;
246
0
      t4 &= MASK_63;
247
0
   }
248
249
   /* now t is between 0 and 2^255-1, properly carried. */
250
   /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
251
252
0
   t0 += 19;
253
254
0
   t1 += t0 >> 51;
255
0
   t0 &= MASK_63;
256
0
   t2 += t1 >> 51;
257
0
   t1 &= MASK_63;
258
0
   t3 += t2 >> 51;
259
0
   t2 &= MASK_63;
260
0
   t4 += t3 >> 51;
261
0
   t3 &= MASK_63;
262
0
   t0 += (t4 >> 51) * 19;
263
0
   t4 &= MASK_63;
264
265
   /* now between 19 and 2^255-1 in both cases, and offset by 19. */
266
267
0
   t0 += 0x8000000000000 - 19;
268
0
   t1 += 0x8000000000000 - 1;
269
0
   t2 += 0x8000000000000 - 1;
270
0
   t3 += 0x8000000000000 - 1;
271
0
   t4 += 0x8000000000000 - 1;
272
273
   /* now between 2^255 and 2^256-20, and offset by 2^255. */
274
275
0
   t1 += t0 >> 51;
276
0
   t0 &= MASK_63;
277
0
   t2 += t1 >> 51;
278
0
   t1 &= MASK_63;
279
0
   t3 += t2 >> 51;
280
0
   t2 &= MASK_63;
281
0
   t4 += t3 >> 51;
282
0
   t3 &= MASK_63;
283
0
   t4 &= MASK_63;
284
285
0
   store_le(out,
286
0
            combine_lower(t0, 0, t1, 51),
287
0
            combine_lower(t1, 13, t2, 38),
288
0
            combine_lower(t2, 26, t3, 25),
289
0
            combine_lower(t3, 39, t4, 12));
290
0
}
291
292
/* Input: Q, Q', Q-Q'
293
* Out: 2Q, Q+Q'
294
*
295
*   result.two_q (2*Q): long form
296
*   result.q_plus_q_dash (Q + Q): long form
297
*   in_q: short form, destroyed
298
*   in_q_dash: short form, destroyed
299
*   in_q_minus_q_dash: short form, preserved
300
*/
301
void fmonty(uint64_t result_two_q_x[5],
302
            uint64_t result_two_q_z[5],
303
            uint64_t result_q_plus_q_dash_x[5],
304
            uint64_t result_q_plus_q_dash_z[5],
305
            uint64_t in_q_x[5],
306
            uint64_t in_q_z[5],
307
            uint64_t in_q_dash_x[5],
308
            uint64_t in_q_dash_z[5],
309
0
            const uint64_t q_minus_q_dash[5]) {
310
0
   uint64_t zzz[5];
311
0
   uint64_t xx[5];
312
0
   uint64_t zz[5];
313
0
   uint64_t xxprime[5];
314
0
   uint64_t zzprime[5];
315
0
   uint64_t zzzprime[5];
316
317
0
   fadd_sub(in_q_z, in_q_x);
318
0
   fadd_sub(in_q_dash_z, in_q_dash_x);
319
320
0
   fmul(xxprime, in_q_dash_x, in_q_z);
321
0
   fmul(zzprime, in_q_dash_z, in_q_x);
322
323
0
   fadd_sub(zzprime, xxprime);
324
325
0
   fsquare(result_q_plus_q_dash_x, xxprime);
326
0
   fsquare(zzzprime, zzprime);
327
0
   fmul(result_q_plus_q_dash_z, zzzprime, q_minus_q_dash);
328
329
0
   fsquare(xx, in_q_x);
330
0
   fsquare(zz, in_q_z);
331
0
   fmul(result_two_q_x, xx, zz);
332
333
0
   fdifference_backwards(zz, xx);  // does zz = xx - zz
334
0
   fscalar_product(zzz, zz, 121665);
335
0
   fsum(zzz, xx);
336
337
0
   fmul(result_two_q_z, zz, zzz);
338
0
}
339
340
/*
341
* Maybe swap the contents of two uint64_t arrays (@a and @b),
342
* Param @iswap is assumed to be either 0 or 1
343
*
344
* This function performs the swap without leaking any side-channel
345
* information.
346
*/
347
0
inline void swap_conditional(uint64_t a[5], uint64_t b[5], uint64_t c[5], uint64_t d[5], uint64_t iswap) {
348
0
   const uint64_t swap = 0 - iswap;
349
350
0
   for(size_t i = 0; i < 5; ++i) {
351
0
      const uint64_t x0 = swap & (a[i] ^ b[i]);
352
0
      const uint64_t x1 = swap & (c[i] ^ d[i]);
353
0
      a[i] ^= x0;
354
0
      b[i] ^= x0;
355
0
      c[i] ^= x1;
356
0
      d[i] ^= x1;
357
0
   }
358
0
}
359
360
/* Calculates nQ where Q is the x-coordinate of a point on the curve
361
*
362
*   resultx/resultz: the x/z coordinate of the resulting curve point (short form)
363
*   n: a little endian, 32-byte number
364
*   q: a point of the curve (short form)
365
*/
366
0
void cmult(uint64_t resultx[5], uint64_t resultz[5], const uint8_t n[32], const uint64_t q[5]) {
367
0
   uint64_t a[5] = {0};  // nqpqx
368
0
   uint64_t b[5] = {1};  // npqpz
369
0
   uint64_t c[5] = {1};  // nqx
370
0
   uint64_t d[5] = {0};  // nqz
371
0
   uint64_t e[5] = {0};  // npqqx2
372
0
   uint64_t f[5] = {1};  // npqqz2
373
0
   uint64_t g[5] = {0};  // nqx2
374
0
   uint64_t h[5] = {1};  // nqz2
375
376
0
   copy_mem(a, q, 5);
377
378
0
   for(size_t i = 0; i < 32; ++i) {
379
0
      const uint64_t bit0 = (n[31 - i] >> 7) & 1;
380
0
      const uint64_t bit1 = (n[31 - i] >> 6) & 1;
381
0
      const uint64_t bit2 = (n[31 - i] >> 5) & 1;
382
0
      const uint64_t bit3 = (n[31 - i] >> 4) & 1;
383
0
      const uint64_t bit4 = (n[31 - i] >> 3) & 1;
384
0
      const uint64_t bit5 = (n[31 - i] >> 2) & 1;
385
0
      const uint64_t bit6 = (n[31 - i] >> 1) & 1;
386
0
      const uint64_t bit7 = (n[31 - i] >> 0) & 1;
387
388
0
      swap_conditional(c, a, d, b, bit0);
389
0
      fmonty(g, h, e, f, c, d, a, b, q);
390
391
0
      swap_conditional(g, e, h, f, bit0 ^ bit1);
392
0
      fmonty(c, d, a, b, g, h, e, f, q);
393
394
0
      swap_conditional(c, a, d, b, bit1 ^ bit2);
395
0
      fmonty(g, h, e, f, c, d, a, b, q);
396
397
0
      swap_conditional(g, e, h, f, bit2 ^ bit3);
398
0
      fmonty(c, d, a, b, g, h, e, f, q);
399
400
0
      swap_conditional(c, a, d, b, bit3 ^ bit4);
401
0
      fmonty(g, h, e, f, c, d, a, b, q);
402
403
0
      swap_conditional(g, e, h, f, bit4 ^ bit5);
404
0
      fmonty(c, d, a, b, g, h, e, f, q);
405
406
0
      swap_conditional(c, a, d, b, bit5 ^ bit6);
407
0
      fmonty(g, h, e, f, c, d, a, b, q);
408
409
0
      swap_conditional(g, e, h, f, bit6 ^ bit7);
410
0
      fmonty(c, d, a, b, g, h, e, f, q);
411
412
0
      swap_conditional(c, a, d, b, bit7);
413
0
   }
414
415
0
   copy_mem(resultx, c, 5);
416
0
   copy_mem(resultz, d, 5);
417
0
}
418
419
// -----------------------------------------------------------------------------
420
// Shamelessly copied from djb's code, tightened a little
421
// -----------------------------------------------------------------------------
422
0
void crecip(uint64_t out[5], const uint64_t z[5]) {
423
0
   uint64_t a[5];
424
0
   uint64_t b[5];
425
0
   uint64_t c[5];
426
0
   uint64_t t0[5];
427
428
0
   fsquare(a, z);        // 2
429
0
   fsquare(t0, a, 2);    // 8
430
0
   fmul(b, t0, z);       // 9
431
0
   fmul(a, b, a);        // 11
432
0
   fsquare(t0, a);       // 22
433
0
   fmul(b, t0, b);       // 2^5 - 2^0 = 31
434
0
   fsquare(t0, b, 5);    // 2^10 - 2^5
435
0
   fmul(b, t0, b);       // 2^10 - 2^0
436
0
   fsquare(t0, b, 10);   // 2^20 - 2^10
437
0
   fmul(c, t0, b);       // 2^20 - 2^0
438
0
   fsquare(t0, c, 20);   // 2^40 - 2^20
439
0
   fmul(t0, t0, c);      // 2^40 - 2^0
440
0
   fsquare(t0, t0, 10);  // 2^50 - 2^10
441
0
   fmul(b, t0, b);       // 2^50 - 2^0
442
0
   fsquare(t0, b, 50);   // 2^100 - 2^50
443
0
   fmul(c, t0, b);       // 2^100 - 2^0
444
0
   fsquare(t0, c, 100);  // 2^200 - 2^100
445
0
   fmul(t0, t0, c);      // 2^200 - 2^0
446
0
   fsquare(t0, t0, 50);  // 2^250 - 2^50
447
0
   fmul(t0, t0, b);      // 2^250 - 2^0
448
0
   fsquare(t0, t0, 5);   // 2^255 - 2^5
449
0
   fmul(out, t0, a);     // 2^255 - 21
450
0
}
451
452
}  // namespace
453
454
0
void curve25519_donna(uint8_t mypublic[32], const uint8_t secret[32], const uint8_t basepoint[32]) {
455
0
   CT::poison(secret, 32);
456
0
   CT::poison(basepoint, 32);
457
458
0
   uint64_t bp[5], x[5], z[5], zmone[5];
459
0
   uint8_t e[32];
460
461
0
   copy_mem(e, secret, 32);
462
0
   e[0] &= 248;
463
0
   e[31] &= 127;
464
0
   e[31] |= 64;
465
466
0
   fexpand(bp, basepoint);
467
0
   cmult(x, z, e, bp);
468
0
   crecip(zmone, z);
469
0
   fmul(z, x, zmone);
470
0
   fcontract(mypublic, z);
471
472
0
   CT::unpoison(secret, 32);
473
0
   CT::unpoison(basepoint, 32);
474
0
   CT::unpoison(mypublic, 32);
475
0
}
476
477
}  // namespace Botan