Skip to main content

ic_ec/
p256.rs

1//! NIST P-256 (secp256r1, prime256v1).
2//!
3//! The most widely deployed approved curve. The field, group law, and schemes
4//! come from `crate::nist`; this module supplies the constants and the
5//! public API.
6
7use crate::mont_field;
8use crate::nist::arith::{sqrt_p3mod4, Field};
9use crate::nist::point::Curve;
10use crate::nist::{ecdh, ecdsa};
11use ic_core::traits::{Algorithm, KeyAgreement, SelfTest, SignatureScheme};
12use ic_core::{ensure, Result};
13
14// On 64-bit targets the field uses arithmetic specialised to p's shape; see
15// `nist::p256_field`. Elsewhere, the generic Montgomery word loop.
16#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
17mont_field!(
18    Fp,
19    4,
20    32,
21    crate::nist::p256_field::P,
22    "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1.",
23    mul = crate::nist::p256_field::active::mul,
24    square = crate::nist::p256_field::active::square,
25    invert = invert_field,
26    add = crate::nist::p256_field::active::add,
27    sub = crate::nist::p256_field::active::sub
28);
29
30#[cfg(not(all(target_pointer_width = "64", not(ic_limb32))))]
31mont_field!(
32    Fp,
33    4,
34    32,
35    crate::nist::p256_field::P,
36    "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1."
37);
38
39/// The group order `n`, little-endian.
40const N: [u64; 4] = [
41    0xf3b9_cac2_fc63_2551,
42    0xbce6_faad_a717_9e84,
43    0xffff_ffff_ffff_ffff,
44    0xffff_ffff_0000_0000,
45];
46
47// On 64-bit targets the scalar ring inverts by Bernstein-Yang divsteps rather
48// than by Fermat; see `nist::safegcd`. Everything else is the generic ring.
49#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
50mont_field!(
51    Fn,
52    4,
53    32,
54    N,
55    "The P-256 scalar ring, Z/nZ where n is the order of the base point.",
56    mul = Self::mont_mul_raw,
57    square = Self::mont_square_raw,
58    invert = invert_scalar,
59    add = Self::add_raw,
60    sub = Self::sub_raw
61);
62
63#[cfg(not(all(target_pointer_width = "64", not(ic_limb32))))]
64mont_field!(
65    Fn,
66    4,
67    32,
68    N,
69    "The P-256 scalar ring, Z/nZ where n is the order of the base point."
70);
71
72/// `a^-1` in the coordinate field, as [`invert_scalar`] does it: 1.0
73/// microseconds against 1.6 for the addition chain in `p256_field`, which
74/// stays as the tests' reference.
75#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
76fn invert_field(a: [u64; 4]) -> [u64; 4] {
77    const R3: [u64; 4] = Fp::mont_mul_const(Fp::R2, Fp::R2);
78    crate::nist::p256_field::active::mul(crate::nist::safegcd::invert::<FpModulus>(&a), R3)
79}
80
81/// The prime, for the divsteps.
82#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
83pub(crate) struct FpModulus;
84
85#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
86impl crate::nist::safegcd::Modulus<5> for FpModulus {
87    const INFO: crate::nist::safegcd::ModInfo<5> =
88        crate::nist::safegcd::ModInfo::new(crate::nist::p256_field::P);
89}
90
91/// The order, for the divsteps.
92#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
93pub(crate) struct FnModulus;
94
95#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
96impl crate::nist::safegcd::Modulus<5> for FnModulus {
97    const INFO: crate::nist::safegcd::ModInfo<5> = crate::nist::safegcd::ModInfo::new(N);
98}
99
100/// `a^-1` in the scalar ring, Montgomery form in and out, by divsteps.
101///
102/// The divsteps invert the integer they are given, so a Montgomery `a * R`
103/// comes back as `a^-1 * R^-1`; one Montgomery multiplication by `R^3`
104/// makes that `a^-1 * R`.
105#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
106fn invert_scalar(a: [u64; 4]) -> [u64; 4] {
107    const R3: [u64; 4] = Fn::mont_mul_const(Fn::R2, Fn::R2);
108    Fn::mont_mul_raw(crate::nist::safegcd::invert::<FnModulus>(&a), R3)
109}
110
111/// The P-256 curve.
112#[derive(Debug, Clone, Copy)]
113pub struct P256;
114
115// Its own generator table, with its own storage; see the macro, which
116// emits the table under `std` and the windowed multiplication without it.
117crate::nist::gentable::generator_table_for!(P256, 32);
118
119/// `scalar * G` by `nist::p256_x86::table_mul` on the generator table,
120/// whatever else the CPU has: for the tests, so that a CPU with IFMA runs
121/// the MULX/ADX path too.
122#[cfg(all(test, feature = "std", target_arch = "x86_64"))]
123pub(crate) fn table_mul_adx_for_tests(scalar: &Fn) -> Option<Point> {
124    let (windows, built) = generator_table();
125    crate::nist::gentable::prepare(windows, built);
126    crate::nist::p256_x86::table_mul(windows, scalar)
127}
128
129impl Curve for P256 {
130    type Field = Fp;
131    type Scalar = Fn;
132
133    const NAME: &'static str = "P-256";
134    const FIELD_BYTES: usize = 32;
135    const SCALAR_BYTES: usize = 32;
136    const ORDER_BITS: usize = 256;
137
138    /// `b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`
139    const B: Fp = Fp::to_mont_const([
140        0x3bce_3c3e_27d2_604b,
141        0x651d_06b0_cc53_b0f6,
142        0xb3eb_bd55_7698_86bc,
143        0x5ac6_35d8_aa3a_93e7,
144    ]);
145
146    const GX: Fp = Fp::to_mont_const([
147        0xf4a1_3945_d898_c296,
148        0x7703_7d81_2deb_33a0,
149        0xf8bc_e6e5_63a4_40f2,
150        0x6b17_d1f2_e12c_4247,
151    ]);
152
153    const GY: Fp = Fp::to_mont_const([
154        0xcbb6_4068_37bf_51f5,
155        0x2bce_3357_6b31_5ece,
156        0x8ee7_eb4a_7c0f_9e16,
157        0x4fe3_42e2_fe1a_7f9b,
158    ]);
159
160    /// `p = 3 mod 4`, so a square root is `x^((p+1)/4)`.
161    fn sqrt(x: &Fp) -> Fp {
162        // p = 3 mod 4, so the root is x^((p+1)/4). The shared helper computes
163        // that exponent rather than this file unrolling it by limb: an unrolled
164        // shift is easy to get subtly wrong and would only misbehave on inputs
165        // rare enough that a round-trip test would not find them.
166        sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
167    }
168
169    fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
170        let mut b = [0u8; 32];
171        if bytes.len() != 32 {
172            return None;
173        }
174        b.copy_from_slice(bytes);
175        Fp::from_bytes(&b)
176    }
177
178    fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
179        let mut b = [0u8; 32];
180        if bytes.len() != 32 {
181            return None;
182        }
183        b.copy_from_slice(bytes);
184        Fn::from_bytes(&b)
185    }
186
187    #[cfg(target_arch = "x86_64")]
188    #[inline(always)]
189    fn double_fast(x: &Fp, y: &Fp, z: &Fp) -> Option<[Fp; 3]> {
190        crate::nist::p256_x86::point_double(&x.0, &y.0, &z.0).map(|[x, y, z]| [Fp(x), Fp(y), Fp(z)])
191    }
192
193    #[cfg(target_arch = "x86_64")]
194    #[inline(always)]
195    fn scan_affine(entries: &[AffinePoint], masks: &[u64]) -> Option<AffinePoint> {
196        Some(crate::nist::p256_x86::scan_affine(entries, masks))
197    }
198
199    #[cfg(target_arch = "x86_64")]
200    #[inline(always)]
201    fn scan_point(entries: &[Point], masks: &[u64]) -> Option<[Fp; 3]> {
202        let [x, y, z] = crate::nist::p256_x86::scan_point(entries, masks);
203        Some([Fp(x), Fp(y), Fp(z)])
204    }
205
206    #[cfg(target_arch = "x86_64")]
207    #[inline(always)]
208    fn add_fast(p: [&Fp; 3], q: [&Fp; 3]) -> Option<([Fp; 3], Fp, Fp)> {
209        crate::nist::p256_x86::point_add([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0, &q[2].0])
210            .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
211    }
212
213    #[cfg(target_arch = "x86_64")]
214    #[inline(always)]
215    fn add_affine_fast(p: [&Fp; 3], q: [&Fp; 2]) -> Option<([Fp; 3], Fp, Fp)> {
216        crate::nist::p256_x86::point_add_affine([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0])
217            .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
218    }
219
220    #[cfg(target_arch = "x86_64")]
221    #[inline(always)]
222    fn mul_scalar_fast(p: &Point, scalar: &Fn) -> Option<Point> {
223        match crate::nist::ifma::Ifma::get() {
224            Some(t) => Some(crate::nist::ifma::mul_scalar(t, p, scalar)),
225            None => crate::nist::p256_x86::mul_scalar(p, scalar),
226        }
227    }
228
229    #[cfg(target_arch = "x86_64")]
230    #[inline(always)]
231    fn comb_mul_fast(
232        windows: &[crate::nist::gentable::AffineWindow<Self, { crate::nist::gentable::ENTRIES }>;
233             crate::nist::gentable::TEETH],
234        scalar: &Fn,
235    ) -> Option<Point> {
236        crate::nist::ifma::Ifma::get().map(|t| crate::nist::ifma::comb_mul(t, windows, scalar))
237    }
238
239    #[cfg(all(target_arch = "x86_64", feature = "std"))]
240    #[inline(always)]
241    fn table_mul_fast<const E: usize>(
242        windows: &crate::nist::gentable::Windows<Self, E>,
243        scalar: &Fn,
244    ) -> Option<Point> {
245        match crate::nist::ifma::Ifma::get() {
246            Some(t) => Some(crate::nist::ifma::table_mul(t, windows, scalar)),
247            None => crate::nist::p256_x86::table_mul(windows, scalar),
248        }
249    }
250
251    /// Verification's joint chain on the formulas' assembly: public inputs
252    /// only, never a secret path (`nist::verify_x86`).
253    #[cfg(target_arch = "x86_64")]
254    #[inline(always)]
255    fn mul_double_vartime_fast(
256        g: &[AffinePoint],
257        q: &[AffinePoint],
258        g_naf: &[i8],
259        q_naf: &[i8],
260    ) -> Option<Point> {
261        crate::nist::verify_x86::p256(g, q, g_naf, q_naf)
262    }
263
264    fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
265        let mut b = [0u8; 32];
266        let n = core::cmp::min(32, bytes.len());
267        // Take the leftmost bytes, which is what bits2int does when the input
268        // is at least as wide as the group order.
269        b[32 - n..].copy_from_slice(&bytes[..n]);
270        Fn::from_bytes_reduced(&b)
271    }
272}
273
274impl ecdsa::EcdsaCurve for P256 {
275    type Digest = ic_hash::Sha256;
276    type Hmac = ic_mac::HmacSha256;
277}
278
279/// ECDSA over P-256 with SHA-256.
280pub struct EcdsaP256Sha256;
281
282impl Algorithm for EcdsaP256Sha256 {
283    const ID: &'static str = "ecdsa-p256-sha256";
284    const NAME: &'static str = "ECDSA P-256 with SHA-256";
285}
286
287impl SignatureScheme for EcdsaP256Sha256 {
288    const PRIVATE_KEY_LEN: usize = 32;
289    /// SEC1 uncompressed: `0x04 || X || Y`.
290    const PUBLIC_KEY_LEN: usize = 65;
291    /// Fixed-width `r || s`.
292    const SIGNATURE_LEN: usize = 64;
293
294    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
295        ecdsa::public_key::<P256>(private_key, out)
296    }
297
298    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
299        ecdsa::sign::<P256>(private_key, message, signature)
300    }
301
302    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
303        ecdsa::verify::<P256>(public_key, message, signature)
304    }
305}
306
307impl EcdsaP256Sha256 {
308    /// Verify a signature over a digest the caller computed, for signatures
309    /// made with a hash other than SHA-256.
310    ///
311    /// `digest` must be one of [`crate::PREHASH_LENS`] and at least 32
312    /// bytes, so that the hash is at least as strong as the curve; narrower is
313    /// refused with `InvalidLength`. Which hash produced it is the caller's to
314    /// establish: nothing here can tell.
315    pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
316        ecdsa::verify_prehash::<P256>(public_key, digest, signature)
317    }
318
319    /// Compute the public key in SEC1 compressed form (33 bytes).
320    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
321        ecdsa::public_key_compressed::<P256>(private_key, out)
322    }
323
324    /// Rewrite a signature to its low-`s` form, if it is not already.
325    ///
326    /// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the
327    /// same message, so a signature is not a unique identifier unless one form
328    /// is chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs
329    /// and verifies per the standard, so normalization is offered rather than
330    /// imposed -- apply it when a signature doubles as a database key or a
331    /// transaction id.
332    pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
333        ecdsa::normalize_s::<P256>(signature)
334    }
335
336    /// Whether a signature is already in low-`s` form.
337    pub fn has_low_s(signature: &[u8]) -> Result<bool> {
338        ecdsa::has_low_s::<P256>(signature)
339    }
340}
341
342impl SelfTest for EcdsaP256Sha256 {
343    fn self_test() -> Result<()> {
344        // RFC 6979 A.2.5: P-256, SHA-256, message "sample".
345        let mut key = [0u8; 32];
346        ic_core::codec::hex_decode(
347            b"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
348            &mut key,
349        )?;
350        let mut want = [0u8; 64];
351        ic_core::codec::hex_decode(
352            b"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
353            &mut want,
354        )?;
355
356        let mut sig = [0u8; 64];
357        <Self as SignatureScheme>::sign(&key, b"sample", &mut sig)?;
358        ensure!(
359            ic_core::ct::verify(&want, &sig),
360            SelfTestFailed,
361            "ecdsa-p256-sha256"
362        );
363
364        let mut pk = [0u8; 65];
365        <Self as SignatureScheme>::public_key(&key, &mut pk)?;
366        <Self as SignatureScheme>::verify(&pk, b"sample", &sig)?;
367
368        // A flipped bit must be rejected.
369        sig[0] ^= 1;
370        ensure!(
371            <Self as SignatureScheme>::verify(&pk, b"sample", &sig).is_err(),
372            SelfTestFailed,
373            "ecdsa-p256-sha256"
374        );
375        Ok(())
376    }
377}
378
379/// ECDH over P-256.
380pub struct EcdhP256;
381
382impl Algorithm for EcdhP256 {
383    const ID: &'static str = "ecdh-p256";
384    const NAME: &'static str = "ECDH P-256";
385}
386
387impl KeyAgreement for EcdhP256 {
388    const PRIVATE_KEY_LEN: usize = 32;
389    /// SEC1 uncompressed: `0x04 || X || Y`.
390    const PUBLIC_KEY_LEN: usize = 65;
391    const SHARED_SECRET_LEN: usize = 32;
392
393    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
394        ecdh::public_key::<P256>(private_key, out)
395    }
396
397    fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
398        ecdh::agree::<P256>(private_key, peer_public_key, out)
399    }
400}
401
402impl EcdhP256 {
403    /// Compute the public key in SEC1 compressed form (33 bytes).
404    ///
405    /// Interoperates with TLS, COSE, and JOSE, which all prefer compressed
406    /// points. [`KeyAgreement::agree`] accepts either form.
407    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
408        ecdh::public_key_compressed::<P256>(private_key, out)
409    }
410}
411
412impl SelfTest for EcdhP256 {
413    fn self_test() -> Result<()> {
414        // NIST CAVP ECC CDH, P-256, the first published key-agreement case.
415        let mut d = [0u8; 32];
416        ic_core::codec::hex_decode(
417            b"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
418            &mut d,
419        )?;
420        let mut peer = [0u8; 65];
421        peer[0] = 0x04;
422        ic_core::codec::hex_decode(
423            b"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
424            &mut peer[1..33],
425        )?;
426        ic_core::codec::hex_decode(
427            b"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
428            &mut peer[33..],
429        )?;
430        let mut want = [0u8; 32];
431        ic_core::codec::hex_decode(
432            b"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
433            &mut want,
434        )?;
435
436        let mut got = [0u8; 32];
437        <Self as KeyAgreement>::agree(&d, &peer, &mut got)?;
438        ensure!(
439            ic_core::ct::verify(&want, &got),
440            SelfTestFailed,
441            "ecdh-p256"
442        );
443        Ok(())
444    }
445}
446
447/// A P-256 point in Jacobian coordinates.
448pub type Point = crate::nist::point::Point<P256>;
449/// A P-256 point in affine coordinates.
450pub type AffinePoint = crate::nist::point::AffinePoint<P256>;
451/// A peer's P-256 public key for [`EcdhP256`], decoded, validated and
452/// expanded once: `new` takes its SEC1 encoding and refuses what
453/// [`EcdhP256`]'s `agree` would refuse; `agree` takes a private key and
454/// writes the 32-byte shared secret, doing only the scalar multiplication
455/// in about 40 percent of the time of an unprepared agreement. About 4 KiB, not
456/// allocated.
457pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P256>;
458
459#[cfg(test)]
460mod tests {
461    use super::*;
462    use ic_core::codec::{hex, unhex};
463
464    /// Where P-256's time goes, piece by piece. A measurement, not a check:
465    /// run with `cargo test --release -p ic-ec -- --ignored --nocapture
466    /// where_the_time_goes`.
467    #[test]
468    #[ignore = "timing; run manually with --release"]
469    fn where_the_time_goes() {
470        use crate::nist::arith::Field;
471        use std::hint::black_box;
472        use std::time::Instant;
473        fn ns(label: &str, iters: u32, mut f: impl FnMut()) {
474            let mut best = f64::INFINITY;
475            for _ in 0..7 {
476                let t = Instant::now();
477                for _ in 0..iters {
478                    f();
479                }
480                best = best.min(t.elapsed().as_secs_f64() * 1e9 / iters as f64);
481            }
482            std::println!("{label:32} {best:10.1} ns");
483        }
484        let a = Fp::to_mont([7, 11, 13, 17]);
485        let b = Fp::to_mont([19, 23, 29, 31]);
486        ns("fp mul", 2_000_000, || {
487            black_box(black_box(a).mul(&black_box(b)));
488        });
489        ns("fp square", 2_000_000, || {
490            black_box(black_box(a).square());
491        });
492        ns("fp add", 2_000_000, || {
493            black_box(black_box(a).add(&black_box(b)));
494        });
495        ns("fp sub", 2_000_000, || {
496            black_box(black_box(a).sub(&black_box(b)));
497        });
498        ns("fp invert", 2_000, || {
499            black_box(black_box(a).invert());
500        });
501        let g = Point::generator();
502        let p = g.double();
503        ns("point double", 200_000, || {
504            black_box(black_box(p).double());
505        });
506        ns("point add (complete)", 200_000, || {
507            black_box(black_box(p).add(&black_box(g)));
508        });
509        ns("point add (unequal)", 200_000, || {
510            black_box(black_box(p).add_unequal(&black_box(g)));
511        });
512        let ga = g.to_affine().unwrap();
513        ns("point add (affine, unequal)", 200_000, || {
514            black_box(black_box(p).add_affine_unequal(&black_box(ga.x), &black_box(ga.y)));
515        });
516        let k = Fn::to_mont([0x1234_5678, 0x9abc_def0, 0x1357_9bdf, 0x2468_ace0]);
517        ns("scalar invert", 2_000, || {
518            black_box(black_box(k).invert());
519        });
520        ns("mul_generator (table)", 2_000, || {
521            black_box(Point::mul_generator(&black_box(k)));
522        });
523        ns("mul_scalar (windowed)", 500, || {
524            black_box(black_box(p).mul_scalar(&black_box(k)));
525        });
526        ns("mul_scalar_vartime", 500, || {
527            black_box(black_box(p).mul_scalar_vartime(&black_box(k)));
528        });
529        ns("to_affine", 2_000, || {
530            black_box(black_box(p).to_affine());
531        });
532        let mut pk = [0u8; 65];
533        EcdhP256::public_key(&[0x5a; 32], &mut pk).unwrap();
534        let mut shared = [0u8; 32];
535        ns("ecdh agree", 500, || {
536            EcdhP256::agree(&[0x5a; 32], &pk, &mut shared).unwrap();
537        });
538        ns("decode point", 2_000, || {
539            black_box(AffinePoint::from_sec1(black_box(&pk)));
540        });
541        ns("operational", 2_000, || {
542            black_box(ic_core::module::operational()).unwrap();
543        });
544        let mut sig = [0u8; 64];
545        ns("ecdsa sign", 500, || {
546            EcdsaP256Sha256::sign(&[0x5a; 32], b"message", &mut sig).unwrap();
547        });
548    }
549
550    fn scalar(v: u64) -> Fn {
551        Fn::to_mont([v, 0, 0, 0])
552    }
553
554    fn fp(v: u64) -> Fp {
555        Fp::to_mont([v, 0, 0, 0])
556    }
557
558    // -- field ------------------------------------------------------------
559
560    #[test]
561    fn montgomery_constants_are_consistent() {
562        assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
563        assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
564    }
565
566    #[test]
567    fn small_arithmetic_matches_integers() {
568        assert_eq!(fp(2).add(&fp(3)), fp(5));
569        assert_eq!(fp(5).sub(&fp(3)), fp(2));
570        assert_eq!(fp(6).mul(&fp(7)), fp(42));
571        assert_eq!(fp(9).square(), fp(81));
572        assert_eq!(fp(5).double(), fp(10));
573        assert_eq!(fp(5).triple(), fp(15));
574        assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0]);
575    }
576
577    #[test]
578    fn inversion_is_correct() {
579        for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
580            assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
581            assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
582        }
583        assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
584        assert_eq!(Fn::ZERO.invert(), Fn::ZERO);
585    }
586
587    /// Both rings' inversion -- divsteps on 64-bit targets -- against the
588    /// generic Fermat exponentiation, value for value in Montgomery form, on
589    /// the edges and on a spread of values from repeated squaring.
590    #[test]
591    fn inversion_matches_fermat() {
592        let mut x = fp(0x1234_5678_9abc_def1);
593        let mut k = scalar(0x0fed_cba9_8765_4321);
594        let mut fps = std::vec![
595            Fp::ONE,
596            Fp::ONE.neg(),
597            Fp::ONE.double(),
598            Fp::ONE.neg().double()
599        ];
600        let mut fns = std::vec![
601            Fn::ONE,
602            Fn::ONE.neg(),
603            Fn::ONE.double(),
604            Fn::ONE.neg().double()
605        ];
606        for _ in 0..300 {
607            x = x.square().add(&Fp::ONE);
608            k = k.square().add(&Fn::ONE);
609            fps.push(x);
610            fns.push(k);
611        }
612        for v in fps {
613            assert_eq!(v.invert().0, Fp::invert_raw(v.0), "{v:?}");
614        }
615        for v in fns {
616            assert_eq!(v.invert().0, Fn::invert_raw(v.0), "{v:?}");
617        }
618    }
619
620    #[test]
621    fn arithmetic_laws_hold_on_large_values() {
622        let a = P256::field_from_slice(&[0x3a; 32]).unwrap();
623        let b = P256::field_from_slice(&[0x91; 32]).unwrap();
624        let c = P256::field_from_slice(&[0xc7; 32]).unwrap();
625        assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
626        assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
627        assert_eq!(
628            a.mul(&b.add(&c)),
629            a.mul(&b).add(&a.mul(&c)),
630            "distributivity"
631        );
632        assert_eq!(a.add(&a.neg()), Fp::ZERO);
633    }
634
635    #[test]
636    fn byte_encoding_round_trips_and_rejects_non_canonical() {
637        let bytes = [0x7fu8; 32];
638        let a = P256::field_from_slice(&bytes).unwrap();
639        assert_eq!(a.to_bytes(), bytes);
640
641        // p itself must be refused but reduce to zero.
642        let mut p_bytes = [0u8; 32];
643        for i in 0..4 {
644            let hi = 32 - i * 8;
645            p_bytes[hi - 8..hi].copy_from_slice(&Fp::MODULUS[i].to_be_bytes());
646        }
647        assert!(P256::field_from_slice(&p_bytes).is_none());
648    }
649
650    // -- group law --------------------------------------------------------
651
652    /// Validates B, GX, GY and the curve equation together: if any of the four
653    /// constants were mistranscribed, the base point would not satisfy it.
654    #[test]
655    fn the_base_point_is_on_the_curve() {
656        let g = Point::generator().to_affine().unwrap();
657        assert!(bool::from(g.is_on_curve()));
658    }
659
660    /// Validates the group order n against the base point.
661    #[test]
662    fn the_base_point_has_order_n() {
663        let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
664        let p = Point::generator().mul_scalar(&n_minus_1);
665        assert!(
666            bool::from(p.ct_eq(&Point::generator().neg())),
667            "[n-1]G == -G"
668        );
669        assert!(
670            bool::from(p.add(&Point::generator()).is_identity()),
671            "[n]G is the identity"
672        );
673    }
674
675    #[test]
676    fn identity_and_negation_behave() {
677        let g = Point::generator();
678        assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
679        assert!(bool::from(Point::identity().add(&g).ct_eq(&g)));
680        assert!(bool::from(Point::identity().double().is_identity()));
681        assert!(bool::from(g.add(&g.neg()).is_identity()));
682        assert!(Point::identity().to_affine().is_none());
683    }
684
685    /// The exceptional case a naive Jacobian addition gets wrong.
686    #[test]
687    fn addition_handles_equal_inputs_as_a_doubling() {
688        let g = Point::generator();
689        assert!(bool::from(g.add(&g).ct_eq(&g.double())));
690        let p = g.mul_scalar(&scalar(5));
691        assert!(bool::from(p.add(&p).ct_eq(&p.double())));
692    }
693
694    /// The variable-time path must agree with the constant-time one.
695    ///
696    /// The RFC 6979 vectors reach it with a couple of scalars, which says
697    /// little about a recoding whose digit pattern differs for every scalar.
698    /// The values here stress it: zero, one, a scalar that carries at every
699    /// position, alternating bits, and the top of the byte range -- which is
700    /// above the group order and so exercises the carry the extra limb exists
701    /// for.
702    #[test]
703    fn the_vartime_multiplication_agrees_with_the_ladder() {
704        let g = Point::generator();
705
706        let mut checked = 0;
707        for raw in [
708            [0u8; 32],
709            {
710                let mut v = [0u8; 32];
711                v[31] = 1;
712                v
713            },
714            [0xffu8; 32],
715            [0x55u8; 32],
716            [0xaau8; 32],
717            [0x9du8; 32],
718        ] {
719            let k = Fn::from_bytes_reduced(&raw);
720            assert!(
721                bool::from(g.mul_scalar_vartime(&k).ct_eq(&g.mul_scalar(&k))),
722                "vartime and ladder differ for {raw:02x?}"
723            );
724            checked += 1;
725        }
726        assert_eq!(checked, 6, "the comparison did not run");
727    }
728
729    #[test]
730    fn scalar_multiplication_matches_repeated_addition() {
731        let g = Point::generator();
732        let mut acc = Point::identity();
733        for k in 1..=10u64 {
734            acc = acc.add(&g);
735            assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
736        }
737    }
738
739    #[test]
740    fn scalar_multiplication_is_linear() {
741        let g = Point::generator();
742        let a = scalar(1_234_567);
743        let b = scalar(7_654_321);
744        assert!(bool::from(
745            g.mul_scalar(&a.add(&b))
746                .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
747        ));
748        assert!(bool::from(
749            g.mul_scalar(&a)
750                .mul_scalar(&b)
751                .ct_eq(&g.mul_scalar(&a.mul(&b)))
752        ));
753    }
754
755    /// The published `[2]G`, an independent check on the group law rather than
756    /// on self-consistency.
757    #[test]
758    fn two_g_matches_the_published_value() {
759        let two_g = Point::generator().double().to_affine().unwrap();
760        assert_eq!(
761            hex(two_g.x.to_bytes().as_ref()),
762            "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
763        );
764        assert_eq!(
765            hex(two_g.y.to_bytes().as_ref()),
766            "07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1"
767        );
768    }
769
770    #[test]
771    fn sec1_round_trips_in_both_forms() {
772        let g = Point::generator();
773        for k in [1u64, 2, 3, 4, 5, 6, 7, 8] {
774            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
775            let mut unc = [0u8; 65];
776            let mut comp = [0u8; 33];
777            assert!(p.write_uncompressed(&mut unc));
778            assert!(p.write_compressed(&mut comp));
779            assert_eq!(unc[0], 0x04);
780            assert!(comp[0] == 0x02 || comp[0] == 0x03);
781
782            let a = AffinePoint::from_sec1(&unc).unwrap();
783            let b = AffinePoint::from_sec1(&comp).unwrap();
784            assert_eq!(a.x, p.x);
785            assert_eq!(a.y, p.y);
786            assert_eq!(b.x, p.x);
787            assert_eq!(b.y, p.y, "compressed y for [{k}]G");
788        }
789    }
790
791    #[test]
792    fn decoding_rejects_bad_encodings() {
793        let g = Point::generator().to_affine().unwrap();
794        let mut unc = [0u8; 65];
795        assert!(g.write_uncompressed(&mut unc));
796
797        assert!(AffinePoint::from_sec1(&[]).is_none());
798        assert!(AffinePoint::from_sec1(&[0u8; 65]).is_none(), "identity");
799        assert!(AffinePoint::from_sec1(&unc[..64]).is_none(), "truncated");
800
801        let mut bad = unc;
802        bad[0] = 0x05;
803        assert!(AffinePoint::from_sec1(&bad).is_none(), "bad tag");
804
805        let mut bad = unc;
806        bad[64] ^= 1;
807        assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
808    }
809
810    // -- ECDSA ------------------------------------------------------------
811
812    const KEY: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
813
814    /// RFC 6979 A.2.5, message "sample". Matching this exercises the field, the
815    /// group law, the scalar ring, the nonce derivation, and the signing
816    /// equation in one shot.
817    #[test]
818    fn rfc6979_sample_vector() {
819        let key = unhex(KEY).unwrap();
820        let mut sig = [0u8; 64];
821        EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
822        assert_eq!(
823            hex(&sig[..32]),
824            "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
825            "r"
826        );
827        assert_eq!(
828            hex(&sig[32..]),
829            "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
830            "s"
831        );
832    }
833
834    /// RFC 6979 A.2.5, message "test".
835    #[test]
836    fn rfc6979_test_vector() {
837        let key = unhex(KEY).unwrap();
838        let mut sig = [0u8; 64];
839        EcdsaP256Sha256::sign(&key, b"test", &mut sig).unwrap();
840        assert_eq!(
841            hex(&sig[..32]),
842            "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
843            "r"
844        );
845        assert_eq!(
846            hex(&sig[32..]),
847            "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
848            "s"
849        );
850    }
851
852    #[test]
853    fn rfc6979_public_key() {
854        let key = unhex(KEY).unwrap();
855        let mut pk = [0u8; 65];
856        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
857        assert_eq!(
858            hex(&pk[1..33]),
859            "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
860            "Ux"
861        );
862        assert_eq!(
863            hex(&pk[33..]),
864            "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
865            "Uy"
866        );
867    }
868
869    #[test]
870    fn signing_is_deterministic_and_message_bound() {
871        let key = unhex(KEY).unwrap();
872        let mut a = [0u8; 64];
873        let mut b = [0u8; 64];
874        EcdsaP256Sha256::sign(&key, b"same", &mut a).unwrap();
875        EcdsaP256Sha256::sign(&key, b"same", &mut b).unwrap();
876        assert_eq!(a, b, "RFC 6979 signing must not depend on an RNG");
877
878        EcdsaP256Sha256::sign(&key, b"other", &mut b).unwrap();
879        assert_ne!(&a[..32], &b[..32], "r must differ between messages");
880    }
881
882    #[test]
883    fn sign_and_verify_round_trip() {
884        let key = unhex(KEY).unwrap();
885        let mut pk = [0u8; 65];
886        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
887        for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
888            let mut sig = [0u8; 64];
889            EcdsaP256Sha256::sign(&key, message, &mut sig).unwrap();
890            EcdsaP256Sha256::verify(&pk, message, &sig).unwrap();
891        }
892    }
893
894    #[test]
895    fn verification_rejects_tampering() {
896        let key = unhex(KEY).unwrap();
897        let mut pk = [0u8; 65];
898        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
899        let mut sig = [0u8; 64];
900        EcdsaP256Sha256::sign(&key, b"authentic", &mut sig).unwrap();
901
902        assert!(EcdsaP256Sha256::verify(&pk, b"forged", &sig).is_err());
903        let mut bad = sig;
904        bad[0] ^= 1;
905        assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
906        let mut bad = sig;
907        bad[63] ^= 1;
908        assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
909
910        let mut other = [0u8; 65];
911        EcdsaP256Sha256::public_key(&[0x11u8; 32], &mut other).unwrap();
912        assert!(EcdsaP256Sha256::verify(&other, b"authentic", &sig).is_err());
913    }
914
915    #[test]
916    fn verification_rejects_degenerate_signatures() {
917        let key = unhex(KEY).unwrap();
918        let mut pk = [0u8; 65];
919        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
920
921        let mut zero_r = [0u8; 64];
922        zero_r[63] = 1;
923        assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_r).is_err());
924
925        let mut zero_s = [0u8; 64];
926        zero_s[31] = 1;
927        assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_s).is_err());
928
929        let n_bytes =
930            unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551").unwrap();
931        let mut at_n = [0u8; 64];
932        at_n[..32].copy_from_slice(&n_bytes);
933        at_n[32..].copy_from_slice(&n_bytes);
934        assert!(EcdsaP256Sha256::verify(&pk, b"m", &at_n).is_err());
935    }
936
937    #[test]
938    fn signing_rejects_invalid_private_keys() {
939        let mut sig = [0u8; 64];
940        assert!(
941            EcdsaP256Sha256::sign(&[0u8; 32], b"m", &mut sig).is_err(),
942            "zero"
943        );
944        assert!(
945            EcdsaP256Sha256::sign(&[0xffu8; 32], b"m", &mut sig).is_err(),
946            ">= n"
947        );
948        assert!(
949            EcdsaP256Sha256::sign(&[1u8; 31], b"m", &mut sig).is_err(),
950            "short"
951        );
952    }
953
954    /// Both `(r, s)` and `(r, n - s)` verify; normalization picks one.
955    #[test]
956    fn malleability_and_normalization() {
957        let key = unhex(KEY).unwrap();
958        let mut pk = [0u8; 65];
959        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
960        let mut sig = [0u8; 64];
961        EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
962        assert!(
963            !EcdsaP256Sha256::has_low_s(&sig).unwrap(),
964            "RFC 6979 s is high here"
965        );
966
967        let mut flipped = sig;
968        EcdsaP256Sha256::normalize_s(&mut flipped).unwrap();
969        assert_ne!(flipped, sig);
970        EcdsaP256Sha256::verify(&pk, b"sample", &flipped).unwrap();
971        assert!(EcdsaP256Sha256::has_low_s(&flipped).unwrap());
972
973        let mut twice = flipped;
974        EcdsaP256Sha256::normalize_s(&mut twice).unwrap();
975        assert_eq!(twice, flipped, "normalization must be idempotent");
976    }
977
978    #[test]
979    fn ecdsa_self_test_passes() {
980        EcdsaP256Sha256::self_test().unwrap();
981    }
982
983    // -- ECDH -------------------------------------------------------------
984
985    /// NIST CAVP ECC CDH, first published case.
986    #[test]
987    fn cavp_ecc_cdh_vector() {
988        let d = unhex("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534").unwrap();
989        let mut peer = vec![0x04u8];
990        peer.extend_from_slice(
991            &unhex("700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287").unwrap(),
992        );
993        peer.extend_from_slice(
994            &unhex("db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac").unwrap(),
995        );
996        let mut z = [0u8; 32];
997        EcdhP256::agree(&d, &peer, &mut z).unwrap();
998        assert_eq!(
999            hex(&z),
1000            "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
1001        );
1002    }
1003
1004    #[test]
1005    fn both_parties_derive_the_same_secret() {
1006        let (alice, bob) = ([0x11u8; 32], [0x22u8; 32]);
1007        let mut alice_pk = [0u8; 65];
1008        let mut bob_pk = [0u8; 65];
1009        EcdhP256::public_key(&alice, &mut alice_pk).unwrap();
1010        EcdhP256::public_key(&bob, &mut bob_pk).unwrap();
1011
1012        let mut z1 = [0u8; 32];
1013        let mut z2 = [0u8; 32];
1014        EcdhP256::agree(&alice, &bob_pk, &mut z1).unwrap();
1015        EcdhP256::agree(&bob, &alice_pk, &mut z2).unwrap();
1016        assert_eq!(z1, z2);
1017        assert_ne!(z1, [0u8; 32]);
1018    }
1019
1020    #[test]
1021    fn compressed_and_uncompressed_peers_agree() {
1022        let (alice, bob) = ([0x33u8; 32], [0x44u8; 32]);
1023        let mut unc = [0u8; 65];
1024        let mut comp = [0u8; 33];
1025        EcdhP256::public_key(&bob, &mut unc).unwrap();
1026        EcdhP256::public_key_compressed(&bob, &mut comp).unwrap();
1027
1028        let mut z1 = [0u8; 32];
1029        let mut z2 = [0u8; 32];
1030        EcdhP256::agree(&alice, &unc, &mut z1).unwrap();
1031        EcdhP256::agree(&alice, &comp, &mut z2).unwrap();
1032        assert_eq!(z1, z2, "the peer key encoding must not matter");
1033    }
1034
1035    #[test]
1036    fn ecdh_rejects_invalid_inputs() {
1037        let alice = [0x11u8; 32];
1038        let mut z = [0u8; 32];
1039        assert!(EcdhP256::agree(&alice, &[0u8; 65], &mut z).is_err());
1040        assert!(EcdhP256::agree(&alice, &[], &mut z).is_err());
1041
1042        let mut bob_pk = [0u8; 65];
1043        EcdhP256::public_key(&[0x22u8; 32], &mut bob_pk).unwrap();
1044        bob_pk[64] ^= 1;
1045        assert!(
1046            EcdhP256::agree(&alice, &bob_pk, &mut z).is_err(),
1047            "off curve"
1048        );
1049
1050        let mut pk = [0u8; 65];
1051        assert!(EcdhP256::public_key(&[0u8; 32], &mut pk).is_err());
1052        assert!(EcdhP256::public_key(&[0xffu8; 32], &mut pk).is_err());
1053    }
1054
1055    #[test]
1056    fn ecdh_self_test_passes() {
1057        EcdhP256::self_test().unwrap();
1058    }
1059}