/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 | | } |