Skip to main content

airbender_crypto/secp256r1/
verify.rs

1use super::{
2    context::{GeneratorMultiplesTable, TABLE_G},
3    points::Affine,
4    scalar::{Scalar, Signature},
5    wnaf::Wnaf,
6    Secp256r1Err, ECMULT_TABLE_SIZE_A, WINDOW_A, WINDOW_G,
7};
8use core::mem::MaybeUninit;
9
10type Jacobian = super::points::Jacobian;
11
12pub fn verify(
13    digest: &[u8; 32],
14    r: &[u8; 32],
15    s: &[u8; 32],
16    x: &[u8; 32],
17    y: &[u8; 32],
18) -> Result<bool, Secp256r1Err> {
19    let Signature { r, s } = Signature::from_scalars(r, s)?;
20    let pk = Affine::from_be_bytes(x, y)?
21        .reject_identity()?
22        .to_jacobian();
23    let z = Scalar::reduce_be_bytes(digest);
24    let mut s_inv = s;
25    s_inv.invert_assign();
26
27    let u1 = z * &s_inv;
28    let u2 = r * &s_inv;
29
30    let x = ecmult(pk, u2, u1, &TABLE_G)
31        .to_affine()
32        .reject_identity()?
33        .x;
34
35    let recovered = Scalar::reduce_be_bytes(&x.to_be_bytes());
36
37    let bool = r == recovered;
38    Ok(bool)
39}
40
41fn ecmult(a: Jacobian, na: Scalar, ng: Scalar, table_g: &GeneratorMultiplesTable) -> Jacobian {
42    let (wnaf_a, table_a) = if !na.is_zero() && !a.is_infinity() {
43        (Wnaf::new(na, WINDOW_A), OddMultiplesTable::from(&a))
44    } else {
45        (Wnaf::default(), OddMultiplesTable::default())
46    };
47
48    let wnaf_ng = if !ng.is_zero() {
49        Wnaf::new(ng, WINDOW_G)
50    } else {
51        Wnaf::default()
52    };
53
54    let bits = wnaf_a.bits().max(wnaf_ng.bits());
55    let mut r = Jacobian::default();
56
57    for i in (0..bits).rev() {
58        r.double_assign();
59
60        if let Some(n) = wnaf_a.get_digit(i) {
61            r.add_assign(&table_a.get(n, WINDOW_A));
62        }
63
64        if let Some(n) = wnaf_ng.get_digit(i) {
65            r.add_ge_assign(&table_g.get_ge(n, WINDOW_G));
66        }
67    }
68
69    r
70}
71
72#[derive(Default)]
73struct OddMultiplesTable([Jacobian; ECMULT_TABLE_SIZE_A]);
74
75impl OddMultiplesTable {
76    fn from(p: &Jacobian) -> Self {
77        debug_assert!(!p.is_infinity());
78
79        let mut table = [const { MaybeUninit::uninit() }; ECMULT_TABLE_SIZE_A];
80
81        let mut p = *p;
82        table[0].write(p);
83        let mut p_double = p;
84        p_double.double_assign();
85
86        for x in table.iter_mut().skip(1) {
87            p.add_assign(&p_double);
88            x.write(p);
89        }
90
91        Self(unsafe {
92            core::mem::transmute::<
93                [MaybeUninit<Jacobian>; ECMULT_TABLE_SIZE_A],
94                [Jacobian; ECMULT_TABLE_SIZE_A],
95            >(table)
96        })
97    }
98
99    fn get(&self, n: i32, w: usize) -> Jacobian {
100        debug_assert!(
101            (2..=31).contains(&w)
102                && ((n & 1) == 1)
103                && (n >= -((1 << (w - 1)) - 1))
104                && (n < (1 << (w - 1)))
105        );
106
107        if n > 0 {
108            self.0[(n - 1) as usize / 2]
109        } else {
110            -self.0[(-n - 1) as usize / 2]
111        }
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use super::{ecmult, Scalar};
118
119    use crate::secp256r1::{
120        context::TABLE_G,
121        field::FieldElement,
122        points::{Jacobian, JacobianConst},
123        test_vectors::MUL_TEST_VECTORS,
124    };
125
126    #[test]
127    fn test_ecmult_mix() {
128        assert_eq!(
129            JacobianConst::GENERATOR.double().to_affine(),
130            ecmult(Jacobian::GENERATOR, Scalar::ONE, Scalar::ONE, &TABLE_G).to_affine()
131        );
132
133        assert_eq!(
134            JacobianConst::GENERATOR
135                .double()
136                .add(&JacobianConst::GENERATOR)
137                .to_affine(),
138            ecmult(
139                Jacobian::GENERATOR,
140                Scalar::from_words([2, 0, 0, 0]),
141                Scalar::ONE,
142                &TABLE_G
143            )
144            .to_affine()
145        );
146
147        assert_eq!(
148            JacobianConst::GENERATOR
149                .double()
150                .add(&JacobianConst::GENERATOR)
151                .to_affine(),
152            ecmult(
153                Jacobian::GENERATOR,
154                Scalar::ONE,
155                Scalar::from_words([2, 0, 0, 0]),
156                &TABLE_G
157            )
158            .to_affine()
159        );
160
161        assert_eq!(
162            JacobianConst::GENERATOR.double().double().to_affine(),
163            ecmult(
164                Jacobian::GENERATOR,
165                Scalar::from_words([2, 0, 0, 0]),
166                Scalar::from_words([2, 0, 0, 0]),
167                &TABLE_G
168            )
169            .to_affine()
170        );
171
172        assert_eq!(
173            JacobianConst::GENERATOR.double().double().to_affine(),
174            ecmult(
175                Jacobian::GENERATOR,
176                Scalar::from_words([3, 0, 0, 0]),
177                Scalar::ONE,
178                &TABLE_G
179            )
180            .to_affine()
181        );
182
183        assert_eq!(
184            JacobianConst::GENERATOR.double().double().to_affine(),
185            ecmult(
186                Jacobian::GENERATOR,
187                Scalar::ONE,
188                Scalar::from_words([3, 0, 0, 0]),
189                &TABLE_G
190            )
191            .to_affine()
192        );
193    }
194
195    #[test]
196    fn test_ecmult() {
197        for (k_bytes, x_bytes, y_bytes) in MUL_TEST_VECTORS {
198            let k = Scalar::reduce_be_bytes(k_bytes);
199            let expected = Jacobian {
200                x: FieldElement::from_be_bytes(x_bytes).unwrap(),
201                y: FieldElement::from_be_bytes(y_bytes).unwrap(),
202                z: FieldElement::ONE,
203            };
204
205            let result = ecmult(Jacobian::default(), Scalar::ZERO, k, &TABLE_G);
206            assert_eq!(result.to_affine(), expected.to_affine());
207
208            let result = ecmult(Jacobian::GENERATOR, k, Scalar::ZERO, &TABLE_G);
209            assert_eq!(result.to_affine(), expected.to_affine());
210        }
211    }
212}