Skip to main content

ic_ec/
p521.rs

1//! NIST P-521 (secp521r1).
2//!
3//! The largest of the NIST prime curves, and the only one here whose field
4//! width is not a multiple of 64 bits: `p = 2^521 - 1`, which needs nine limbs
5//! of which the top carries nine significant bits. That awkwardness is confined
6//! to this module's constants and to the byte conversion in
7//! `crate::nist::arith`; the group law and the schemes are the same generic
8//! code P-256 and P-384 use.
9//!
10//! Paired with SHA-512. The hash is 512 bits and the group order is 521, so
11//! RFC 6979's `bits2int` takes the digest whole with no truncation and no
12//! shift — the one case where a shorter hash than the order is handled by doing
13//! nothing.
14//!
15//! # A Mersenne prime has conveniences
16//!
17//! `p = 2^521 - 1` means `(p + 1) / 4 = 2^519` exactly, so a square root is 519
18//! squarings with no multiplications at all. `P521::sqrt` says so directly
19//! rather than running the generic square-and-multiply over an exponent that
20//! happens to be a power of two.
21
22use crate::mont_field;
23use crate::nist::arith::Field;
24use crate::nist::point::Curve;
25use crate::nist::{ecdh, ecdsa};
26use ic_core::traits::{Algorithm, KeyAgreement, SelfTest, SignatureScheme};
27use ic_core::{ensure, Result};
28
29mont_field!(
30    Fp,
31    9,
32    66,
33    [
34        0xffff_ffff_ffff_ffff,
35        0xffff_ffff_ffff_ffff,
36        0xffff_ffff_ffff_ffff,
37        0xffff_ffff_ffff_ffff,
38        0xffff_ffff_ffff_ffff,
39        0xffff_ffff_ffff_ffff,
40        0xffff_ffff_ffff_ffff,
41        0xffff_ffff_ffff_ffff,
42        0x0000_0000_0000_01ff,
43    ],
44    "The P-521 coordinate field, GF(p) with p = 2^521 - 1."
45);
46
47mont_field!(
48    Fn,
49    9,
50    66,
51    [
52        0xbb6f_b71e_9138_6409,
53        0x3bb5_c9b8_899c_47ae,
54        0x7fcc_0148_f709_a5d0,
55        0x5186_8783_bf2f_966b,
56        0xffff_ffff_ffff_fffa,
57        0xffff_ffff_ffff_ffff,
58        0xffff_ffff_ffff_ffff,
59        0xffff_ffff_ffff_ffff,
60        0x0000_0000_0000_01ff,
61    ],
62    "The P-521 scalar ring, Z/nZ where n is the order of the base point."
63);
64
65/// The P-521 curve.
66#[derive(Debug, Clone, Copy)]
67pub struct P521;
68
69// Its own generator table, with its own storage; see the macro, which
70// emits the table under `std` and the windowed multiplication without it.
71crate::nist::gentable::generator_table_for!(P521, 8);
72
73impl Curve for P521 {
74    type Field = Fp;
75    type Scalar = Fn;
76
77    const NAME: &'static str = "P-521";
78    /// 521 bits rounds up to 66 bytes, with the top seven bits of the first
79    /// byte always zero.
80    const FIELD_BYTES: usize = 66;
81    const SCALAR_BYTES: usize = 66;
82    /// 521, not 528. The seven-bit gap is what makes RFC 6979's `bits2int`
83    /// shift here where it does not for the other curves.
84    const ORDER_BITS: usize = 521;
85
86    /// `b = 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef1`
87    ///     `09e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00`
88    const B: Fp = Fp::to_mont_const([
89        0xef45_1fd4_6b50_3f00,
90        0x3573_df88_3d2c_34f1,
91        0x1652_c0bd_3bb1_bf07,
92        0x5619_3951_ec7e_937b,
93        0xb8b4_8991_8ef1_09e1,
94        0xa2da_725b_99b3_15f3,
95        0x929a_21a0_b685_40ee,
96        0x953e_b961_8e1c_9a1f,
97        0x0000_0000_0000_0051,
98    ]);
99
100    const GX: Fp = Fp::to_mont_const([
101        0xf97e_7e31_c2e5_bd66,
102        0x3348_b3c1_856a_429b,
103        0xfe1d_c127_a2ff_a8de,
104        0xa14b_5e77_efe7_5928,
105        0xf828_af60_6b4d_3dba,
106        0x9c64_8139_053f_b521,
107        0x9e3e_cb66_2395_b442,
108        0x858e_06b7_0404_e9cd,
109        0x0000_0000_0000_00c6,
110    ]);
111
112    const GY: Fp = Fp::to_mont_const([
113        0x88be_9476_9fd1_6650,
114        0x353c_7086_a272_c240,
115        0xc550_b901_3fad_0761,
116        0x97ee_7299_5ef4_2640,
117        0x17af_bd17_273e_662c,
118        0x98f5_4449_579b_4468,
119        0x5c8a_5fb4_2c7d_1bd9,
120        0x3929_6a78_9a3b_c004,
121        0x0000_0000_0000_0118,
122    ]);
123
124    /// `(p + 1) / 4 = 2^519`, so the square root is a chain of squarings.
125    ///
126    /// The other curves compute the exponent from the modulus and run
127    /// square-and-multiply. Here the exponent is a power of two, so every
128    /// multiply in that loop would be by one.
129    fn sqrt(x: &Fp) -> Fp {
130        x.square_n(519)
131    }
132
133    fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
134        let mut b = [0u8; 66];
135        if bytes.len() != 66 {
136            return None;
137        }
138        b.copy_from_slice(bytes);
139        Fp::from_bytes(&b)
140    }
141
142    fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
143        let mut b = [0u8; 66];
144        if bytes.len() != 66 {
145            return None;
146        }
147        b.copy_from_slice(bytes);
148        Fn::from_bytes(&b)
149    }
150
151    fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
152        let mut b = [0u8; 66];
153        let n = core::cmp::min(66, bytes.len());
154        // RFC 6979 bits2int: take the leftmost min(blen, qlen) bits. SHA-512 is
155        // 512 bits and the order is 521, so the digest is used whole and lands
156        // right-aligned here — no shift, which is what the specification means
157        // by "the integer represented by those bits".
158        b[66 - n..].copy_from_slice(&bytes[..n]);
159        Fn::from_bytes_reduced(&b)
160    }
161}
162
163impl ecdsa::EcdsaCurve for P521 {
164    type Digest = ic_hash::Sha512;
165    type Hmac = ic_mac::HmacSha512;
166}
167
168/// ECDSA over P-521 with SHA-512.
169pub struct EcdsaP521Sha512;
170
171impl Algorithm for EcdsaP521Sha512 {
172    const ID: &'static str = "ecdsa-p521-sha512";
173    const NAME: &'static str = "ECDSA P-521 with SHA-512";
174}
175
176impl SignatureScheme for EcdsaP521Sha512 {
177    const PRIVATE_KEY_LEN: usize = 66;
178    /// SEC1 uncompressed: `0x04 || X || Y`.
179    const PUBLIC_KEY_LEN: usize = 133;
180    /// Fixed-width `r || s`.
181    const SIGNATURE_LEN: usize = 132;
182
183    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
184        ecdsa::public_key::<P521>(private_key, out)
185    }
186
187    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
188        ecdsa::sign::<P521>(private_key, message, signature)
189    }
190
191    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
192        ecdsa::verify::<P521>(public_key, message, signature)
193    }
194}
195
196impl EcdsaP521Sha512 {
197    /// Verify a signature over a digest the caller computed, for signatures
198    /// made with a hash other than SHA-512.
199    ///
200    /// `digest` must be one of [`crate::PREHASH_LENS`] and at least 64
201    /// bytes, so that the hash is at least as strong as the curve; narrower is
202    /// refused with `InvalidLength`. Which hash produced it is the caller's to
203    /// establish: nothing here can tell.
204    pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
205        ecdsa::verify_prehash::<P521>(public_key, digest, signature)
206    }
207
208    /// Compute the public key in SEC1 compressed form (67 bytes).
209    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
210        ecdsa::public_key_compressed::<P521>(private_key, out)
211    }
212
213    /// Rewrite a signature to its low-`s` form, if it is not already.
214    ///
215    /// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the
216    /// same message, so a signature is not a unique identifier unless one form
217    /// is chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs
218    /// and verifies per the standard, so normalization is offered rather than
219    /// imposed -- apply it when a signature doubles as a database key or a
220    /// transaction id.
221    pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
222        ecdsa::normalize_s::<P521>(signature)
223    }
224
225    /// Whether a signature is already in low-`s` form.
226    pub fn has_low_s(signature: &[u8]) -> Result<bool> {
227        ecdsa::has_low_s::<P521>(signature)
228    }
229}
230
231impl SelfTest for EcdsaP521Sha512 {
232    fn self_test() -> Result<()> {
233        // Round-trip plus tamper rejection. The cross-check against an
234        // independent RFC 6979 implementation lives in the unit tests; this
235        // CAST is the startup integrity check.
236        // A 66-byte scalar must stay below the 521-bit order, so the top byte
237        // cannot be filled the way the other curves' self-test keys are.
238        let mut key = [0x2au8; 66];
239        key[0] = 0x00;
240        let mut pk = [0u8; 133];
241        <Self as SignatureScheme>::public_key(&key, &mut pk)?;
242
243        let mut sig = [0u8; 132];
244        <Self as SignatureScheme>::sign(&key, b"self-test", &mut sig)?;
245        <Self as SignatureScheme>::verify(&pk, b"self-test", &sig)?;
246
247        // Signing is deterministic, so a repeat must agree exactly.
248        let mut again = [0u8; 132];
249        <Self as SignatureScheme>::sign(&key, b"self-test", &mut again)?;
250        ensure!(
251            ic_core::ct::verify(&sig, &again),
252            SelfTestFailed,
253            "ecdsa-p521-sha512"
254        );
255
256        sig[0] ^= 1;
257        ensure!(
258            <Self as SignatureScheme>::verify(&pk, b"self-test", &sig).is_err(),
259            SelfTestFailed,
260            "ecdsa-p521-sha512"
261        );
262        Ok(())
263    }
264}
265
266/// ECDH over P-521.
267pub struct EcdhP521;
268
269impl Algorithm for EcdhP521 {
270    const ID: &'static str = "ecdh-p521";
271    const NAME: &'static str = "ECDH P-521";
272}
273
274impl KeyAgreement for EcdhP521 {
275    const PRIVATE_KEY_LEN: usize = 66;
276    const PUBLIC_KEY_LEN: usize = 133;
277    const SHARED_SECRET_LEN: usize = 66;
278
279    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
280        ecdh::public_key::<P521>(private_key, out)
281    }
282
283    fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
284        ecdh::agree::<P521>(private_key, peer_public_key, out)
285    }
286}
287
288impl EcdhP521 {
289    /// Compute the public key in SEC1 compressed form (67 bytes).
290    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
291        ecdh::public_key_compressed::<P521>(private_key, out)
292    }
293}
294
295impl SelfTest for EcdhP521 {
296    fn self_test() -> Result<()> {
297        // Both sides of an exchange must agree, and the result must not be the
298        // trivial one.
299        // As above: the leading byte is cleared so both scalars are in range.
300        let (mut a, mut b) = ([0x11u8; 66], [0x22u8; 66]);
301        a[0] = 0x00;
302        b[0] = 0x00;
303        let mut a_pk = [0u8; 133];
304        let mut b_pk = [0u8; 133];
305        <Self as KeyAgreement>::public_key(&a, &mut a_pk)?;
306        <Self as KeyAgreement>::public_key(&b, &mut b_pk)?;
307
308        let mut z1 = [0u8; 66];
309        let mut z2 = [0u8; 66];
310        <Self as KeyAgreement>::agree(&a, &b_pk, &mut z1)?;
311        <Self as KeyAgreement>::agree(&b, &a_pk, &mut z2)?;
312        ensure!(ic_core::ct::verify(&z1, &z2), SelfTestFailed, "ecdh-p521");
313        ensure!(z1 != [0u8; 66], SelfTestFailed, "ecdh-p521");
314        Ok(())
315    }
316}
317
318/// A P-521 point in Jacobian coordinates.
319pub type Point = crate::nist::point::Point<P521>;
320/// A P-521 point in affine coordinates.
321pub type AffinePoint = crate::nist::point::AffinePoint<P521>;
322/// A peer's P-521 public key for [`EcdhP521`], decoded, validated and
323/// expanded once: `new` takes its SEC1 encoding and refuses what
324/// [`EcdhP521`]'s `agree` would refuse; `agree` takes a private key and
325/// writes the 66-byte shared secret, doing only the scalar multiplication
326/// in about 40 percent of the time of an unprepared agreement. About 9 KiB, not
327/// allocated.
328pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P521>;
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    /// P-521 takes a shortcut for square roots, and this is what checks it.
335    ///
336    /// Because `p = 2^521 - 1`, the exponent `(p+1)/4` is exactly `2^519`, so
337    /// the root is 519 repeated squarings and no exponentiation ladder is
338    /// needed. That is a genuine saving and a genuine risk: an off-by-one in
339    /// the count produces a value that is wrong for every input, but a
340    /// round-trip through point compression would still reject it as "not on
341    /// the curve" rather than pointing at the square root.
342    ///
343    /// So the shortcut is compared against the generic `(p+1)/4` computation
344    /// used by P-256 and P-384. Two independent routes to the same value.
345    #[test]
346    fn the_square_root_shortcut_matches_the_generic_exponent() {
347        use crate::nist::arith::sqrt_p3mod4;
348
349        let mut checked = 0;
350        for seed in 1u64..40 {
351            let mut bytes = [0u8; 66];
352            for (i, b) in bytes.iter_mut().enumerate() {
353                *b = (seed.wrapping_mul(i as u64 + 7) & 0xff) as u8;
354            }
355            // Keep it inside the field.
356            bytes[0] &= 0x01;
357            let Some(x) = P521::field_from_slice(&bytes) else {
358                continue;
359            };
360            // Square first, so the input is definitely a quadratic residue and
361            // both routes must land on a genuine root.
362            let y2 = x.square();
363
364            let shortcut = <P521 as Curve>::sqrt(&y2);
365            let generic = sqrt_p3mod4(&y2, Fp::MODULUS, |v, e| v.pow(e));
366            assert_eq!(
367                shortcut, generic,
368                "the shortcut disagrees with the generic exponent at seed {seed}"
369            );
370            assert_eq!(shortcut.square(), y2, "and it must actually be a root");
371            checked += 1;
372        }
373        assert!(
374            checked > 20,
375            "the sweep should reach real inputs: {checked}"
376        );
377    }
378
379    /// The generic helper must agree with each curve's own notion of a root.
380    ///
381    /// P-256 and P-384 now call it directly, so this mostly guards against the
382    /// helper being changed in a way that happens to keep those two working.
383    #[test]
384    fn a_root_squares_back_to_its_input_on_every_curve() {
385        for seed in 1u8..12 {
386            let mut b = [0u8; 66];
387            b[65] = seed;
388            let x = P521::field_from_slice(&b).unwrap();
389            let y2 = x.square();
390            let root = <P521 as Curve>::sqrt(&y2);
391            assert_eq!(root.square(), y2, "P-521 at seed {seed}");
392        }
393    }
394    use ic_core::codec::hex;
395
396    fn scalar(v: u64) -> Fn {
397        Fn::to_mont([v, 0, 0, 0, 0, 0, 0, 0, 0])
398    }
399
400    fn fp(v: u64) -> Fp {
401        Fp::to_mont([v, 0, 0, 0, 0, 0, 0, 0, 0])
402    }
403
404    // -- field ------------------------------------------------------------
405
406    #[test]
407    fn montgomery_constants_are_consistent() {
408        assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
409        assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
410    }
411
412    /// `p = 2^521 - 1` is a Mersenne prime, so the modulus is checkable by
413    /// inspection: eight limbs of ones and a ninth holding nine more bits.
414    #[test]
415    fn the_modulus_is_two_to_the_521_minus_one() {
416        for (i, limb) in Fp::MODULUS.iter().enumerate().take(8) {
417            assert_eq!(*limb, u64::MAX, "limb {i}");
418        }
419        assert_eq!(Fp::MODULUS[8], 0x1ff);
420        // 8 * 64 + 9 = 521 significant bits.
421        assert_eq!(64 - Fp::MODULUS[8].leading_zeros(), 9);
422    }
423
424    #[test]
425    fn small_arithmetic_matches_integers() {
426        assert_eq!(fp(2).add(&fp(3)), fp(5));
427        assert_eq!(fp(5).sub(&fp(3)), fp(2));
428        assert_eq!(fp(6).mul(&fp(7)), fp(42));
429        assert_eq!(fp(9).square(), fp(81));
430        assert_eq!(fp(5).triple(), fp(15));
431        assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0, 0, 0, 0, 0, 0]);
432    }
433
434    #[test]
435    fn inversion_is_correct() {
436        for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
437            assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
438            assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
439        }
440        assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
441    }
442
443    #[test]
444    fn arithmetic_laws_hold_on_large_values() {
445        // The top byte must stay below 0x02: field elements are 521 bits in a
446        // 66-byte encoding, so seven leading bits are always zero.
447        let mut a_bytes = [0x3au8; 66];
448        a_bytes[0] = 0x01;
449        let mut b_bytes = [0x91u8; 66];
450        b_bytes[0] = 0x00;
451        let mut c_bytes = [0xc7u8; 66];
452        c_bytes[0] = 0x01;
453        let a = P521::field_from_slice(&a_bytes).unwrap();
454        let b = P521::field_from_slice(&b_bytes).unwrap();
455        let c = P521::field_from_slice(&c_bytes).unwrap();
456        assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
457        assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
458        assert_eq!(
459            a.mul(&b.add(&c)),
460            a.mul(&b).add(&a.mul(&c)),
461            "distributivity"
462        );
463        assert_eq!(a.add(&a.neg()), Fp::ZERO);
464    }
465
466    /// The nine-limb field is the first here whose byte width is not eight
467    /// times its limb count, so the encoding boundary gets its own test.
468    #[test]
469    fn byte_encoding_round_trips_across_the_partial_top_limb() {
470        // A value whose top bits sit in the ninth limb.
471        let mut bytes = [0x00u8; 66];
472        bytes[0] = 0x01;
473        bytes[1] = 0xff;
474        for (i, b) in bytes[2..].iter_mut().enumerate() {
475            *b = i as u8;
476        }
477        let a = P521::field_from_slice(&bytes).unwrap();
478        assert_eq!(a.to_bytes(), bytes);
479
480        // The largest value below the modulus: p - 1, which is 2^521 - 2.
481        let p_minus_1 = Fp::ZERO.sub(&Fp::ONE);
482        let encoded = p_minus_1.to_bytes();
483        assert_eq!(encoded[0], 0x01, "bit 520 is set");
484        assert_eq!(encoded[1], 0xff);
485        assert_eq!(encoded[65], 0xfe, "and the low bit is clear");
486        assert_eq!(P521::field_from_slice(&encoded).unwrap(), p_minus_1);
487    }
488
489    /// Anything at or above the modulus is refused rather than reduced.
490    #[test]
491    fn out_of_range_encodings_are_rejected() {
492        // p itself.
493        let mut p_bytes = [0xffu8; 66];
494        p_bytes[0] = 0x01;
495        assert!(P521::field_from_slice(&p_bytes).is_none(), "p");
496
497        // A value with bits above 521 set.
498        let too_big = [0xffu8; 66];
499        assert!(P521::field_from_slice(&too_big).is_none(), "2^528 - 1");
500
501        // Wrong width.
502        assert!(P521::field_from_slice(&[0u8; 65]).is_none());
503        assert!(P521::field_from_slice(&[0u8; 67]).is_none());
504    }
505
506    /// `(p + 1) / 4 = 2^519`, so the square root is 519 squarings. Check the
507    /// shortcut against the property it is supposed to have.
508    #[test]
509    fn square_roots_are_correct() {
510        for v in [1u64, 4, 9, 16, 12345] {
511            let x = fp(v);
512            let root = P521::sqrt(&x.square());
513            // The root is +/-x; squaring it must return the input either way.
514            assert_eq!(root.square(), x.square(), "sqrt({v}^2)^2");
515            assert!(
516                bool::from(root.ct_eq(&x)) || bool::from(root.ct_eq(&x.neg())),
517                "sqrt({v}^2) is +/-{v}"
518            );
519        }
520    }
521
522    // -- group law --------------------------------------------------------
523
524    /// Validates B, GX and GY together. A single mistyped digit in any of them
525    /// puts the base point off the curve.
526    #[test]
527    fn the_base_point_is_on_the_curve() {
528        let g = Point::generator().to_affine().unwrap();
529        assert!(bool::from(g.is_on_curve()));
530    }
531
532    /// Validates the group order n. With the test above, every curve constant
533    /// is pinned down.
534    #[test]
535    fn the_base_point_has_order_n() {
536        let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
537        let p = Point::generator().mul_scalar(&n_minus_1);
538        assert!(
539            bool::from(p.ct_eq(&Point::generator().neg())),
540            "[n-1]G == -G"
541        );
542        assert!(
543            bool::from(p.add(&Point::generator()).is_identity()),
544            "[n]G is the identity"
545        );
546    }
547
548    #[test]
549    fn identity_and_negation_behave() {
550        let g = Point::generator();
551        assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
552        assert!(bool::from(Point::identity().double().is_identity()));
553        assert!(bool::from(g.add(&g.neg()).is_identity()));
554    }
555
556    #[test]
557    fn addition_handles_equal_inputs_as_a_doubling() {
558        let g = Point::generator();
559        assert!(bool::from(g.add(&g).ct_eq(&g.double())));
560    }
561
562    #[test]
563    fn scalar_multiplication_matches_repeated_addition() {
564        let g = Point::generator();
565        let mut acc = Point::identity();
566        for k in 1..=8u64 {
567            acc = acc.add(&g);
568            assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
569        }
570    }
571
572    #[test]
573    fn scalar_multiplication_is_linear() {
574        let g = Point::generator();
575        let a = scalar(1_234_567);
576        let b = scalar(7_654_321);
577        assert!(bool::from(
578            g.mul_scalar(&a.add(&b))
579                .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
580        ));
581    }
582
583    /// `[2]G`, computed by a separate naive implementation over Python
584    /// integers rather than by this code. See docs/FIPS.md on provenance: no
585    /// published P-521 vector is wired in here, so the oracle is an independent
586    /// implementation of the same group law.
587    #[test]
588    fn two_g_matches_an_independent_computation() {
589        let two_g = Point::generator().double().to_affine().unwrap();
590        assert_eq!(
591            hex(two_g.x.to_bytes().as_ref()),
592            "00433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769\
593             be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d"
594                .replace(char::is_whitespace, "")
595        );
596        assert_eq!(
597            hex(two_g.y.to_bytes().as_ref()),
598            "00f4bb8cc7f86db26700a7f3eceeeed3f0b5c6b5107c4da97740ab21a29906c4\
599             2dbbb3e377de9f251f6b93937fa99a3248f4eafcbe95edc0f4f71be356d661f41b02"
600                .replace(char::is_whitespace, "")
601        );
602    }
603
604    /// `[k]G` for a k large enough to exercise the whole ladder, again against
605    /// the independent computation.
606    #[test]
607    fn a_large_multiple_matches_an_independent_computation() {
608        let k = scalar(0x0123_4567_89ab_cdef);
609        let p = Point::generator().mul_scalar(&k).to_affine().unwrap();
610        assert_eq!(
611            hex(p.x.to_bytes().as_ref()),
612            "004e54b334cb2a1e40cc9712808f78e4adf7e1cd31acb0bc0d969efdfa82de8f\
613             bada7ca6c3e22ba5d47b5dc024e93ffd8c2cb3f1f88d3224050914a8ad9dcd593a59"
614                .replace(char::is_whitespace, "")
615        );
616        assert_eq!(
617            hex(p.y.to_bytes().as_ref()),
618            "010b8759ce9c47342e92da648fd25aeaadd28c3f6cfad8c5fa1beec990ca9e7f\
619             bf0939bf66c1b8d9918db4795980329872afcf99e0f774f84b144bfa60e5587d7abd"
620                .replace(char::is_whitespace, "")
621        );
622    }
623
624    #[test]
625    fn every_multiple_stays_on_the_curve() {
626        let g = Point::generator();
627        for k in [1u64, 2, 3, 17, 255, 65537, u32::MAX as u64] {
628            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
629            assert!(bool::from(p.is_on_curve()), "[{k}]G is off the curve");
630        }
631    }
632
633    #[test]
634    fn sec1_round_trips_in_both_forms() {
635        let g = Point::generator();
636        for k in [1u64, 2, 3, 4, 5, 6] {
637            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
638            let mut unc = [0u8; 133];
639            let mut comp = [0u8; 67];
640            assert!(p.write_uncompressed(&mut unc));
641            assert!(p.write_compressed(&mut comp));
642
643            let a = AffinePoint::from_sec1(&unc).unwrap();
644            let b = AffinePoint::from_sec1(&comp).unwrap();
645            assert_eq!(a.x, p.x);
646            assert_eq!(a.y, p.y);
647            assert_eq!(b.x, p.x);
648            assert_eq!(b.y, p.y, "compressed y for [{k}]G");
649        }
650    }
651
652    #[test]
653    fn decoding_rejects_bad_encodings() {
654        let g = Point::generator().to_affine().unwrap();
655        let mut unc = [0u8; 133];
656        assert!(g.write_uncompressed(&mut unc));
657
658        assert!(AffinePoint::from_sec1(&[0u8; 133]).is_none(), "identity");
659        assert!(AffinePoint::from_sec1(&unc[..132]).is_none(), "truncated");
660        // A P-384-sized encoding must not be accepted here.
661        assert!(
662            AffinePoint::from_sec1(&[0x04u8; 97]).is_none(),
663            "wrong width"
664        );
665
666        let mut bad = unc;
667        bad[132] ^= 1;
668        assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
669    }
670
671    // -- ECDSA ------------------------------------------------------------
672
673    #[test]
674    fn sign_and_verify_round_trip() {
675        let mut key = [0u8; 66];
676        key[65] = 7;
677        let mut public = [0u8; 133];
678        EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
679
680        for message in [&b""[..], b"a", b"the quick brown fox", &[0x5au8; 1000][..]] {
681            let mut signature = [0u8; 132];
682            EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
683            EcdsaP521Sha512::verify(&public, message, &signature).unwrap();
684        }
685    }
686
687    /// ECDSA signatures are malleable: `(r, s)` and `(r, n - s)` both verify.
688    /// Normalizing picks the low-`s` representative, and doing it twice must
689    /// change nothing.
690    #[test]
691    fn normalizing_s_is_idempotent() {
692        let mut key = [0u8; 66];
693        key[65] = 7;
694        let mut public = [0u8; 133];
695        EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
696
697        for message in [&b"a"[..], b"b", b"c", b"d"] {
698            let mut signature = [0u8; 132];
699            EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
700
701            let mut normalized = signature;
702            EcdsaP521Sha512::normalize_s(&mut normalized).unwrap();
703            assert!(EcdsaP521Sha512::has_low_s(&normalized).unwrap());
704            EcdsaP521Sha512::verify(&public, message, &normalized).unwrap();
705
706            let mut twice = normalized;
707            EcdsaP521Sha512::normalize_s(&mut twice).unwrap();
708            assert_eq!(twice, normalized, "normalization is idempotent");
709        }
710    }
711
712    /// Cross-check against an independent RFC 6979 implementation.
713    ///
714    /// # Provenance
715    ///
716    /// RFC 6979 publishes P-521 vectors, but this project's rule is not to
717    /// assert a constant it cannot verify, and those were not available to
718    /// check against here. So these come from a separate implementation
719    /// written from the text of RFC 6979 section 3.2 and from the affine group
720    /// law, sharing no code with this one. docs/FIPS.md records the
721    /// distinction.
722    ///
723    /// What makes this worth having: P-521 is the only pairing where the HMAC
724    /// output is *narrower* than the group order, so `T` takes two rounds and
725    /// `bits2int` has seven bits to shift off. Nothing in the P-256 or P-384
726    /// vectors exercises either path.
727    #[test]
728    fn signatures_match_an_independent_rfc6979_implementation() {
729        let mut key = [0u8; 66];
730        key[65] = 7;
731
732        // The public key for x = 7, from the same reference.
733        let mut public = [0u8; 133];
734        EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
735        assert_eq!(
736            hex(&public[1..67]),
737            "0056d5d1d99d5b7f6346eeb65fda0b073a0c5f22e0e8f5483228f018d2c2f711             4c5d8c308d0abfc698d8c9a6df30dce3bbc46f953f50fdc2619a01cead882816ecd4"
738                .replace(char::is_whitespace, ""),
739            "public key x"
740        );
741        assert_eq!(
742            hex(&public[67..]),
743            "003d2d1b7d9baaa2a110d1d8317a39d68478b5c582d02824f0dd71dbd98a26cb             de556bd0f293cdec9e2b9523a34591ce1a5f9e76712a5ddefc7b5c6b8bc90525251b"
744                .replace(char::is_whitespace, ""),
745            "public key y"
746        );
747
748        let cases: &[(&[u8], &str, &str)] = &[
749            (
750                b"",
751                "018a0314748952a0558e30db613981ac046c21bb434d98e8825ad07d192adcfb                 12f0f29c86fee2f59368c77d101e208f289b5b8d563fd0dcb126450a4cf64f33af21",
752                "00c17a5af4890ee28950f4477900ad734ea90aa9985cc98c4e5a9242b1aece0a                 19f05ecdfd30e67dab5c0539239913aa82fd19a3d9e250bd6e46f2b30e43d1e47d61",
753            ),
754            (
755                b"a",
756                "01e49d6aaa49524d7d9d0a9724bc96ab5271edff11ccbcb56ad4c7353b5d5e35                 d66d7fc592c3039f020cf61388a67a73a9d1dada4fa286357f8fd2f80726383967ca",
757                "0185747858829becbeb6d1ae2a1138a56661658ec1c866d9400ca134e1572254                 8ee41e7e0b7852d68c91e5650be30a4da44f72125c6eb2bf382251304ea74109bdf0",
758            ),
759            (
760                b"the quick brown fox",
761                "01e8f7a260a7462706d1a3eeb21b244aad1894084cb39d05f5ecb667086d1087                 c9d3d66666aa86b411e81318bf2741120acf0f89ba9494277663dda70ab13e6c645c",
762                "0080157cf57486201170f705525fa22c05fcd8e1bd0dd382935f20a4123c2b59                 0f80e15854f33b18a770f1d746218ecff89832af5b62f3bc61e72a013051a2a5d47c",
763            ),
764        ];
765
766        for (message, want_r, want_s) in cases {
767            let mut signature = [0u8; 132];
768            EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
769            assert_eq!(
770                hex(&signature[..66]),
771                want_r.replace(char::is_whitespace, ""),
772                "r for {message:?}"
773            );
774            assert_eq!(
775                hex(&signature[66..]),
776                want_s.replace(char::is_whitespace, ""),
777                "s for {message:?}"
778            );
779            EcdsaP521Sha512::verify(&public, message, &signature).unwrap();
780        }
781    }
782
783    /// RFC 6979 nonces make signing deterministic, so two runs must agree.
784    #[test]
785    fn signing_is_deterministic() {
786        let mut key = [0u8; 66];
787        key[65] = 9;
788        let mut a = [0u8; 132];
789        let mut b = [0u8; 132];
790        EcdsaP521Sha512::sign(&key, b"determinism", &mut a).unwrap();
791        EcdsaP521Sha512::sign(&key, b"determinism", &mut b).unwrap();
792        assert_eq!(a, b);
793    }
794
795    #[test]
796    fn verification_rejects_tampering() {
797        let mut key = [0u8; 66];
798        key[65] = 11;
799        let mut public = [0u8; 133];
800        EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
801        let mut signature = [0u8; 132];
802        EcdsaP521Sha512::sign(&key, b"message", &mut signature).unwrap();
803
804        assert!(EcdsaP521Sha512::verify(&public, b"messagf", &signature).is_err());
805        for bit in [0usize, 7, 260, 527, 1055] {
806            let mut bad = signature;
807            bad[bit / 8] ^= 1 << (bit % 8);
808            assert!(
809                EcdsaP521Sha512::verify(&public, b"message", &bad).is_err(),
810                "flipped signature bit {bit}"
811            );
812        }
813        assert!(EcdsaP521Sha512::verify(&public, b"message", &signature[..131]).is_err());
814    }
815
816    /// A P-384 key must not verify as P-521, which the length checks enforce.
817    #[test]
818    fn keys_from_another_curve_are_refused() {
819        let mut signature = [0u8; 132];
820        assert!(EcdsaP521Sha512::sign(&[7u8; 48], b"x", &mut signature).is_err());
821        assert!(EcdsaP521Sha512::verify(&[4u8; 97], b"x", &signature).is_err());
822    }
823
824    // -- ECDH -------------------------------------------------------------
825
826    #[test]
827    fn ecdh_agrees_in_both_directions() {
828        let mut alice = [0u8; 66];
829        alice[65] = 3;
830        let mut bob = [0u8; 66];
831        bob[65] = 5;
832
833        let mut alice_public = [0u8; 133];
834        let mut bob_public = [0u8; 133];
835        EcdhP521::public_key(&alice, &mut alice_public).unwrap();
836        EcdhP521::public_key(&bob, &mut bob_public).unwrap();
837
838        let mut a = [0u8; 66];
839        let mut b = [0u8; 66];
840        EcdhP521::agree(&alice, &bob_public, &mut a).unwrap();
841        EcdhP521::agree(&bob, &alice_public, &mut b).unwrap();
842        assert_eq!(a, b, "both sides derive the same secret");
843
844        // And the secret is the x-coordinate of [ab]G, not something else.
845        let ab = Point::generator()
846            .mul_scalar(&scalar(15))
847            .to_affine()
848            .unwrap();
849        assert_eq!(a, ab.x.to_bytes());
850    }
851
852    #[test]
853    fn ecdh_rejects_a_malformed_peer_key() {
854        let mut alice = [0u8; 66];
855        alice[65] = 3;
856        let mut out = [0u8; 66];
857        assert!(
858            EcdhP521::agree(&alice, &[0u8; 133], &mut out).is_err(),
859            "identity"
860        );
861        assert!(
862            EcdhP521::agree(&alice, &[0x04u8; 133], &mut out).is_err(),
863            "off curve"
864        );
865        assert!(
866            EcdhP521::agree(&alice, &[0x04u8; 97], &mut out).is_err(),
867            "p-384 width"
868        );
869    }
870
871    #[test]
872    fn compressed_public_keys_match_the_uncompressed_ones() {
873        let mut key = [0u8; 66];
874        key[65] = 13;
875        let mut unc = [0u8; 133];
876        let mut comp = [0u8; 67];
877        EcdsaP521Sha512::public_key(&key, &mut unc).unwrap();
878        EcdsaP521Sha512::public_key_compressed(&key, &mut comp).unwrap();
879        assert_eq!(&comp[1..], &unc[1..67], "the x-coordinates agree");
880        assert_eq!(comp[0], 0x02 | (unc[132] & 1), "the sign bit");
881    }
882}