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
14mont_field!(
15    Fp,
16    4,
17    32,
18    [
19        0xffff_ffff_ffff_ffff,
20        0x0000_0000_ffff_ffff,
21        0x0000_0000_0000_0000,
22        0xffff_ffff_0000_0001,
23    ],
24    "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1."
25);
26
27mont_field!(
28    Fn,
29    4,
30    32,
31    [
32        0xf3b9_cac2_fc63_2551,
33        0xbce6_faad_a717_9e84,
34        0xffff_ffff_ffff_ffff,
35        0xffff_ffff_0000_0000,
36    ],
37    "The P-256 scalar ring, Z/nZ where n is the order of the base point."
38);
39
40/// The P-256 curve.
41#[derive(Debug, Clone, Copy)]
42pub struct P256;
43
44// Its own generator table, with its own storage; see the macro, which
45// emits the table under `std` and the windowed multiplication without it.
46crate::nist::gentable::generator_table_for!(P256);
47
48impl Curve for P256 {
49    type Field = Fp;
50    type Scalar = Fn;
51
52    const NAME: &'static str = "P-256";
53    const FIELD_BYTES: usize = 32;
54    const SCALAR_BYTES: usize = 32;
55    const ORDER_BITS: usize = 256;
56
57    /// `b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`
58    const B: Fp = Fp::to_mont_const([
59        0x3bce_3c3e_27d2_604b,
60        0x651d_06b0_cc53_b0f6,
61        0xb3eb_bd55_7698_86bc,
62        0x5ac6_35d8_aa3a_93e7,
63    ]);
64
65    const GX: Fp = Fp::to_mont_const([
66        0xf4a1_3945_d898_c296,
67        0x7703_7d81_2deb_33a0,
68        0xf8bc_e6e5_63a4_40f2,
69        0x6b17_d1f2_e12c_4247,
70    ]);
71
72    const GY: Fp = Fp::to_mont_const([
73        0xcbb6_4068_37bf_51f5,
74        0x2bce_3357_6b31_5ece,
75        0x8ee7_eb4a_7c0f_9e16,
76        0x4fe3_42e2_fe1a_7f9b,
77    ]);
78
79    /// `p = 3 mod 4`, so a square root is `x^((p+1)/4)`.
80    fn sqrt(x: &Fp) -> Fp {
81        // p = 3 mod 4, so the root is x^((p+1)/4). The shared helper computes
82        // that exponent rather than this file unrolling it by limb: an unrolled
83        // shift is easy to get subtly wrong and would only misbehave on inputs
84        // rare enough that a round-trip test would not find them.
85        sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
86    }
87
88    fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
89        let mut b = [0u8; 32];
90        if bytes.len() != 32 {
91            return None;
92        }
93        b.copy_from_slice(bytes);
94        Fp::from_bytes(&b)
95    }
96
97    fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
98        let mut b = [0u8; 32];
99        if bytes.len() != 32 {
100            return None;
101        }
102        b.copy_from_slice(bytes);
103        Fn::from_bytes(&b)
104    }
105
106    fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
107        let mut b = [0u8; 32];
108        let n = core::cmp::min(32, bytes.len());
109        // Take the leftmost bytes, which is what bits2int does when the input
110        // is at least as wide as the group order.
111        b[32 - n..].copy_from_slice(&bytes[..n]);
112        Fn::from_bytes_reduced(&b)
113    }
114}
115
116impl ecdsa::EcdsaCurve for P256 {
117    type Digest = ic_hash::Sha256;
118    type Hmac = ic_mac::HmacSha256;
119}
120
121/// ECDSA over P-256 with SHA-256.
122pub struct EcdsaP256Sha256;
123
124impl Algorithm for EcdsaP256Sha256 {
125    const ID: &'static str = "ecdsa-p256-sha256";
126    const NAME: &'static str = "ECDSA P-256 with SHA-256";
127}
128
129impl SignatureScheme for EcdsaP256Sha256 {
130    const PRIVATE_KEY_LEN: usize = 32;
131    /// SEC1 uncompressed: `0x04 || X || Y`.
132    const PUBLIC_KEY_LEN: usize = 65;
133    /// Fixed-width `r || s`.
134    const SIGNATURE_LEN: usize = 64;
135
136    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
137        ecdsa::public_key::<P256>(private_key, out)
138    }
139
140    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
141        ecdsa::sign::<P256>(private_key, message, signature)
142    }
143
144    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
145        ecdsa::verify::<P256>(public_key, message, signature)
146    }
147}
148
149impl EcdsaP256Sha256 {
150    /// Verify a signature over a digest the caller computed, for signatures
151    /// made with a hash other than SHA-256.
152    ///
153    /// `digest` must be one of [`crate::PREHASH_LENS`] and at least 32
154    /// bytes, so that the hash is at least as strong as the curve; narrower is
155    /// refused with `InvalidLength`. Which hash produced it is the caller's to
156    /// establish: nothing here can tell.
157    pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
158        ecdsa::verify_prehash::<P256>(public_key, digest, signature)
159    }
160
161    /// Compute the public key in SEC1 compressed form (33 bytes).
162    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
163        ecdsa::public_key_compressed::<P256>(private_key, out)
164    }
165
166    /// Rewrite a signature to its low-`s` form, if it is not already.
167    ///
168    /// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the
169    /// same message, so a signature is not a unique identifier unless one form
170    /// is chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs
171    /// and verifies per the standard, so normalization is offered rather than
172    /// imposed -- apply it when a signature doubles as a database key or a
173    /// transaction id.
174    pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
175        ecdsa::normalize_s::<P256>(signature)
176    }
177
178    /// Whether a signature is already in low-`s` form.
179    pub fn has_low_s(signature: &[u8]) -> Result<bool> {
180        ecdsa::has_low_s::<P256>(signature)
181    }
182}
183
184impl SelfTest for EcdsaP256Sha256 {
185    fn self_test() -> Result<()> {
186        // RFC 6979 A.2.5: P-256, SHA-256, message "sample".
187        let mut key = [0u8; 32];
188        ic_core::codec::hex_decode(
189            b"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
190            &mut key,
191        )?;
192        let mut want = [0u8; 64];
193        ic_core::codec::hex_decode(
194            b"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
195            &mut want,
196        )?;
197
198        let mut sig = [0u8; 64];
199        <Self as SignatureScheme>::sign(&key, b"sample", &mut sig)?;
200        ensure!(
201            ic_core::ct::verify(&want, &sig),
202            SelfTestFailed,
203            "ecdsa-p256-sha256"
204        );
205
206        let mut pk = [0u8; 65];
207        <Self as SignatureScheme>::public_key(&key, &mut pk)?;
208        <Self as SignatureScheme>::verify(&pk, b"sample", &sig)?;
209
210        // A flipped bit must be rejected.
211        sig[0] ^= 1;
212        ensure!(
213            <Self as SignatureScheme>::verify(&pk, b"sample", &sig).is_err(),
214            SelfTestFailed,
215            "ecdsa-p256-sha256"
216        );
217        Ok(())
218    }
219}
220
221/// ECDH over P-256.
222pub struct EcdhP256;
223
224impl Algorithm for EcdhP256 {
225    const ID: &'static str = "ecdh-p256";
226    const NAME: &'static str = "ECDH P-256";
227}
228
229impl KeyAgreement for EcdhP256 {
230    const PRIVATE_KEY_LEN: usize = 32;
231    /// SEC1 uncompressed: `0x04 || X || Y`.
232    const PUBLIC_KEY_LEN: usize = 65;
233    const SHARED_SECRET_LEN: usize = 32;
234
235    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
236        ecdh::public_key::<P256>(private_key, out)
237    }
238
239    fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
240        ecdh::agree::<P256>(private_key, peer_public_key, out)
241    }
242}
243
244impl EcdhP256 {
245    /// Compute the public key in SEC1 compressed form (33 bytes).
246    ///
247    /// Interoperates with TLS, COSE, and JOSE, which all prefer compressed
248    /// points. [`KeyAgreement::agree`] accepts either form.
249    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
250        ecdh::public_key_compressed::<P256>(private_key, out)
251    }
252}
253
254impl SelfTest for EcdhP256 {
255    fn self_test() -> Result<()> {
256        // NIST CAVP ECC CDH, P-256, the first published key-agreement case.
257        let mut d = [0u8; 32];
258        ic_core::codec::hex_decode(
259            b"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
260            &mut d,
261        )?;
262        let mut peer = [0u8; 65];
263        peer[0] = 0x04;
264        ic_core::codec::hex_decode(
265            b"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
266            &mut peer[1..33],
267        )?;
268        ic_core::codec::hex_decode(
269            b"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
270            &mut peer[33..],
271        )?;
272        let mut want = [0u8; 32];
273        ic_core::codec::hex_decode(
274            b"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
275            &mut want,
276        )?;
277
278        let mut got = [0u8; 32];
279        <Self as KeyAgreement>::agree(&d, &peer, &mut got)?;
280        ensure!(
281            ic_core::ct::verify(&want, &got),
282            SelfTestFailed,
283            "ecdh-p256"
284        );
285        Ok(())
286    }
287}
288
289/// A P-256 point in Jacobian coordinates.
290pub type Point = crate::nist::point::Point<P256>;
291/// A P-256 point in affine coordinates.
292pub type AffinePoint = crate::nist::point::AffinePoint<P256>;
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297    use ic_core::codec::{hex, unhex};
298
299    /// Where P-256's time goes, piece by piece. A measurement, not a check:
300    /// run with `cargo test --release -p ic-ec -- --ignored --nocapture
301    /// where_the_time_goes`.
302    #[test]
303    #[ignore = "timing; run manually with --release"]
304    fn where_the_time_goes() {
305        use crate::nist::arith::Field;
306        use std::hint::black_box;
307        use std::time::Instant;
308        fn ns(label: &str, iters: u32, mut f: impl FnMut()) {
309            let mut best = f64::INFINITY;
310            for _ in 0..7 {
311                let t = Instant::now();
312                for _ in 0..iters {
313                    f();
314                }
315                best = best.min(t.elapsed().as_secs_f64() * 1e9 / iters as f64);
316            }
317            std::println!("{label:32} {best:10.1} ns");
318        }
319        let a = Fp::to_mont([7, 11, 13, 17]);
320        let b = Fp::to_mont([19, 23, 29, 31]);
321        ns("fp mul", 2_000_000, || {
322            black_box(black_box(a).mul(&black_box(b)));
323        });
324        ns("fp square", 2_000_000, || {
325            black_box(black_box(a).square());
326        });
327        ns("fp add", 2_000_000, || {
328            black_box(black_box(a).add(&black_box(b)));
329        });
330        ns("fp sub", 2_000_000, || {
331            black_box(black_box(a).sub(&black_box(b)));
332        });
333        ns("fp invert", 2_000, || {
334            black_box(black_box(a).invert());
335        });
336        let g = Point::generator();
337        let p = g.double();
338        ns("point double", 200_000, || {
339            black_box(black_box(p).double());
340        });
341        ns("point add (complete)", 200_000, || {
342            black_box(black_box(p).add(&black_box(g)));
343        });
344        let k = Fn::to_mont([0x1234_5678, 0x9abc_def0, 0x1357_9bdf, 0x2468_ace0]);
345        ns("scalar invert", 2_000, || {
346            black_box(black_box(k).invert());
347        });
348        ns("mul_generator (table)", 2_000, || {
349            black_box(Point::mul_generator(&black_box(k)));
350        });
351        ns("mul_scalar (windowed)", 500, || {
352            black_box(black_box(p).mul_scalar(&black_box(k)));
353        });
354        ns("mul_scalar_vartime", 500, || {
355            black_box(black_box(p).mul_scalar_vartime(&black_box(k)));
356        });
357        ns("to_affine", 2_000, || {
358            black_box(black_box(p).to_affine());
359        });
360        let mut sig = [0u8; 64];
361        ns("ecdsa sign", 500, || {
362            EcdsaP256Sha256::sign(&[0x5a; 32], b"message", &mut sig).unwrap();
363        });
364    }
365
366    fn scalar(v: u64) -> Fn {
367        Fn::to_mont([v, 0, 0, 0])
368    }
369
370    fn fp(v: u64) -> Fp {
371        Fp::to_mont([v, 0, 0, 0])
372    }
373
374    // -- field ------------------------------------------------------------
375
376    #[test]
377    fn montgomery_constants_are_consistent() {
378        assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
379        assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
380    }
381
382    #[test]
383    fn small_arithmetic_matches_integers() {
384        assert_eq!(fp(2).add(&fp(3)), fp(5));
385        assert_eq!(fp(5).sub(&fp(3)), fp(2));
386        assert_eq!(fp(6).mul(&fp(7)), fp(42));
387        assert_eq!(fp(9).square(), fp(81));
388        assert_eq!(fp(5).double(), fp(10));
389        assert_eq!(fp(5).triple(), fp(15));
390        assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0]);
391    }
392
393    #[test]
394    fn inversion_is_correct() {
395        for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
396            assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
397            assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
398        }
399        assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
400    }
401
402    #[test]
403    fn arithmetic_laws_hold_on_large_values() {
404        let a = P256::field_from_slice(&[0x3a; 32]).unwrap();
405        let b = P256::field_from_slice(&[0x91; 32]).unwrap();
406        let c = P256::field_from_slice(&[0xc7; 32]).unwrap();
407        assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
408        assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
409        assert_eq!(
410            a.mul(&b.add(&c)),
411            a.mul(&b).add(&a.mul(&c)),
412            "distributivity"
413        );
414        assert_eq!(a.add(&a.neg()), Fp::ZERO);
415    }
416
417    #[test]
418    fn byte_encoding_round_trips_and_rejects_non_canonical() {
419        let bytes = [0x7fu8; 32];
420        let a = P256::field_from_slice(&bytes).unwrap();
421        assert_eq!(a.to_bytes(), bytes);
422
423        // p itself must be refused but reduce to zero.
424        let mut p_bytes = [0u8; 32];
425        for i in 0..4 {
426            let hi = 32 - i * 8;
427            p_bytes[hi - 8..hi].copy_from_slice(&Fp::MODULUS[i].to_be_bytes());
428        }
429        assert!(P256::field_from_slice(&p_bytes).is_none());
430    }
431
432    // -- group law --------------------------------------------------------
433
434    /// Validates B, GX, GY and the curve equation together: if any of the four
435    /// constants were mistranscribed, the base point would not satisfy it.
436    #[test]
437    fn the_base_point_is_on_the_curve() {
438        let g = Point::generator().to_affine().unwrap();
439        assert!(bool::from(g.is_on_curve()));
440    }
441
442    /// Validates the group order n against the base point.
443    #[test]
444    fn the_base_point_has_order_n() {
445        let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
446        let p = Point::generator().mul_scalar(&n_minus_1);
447        assert!(
448            bool::from(p.ct_eq(&Point::generator().neg())),
449            "[n-1]G == -G"
450        );
451        assert!(
452            bool::from(p.add(&Point::generator()).is_identity()),
453            "[n]G is the identity"
454        );
455    }
456
457    #[test]
458    fn identity_and_negation_behave() {
459        let g = Point::generator();
460        assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
461        assert!(bool::from(Point::identity().add(&g).ct_eq(&g)));
462        assert!(bool::from(Point::identity().double().is_identity()));
463        assert!(bool::from(g.add(&g.neg()).is_identity()));
464        assert!(Point::identity().to_affine().is_none());
465    }
466
467    /// The exceptional case a naive Jacobian addition gets wrong.
468    #[test]
469    fn addition_handles_equal_inputs_as_a_doubling() {
470        let g = Point::generator();
471        assert!(bool::from(g.add(&g).ct_eq(&g.double())));
472        let p = g.mul_scalar(&scalar(5));
473        assert!(bool::from(p.add(&p).ct_eq(&p.double())));
474    }
475
476    /// The variable-time path must agree with the constant-time one.
477    ///
478    /// The RFC 6979 vectors reach it with a couple of scalars, which says
479    /// little about a recoding whose digit pattern differs for every scalar.
480    /// The values here stress it: zero, one, a scalar that carries at every
481    /// position, alternating bits, and the top of the byte range -- which is
482    /// above the group order and so exercises the carry the extra limb exists
483    /// for.
484    #[test]
485    fn the_vartime_multiplication_agrees_with_the_ladder() {
486        let g = Point::generator();
487
488        let mut checked = 0;
489        for raw in [
490            [0u8; 32],
491            {
492                let mut v = [0u8; 32];
493                v[31] = 1;
494                v
495            },
496            [0xffu8; 32],
497            [0x55u8; 32],
498            [0xaau8; 32],
499            [0x9du8; 32],
500        ] {
501            let k = Fn::from_bytes_reduced(&raw);
502            assert!(
503                bool::from(g.mul_scalar_vartime(&k).ct_eq(&g.mul_scalar(&k))),
504                "vartime and ladder differ for {raw:02x?}"
505            );
506            checked += 1;
507        }
508        assert_eq!(checked, 6, "the comparison did not run");
509    }
510
511    #[test]
512    fn scalar_multiplication_matches_repeated_addition() {
513        let g = Point::generator();
514        let mut acc = Point::identity();
515        for k in 1..=10u64 {
516            acc = acc.add(&g);
517            assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
518        }
519    }
520
521    #[test]
522    fn scalar_multiplication_is_linear() {
523        let g = Point::generator();
524        let a = scalar(1_234_567);
525        let b = scalar(7_654_321);
526        assert!(bool::from(
527            g.mul_scalar(&a.add(&b))
528                .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
529        ));
530        assert!(bool::from(
531            g.mul_scalar(&a)
532                .mul_scalar(&b)
533                .ct_eq(&g.mul_scalar(&a.mul(&b)))
534        ));
535    }
536
537    /// The published `[2]G`, an independent check on the group law rather than
538    /// on self-consistency.
539    #[test]
540    fn two_g_matches_the_published_value() {
541        let two_g = Point::generator().double().to_affine().unwrap();
542        assert_eq!(
543            hex(two_g.x.to_bytes().as_ref()),
544            "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
545        );
546        assert_eq!(
547            hex(two_g.y.to_bytes().as_ref()),
548            "07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1"
549        );
550    }
551
552    #[test]
553    fn sec1_round_trips_in_both_forms() {
554        let g = Point::generator();
555        for k in [1u64, 2, 3, 4, 5, 6, 7, 8] {
556            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
557            let mut unc = [0u8; 65];
558            let mut comp = [0u8; 33];
559            assert!(p.write_uncompressed(&mut unc));
560            assert!(p.write_compressed(&mut comp));
561            assert_eq!(unc[0], 0x04);
562            assert!(comp[0] == 0x02 || comp[0] == 0x03);
563
564            let a = AffinePoint::from_sec1(&unc).unwrap();
565            let b = AffinePoint::from_sec1(&comp).unwrap();
566            assert_eq!(a.x, p.x);
567            assert_eq!(a.y, p.y);
568            assert_eq!(b.x, p.x);
569            assert_eq!(b.y, p.y, "compressed y for [{k}]G");
570        }
571    }
572
573    #[test]
574    fn decoding_rejects_bad_encodings() {
575        let g = Point::generator().to_affine().unwrap();
576        let mut unc = [0u8; 65];
577        assert!(g.write_uncompressed(&mut unc));
578
579        assert!(AffinePoint::from_sec1(&[]).is_none());
580        assert!(AffinePoint::from_sec1(&[0u8; 65]).is_none(), "identity");
581        assert!(AffinePoint::from_sec1(&unc[..64]).is_none(), "truncated");
582
583        let mut bad = unc;
584        bad[0] = 0x05;
585        assert!(AffinePoint::from_sec1(&bad).is_none(), "bad tag");
586
587        let mut bad = unc;
588        bad[64] ^= 1;
589        assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
590    }
591
592    // -- ECDSA ------------------------------------------------------------
593
594    const KEY: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
595
596    /// RFC 6979 A.2.5, message "sample". Matching this exercises the field, the
597    /// group law, the scalar ring, the nonce derivation, and the signing
598    /// equation in one shot.
599    #[test]
600    fn rfc6979_sample_vector() {
601        let key = unhex(KEY).unwrap();
602        let mut sig = [0u8; 64];
603        EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
604        assert_eq!(
605            hex(&sig[..32]),
606            "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
607            "r"
608        );
609        assert_eq!(
610            hex(&sig[32..]),
611            "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
612            "s"
613        );
614    }
615
616    /// RFC 6979 A.2.5, message "test".
617    #[test]
618    fn rfc6979_test_vector() {
619        let key = unhex(KEY).unwrap();
620        let mut sig = [0u8; 64];
621        EcdsaP256Sha256::sign(&key, b"test", &mut sig).unwrap();
622        assert_eq!(
623            hex(&sig[..32]),
624            "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
625            "r"
626        );
627        assert_eq!(
628            hex(&sig[32..]),
629            "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
630            "s"
631        );
632    }
633
634    #[test]
635    fn rfc6979_public_key() {
636        let key = unhex(KEY).unwrap();
637        let mut pk = [0u8; 65];
638        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
639        assert_eq!(
640            hex(&pk[1..33]),
641            "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
642            "Ux"
643        );
644        assert_eq!(
645            hex(&pk[33..]),
646            "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
647            "Uy"
648        );
649    }
650
651    #[test]
652    fn signing_is_deterministic_and_message_bound() {
653        let key = unhex(KEY).unwrap();
654        let mut a = [0u8; 64];
655        let mut b = [0u8; 64];
656        EcdsaP256Sha256::sign(&key, b"same", &mut a).unwrap();
657        EcdsaP256Sha256::sign(&key, b"same", &mut b).unwrap();
658        assert_eq!(a, b, "RFC 6979 signing must not depend on an RNG");
659
660        EcdsaP256Sha256::sign(&key, b"other", &mut b).unwrap();
661        assert_ne!(&a[..32], &b[..32], "r must differ between messages");
662    }
663
664    #[test]
665    fn sign_and_verify_round_trip() {
666        let key = unhex(KEY).unwrap();
667        let mut pk = [0u8; 65];
668        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
669        for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
670            let mut sig = [0u8; 64];
671            EcdsaP256Sha256::sign(&key, message, &mut sig).unwrap();
672            EcdsaP256Sha256::verify(&pk, message, &sig).unwrap();
673        }
674    }
675
676    #[test]
677    fn verification_rejects_tampering() {
678        let key = unhex(KEY).unwrap();
679        let mut pk = [0u8; 65];
680        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
681        let mut sig = [0u8; 64];
682        EcdsaP256Sha256::sign(&key, b"authentic", &mut sig).unwrap();
683
684        assert!(EcdsaP256Sha256::verify(&pk, b"forged", &sig).is_err());
685        let mut bad = sig;
686        bad[0] ^= 1;
687        assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
688        let mut bad = sig;
689        bad[63] ^= 1;
690        assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
691
692        let mut other = [0u8; 65];
693        EcdsaP256Sha256::public_key(&[0x11u8; 32], &mut other).unwrap();
694        assert!(EcdsaP256Sha256::verify(&other, b"authentic", &sig).is_err());
695    }
696
697    #[test]
698    fn verification_rejects_degenerate_signatures() {
699        let key = unhex(KEY).unwrap();
700        let mut pk = [0u8; 65];
701        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
702
703        let mut zero_r = [0u8; 64];
704        zero_r[63] = 1;
705        assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_r).is_err());
706
707        let mut zero_s = [0u8; 64];
708        zero_s[31] = 1;
709        assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_s).is_err());
710
711        let n_bytes =
712            unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551").unwrap();
713        let mut at_n = [0u8; 64];
714        at_n[..32].copy_from_slice(&n_bytes);
715        at_n[32..].copy_from_slice(&n_bytes);
716        assert!(EcdsaP256Sha256::verify(&pk, b"m", &at_n).is_err());
717    }
718
719    #[test]
720    fn signing_rejects_invalid_private_keys() {
721        let mut sig = [0u8; 64];
722        assert!(
723            EcdsaP256Sha256::sign(&[0u8; 32], b"m", &mut sig).is_err(),
724            "zero"
725        );
726        assert!(
727            EcdsaP256Sha256::sign(&[0xffu8; 32], b"m", &mut sig).is_err(),
728            ">= n"
729        );
730        assert!(
731            EcdsaP256Sha256::sign(&[1u8; 31], b"m", &mut sig).is_err(),
732            "short"
733        );
734    }
735
736    /// Both `(r, s)` and `(r, n - s)` verify; normalization picks one.
737    #[test]
738    fn malleability_and_normalization() {
739        let key = unhex(KEY).unwrap();
740        let mut pk = [0u8; 65];
741        EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
742        let mut sig = [0u8; 64];
743        EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
744        assert!(
745            !EcdsaP256Sha256::has_low_s(&sig).unwrap(),
746            "RFC 6979 s is high here"
747        );
748
749        let mut flipped = sig;
750        EcdsaP256Sha256::normalize_s(&mut flipped).unwrap();
751        assert_ne!(flipped, sig);
752        EcdsaP256Sha256::verify(&pk, b"sample", &flipped).unwrap();
753        assert!(EcdsaP256Sha256::has_low_s(&flipped).unwrap());
754
755        let mut twice = flipped;
756        EcdsaP256Sha256::normalize_s(&mut twice).unwrap();
757        assert_eq!(twice, flipped, "normalization must be idempotent");
758    }
759
760    #[test]
761    fn ecdsa_self_test_passes() {
762        EcdsaP256Sha256::self_test().unwrap();
763    }
764
765    // -- ECDH -------------------------------------------------------------
766
767    /// NIST CAVP ECC CDH, first published case.
768    #[test]
769    fn cavp_ecc_cdh_vector() {
770        let d = unhex("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534").unwrap();
771        let mut peer = vec![0x04u8];
772        peer.extend_from_slice(
773            &unhex("700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287").unwrap(),
774        );
775        peer.extend_from_slice(
776            &unhex("db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac").unwrap(),
777        );
778        let mut z = [0u8; 32];
779        EcdhP256::agree(&d, &peer, &mut z).unwrap();
780        assert_eq!(
781            hex(&z),
782            "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
783        );
784    }
785
786    #[test]
787    fn both_parties_derive_the_same_secret() {
788        let (alice, bob) = ([0x11u8; 32], [0x22u8; 32]);
789        let mut alice_pk = [0u8; 65];
790        let mut bob_pk = [0u8; 65];
791        EcdhP256::public_key(&alice, &mut alice_pk).unwrap();
792        EcdhP256::public_key(&bob, &mut bob_pk).unwrap();
793
794        let mut z1 = [0u8; 32];
795        let mut z2 = [0u8; 32];
796        EcdhP256::agree(&alice, &bob_pk, &mut z1).unwrap();
797        EcdhP256::agree(&bob, &alice_pk, &mut z2).unwrap();
798        assert_eq!(z1, z2);
799        assert_ne!(z1, [0u8; 32]);
800    }
801
802    #[test]
803    fn compressed_and_uncompressed_peers_agree() {
804        let (alice, bob) = ([0x33u8; 32], [0x44u8; 32]);
805        let mut unc = [0u8; 65];
806        let mut comp = [0u8; 33];
807        EcdhP256::public_key(&bob, &mut unc).unwrap();
808        EcdhP256::public_key_compressed(&bob, &mut comp).unwrap();
809
810        let mut z1 = [0u8; 32];
811        let mut z2 = [0u8; 32];
812        EcdhP256::agree(&alice, &unc, &mut z1).unwrap();
813        EcdhP256::agree(&alice, &comp, &mut z2).unwrap();
814        assert_eq!(z1, z2, "the peer key encoding must not matter");
815    }
816
817    #[test]
818    fn ecdh_rejects_invalid_inputs() {
819        let alice = [0x11u8; 32];
820        let mut z = [0u8; 32];
821        assert!(EcdhP256::agree(&alice, &[0u8; 65], &mut z).is_err());
822        assert!(EcdhP256::agree(&alice, &[], &mut z).is_err());
823
824        let mut bob_pk = [0u8; 65];
825        EcdhP256::public_key(&[0x22u8; 32], &mut bob_pk).unwrap();
826        bob_pk[64] ^= 1;
827        assert!(
828            EcdhP256::agree(&alice, &bob_pk, &mut z).is_err(),
829            "off curve"
830        );
831
832        let mut pk = [0u8; 65];
833        assert!(EcdhP256::public_key(&[0u8; 32], &mut pk).is_err());
834        assert!(EcdhP256::public_key(&[0xffu8; 32], &mut pk).is_err());
835    }
836
837    #[test]
838    fn ecdh_self_test_passes() {
839        EcdhP256::self_test().unwrap();
840    }
841}