Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCrypto/Curves/Ed25519.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Random.h>
8
#include <LibCrypto/Curves/Curve25519.h>
9
#include <LibCrypto/Curves/Ed25519.h>
10
#include <LibCrypto/Hash/SHA2.h>
11
12
namespace Crypto::Curves {
13
14
namespace {
15
16
struct Ed25519Point {
17
    u32 x[8] {};
18
    u32 y[8] {};
19
    u32 z[8] {};
20
    u32 t[8] {};
21
};
22
23
constexpr Ed25519Point BASE_POINT = {
24
    { 0x8F25D51A, 0xC9562D60, 0x9525A7B2, 0x692CC760, 0xFDD6DC5C, 0xC0A4E231, 0xCD6E53FE, 0x216936D3 },
25
    { 0x66666658, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666 },
26
    { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
27
    { 0xA5B7DDA3, 0x6DDE8AB3, 0x775152F5, 0x20F09F80, 0x64ABE37D, 0x66EA4E8E, 0xD78B7665, 0x67875F0F }
28
};
29
30
void point_double(Ed25519Point* result, Ed25519Point const* point)
31
0
{
32
0
    u32 a[8] {};
33
0
    u32 b[8] {};
34
0
    u32 c[8] {};
35
0
    u32 e[8] {};
36
0
    u32 f[8] {};
37
0
    u32 g[8] {};
38
0
    u32 h[8] {};
39
40
0
    Curve25519::modular_square(a, point->x);
41
0
    Curve25519::modular_square(b, point->y);
42
0
    Curve25519::modular_square(c, point->z);
43
0
    Curve25519::modular_add(c, c, c);
44
0
    Curve25519::modular_add(e, a, b);
45
0
    Curve25519::modular_add(f, point->x, point->y);
46
0
    Curve25519::modular_square(f, f);
47
0
    Curve25519::modular_subtract(f, e, f);
48
0
    Curve25519::modular_subtract(g, a, b);
49
0
    Curve25519::modular_add(h, c, g);
50
0
    Curve25519::modular_multiply(result->x, f, h);
51
0
    Curve25519::modular_multiply(result->y, e, g);
52
0
    Curve25519::modular_multiply(result->z, g, h);
53
0
    Curve25519::modular_multiply(result->t, e, f);
54
0
}
55
56
void point_add(Ed25519Point* result, Ed25519Point const* p, Ed25519Point const* q)
57
0
{
58
    // Compute R = P + Q
59
0
    u32 a[8] {};
60
0
    u32 b[8] {};
61
0
    u32 c[8] {};
62
0
    u32 d[8] {};
63
0
    u32 e[8] {};
64
0
    u32 f[8] {};
65
0
    u32 g[8] {};
66
0
    u32 h[8] {};
67
68
0
    Curve25519::modular_add(c, p->y, p->x);
69
0
    Curve25519::modular_add(d, q->y, q->x);
70
0
    Curve25519::modular_multiply(a, c, d);
71
0
    Curve25519::modular_subtract(c, p->y, p->x);
72
0
    Curve25519::modular_subtract(d, q->y, q->x);
73
0
    Curve25519::modular_multiply(b, c, d);
74
0
    Curve25519::modular_multiply(c, p->z, q->z);
75
0
    Curve25519::modular_add(c, c, c);
76
0
    Curve25519::modular_multiply(d, p->t, q->t);
77
0
    Curve25519::modular_multiply(d, d, Curve25519::CURVE_D_2);
78
0
    Curve25519::modular_add(e, a, b);
79
0
    Curve25519::modular_subtract(f, a, b);
80
0
    Curve25519::modular_add(g, c, d);
81
0
    Curve25519::modular_subtract(h, c, d);
82
0
    Curve25519::modular_multiply(result->x, f, h);
83
0
    Curve25519::modular_multiply(result->y, e, g);
84
0
    Curve25519::modular_multiply(result->z, g, h);
85
0
    Curve25519::modular_multiply(result->t, e, f);
86
0
}
87
88
void point_multiply_scalar(Ed25519Point* result, u8 const* scalar, Ed25519Point const* point)
89
0
{
90
    // Set U to the neutral element (0, 1, 1, 0)
91
0
    Ed25519Point u {};
92
0
    Ed25519Point v {};
93
0
    Curve25519::set(u.x, 0);
94
0
    Curve25519::set(u.y, 1);
95
0
    Curve25519::set(u.z, 1);
96
0
    Curve25519::set(u.t, 0);
97
98
0
    for (i32 i = Curve25519::BITS - 1; i >= 0; i--) {
99
0
        u8 b = (scalar[i / 8] >> (i % 8)) & 1;
100
101
        // Compute U = 2 * U
102
0
        point_double(&u, &u);
103
        // Compute V = U + P
104
0
        point_add(&v, &u, point);
105
106
        // If b is set, then U = V
107
0
        Curve25519::select(u.x, u.x, v.x, b);
108
0
        Curve25519::select(u.y, u.y, v.y, b);
109
0
        Curve25519::select(u.z, u.z, v.z, b);
110
0
        Curve25519::select(u.t, u.t, v.t, b);
111
0
    }
112
113
0
    Curve25519::copy(result->x, u.x);
114
0
    Curve25519::copy(result->y, u.y);
115
0
    Curve25519::copy(result->z, u.z);
116
0
    Curve25519::copy(result->t, u.t);
117
0
}
118
119
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2
120
void encode_point(Ed25519Point* point, u8* data)
121
0
{
122
    // Retrieve affine representation
123
0
    Curve25519::modular_multiply_inverse(point->z, point->z);
124
0
    Curve25519::modular_multiply(point->x, point->x, point->z);
125
0
    Curve25519::modular_multiply(point->y, point->y, point->z);
126
0
    Curve25519::set(point->z, 1);
127
0
    Curve25519::modular_multiply(point->t, point->x, point->y);
128
129
    // First, encode the y-coordinate (in the range 0 <= y < p) as a little-endian string of 32 octets.
130
    // The most significant bit of the final octet is always zero.
131
0
    Curve25519::export_state(point->y, data);
132
133
    // To form the encoding of the point [s]B,
134
    // copy the least significant bit of the x coordinate to the most significant bit of the final octet.
135
0
    data[31] |= (point->x[0] & 1) << 7;
136
0
}
137
138
void multiply(u8* result_low, u8* result_high, u8 const* a, u8 const* b, u8 n)
139
0
{
140
    // Comba's algorithm
141
0
    u32 temp = 0;
142
0
    for (u32 i = 0; i < n; i++) {
143
0
        for (u32 j = 0; j <= i; j++) {
144
0
            temp += (uint16_t)a[j] * b[i - j];
145
0
        }
146
147
0
        if (result_low != NULL) {
148
0
            result_low[i] = temp & 0xFF;
149
0
        }
150
151
0
        temp >>= 8;
152
0
    }
153
154
0
    if (result_high != NULL) {
155
0
        for (u32 i = n; i < (2 * n); i++) {
156
0
            for (u32 j = i + 1 - n; j < n; j++) {
157
0
                temp += (uint16_t)a[j] * b[i - j];
158
0
            }
159
160
0
            result_high[i - n] = temp & 0xFF;
161
0
            temp >>= 8;
162
0
        }
163
0
    }
164
0
}
165
166
u8 subtract(u8* result, u8 const* a, u8 const* b, u8 n)
167
0
{
168
0
    i16 temp = 0;
169
170
    // Compute R = A - B
171
0
    for (i8 i = 0; i < n; i++) {
172
0
        temp += a[i];
173
0
        temp -= b[i];
174
0
        result[i] = temp & 0xFF;
175
0
        temp >>= 8;
176
0
    }
177
178
    // Return 1 if the result of the subtraction is negative
179
0
    return temp & 1;
180
0
}
181
182
void copy(u8* a, u8 const* b, u32 n)
183
0
{
184
0
    for (u32 i = 0; i < n; i++) {
185
0
        a[i] = b[i];
186
0
    }
187
0
}
188
189
void select(u8* r, u8 const* a, u8 const* b, u8 c, u8 n)
190
0
{
191
0
    u8 mask = c - 1;
192
0
    for (u8 i = 0; i < n; i++) {
193
0
        r[i] = (a[i] & mask) | (b[i] & ~mask);
194
0
    }
195
0
}
196
197
void barrett_reduce(u8* result, u8 const* input)
198
0
{
199
    // Barrett reduction b = 2^8 && k = 32
200
0
    u8 is_negative;
201
0
    u8 u[33];
202
0
    u8 v[33];
203
204
0
    multiply(NULL, u, input + 31, Curve25519::BARRETT_REDUCTION_QUOTIENT, 33);
205
0
    multiply(v, NULL, u, Curve25519::BASE_POINT_L_ORDER, 33);
206
207
0
    subtract(u, input, v, 33);
208
209
0
    is_negative = subtract(v, u, Curve25519::BASE_POINT_L_ORDER, 33);
210
0
    select(u, v, u, is_negative, 33);
211
0
    is_negative = subtract(v, u, Curve25519::BASE_POINT_L_ORDER, 33);
212
0
    select(u, v, u, is_negative, 33);
213
214
0
    copy(result, u, 32);
215
0
}
216
217
void add(u8* result, u8 const* a, u8 const* b, u8 n)
218
0
{
219
    // Compute R = A + B
220
0
    u16 temp = 0;
221
0
    for (u8 i = 0; i < n; i++) {
222
0
        temp += a[i];
223
0
        temp += b[i];
224
0
        result[i] = temp & 0xFF;
225
0
        temp >>= 8;
226
0
    }
227
0
}
228
229
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.3
230
u32 decode_point(Ed25519Point* point, u8 const* data)
231
0
{
232
0
    u32 u[8];
233
0
    u32 v[8];
234
0
    u32 ret;
235
0
    u64 temp = 19;
236
237
    // 1. First, interpret the string as an integer in little-endian representation.
238
    // Bit 255 of this number is the least significant bit of the x-coordinate and denote this value x_0.
239
0
    u8 x0 = data[31] >> 7;
240
241
    // The y-coordinate is recovered simply by clearing this bit.
242
0
    Curve25519::import_state(point->y, data);
243
0
    point->y[7] &= 0x7FFFFFFF;
244
245
    // Compute U = Y + 19
246
0
    for (u32 i = 0; i < 8; i++) {
247
0
        temp += point->y[i];
248
0
        u[i] = temp & 0xFFFFFFFF;
249
0
        temp >>= 32;
250
0
    }
251
252
    // If the resulting value is >= p, decoding fails.
253
0
    ret = (u[7] >> 31) & 1;
254
255
    // 2. To recover the x-coordinate, the curve equation implies x^2 = (y^2 - 1) / (d y^2 + 1) (mod p).
256
    // The denominator is always non-zero mod p.
257
    // Let u = y^2 - 1 and v = d * y^2 + 1
258
0
    Curve25519::modular_square(v, point->y);
259
0
    Curve25519::modular_subtract_single(u, v, 1);
260
0
    Curve25519::modular_multiply(v, v, Curve25519::CURVE_D);
261
0
    Curve25519::modular_add_single(v, v, 1);
262
263
    // 3. Compute u = sqrt(u / v)
264
0
    ret |= Curve25519::modular_square_root(u, u, v);
265
266
    // If x = 0, and x_0 = 1, decoding fails.
267
0
    ret |= (Curve25519::compare(u, Curve25519::ZERO) ^ 1) & x0;
268
269
    // 4.  Finally, use the x_0 bit to select the right square root.
270
0
    Curve25519::modular_subtract(v, Curve25519::ZERO, u);
271
0
    Curve25519::select(point->x, u, v, (x0 ^ u[0]) & 1);
272
0
    Curve25519::set(point->z, 1);
273
0
    Curve25519::modular_multiply(point->t, point->x, point->y);
274
275
    // Return 0 if the point has been successfully decoded, else 1
276
0
    return ret;
277
0
}
278
279
u8 compare(u8 const* a, u8 const* b, u8 n)
280
0
{
281
0
    u8 mask = 0;
282
0
    for (u32 i = 0; i < n; i++) {
283
0
        mask |= a[i] ^ b[i];
284
0
    }
285
286
    // Return 0 if A = B, else 1
287
0
    return ((u8)(mask | (~mask + 1))) >> 7;
288
0
}
289
290
}
291
292
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
293
ErrorOr<ByteBuffer> Ed25519::generate_private_key()
294
0
{
295
    // The private key is 32 octets (256 bits, corresponding to b) of
296
    // cryptographically secure random data.  See [RFC4086] for a discussion
297
    // about randomness.
298
299
0
    auto buffer = TRY(ByteBuffer::create_uninitialized(key_size()));
300
0
    fill_with_random(buffer);
301
0
    return buffer;
302
0
}
303
304
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
305
ErrorOr<ByteBuffer> Ed25519::generate_public_key(ReadonlyBytes private_key)
306
0
{
307
    // The 32-byte public key is generated by the following steps.
308
309
    // 1. Hash the 32-byte private key using SHA-512, storing the digest in a 64-octet large buffer, denoted h.
310
    // Only the lower 32 bytes are used for generating the public key.
311
0
    auto digest = Crypto::Hash::SHA512::hash(private_key);
312
313
    // NOTE: we do these steps in the opposite order (since its easier to modify s)
314
    // 3. Interpret the buffer as the little-endian integer, forming a secret scalar s.
315
0
    u8 s[32] {};
316
0
    memcpy(s, digest.data, 32);
317
318
    // 2. Prune the buffer:
319
    // The lowest three bits of the first octet are cleared,
320
0
    s[0] &= 0xF8;
321
    // the highest bit of the last octet is cleared,
322
0
    s[31] &= 0x7F;
323
    // and the second highest bit of the last octet is set.
324
0
    s[31] |= 0x40;
325
326
    // Perform a fixed-base scalar multiplication [s]B.
327
0
    Ed25519Point sb {};
328
0
    point_multiply_scalar(&sb, s, &BASE_POINT);
329
330
    // 4.  The public key A is the encoding of the point [s]B.
331
    // First, encode the y-coordinate (in the range 0 <= y < p) as a little-endian string of 32 octets.
332
    // The most significant bit of the final octet is always zero.
333
    // To form the encoding of the point [s]B, copy the least significant bit of the x coordinate
334
    // to the most significant bit of the final octet.  The result is the public key.
335
0
    auto public_key = TRY(ByteBuffer::create_uninitialized(key_size()));
336
0
    encode_point(&sb, public_key.data());
337
338
0
    return public_key;
339
0
}
340
341
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.6
342
ErrorOr<ByteBuffer> Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes private_key, ReadonlyBytes message)
343
0
{
344
    // 1. Hash the private key, 32 octets, using SHA-512.
345
    // Let h denote the resulting digest.
346
0
    auto h = Crypto::Hash::SHA512::hash(private_key);
347
348
    // Construct the secret scalar s from the first half of the digest,
349
0
    u8 s[32] {};
350
0
    memcpy(s, h.data, 32);
351
    // NOTE: This is done later in step 4, but we can also do it here.
352
0
    s[0] &= 0xF8;
353
0
    s[31] &= 0x7F;
354
0
    s[31] |= 0x40;
355
356
    // and the corresponding public key A, as described in the previous section.
357
    // NOTE: The public key A is taken as input to this function.
358
359
    // Let prefix denote the second half of the hash digest, h[32],...,h[63].
360
0
    u8 prefix[32] {};
361
0
    memcpy(prefix, h.data + 32, 32);
362
363
    // 2. Compute SHA-512(dom2(F, C) || p || PH(M)), where M is the message to be signed.
364
0
    Crypto::Hash::SHA512 hash;
365
    // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
366
0
    hash.update(prefix, 32);
367
    // NOTE: PH(M) = M
368
0
    hash.update(message.data(), message.size());
369
370
    // Interpret the 64-octet digest as a little-endian integer r.
371
    // For efficiency, do this by first reducing r modulo L, the group order of B.
372
0
    auto digest = hash.digest();
373
0
    u8 r[32] {};
374
0
    barrett_reduce(r, digest.data);
375
376
    // 3.  Compute the point [r]B.
377
0
    Ed25519Point rb {};
378
0
    point_multiply_scalar(&rb, r, &BASE_POINT);
379
380
0
    auto R = TRY(ByteBuffer::create_uninitialized(32));
381
    // Let the string R be the encoding of this point
382
0
    encode_point(&rb, R.data());
383
384
    // 4. Compute SHA512(dom2(F, C) || R || A || PH(M)),
385
    // NOTE: We can reuse hash here, since digest() calls reset()
386
    // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
387
0
    hash.update(R.data(), R.size());
388
    // NOTE: A == public_key
389
0
    hash.update(public_key.data(), public_key.size());
390
    // NOTE: PH(M) = M
391
0
    hash.update(message.data(), message.size());
392
393
0
    digest = hash.digest();
394
    // and interpret the 64-octet digest as a little-endian integer k.
395
0
    u8 k[64] {};
396
0
    memcpy(k, digest.data, 64);
397
398
    // 5. Compute S = (r + k * s) mod L. For efficiency, again reduce k modulo L first.
399
0
    barrett_reduce(prefix, k);
400
0
    multiply(k, k + 32, prefix, s, 32);
401
0
    barrett_reduce(prefix, k);
402
0
    add(s, prefix, r, 32);
403
404
    // modular reduction
405
0
    auto reduced_s = TRY(ByteBuffer::create_uninitialized(32));
406
0
    auto is_negative = subtract(prefix, s, Curve25519::BASE_POINT_L_ORDER, 32);
407
0
    select(reduced_s.data(), prefix, s, is_negative, 32);
408
409
    // 6.  Form the signature of the concatenation of R (32 octets) and the little-endian encoding of S
410
    // (32 octets; the three most significant bits of the final octet are always zero).
411
0
    auto signature = TRY(ByteBuffer::copy(R));
412
0
    signature.append(reduced_s);
413
414
0
    return signature;
415
0
}
416
417
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7
418
bool Ed25519::verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message)
419
0
{
420
0
    auto not_valid = false;
421
422
    // 1. To verify a signature on a message M using public key A,
423
    // with F being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or Ed25519ph is being used, C being the context
424
    // first split the signature into two 32-octet halves.
425
    // If any of the decodings fail (including S being out of range), the signature is invalid.
426
427
    // NOTE: We dont care about F, since we dont implement Ed25519ctx or Ed25519PH
428
    // NOTE: C is the internal state, so its not a parameter
429
430
0
    if (public_key.size() != key_size() || signature.size() != signature_size())
431
0
        return false;
432
433
0
    auto half_signature_size = signature_size() / 2;
434
435
    // Decode the first half as a point R
436
0
    u8 r[32] {};
437
0
    memcpy(r, signature.data(), half_signature_size);
438
439
    // and the second half as an integer S, in the range  0 <= s < L.
440
0
    u8 s[32] {};
441
0
    memcpy(s, signature.data() + half_signature_size, half_signature_size);
442
443
    // NOTE: Ed25519 and Ed448 signatures are not malleable due to the verification check that decoded S is smaller than l.
444
    // Without this check, one can add a multiple of l into a scalar part and still pass signature verification,
445
    // resulting in malleable signatures.
446
0
    u8 p[32] {};
447
0
    auto is_negative = subtract(p, s, Curve25519::BASE_POINT_L_ORDER, half_signature_size);
448
0
    not_valid |= 1 ^ is_negative;
449
450
    // Decode the public key A as point A'.
451
0
    Ed25519Point ka {};
452
0
    not_valid |= decode_point(&ka, public_key.data());
453
454
    // 2. Compute SHA512(dom2(F, C) || R || A || PH(M)), and interpret the 64-octet digest as a little-endian integer k.
455
0
    Crypto::Hash::SHA512 hash;
456
    // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
457
0
    hash.update(r, half_signature_size);
458
    // NOTE: A == public_key
459
0
    hash.update(public_key.data(), key_size());
460
    // NOTE: PH(M) = M
461
0
    hash.update(message.data(), message.size());
462
463
0
    auto digest = hash.digest();
464
0
    auto k = digest.data;
465
466
    // 3.  Check the group equation [8][S]B = [8]R + [8][k]A'.
467
    // It's sufficient, but not required, to instead check [S]B = R + [k]A'.
468
    // NOTE: For efficiency, do this by first reducing k modulo L.
469
0
    barrett_reduce(k, k);
470
471
    // NOTE: We check [S]B - [k]A' == R
472
0
    Curve25519::modular_subtract(ka.x, Curve25519::ZERO, ka.x);
473
0
    Curve25519::modular_subtract(ka.t, Curve25519::ZERO, ka.t);
474
0
    Ed25519Point sb {};
475
0
    point_multiply_scalar(&sb, s, &BASE_POINT);
476
0
    point_multiply_scalar(&ka, k, &ka);
477
0
    point_add(&ka, &sb, &ka);
478
0
    encode_point(&ka, p);
479
480
0
    not_valid |= compare(p, r, half_signature_size);
481
482
0
    return !not_valid;
483
0
}
484
485
}