mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-27 06:08:13 +00:00
581 lines
13 KiB
Odin
581 lines
13 KiB
Odin
package _weierstrass
|
||
|
||
/*
|
||
This implements prime order short Weierstrass curves defined over a field
|
||
k with char(k) != 2, 3 (`y^2 = x^3 + ax + b`). for the purpose of
|
||
implementing ECDH and ECDSA. Use of this package for other purposes is
|
||
NOT RECOMMENDED.
|
||
|
||
As an explicit simplicity/performance tradeoff, projective representation
|
||
was chosen so that it is possible to use the complete addition
|
||
formulas.
|
||
|
||
See:
|
||
- https://eprint.iacr.org/2015/1060.pdf
|
||
- https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html
|
||
|
||
WARNING: The point addition and doubling formulas are specialized for
|
||
`a = -3`, which covers secp256r1, secp384r1, secp521r1, FRP256v1, SM2,
|
||
and GOST 34.10. The brainpool curves and secp256k1 are NOT SUPPORTED
|
||
and would require slightly different formulas.
|
||
*/
|
||
|
||
Point_p256r1 :: struct {
|
||
x: Field_Element_p256r1,
|
||
y: Field_Element_p256r1,
|
||
z: Field_Element_p256r1,
|
||
}
|
||
|
||
Point_p384r1 :: struct {
|
||
x: Field_Element_p384r1,
|
||
y: Field_Element_p384r1,
|
||
z: Field_Element_p384r1,
|
||
}
|
||
|
||
@(require_results)
|
||
pt_set_xy_bytes :: proc "contextless" (p: ^$T, x_raw, y_raw: []byte) -> bool {
|
||
when T == Point_p256r1 {
|
||
FE_SZ :: FE_SIZE_P256R1
|
||
x, y: Field_Element_p256r1
|
||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y})
|
||
} else when T == Point_p384r1 {
|
||
FE_SZ :: FE_SIZE_P384R1
|
||
x, y: Field_Element_p384r1
|
||
defer fe_clear_vec([]^Field_Element_p384r1{&x, &y})
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
if len(x_raw) != FE_SZ || len(y_raw) != FE_SZ {
|
||
return false
|
||
}
|
||
|
||
if !fe_set_bytes(&x, x_raw) {
|
||
return false
|
||
}
|
||
if !fe_set_bytes(&y, y_raw) {
|
||
return false
|
||
}
|
||
if !is_on_curve(&x, &y) {
|
||
return false
|
||
}
|
||
|
||
fe_set(&p.x, &x)
|
||
fe_set(&p.y, &y)
|
||
fe_one(&p.z)
|
||
|
||
return true
|
||
}
|
||
|
||
@(require_results)
|
||
pt_set_x_bytes :: proc "contextless" (p: ^$T, x_raw: []byte, y_is_odd: int) -> bool {
|
||
when T == Point_p256r1 {
|
||
FE_SZ :: FE_SIZE_P256R1
|
||
x, y, yy, y_neg: Field_Element_p256r1
|
||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y, &yy, &y_neg})
|
||
} else when T == Point_p384r1 {
|
||
FE_SZ :: FE_SIZE_P384R1
|
||
x, y, yy, y_neg: Field_Element_p384r1
|
||
defer fe_clear_vec([]^Field_Element_p384r1{&x, &y, &yy, &y_neg})
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
if len(x_raw) != FE_SZ {
|
||
return false
|
||
}
|
||
|
||
if !fe_set_bytes(&x, x_raw) {
|
||
return false
|
||
}
|
||
set_yy_candidate(&yy, &x)
|
||
if fe_sqrt(&y, &yy) != 1 {
|
||
return false
|
||
}
|
||
|
||
// Pick the correct y-coordinate.
|
||
fe_negate(&y_neg, &y)
|
||
parity_neq := (y_is_odd ~ fe_is_odd(&y)) & 1
|
||
|
||
fe_set(&p.x, &x)
|
||
fe_cond_select(&p.y, &y, &y_neg, parity_neq)
|
||
fe_one(&p.z)
|
||
|
||
return true
|
||
}
|
||
|
||
@(require_results)
|
||
pt_bytes :: proc "contextless" (x, y: []byte, p: ^$T) -> bool {
|
||
when T == Point_p256r1 {
|
||
FE_SZ :: FE_SIZE_P256R1
|
||
} else when T == Point_p384r1 {
|
||
FE_SZ :: FE_SIZE_P384R1
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
if pt_is_identity(p) == 1 {
|
||
return false
|
||
}
|
||
|
||
// Convert to affine coordinates.
|
||
pt_rescale(p, p)
|
||
|
||
switch len(x) {
|
||
case 0:
|
||
case FE_SZ:
|
||
fe_bytes(x, &p.x)
|
||
case:
|
||
panic_contextless("weierstrass: invalid x buffer")
|
||
}
|
||
switch len(y) {
|
||
case 0:
|
||
case FE_SZ:
|
||
fe_bytes(y, &p.y)
|
||
case:
|
||
panic_contextless("weierstrass: invalid y buffer")
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
pt_set :: proc "contextless" (p, q: ^$T) {
|
||
fe_set(&p.x, &q.x)
|
||
fe_set(&p.y, &q.y)
|
||
fe_set(&p.z, &q.z)
|
||
}
|
||
|
||
pt_identity :: proc "contextless" (p: ^$T) {
|
||
fe_zero(&p.x)
|
||
fe_one(&p.y)
|
||
fe_zero(&p.z)
|
||
}
|
||
|
||
pt_generator :: proc "contextless" (p: ^$T) {
|
||
fe_gen_x(&p.x)
|
||
fe_gen_y(&p.y)
|
||
fe_one(&p.z)
|
||
}
|
||
|
||
pt_clear :: proc "contextless" (p: ^$T) {
|
||
fe_clear(&p.x)
|
||
fe_clear(&p.y)
|
||
fe_clear(&p.z)
|
||
}
|
||
|
||
pt_clear_vec :: proc "contextless" (arg: []^$T) {
|
||
for p in arg {
|
||
pt_clear(p)
|
||
}
|
||
}
|
||
|
||
pt_add :: proc "contextless" (p, a, b: ^$T) {
|
||
// Algorithm 4 from "Complete addition formulas for prime
|
||
// order elliptic curves" by Renes, Costello, and Batina.
|
||
//
|
||
// The formula is complete in that it is valid for all a and b,
|
||
// without exceptions or extra assumptions about the inputs.
|
||
//
|
||
// The operation costs are `12M + 2mb + 29a`.
|
||
|
||
when T == Point_p256r1 {
|
||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||
x3, y3, z3: Field_Element_p256r1
|
||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||
} else when T == Point_p384r1 {
|
||
t0, t1, t2, t3, t4, b_fe: Field_Element_p384r1
|
||
x3, y3, z3: Field_Element_p384r1
|
||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||
x2, y2, z2 := &b.x, &b.y, &b.z
|
||
|
||
fe_b(&b_fe)
|
||
|
||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t2 := Z1 * Z2 ;
|
||
fe_mul(&t0, x1, x2)
|
||
fe_mul(&t1, y1, y2)
|
||
fe_mul(&t2, z1, z2)
|
||
|
||
// t3 := X1 + Y1 ; t4 := X2 + Y2 ; t3 := t3 * t4 ;
|
||
fe_add(&t3, x1, y1)
|
||
fe_add(&t4, x2, y2)
|
||
fe_mul(&t3, &t3, &t4)
|
||
|
||
// t4 := t0 + t1 ; t3 := t3 - t4 ; t4 := Y1 + Z1 ;
|
||
fe_add(&t4, &t0, &t1)
|
||
fe_sub(&t3, &t3, &t4)
|
||
fe_add(&t4, y1, z1)
|
||
|
||
// X3 := Y2 + Z2 ; t4 := t4 * X3 ; X3 := t1 + t2 ;
|
||
fe_add(&x3, y2, z2)
|
||
fe_mul(&t4, &t4, &x3)
|
||
fe_add(&x3, &t1, &t2)
|
||
|
||
// t4 := t4 - X3 ; X3 := X1 + Z1 ; Y3 := X2 + Z2 ;
|
||
fe_sub(&t4, &t4, &x3)
|
||
fe_add(&x3, x1, z1)
|
||
fe_add(&y3, x2, z2)
|
||
|
||
// X3 := X3 * Y3 ; Y3 := t0 + t2 ; Y3 := X3 - Y3 ;
|
||
fe_mul(&x3, &x3, &y3)
|
||
fe_add(&y3, &t0, &t2)
|
||
fe_sub(&y3, &x3, &y3)
|
||
|
||
// Z3 := b * t2 ; X3 := Y3 - Z3 ; Z3 := X3 + X3 ;
|
||
fe_mul(&z3, &b_fe, &t2)
|
||
fe_sub(&x3, &y3, &z3)
|
||
fe_add(&z3, &x3, &x3)
|
||
|
||
// X3 := X3 + Z3 ; Z3 := t1 - X3 ; X3 := t1 + X3 ;
|
||
fe_add(&x3, &x3, &z3)
|
||
fe_sub(&z3, &t1, &x3)
|
||
fe_add(&x3, &t1, &x3)
|
||
|
||
// Y3 := b * Y3 ; t1 := t2 + t2 ; t2 := t1 + t2 ;
|
||
fe_mul(&y3, &b_fe, &y3)
|
||
fe_add(&t1, &t2, &t2)
|
||
fe_add(&t2, &t1, &t2)
|
||
|
||
// Y3 := Y3 - t2 ; Y3 := Y3 - t0 ; t1 := Y3 + Y3 ;
|
||
fe_sub(&y3, &y3, &t2)
|
||
fe_sub(&y3, &y3, &t0)
|
||
fe_add(&t1, &y3, &y3)
|
||
|
||
// Y3 := t1 + Y3 ; t1 := t0 + t0 ; t0 := t1 + t0 ;
|
||
fe_add(&y3, &t1, &y3)
|
||
fe_add(&t1, &t0, &t0)
|
||
fe_add(&t0, &t1, &t0)
|
||
|
||
// t0 := t0 - t2 ; t1 := t4 * Y3 ; t2 := t0 * Y3 ;
|
||
fe_sub(&t0, &t0, &t2)
|
||
fe_mul(&t1, &t4, &y3)
|
||
fe_mul(&t2, &t0, &y3)
|
||
|
||
// Y3 := X3 * Z3 ; Y3 := Y3 + t2 ; X3 := t3 * X3 ;
|
||
fe_mul(&y3, &x3, &z3)
|
||
fe_add(&y3, &y3, &t2)
|
||
fe_mul(&x3, &t3, &x3)
|
||
|
||
// X3 := X3 - t1 ; Z3 := t4 * Z3 ; t1 := t3 * t0 ;
|
||
fe_sub(&x3, &x3, &t1)
|
||
fe_mul(&z3, &t4, &z3)
|
||
fe_mul(&t1, &t3, &t0)
|
||
|
||
// Z3 := Z3 + t1 ;
|
||
fe_add(&z3, &z3, &t1)
|
||
|
||
// return X3 , Y3 , Z3 ;
|
||
fe_set(&p.x, &x3)
|
||
fe_set(&p.y, &y3)
|
||
fe_set(&p.z, &z3)
|
||
}
|
||
|
||
@(private)
|
||
pt_add_mixed :: proc "contextless" (p, a: ^$T, x2, y2: ^$U) {
|
||
// Algorithm 5 from "Complete addition formulas for prime
|
||
// order elliptic curves" by Renes, Costello, and Batina.
|
||
//
|
||
// The formula is mixed in that it assumes the z-coordinate
|
||
// of the addend (`Z2`) is `1`, meaning that it CAN NOT
|
||
// handle the addend being the point at infinity.
|
||
//
|
||
// The operation costs are `11M + 2mb + 23a` saving
|
||
// `1M + 6a` over `pt_add`.
|
||
|
||
when T == Point_p256r1 && U == Field_Element_p256r1 {
|
||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||
x3, y3, z3: Field_Element_p256r1
|
||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||
} else when T == Point_p384r1 {
|
||
t0, t1, t2, t3, t4, b_fe: Field_Element_p384r1
|
||
x3, y3, z3: Field_Element_p384r1
|
||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||
|
||
fe_b(&b_fe)
|
||
|
||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t3 := X2 + Y2 ;
|
||
fe_mul(&t0, x1, x2)
|
||
fe_mul(&t1, y1, y2)
|
||
fe_add(&t3, x2, y2)
|
||
|
||
// t4 := X1 + Y1 ; t3 := t3 * t4 ; t4 := t0 + t1 ;
|
||
fe_add(&t4, x1, y1)
|
||
fe_mul(&t3, &t3, &t4)
|
||
fe_add(&t4, &t0, &t1)
|
||
|
||
// t3 := t3 − t4 ; t4 := Y2 * Z1 ; t4 := t4 + Y1 ;
|
||
fe_sub(&t3, &t3, &t4)
|
||
fe_mul(&t4, y2, z1)
|
||
fe_add(&t4, &t4, y1)
|
||
|
||
// Y3 := X2 * Z1 ; Y3 := Y3 + X1 ; Z3 := b * Z1 ;
|
||
fe_mul(&y3, x2, z1)
|
||
fe_add(&y3, &y3, x1)
|
||
fe_mul(&z3, &b_fe, z1)
|
||
|
||
// X3 := Y3 − Z3 ; Z3 := X3 + X3 ; X3 := X3 + Z3 ;
|
||
fe_sub(&x3, &y3, &z3)
|
||
fe_add(&z3, &x3, &x3)
|
||
fe_add(&x3, &x3, &z3)
|
||
|
||
// Z3 := t1 − X3 ; X3 := t1 + X3 ;. Y3 := b * Y3 ;
|
||
fe_sub(&z3, &t1, &x3)
|
||
fe_add(&x3, &t1, &x3)
|
||
fe_mul(&y3, &b_fe, &y3)
|
||
|
||
// t1 := Z1 + Z1 ; t2 := t1 + Z1 ; Y3 := Y3 − t2 ;
|
||
fe_add(&t1, z1, z1)
|
||
fe_add(&t2, &t1, z1)
|
||
fe_sub(&y3, &y3, &t2)
|
||
|
||
// Y3 := Y3 − t0 ; t1 := Y3 + Y3 ; Y3 := t1 + Y3 ;
|
||
fe_sub(&y3, &y3, &t0)
|
||
fe_add(&t1, &y3, &y3)
|
||
fe_add(&y3, &t1, &y3)
|
||
|
||
// t1 := t0 + t0 ; t0 := t1 + t0 ; t0 := t0 − t2 ;
|
||
fe_add(&t1, &t0, &t0)
|
||
fe_add(&t0, &t1, &t0)
|
||
fe_sub(&t0, &t0, &t2)
|
||
|
||
// t1 := t4 * Y3 ; t2 := t0 * Y3 ; Y3 := X3 * Z3 ;
|
||
fe_mul(&t1, &t4, &y3)
|
||
fe_mul(&t2, &t0, &y3)
|
||
fe_mul(&y3, &x3, &z3)
|
||
|
||
// Y3 := Y3 + t2 ; X3 := t3 * X3 ; X3 := X3 − t1 ;
|
||
fe_add(&y3, &y3, &t2)
|
||
fe_mul(&x3, &t3, &x3)
|
||
fe_sub(&x3, &x3, &t1)
|
||
|
||
// Z3 := t4 * Z3 ; t1 := t3 * t0 ; Z3 := Z3 + t1 ;
|
||
fe_mul(&z3, &t4, &z3)
|
||
fe_mul(&t1, &t3, &t0)
|
||
fe_add(&z3, &z3, &t1)
|
||
|
||
// return X3 , Y3 , Z3 ;
|
||
fe_set(&p.x, &x3)
|
||
fe_set(&p.y, &y3)
|
||
fe_set(&p.z, &z3)
|
||
}
|
||
|
||
pt_double :: proc "contextless" (p, a: ^$T) {
|
||
// Algorithm 6 from "Complete addition formulas for prime
|
||
// order elliptic curves" by Renes, Costello, and Batina.
|
||
//
|
||
// The formula is complete in that it is valid for all a,
|
||
// without exceptions or extra assumptions about the inputs.
|
||
//
|
||
// The operation costs are `8M + 3S + 2mb + 21a`.
|
||
|
||
when T == Point_p256r1 {
|
||
t0, t1, t2, t3, b_fe: Field_Element_p256r1
|
||
x3, y3, z3: Field_Element_p256r1
|
||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &x3, &y3, &z3})
|
||
} else when T == Point_p384r1 {
|
||
t0, t1, t2, t3, b_fe: Field_Element_p384r1
|
||
x3, y3, z3: Field_Element_p384r1
|
||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &x3, &y3, &z3})
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
x, y, z := &a.x, &a.y, &a.z
|
||
|
||
fe_b(&b_fe)
|
||
|
||
// t0 := X ^2; t1 := Y ^2; t2 := Z ^2;
|
||
fe_square(&t0, x)
|
||
fe_square(&t1, y)
|
||
fe_square(&t2, z)
|
||
|
||
// t3 := X * Y ; t3 := t3 + t3 ; Z3 := X * Z ;
|
||
fe_mul(&t3, x, y)
|
||
fe_add(&t3, &t3, &t3)
|
||
fe_mul(&z3, x, z)
|
||
|
||
// Z3 := Z3 + Z3 ; Y3 := b * t2 ; Y3 := Y3 - Z3 ;
|
||
fe_add(&z3, &z3, &z3)
|
||
fe_mul(&y3, &b_fe, &t2)
|
||
fe_sub(&y3, &y3, &z3)
|
||
|
||
// X3 := Y3 + Y3 ; Y3 := X3 + Y3 ; X3 := t1 - Y3 ;
|
||
fe_add(&x3, &y3, &y3)
|
||
fe_add(&y3, &x3, &y3)
|
||
fe_sub(&x3, &t1, &y3)
|
||
|
||
// Y3 := t1 + Y3 ; Y3 := X3 * Y3 ; X3 := X3 * t3 ;
|
||
fe_add(&y3, &t1, &y3)
|
||
fe_mul(&y3, &x3, &y3)
|
||
fe_mul(&x3, &x3, &t3)
|
||
|
||
// t3 := t2 + t2 ; t2 := t2 + t3 ; Z3 := b * Z3 ;
|
||
fe_add(&t3, &t2, &t2)
|
||
fe_add(&t2, &t2, &t3)
|
||
fe_mul(&z3, &b_fe, &z3)
|
||
|
||
// Z3 := Z3 - t2 ; Z3 := Z3 - t0 ; t3 := Z3 + Z3 ;
|
||
fe_sub(&z3, &z3, &t2)
|
||
fe_sub(&z3, &z3, &t0)
|
||
fe_add(&t3, &z3, &z3)
|
||
|
||
// Z3 := Z3 + t3 ; t3 := t0 + t0 ; t0 := t3 + t0 ;
|
||
fe_add(&z3, &z3, &t3)
|
||
fe_add(&t3, &t0, &t0)
|
||
fe_add(&t0, &t3, &t0)
|
||
|
||
// t0 := t0 - t2 ; t0 := t0 * Z3 ; Y3 := Y3 + t0 ;
|
||
fe_sub(&t0, &t0, &t2)
|
||
fe_mul(&t0, &t0, &z3)
|
||
fe_add(&y3, &y3, &t0)
|
||
|
||
// t0 := Y * Z ; t0 := t0 + t0 ; Z3 := t0 * Z3 ;
|
||
fe_mul(&t0, y, z)
|
||
fe_add(&t0, &t0, &t0)
|
||
fe_mul(&z3, &t0, &z3)
|
||
|
||
// X3 := X3 - Z3 ; Z3 := t0 * t1 ; Z3 := Z3 + Z3 ;
|
||
fe_sub(&x3, &x3, &z3)
|
||
fe_mul(&z3, &t0, &t1)
|
||
fe_add(&z3, &z3, &z3)
|
||
|
||
// Z3 := Z3 + Z3 ;
|
||
fe_add(&z3, &z3, &z3)
|
||
|
||
// return X3 , Y3 , Z3 ;
|
||
fe_set(&p.x, &x3)
|
||
fe_set(&p.y, &y3)
|
||
fe_set(&p.z, &z3)
|
||
}
|
||
|
||
pt_sub :: proc "contextless" (p, a, b: ^$T) {
|
||
b_neg: T
|
||
pt_negate(&b_neg, b)
|
||
pt_add(p, a, &b_neg)
|
||
|
||
fe_clear(&b_neg)
|
||
}
|
||
|
||
pt_negate :: proc "contextless" (p, a: ^$T) {
|
||
fe_set(&p.x, &a.x)
|
||
fe_negate(&p.y, &a.y)
|
||
fe_set(&p.z, &a.z)
|
||
}
|
||
|
||
pt_rescale :: proc "contextless" (p, a: ^$T) {
|
||
// A = 1/Z1
|
||
// X3 = A*X1
|
||
// Y3 = A*Y1
|
||
// Z3 = 1
|
||
//
|
||
// As per "From A to Z: Projective coordinates leakage in the wild"
|
||
// leaking the Z-coordinate is bad. The modular inversion algorithm
|
||
// used in this library is based on Fermat's Little Theorem.
|
||
//
|
||
// See: https://eprint.iacr.org/2020/432.pdf
|
||
|
||
was_identity := pt_is_identity(a)
|
||
|
||
when T == Point_p256r1 {
|
||
z_inv: Field_Element_p256r1
|
||
} else when T == Point_p384r1 {
|
||
z_inv: Field_Element_p384r1
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
ident: T
|
||
fe_inv(&z_inv, &a.z)
|
||
fe_mul(&p.x, &a.x, &z_inv)
|
||
fe_mul(&p.y, &a.y, &z_inv)
|
||
fe_one(&p.z)
|
||
|
||
pt_identity(&ident)
|
||
pt_cond_select(p, p, &ident, was_identity)
|
||
|
||
fe_clear(&z_inv)
|
||
}
|
||
|
||
pt_cond_select :: proc "contextless" (p, a, b: ^$T, ctrl: int) {
|
||
fe_cond_select(&p.x, &a.x, &b.x, ctrl)
|
||
fe_cond_select(&p.y, &a.y, &b.y, ctrl)
|
||
fe_cond_select(&p.z, &a.z, &b.z, ctrl)
|
||
}
|
||
|
||
@(require_results)
|
||
pt_equal :: proc "contextless" (a, b: ^$T) -> int {
|
||
when T == Point_p256r1 {
|
||
x1z2, x2z1, y1z2, y2z1: Field_Element_p256r1
|
||
} else when T == Point_p384r1 {
|
||
x1z2, x2z1, y1z2, y2z1: Field_Element_p384r1
|
||
} else {
|
||
#panic("weierstrass: invalid curve")
|
||
}
|
||
|
||
// Check X1Z2 == X2Z1 && Y1Z2 == Y2Z1
|
||
fe_mul(&x1z2, &a.x, &b.z)
|
||
fe_mul(&x2z1, &b.x, &a.z)
|
||
|
||
fe_mul(&y1z2, &a.y, &b.z)
|
||
fe_mul(&y2z1, &b.y, &a.z)
|
||
|
||
return fe_equal(&x1z2, &x2z1) & fe_equal(&y1z2, &y2z1)
|
||
}
|
||
|
||
@(require_results)
|
||
pt_is_identity :: proc "contextless" (p: ^$T) -> int {
|
||
return fe_is_zero(&p.z)
|
||
}
|
||
|
||
@(require_results)
|
||
pt_is_y_odd :: proc "contextless" (p: ^$T) -> int {
|
||
tmp: T
|
||
defer pt_clear(&tmp)
|
||
|
||
fe_set(&tmp, p)
|
||
pt_rescale(&tmp)
|
||
|
||
return fe_is_odd(&tmp.y)
|
||
}
|
||
|
||
@(private)
|
||
is_on_curve :: proc "contextless" (x, y: ^$T) -> bool {
|
||
maybe_yy, yy: T
|
||
defer fe_clear_vec([]^T{&maybe_yy, &yy})
|
||
|
||
// RHS: x^3 + ax + b
|
||
set_yy_candidate(&maybe_yy, x)
|
||
|
||
// LHS: y^2
|
||
fe_square(&yy, y)
|
||
|
||
return fe_equal(&maybe_yy, &yy) == 1
|
||
}
|
||
|
||
@(private)
|
||
set_yy_candidate :: proc "contextless" (maybe_yy, x: ^$T) {
|
||
// RHS: x^3 + ax + b
|
||
rhs, tmp: T
|
||
|
||
fe_square(&tmp, x)
|
||
fe_mul(&rhs, &tmp, x)
|
||
|
||
fe_a(&tmp)
|
||
fe_mul(&tmp, &tmp, x)
|
||
fe_add(&rhs, &rhs, &tmp)
|
||
|
||
fe_b(&tmp)
|
||
fe_add(maybe_yy, &rhs, &tmp)
|
||
|
||
fe_clear(&rhs)
|
||
}
|