v / vlib / crypto / ed25519 / internal / edwards25519 / point.v
548 lines · 464 sloc · 12.36 KB
Raw
1module edwards25519
2
3// d is a constant in the curve equation.
4const d_bytes = [u8(0xa3), 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 0xab, 0xd8, 0x41, 0x41, 0x4d,
5 0x0a, 0x70, 0x00, 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 0x73, 0xfe, 0x6f, 0x2b, 0xee,
6 0x6c, 0x03, 0x52]
7const id_bytes = [u8(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8 0, 0, 0, 0, 0, 0, 0]
9const gen_bytes = [u8(0x58), 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
10 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
11 0x66, 0x66, 0x66, 0x66]
12const d_const = d_const_generate() or { panic(err) }
13const d2_const = d2_const_generate() or { panic(err) }
14// id_point is the point at infinity.
15const id_point = id_point_generate() or { panic(err) }
16// generator point
17const gen_point = generator() or { panic(err) }
18
19fn d_const_generate() !Element {
20 mut v := Element{}
21 v.set_bytes(d_bytes)!
22 return v
23}
24
25fn d2_const_generate() !Element {
26 mut v := Element{}
27 v.add(d_const, d_const)
28 return v
29}
30
31// id_point_generate is the point at infinity.
32fn id_point_generate() !Point {
33 mut p := Point{}
34 p.set_bytes(id_bytes)!
35 return p
36}
37
38// generator is the canonical curve basepoint. See TestGenerator for the
39// correspondence of this encoding with the values in RFC 8032.
40fn generator() !Point {
41 mut p := Point{}
42 p.set_bytes(gen_bytes)!
43 return p
44}
45
46// Point types.
47struct ProjectiveP1 {
48mut:
49 x Element
50 y Element
51 z Element
52 t Element
53}
54
55struct ProjectiveP2 {
56mut:
57 x Element
58 y Element
59 z Element
60}
61
62// Point represents a point on the edwards25519 curve.
63//
64// This type works similarly to math/big.Int, and all arguments and receivers
65// are allowed to alias.
66//
67// The zero value is NOT valid, and it may be used only as a receiver.
68pub struct Point {
69mut:
70 // The point is internally represented in extended coordinates (x, y, z, T)
71 // where x = x/z, y = y/z, and xy = T/z per https://eprint.iacr.org/2008/522.
72 x Element
73 y Element
74 z Element
75 t Element
76 // Make the type not comparable (i.e. used with == or as a map key), as
77 // equivalent points can be represented by different values.
78 // _ incomparable
79}
80
81fn check_initialized(points ...Point) {
82 for _, p in points {
83 if p.x == fe_zero && p.y == fe_zero {
84 panic('edwards25519: use of uninitialized Point')
85 }
86 }
87}
88
89struct ProjectiveCached {
90mut:
91 ypx Element // y + x
92 ymx Element // y - x
93 z Element
94 t2d Element
95}
96
97struct AffineCached {
98mut:
99 ypx Element // y + x
100 ymx Element // y - x
101 t2d Element
102}
103
104fn (mut v ProjectiveP2) zero() ProjectiveP2 {
105 v.x.zero()
106 v.y.one()
107 v.z.one()
108 return v
109}
110
111// set_bytes sets v = x, where x is a 32-byte encoding of v. If x does not
112// represent a valid point on the curve, set_bytes returns an error and
113// the receiver is unchanged. Otherwise, set_bytes returns v.
114//
115// Note that set_bytes accepts all non-canonical encodings of valid points.
116// That is, it follows decoding rules that match most implementations in
117// the ecosystem rather than RFC 8032.
118pub fn (mut v Point) set_bytes(x []u8) !Point {
119 // Specifically, the non-canonical encodings that are accepted are
120 // 1) the ones where the edwards25519 element is not reduced (see the
121 // (*edwards25519.Element).set_bytes docs) and
122 // 2) the ones where the x-coordinate is zero and the sign bit is set.
123 //
124 // This is consistent with crypto/ed25519/internal/edwards25519. Read more
125 // at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
126 // "Canonical A, R" section.
127 mut el0 := Element{}
128 y := el0.set_bytes(x) or { return error('edwards25519: invalid point encoding length') }
129
130 // -x² + y² = 1 + dx²y²
131 // x² + dx²y² = x²(dy² + 1) = y² - 1
132 // x² = (y² - 1) / (dy² + 1)
133
134 // u = y² - 1
135 mut el1 := Element{}
136 y2 := el1.square(y)
137 mut el2 := Element{}
138 u := el2.subtract(y2, fe_one)
139
140 // v = dy² + 1
141 mut el3 := Element{}
142 mut vv := el3.multiply(y2, d_const)
143 vv = vv.add(vv, fe_one)
144
145 // x = +√(u/v)
146 mut el4 := Element{}
147 mut xx, was_square := el4.sqrt_ratio(u, vv)
148 if was_square == 0 {
149 return error('edwards25519: invalid point encoding')
150 }
151
152 // selected the negative square root if the sign bit is set.
153 mut el5 := Element{}
154 xx_neg := el5.negate(xx)
155 xx.selected(xx_neg, xx, int(x[31] >> 7))
156
157 v.x.set(xx)
158 v.y.set(y)
159 v.z.one()
160 v.t.multiply(xx, y) // xy = T / z
161
162 return v
163}
164
165// set sets v = u, and returns v.
166pub fn (mut v Point) set(u Point) Point {
167 v = u
168 return v
169}
170
171// new_identity_point returns a new Point set to the identity.
172pub fn new_identity_point() Point {
173 mut p := Point{}
174 return p.set(id_point)
175}
176
177// new_generator_point returns a new Point set to the canonical generator.
178pub fn new_generator_point() Point {
179 mut p := Point{}
180 return p.set(gen_point)
181}
182
183fn (mut v ProjectiveCached) zero() ProjectiveCached {
184 v.ypx.one()
185 v.ymx.one()
186 v.z.one()
187 v.t2d.zero()
188 return v
189}
190
191fn (mut v AffineCached) zero() AffineCached {
192 v.ypx.one()
193 v.ymx.one()
194 v.t2d.zero()
195 return v
196}
197
198// Encoding.
199
200// bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
201// Section 5.1.2.
202pub fn (mut v Point) bytes() []u8 {
203 // This function is outlined to make the allocations inline in the caller
204 // rather than happen on the heap.
205 mut buf := [32]u8{}
206 return v.bytes_generic(mut buf)
207}
208
209fn (mut v Point) bytes_generic(mut buf [32]u8) []u8 {
210 check_initialized(v)
211
212 mut zinv := Element{}
213 mut x := Element{}
214 mut y := Element{}
215 zinv.invert(v.z) // zinv = 1 / z
216 x.multiply(v.x, zinv) // x = x / z
217 y.multiply(v.y, zinv) // y = y / z
218
219 mut out := copy_field_element(mut buf, mut y)
220 unsafe {
221 // out[31] |= u8(x.is_negative() << 7) //original one
222 out[31] |= u8(x.is_negative() * 128) // x << 7 == x * 2^7
223 }
224 return out
225}
226
227fn copy_field_element(mut buf [32]u8, mut v Element) []u8 {
228 // this fail in test
229 /*
230 copy(mut buf[..], v.bytes())
231 return buf[..]
232 */
233
234 // this pass the test
235 mut out := []u8{len: 32}
236 for i := 0; i <= buf.len - 1; i++ {
237 out[i] = v.bytes()[i]
238 }
239
240 return out
241}
242
243// Conversions.
244
245fn (mut v ProjectiveP2) from_p1(p ProjectiveP1) ProjectiveP2 {
246 v.x.multiply(p.x, p.t)
247 v.y.multiply(p.y, p.z)
248 v.z.multiply(p.z, p.t)
249 return v
250}
251
252fn (mut v ProjectiveP2) from_p3(p Point) ProjectiveP2 {
253 v.x.set(p.x)
254 v.y.set(p.y)
255 v.z.set(p.z)
256 return v
257}
258
259fn (mut v Point) from_p1(p ProjectiveP1) Point {
260 v.x.multiply(p.x, p.t)
261 v.y.multiply(p.y, p.z)
262 v.z.multiply(p.z, p.t)
263 v.t.multiply(p.x, p.y)
264 return v
265}
266
267fn (mut v Point) from_p2(p ProjectiveP2) Point {
268 v.x.multiply(p.x, p.z)
269 v.y.multiply(p.y, p.z)
270 v.z.square(p.z)
271 v.t.multiply(p.x, p.y)
272 return v
273}
274
275fn (mut v ProjectiveCached) from_p3(p Point) ProjectiveCached {
276 v.ypx.add(p.y, p.x)
277 v.ymx.subtract(p.y, p.x)
278 v.z.set(p.z)
279 v.t2d.multiply(p.t, d2_const)
280 return v
281}
282
283fn (mut v AffineCached) from_p3(p Point) AffineCached {
284 v.ypx.add(p.y, p.x)
285 v.ymx.subtract(p.y, p.x)
286 v.t2d.multiply(p.t, d2_const)
287
288 mut invz := Element{}
289 invz.invert(p.z)
290 v.ypx.multiply(v.ypx, invz)
291 v.ymx.multiply(v.ymx, invz)
292 v.t2d.multiply(v.t2d, invz)
293 return v
294}
295
296// (Re)addition and subtraction.
297
298// add sets v = p + q, and returns v.
299pub fn (mut v Point) add(p Point, q Point) Point {
300 check_initialized(p, q)
301 mut pc := ProjectiveCached{}
302 mut p1 := ProjectiveP1{}
303 qcached := pc.from_p3(q)
304
305 result := p1.add(p, qcached)
306 return v.from_p1(result)
307}
308
309// subtract sets v = p - q, and returns v.
310pub fn (mut v Point) subtract(p Point, q Point) Point {
311 check_initialized(p, q)
312 mut pc := ProjectiveCached{}
313 mut p1 := ProjectiveP1{}
314 qcached := pc.from_p3(q)
315 result := p1.sub(p, qcached)
316 return v.from_p1(result)
317}
318
319fn (mut v ProjectiveP1) add(p Point, q ProjectiveCached) ProjectiveP1 {
320 // var ypx, ymx, pp, mm, tt2d, zz2 edwards25519.Element
321 mut ypx := Element{}
322 mut ymx := Element{}
323 mut pp := Element{}
324 mut mm := Element{}
325 mut tt2d := Element{}
326 mut zz2 := Element{}
327
328 ypx.add(p.y, p.x)
329 ymx.subtract(p.y, p.x)
330
331 pp.multiply(ypx, q.ypx)
332 mm.multiply(ymx, q.ymx)
333 tt2d.multiply(p.t, q.t2d)
334 zz2.multiply(p.z, q.z)
335
336 zz2.add(zz2, zz2)
337
338 v.x.subtract(pp, mm)
339 v.y.add(pp, mm)
340 v.z.add(zz2, tt2d)
341 v.t.subtract(zz2, tt2d)
342 return v
343}
344
345fn (mut v ProjectiveP1) sub(p Point, q ProjectiveCached) ProjectiveP1 {
346 mut ypx := Element{}
347 mut ymx := Element{}
348 mut pp := Element{}
349 mut mm := Element{}
350 mut tt2d := Element{}
351 mut zz2 := Element{}
352
353 ypx.add(p.y, p.x)
354 ymx.subtract(p.y, p.x)
355
356 pp.multiply(ypx, q.ymx) // flipped sign
357 mm.multiply(ymx, q.ypx) // flipped sign
358 tt2d.multiply(p.t, q.t2d)
359 zz2.multiply(p.z, q.z)
360
361 zz2.add(zz2, zz2)
362
363 v.x.subtract(pp, mm)
364 v.y.add(pp, mm)
365 v.z.subtract(zz2, tt2d) // flipped sign
366 v.t.add(zz2, tt2d) // flipped sign
367 return v
368}
369
370fn (mut v ProjectiveP1) add_affine(p Point, q AffineCached) ProjectiveP1 {
371 mut ypx := Element{}
372 mut ymx := Element{}
373 mut pp := Element{}
374 mut mm := Element{}
375 mut tt2d := Element{}
376 mut z2 := Element{}
377
378 ypx.add(p.y, p.x)
379 ymx.subtract(p.y, p.x)
380
381 pp.multiply(ypx, q.ypx)
382 mm.multiply(ymx, q.ymx)
383 tt2d.multiply(p.t, q.t2d)
384
385 z2.add(p.z, p.z)
386
387 v.x.subtract(pp, mm)
388 v.y.add(pp, mm)
389 v.z.add(z2, tt2d)
390 v.t.subtract(z2, tt2d)
391 return v
392}
393
394fn (mut v ProjectiveP1) sub_affine(p Point, q AffineCached) ProjectiveP1 {
395 mut ypx := Element{}
396 mut ymx := Element{}
397 mut pp := Element{}
398 mut mm := Element{}
399 mut tt2d := Element{}
400 mut z2 := Element{}
401
402 ypx.add(p.y, p.x)
403 ymx.subtract(p.y, p.x)
404
405 pp.multiply(ypx, q.ymx) // flipped sign
406 mm.multiply(ymx, q.ypx) // flipped sign
407 tt2d.multiply(p.t, q.t2d)
408
409 z2.add(p.z, p.z)
410
411 v.x.subtract(pp, mm)
412 v.y.add(pp, mm)
413 v.z.subtract(z2, tt2d) // flipped sign
414 v.t.add(z2, tt2d) // flipped sign
415 return v
416}
417
418// Doubling.
419
420fn (mut v ProjectiveP1) double(p ProjectiveP2) ProjectiveP1 {
421 // var xx, yy, zz2, xplusysq edwards25519.Element
422 mut xx := Element{}
423 mut yy := Element{}
424 mut zz2 := Element{}
425 mut xplusysq := Element{}
426
427 xx.square(p.x)
428 yy.square(p.y)
429 zz2.square(p.z)
430 zz2.add(zz2, zz2)
431 xplusysq.add(p.x, p.y)
432 xplusysq.square(xplusysq)
433
434 v.y.add(yy, xx)
435 v.z.subtract(yy, xx)
436
437 v.x.subtract(xplusysq, v.y)
438 v.t.subtract(zz2, v.z)
439 return v
440}
441
442// Negation.
443
444// negate sets v = -p, and returns v.
445pub fn (mut v Point) negate(p Point) Point {
446 check_initialized(p)
447 v.x.negate(p.x)
448 v.y.set(p.y)
449 v.z.set(p.z)
450 v.t.negate(p.t)
451 return v
452}
453
454// equal returns 1 if v is equivalent to u, and 0 otherwise.
455pub fn (mut v Point) equal(u Point) int {
456 check_initialized(v, u)
457
458 mut t1 := Element{}
459 mut t2 := Element{}
460 mut t3 := Element{}
461 mut t4 := Element{}
462
463 t1.multiply(v.x, u.z)
464 t2.multiply(u.x, v.z)
465 t3.multiply(v.y, u.z)
466 t4.multiply(u.y, v.z)
467
468 return t1.equal(t2) & t3.equal(t4)
469}
470
471// Constant-time operations
472
473// selected sets v to a if cond == 1 and to b if cond == 0.
474fn (mut v ProjectiveCached) selected(a ProjectiveCached, b ProjectiveCached, cond int) ProjectiveCached {
475 v.ypx.selected(a.ypx, b.ypx, cond)
476 v.ymx.selected(a.ymx, b.ymx, cond)
477 v.z.selected(a.z, b.z, cond)
478 v.t2d.selected(a.t2d, b.t2d, cond)
479 return v
480}
481
482// selected sets v to a if cond == 1 and to b if cond == 0.
483fn (mut v AffineCached) selected(a AffineCached, b AffineCached, cond int) AffineCached {
484 v.ypx.selected(a.ypx, b.ypx, cond)
485 v.ymx.selected(a.ymx, b.ymx, cond)
486 v.t2d.selected(a.t2d, b.t2d, cond)
487 return v
488}
489
490// cond_neg negates v if cond == 1 and leaves it unchanged if cond == 0.
491fn (mut v ProjectiveCached) cond_neg(cond int) ProjectiveCached {
492 mut el := Element{}
493 v.ypx.swap(mut v.ymx, cond)
494 v.t2d.selected(el.negate(v.t2d), v.t2d, cond)
495 return v
496}
497
498// cond_neg negates v if cond == 1 and leaves it unchanged if cond == 0.
499fn (mut v AffineCached) cond_neg(cond int) AffineCached {
500 mut el := Element{}
501 v.ypx.swap(mut v.ymx, cond)
502 v.t2d.selected(el.negate(v.t2d), v.t2d, cond)
503 return v
504}
505
506fn check_on_curve(points ...Point) bool {
507 for p in points {
508 mut xx := Element{}
509 mut yy := Element{}
510 mut zz := Element{}
511 mut zzzz := Element{}
512 xx.square(p.x)
513 yy.square(p.y)
514 zz.square(p.z)
515 zzzz.square(zz)
516 // -x² + y² = 1 + dx²y²
517 // -(X/Z)² + (Y/Z)² = 1 + d(X/Z)²(Y/Z)²
518 // (-X² + Y²)/Z² = 1 + (dX²Y²)/Z⁴
519 // (-X² + Y²)*Z² = Z⁴ + dX²Y²
520 mut lhs := Element{}
521 mut rhs := Element{}
522 lhs.subtract(yy, xx)
523 lhs.multiply(lhs, zz)
524 rhs.multiply(d_const, xx)
525 rhs.multiply(rhs, yy)
526 rhs.add(rhs, zzzz)
527
528 if lhs.equal(rhs) != 1 {
529 return false
530 }
531 /*
532 if lhs.equal(rhs) != 1 {
533 lg.error('X, Y, and Z do not specify a point on the curve\nX = ${p.x} \nY = ${p.y}\nZ = ${p.z}')
534 }*/
535
536 // xy = T/Z
537 lhs.multiply(p.x, p.y)
538 rhs.multiply(p.z, p.t)
539 /*
540 if lhs.equal(rhs) != 1 {
541 lg.error('point ${i} is not valid\nX = ${p.x}\nY = ${p.y}\nZ = ${p.z}')
542 }*/
543 if lhs.equal(rhs) != 1 {
544 return false
545 }
546 }
547 return true
548}
549