plonky2-ecdsa/src/curve/glv.rs
src/curve/glv.rsRaw
1use num::rational::Ratio;
2use num::BigUint;
3use plonky2::field::secp256k1_base::Secp256K1Base;
4use plonky2::field::secp256k1_scalar::Secp256K1Scalar;
5use plonky2::field::types::{Field, PrimeField};
6
7use crate::curve::curve_msm::msm_parallel;
8use crate::curve::curve_types::{AffinePoint, ProjectivePoint};
9use crate::curve::secp256k1::Secp256K1;
10
11pub const GLV_BETA: Secp256K1Base = Secp256K1Base([
12 13923278643952681454,
13 11308619431505398165,
14 7954561588662645993,
15 8856726876819556112,
16]);
17
18pub const GLV_S: Secp256K1Scalar = Secp256K1Scalar([
19 16069571880186789234,
20 1310022930574435960,
21 11900229862571533402,
22 6008836872998760672,
23]);
24
25const A1: Secp256K1Scalar = Secp256K1Scalar([16747920425669159701, 3496713202691238861, 0, 0]);
26
27const MINUS_B1: Secp256K1Scalar =
28 Secp256K1Scalar([8022177200260244675, 16448129721693014056, 0, 0]);
29
30const A2: Secp256K1Scalar = Secp256K1Scalar([6323353552219852760, 1498098850674701302, 1, 0]);
31
32const B2: Secp256K1Scalar = Secp256K1Scalar([16747920425669159701, 3496713202691238861, 0, 0]);
33
34/// Algorithm 15.41 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
35/// Decompose a scalar `k` into two small scalars `k1, k2` with `|k1|, |k2| < √p` that satisfy
36/// `k1 + s * k2 = k`.
37/// Returns `(|k1|, |k2|, k1 < 0, k2 < 0)`.
38pub fn decompose_secp256k1_scalar(
39 k: Secp256K1Scalar,
40) -> (Secp256K1Scalar, Secp256K1Scalar, bool, bool) {
41 let p = Secp256K1Scalar::order();
42 let c1_biguint = Ratio::new(
43 B2.to_canonical_biguint() * k.to_canonical_biguint(),
44 p.clone(),
45 )
46 .round()
47 .to_integer();
48 let c1 = Secp256K1Scalar::from_noncanonical_biguint(c1_biguint);
49 let c2_biguint = Ratio::new(
50 MINUS_B1.to_canonical_biguint() * k.to_canonical_biguint(),
51 p.clone(),
52 )
53 .round()
54 .to_integer();
55 let c2 = Secp256K1Scalar::from_noncanonical_biguint(c2_biguint);
56
57 let k1_raw = k - c1 * A1 - c2 * A2;
58 let k2_raw = c1 * MINUS_B1 - c2 * B2;
59 debug_assert!(k1_raw + GLV_S * k2_raw == k);
60
61 let two = BigUint::from_slice(&[2]);
62 let k1_neg = k1_raw.to_canonical_biguint() > p.clone() / two.clone();
63 let k1 = if k1_neg {
64 Secp256K1Scalar::from_noncanonical_biguint(p.clone() - k1_raw.to_canonical_biguint())
65 } else {
66 k1_raw
67 };
68 let k2_neg = k2_raw.to_canonical_biguint() > p.clone() / two;
69 let k2 = if k2_neg {
70 Secp256K1Scalar::from_noncanonical_biguint(p - k2_raw.to_canonical_biguint())
71 } else {
72 k2_raw
73 };
74
75 (k1, k2, k1_neg, k2_neg)
76}
77
78/// See Section 15.2.1 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
79/// GLV scalar multiplication `k * P = k1 * P + k2 * psi(P)`, where `k = k1 + s * k2` is the
80/// decomposition computed in `decompose_secp256k1_scalar(k)` and `psi` is the Secp256k1
81/// endomorphism `psi: (x, y) |-> (beta * x, y)` equivalent to scalar multiplication by `s`.
82pub fn glv_mul(p: ProjectivePoint<Secp256K1>, k: Secp256K1Scalar) -> ProjectivePoint<Secp256K1> {
83 let (k1, k2, k1_neg, k2_neg) = decompose_secp256k1_scalar(k);
84
85 let p_affine = p.to_affine();
86 let sp = AffinePoint::<Secp256K1> {
87 x: p_affine.x * GLV_BETA,
88 y: p_affine.y,
89 zero: p_affine.zero,
90 };
91
92 let first = if k1_neg { p.neg() } else { p };
93 let second = if k2_neg {
94 sp.to_projective().neg()
95 } else {
96 sp.to_projective()
97 };
98
99 msm_parallel(&[k1, k2], &[first, second], 5)
100}
101
102#[cfg(test)]
103mod tests {
104 use anyhow::Result;
105 use plonky2::field::secp256k1_scalar::Secp256K1Scalar;
106 use plonky2::field::types::{Field, Sample};
107
108 use crate::curve::curve_types::{Curve, CurveScalar};
109 use crate::curve::glv::{decompose_secp256k1_scalar, glv_mul, GLV_S};
110 use crate::curve::secp256k1::Secp256K1;
111
112 #[test]
113 fn test_glv_decompose() -> Result<()> {
114 let k = Secp256K1Scalar::rand();
115 let (k1, k2, k1_neg, k2_neg) = decompose_secp256k1_scalar(k);
116 let one = Secp256K1Scalar::ONE;
117 let m1 = if k1_neg { -one } else { one };
118 let m2 = if k2_neg { -one } else { one };
119
120 assert!(k1 * m1 + GLV_S * k2 * m2 == k);
121
122 Ok(())
123 }
124
125 #[test]
126 fn test_glv_mul() -> Result<()> {
127 for _ in 0..20 {
128 let k = Secp256K1Scalar::rand();
129
130 let p = CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE;
131
132 let kp = CurveScalar(k) * p;
133 let glv = glv_mul(p, k);
134
135 assert!(kp == glv);
136 }
137
138 Ok(())
139 }
140}