Skip to main content

ic_ec/
p384.rs

1//! NIST P-384 (secp384r1).
2//!
3//! The curve a CNSA-aligned profile requires, and the one TLS reaches for when
4//! 128-bit security is not considered enough. The field, group law, and schemes
5//! come from `crate::nist`; this module supplies the constants and the public
6//! API.
7//!
8//! Paired with SHA-384 throughout, which is the matching security level and
9//! also makes RFC 6979's bit-length handling collapse to a straight reduction,
10//! since the hash and the group order are both 384 bits wide.
11
12use crate::mont_field;
13use crate::nist::arith::{sqrt_p3mod4, Field};
14use crate::nist::point::Curve;
15use crate::nist::{ecdh, ecdsa};
16use ic_core::traits::{Algorithm, KeyAgreement, SelfTest, SignatureScheme};
17use ic_core::{ensure, Result};
18
19mont_field!(
20    Fp,
21    6,
22    48,
23    [
24        0x0000_0000_ffff_ffff,
25        0xffff_ffff_0000_0000,
26        0xffff_ffff_ffff_fffe,
27        0xffff_ffff_ffff_ffff,
28        0xffff_ffff_ffff_ffff,
29        0xffff_ffff_ffff_ffff,
30    ],
31    "The P-384 coordinate field, GF(p) with p = 2^384 - 2^128 - 2^96 + 2^32 - 1."
32);
33
34mont_field!(
35    Fn,
36    6,
37    48,
38    [
39        0xecec_196a_ccc5_2973,
40        0x581a_0db2_48b0_a77a,
41        0xc763_4d81_f437_2ddf,
42        0xffff_ffff_ffff_ffff,
43        0xffff_ffff_ffff_ffff,
44        0xffff_ffff_ffff_ffff,
45    ],
46    "The P-384 scalar ring, Z/nZ where n is the order of the base point."
47);
48
49/// The P-384 curve.
50#[derive(Debug, Clone, Copy)]
51pub struct P384;
52
53// Its own generator table, with its own storage; see the macro, which
54// emits the table under `std` and the windowed multiplication without it.
55crate::nist::gentable::generator_table_for!(P384);
56
57impl Curve for P384 {
58    type Field = Fp;
59    type Scalar = Fn;
60
61    const NAME: &'static str = "P-384";
62    const FIELD_BYTES: usize = 48;
63    const SCALAR_BYTES: usize = 48;
64    const ORDER_BITS: usize = 384;
65
66    /// `b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a`
67    ///     `c656398d8a2ed19d2a85c8edd3ec2aef`
68    const B: Fp = Fp::to_mont_const([
69        0x2a85_c8ed_d3ec_2aef,
70        0xc656_398d_8a2e_d19d,
71        0x0314_088f_5013_875a,
72        0x181d_9c6e_fe81_4112,
73        0x988e_056b_e3f8_2d19,
74        0xb331_2fa7_e23e_e7e4,
75    ]);
76
77    const GX: Fp = Fp::to_mont_const([
78        0x3a54_5e38_7276_0ab7,
79        0x5502_f25d_bf55_296c,
80        0x59f7_41e0_8254_2a38,
81        0x6e1d_3b62_8ba7_9b98,
82        0x8eb1_c71e_f320_ad74,
83        0xaa87_ca22_be8b_0537,
84    ]);
85
86    const GY: Fp = Fp::to_mont_const([
87        0x7a43_1d7c_90ea_0e5f,
88        0x0a60_b1ce_1d7e_819d,
89        0xe9da_3113_b5f0_b8c0,
90        0xf8f4_1dbd_289a_147c,
91        0x5d9e_98bf_9292_dc29,
92        0x3617_de4a_9626_2c6f,
93    ]);
94
95    /// `p = 3 mod 4`, so a square root is `x^((p+1)/4)`.
96    fn sqrt(x: &Fp) -> Fp {
97        sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
98    }
99
100    fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
101        let mut b = [0u8; 48];
102        if bytes.len() != 48 {
103            return None;
104        }
105        b.copy_from_slice(bytes);
106        Fp::from_bytes(&b)
107    }
108
109    fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
110        let mut b = [0u8; 48];
111        if bytes.len() != 48 {
112            return None;
113        }
114        b.copy_from_slice(bytes);
115        Fn::from_bytes(&b)
116    }
117
118    fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
119        let mut b = [0u8; 48];
120        let n = core::cmp::min(48, bytes.len());
121        // Take the leftmost bytes, which is what bits2int does when the input
122        // is at least as wide as the group order.
123        b[48 - n..].copy_from_slice(&bytes[..n]);
124        Fn::from_bytes_reduced(&b)
125    }
126}
127
128impl ecdsa::EcdsaCurve for P384 {
129    type Digest = ic_hash::Sha384;
130    type Hmac = ic_mac::HmacSha384;
131}
132
133/// ECDSA over P-384 with SHA-384.
134pub struct EcdsaP384Sha384;
135
136impl Algorithm for EcdsaP384Sha384 {
137    const ID: &'static str = "ecdsa-p384-sha384";
138    const NAME: &'static str = "ECDSA P-384 with SHA-384";
139}
140
141impl SignatureScheme for EcdsaP384Sha384 {
142    const PRIVATE_KEY_LEN: usize = 48;
143    /// SEC1 uncompressed: `0x04 || X || Y`.
144    const PUBLIC_KEY_LEN: usize = 97;
145    /// Fixed-width `r || s`.
146    const SIGNATURE_LEN: usize = 96;
147
148    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
149        ecdsa::public_key::<P384>(private_key, out)
150    }
151
152    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
153        ecdsa::sign::<P384>(private_key, message, signature)
154    }
155
156    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
157        ecdsa::verify::<P384>(public_key, message, signature)
158    }
159}
160
161impl EcdsaP384Sha384 {
162    /// Verify a signature over a digest the caller computed, for signatures
163    /// made with a hash other than SHA-384.
164    ///
165    /// `digest` must be one of [`crate::PREHASH_LENS`] and at least 48
166    /// bytes, so that the hash is at least as strong as the curve; narrower is
167    /// refused with `InvalidLength`. Which hash produced it is the caller's to
168    /// establish: nothing here can tell.
169    pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
170        ecdsa::verify_prehash::<P384>(public_key, digest, signature)
171    }
172
173    /// Compute the public key in SEC1 compressed form (49 bytes).
174    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
175        ecdsa::public_key_compressed::<P384>(private_key, out)
176    }
177
178    /// Rewrite a signature to its low-`s` form, if it is not already.
179    ///
180    /// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the
181    /// same message, so a signature is not a unique identifier unless one form
182    /// is chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs
183    /// and verifies per the standard, so normalization is offered rather than
184    /// imposed -- apply it when a signature doubles as a database key or a
185    /// transaction id.
186    pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
187        ecdsa::normalize_s::<P384>(signature)
188    }
189
190    /// Whether a signature is already in low-`s` form.
191    pub fn has_low_s(signature: &[u8]) -> Result<bool> {
192        ecdsa::has_low_s::<P384>(signature)
193    }
194}
195
196impl SelfTest for EcdsaP384Sha384 {
197    fn self_test() -> Result<()> {
198        // Round-trip plus tamper rejection. The published RFC 6979 vector is
199        // asserted by the unit tests; this CAST is the startup integrity check.
200        let key = [0x2au8; 48];
201        let mut pk = [0u8; 97];
202        <Self as SignatureScheme>::public_key(&key, &mut pk)?;
203
204        let mut sig = [0u8; 96];
205        <Self as SignatureScheme>::sign(&key, b"self-test", &mut sig)?;
206        <Self as SignatureScheme>::verify(&pk, b"self-test", &sig)?;
207
208        // Signing is deterministic, so a repeat must agree exactly.
209        let mut again = [0u8; 96];
210        <Self as SignatureScheme>::sign(&key, b"self-test", &mut again)?;
211        ensure!(
212            ic_core::ct::verify(&sig, &again),
213            SelfTestFailed,
214            "ecdsa-p384-sha384"
215        );
216
217        sig[0] ^= 1;
218        ensure!(
219            <Self as SignatureScheme>::verify(&pk, b"self-test", &sig).is_err(),
220            SelfTestFailed,
221            "ecdsa-p384-sha384"
222        );
223        Ok(())
224    }
225}
226
227/// ECDH over P-384.
228pub struct EcdhP384;
229
230impl Algorithm for EcdhP384 {
231    const ID: &'static str = "ecdh-p384";
232    const NAME: &'static str = "ECDH P-384";
233}
234
235impl KeyAgreement for EcdhP384 {
236    const PRIVATE_KEY_LEN: usize = 48;
237    /// SEC1 uncompressed: `0x04 || X || Y`.
238    const PUBLIC_KEY_LEN: usize = 97;
239    const SHARED_SECRET_LEN: usize = 48;
240
241    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
242        ecdh::public_key::<P384>(private_key, out)
243    }
244
245    fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
246        ecdh::agree::<P384>(private_key, peer_public_key, out)
247    }
248}
249
250impl EcdhP384 {
251    /// Compute the public key in SEC1 compressed form (49 bytes).
252    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
253        ecdh::public_key_compressed::<P384>(private_key, out)
254    }
255}
256
257impl SelfTest for EcdhP384 {
258    fn self_test() -> Result<()> {
259        // Both sides of an exchange must agree, and the result must not be the
260        // trivial one.
261        let (a, b) = ([0x11u8; 48], [0x22u8; 48]);
262        let mut a_pk = [0u8; 97];
263        let mut b_pk = [0u8; 97];
264        <Self as KeyAgreement>::public_key(&a, &mut a_pk)?;
265        <Self as KeyAgreement>::public_key(&b, &mut b_pk)?;
266
267        let mut z1 = [0u8; 48];
268        let mut z2 = [0u8; 48];
269        <Self as KeyAgreement>::agree(&a, &b_pk, &mut z1)?;
270        <Self as KeyAgreement>::agree(&b, &a_pk, &mut z2)?;
271        ensure!(ic_core::ct::verify(&z1, &z2), SelfTestFailed, "ecdh-p384");
272        ensure!(z1 != [0u8; 48], SelfTestFailed, "ecdh-p384");
273        Ok(())
274    }
275}
276
277/// A P-384 point in Jacobian coordinates.
278pub type Point = crate::nist::point::Point<P384>;
279/// A P-384 point in affine coordinates.
280pub type AffinePoint = crate::nist::point::AffinePoint<P384>;
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285    use ic_core::codec::{hex, unhex};
286
287    fn scalar(v: u64) -> Fn {
288        Fn::to_mont([v, 0, 0, 0, 0, 0])
289    }
290
291    fn fp(v: u64) -> Fp {
292        Fp::to_mont([v, 0, 0, 0, 0, 0])
293    }
294
295    // -- field ------------------------------------------------------------
296
297    #[test]
298    fn montgomery_constants_are_consistent() {
299        assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
300        assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
301    }
302
303    #[test]
304    fn small_arithmetic_matches_integers() {
305        assert_eq!(fp(2).add(&fp(3)), fp(5));
306        assert_eq!(fp(5).sub(&fp(3)), fp(2));
307        assert_eq!(fp(6).mul(&fp(7)), fp(42));
308        assert_eq!(fp(9).square(), fp(81));
309        assert_eq!(fp(5).triple(), fp(15));
310        assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0, 0, 0]);
311    }
312
313    #[test]
314    fn inversion_is_correct() {
315        for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
316            assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
317            assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
318        }
319        assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
320    }
321
322    #[test]
323    fn arithmetic_laws_hold_on_large_values() {
324        let a = P384::field_from_slice(&[0x3a; 48]).unwrap();
325        let b = P384::field_from_slice(&[0x91; 48]).unwrap();
326        let c = P384::field_from_slice(&[0xc7; 48]).unwrap();
327        assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
328        assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
329        assert_eq!(
330            a.mul(&b.add(&c)),
331            a.mul(&b).add(&a.mul(&c)),
332            "distributivity"
333        );
334        assert_eq!(a.add(&a.neg()), Fp::ZERO);
335    }
336
337    #[test]
338    fn byte_encoding_round_trips() {
339        let bytes = [0x7fu8; 48];
340        let a = P384::field_from_slice(&bytes).unwrap();
341        assert_eq!(a.to_bytes(), bytes);
342    }
343
344    // -- group law --------------------------------------------------------
345
346    /// Validates B, GX, GY and the curve equation together: if any of the four
347    /// constants were mistranscribed, the base point would not satisfy it.
348    #[test]
349    fn the_base_point_is_on_the_curve() {
350        let g = Point::generator().to_affine().unwrap();
351        assert!(bool::from(g.is_on_curve()));
352    }
353
354    /// Validates the group order n against the base point. Together with the
355    /// test above this pins down every curve constant.
356    #[test]
357    fn the_base_point_has_order_n() {
358        let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
359        let p = Point::generator().mul_scalar(&n_minus_1);
360        assert!(
361            bool::from(p.ct_eq(&Point::generator().neg())),
362            "[n-1]G == -G"
363        );
364        assert!(
365            bool::from(p.add(&Point::generator()).is_identity()),
366            "[n]G is the identity"
367        );
368    }
369
370    #[test]
371    fn identity_and_negation_behave() {
372        let g = Point::generator();
373        assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
374        assert!(bool::from(Point::identity().double().is_identity()));
375        assert!(bool::from(g.add(&g.neg()).is_identity()));
376    }
377
378    #[test]
379    fn addition_handles_equal_inputs_as_a_doubling() {
380        let g = Point::generator();
381        assert!(bool::from(g.add(&g).ct_eq(&g.double())));
382    }
383
384    #[test]
385    fn scalar_multiplication_matches_repeated_addition() {
386        let g = Point::generator();
387        let mut acc = Point::identity();
388        for k in 1..=8u64 {
389            acc = acc.add(&g);
390            assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
391        }
392    }
393
394    #[test]
395    fn scalar_multiplication_is_linear() {
396        let g = Point::generator();
397        let a = scalar(1_234_567);
398        let b = scalar(7_654_321);
399        assert!(bool::from(
400            g.mul_scalar(&a.add(&b))
401                .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
402        ));
403    }
404
405    /// The published `[2]G`, an independent check on the group law rather than
406    /// on self-consistency.
407    #[test]
408    fn two_g_matches_the_published_value() {
409        let two_g = Point::generator().double().to_affine().unwrap();
410        assert_eq!(
411            hex(two_g.x.to_bytes().as_ref()),
412            "08d999057ba3d2d969260045c55b97f089025959a6f434d651d207d19fb96e9e\
413             4fe0e86ebe0e64f85b96a9c75295df61"
414                .replace(char::is_whitespace, "")
415        );
416        assert_eq!(
417            hex(two_g.y.to_bytes().as_ref()),
418            "8e80f1fa5b1b3cedb7bfe8dffd6dba74b275d875bc6cc43e904e505f256ab425\
419             5ffd43e94d39e22d61501e700a940e80"
420                .replace(char::is_whitespace, "")
421        );
422    }
423
424    #[test]
425    fn every_multiple_stays_on_the_curve() {
426        let g = Point::generator();
427        for k in [1u64, 2, 3, 17, 255, 65537] {
428            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
429            assert!(bool::from(p.is_on_curve()), "[{k}]G is off the curve");
430        }
431    }
432
433    #[test]
434    fn sec1_round_trips_in_both_forms() {
435        let g = Point::generator();
436        for k in [1u64, 2, 3, 4, 5, 6] {
437            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
438            let mut unc = [0u8; 97];
439            let mut comp = [0u8; 49];
440            assert!(p.write_uncompressed(&mut unc));
441            assert!(p.write_compressed(&mut comp));
442
443            let a = AffinePoint::from_sec1(&unc).unwrap();
444            let b = AffinePoint::from_sec1(&comp).unwrap();
445            assert_eq!(a.x, p.x);
446            assert_eq!(a.y, p.y);
447            assert_eq!(b.x, p.x);
448            assert_eq!(b.y, p.y, "compressed y for [{k}]G");
449        }
450    }
451
452    #[test]
453    fn decoding_rejects_bad_encodings() {
454        let g = Point::generator().to_affine().unwrap();
455        let mut unc = [0u8; 97];
456        assert!(g.write_uncompressed(&mut unc));
457
458        assert!(AffinePoint::from_sec1(&[0u8; 97]).is_none(), "identity");
459        assert!(AffinePoint::from_sec1(&unc[..96]).is_none(), "truncated");
460        // A P-256-sized encoding must not be accepted here.
461        assert!(
462            AffinePoint::from_sec1(&[0x04u8; 65]).is_none(),
463            "wrong width"
464        );
465
466        let mut bad = unc;
467        bad[96] ^= 1;
468        assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
469    }
470
471    // -- ECDSA ------------------------------------------------------------
472
473    /// RFC 6979 A.2.6: P-384 with SHA-384.
474    ///
475    /// The private key, public key and both message signatures are published
476    /// together, so matching them exercises the whole stack: the 6-limb field,
477    /// the group law, the scalar ring, the nonce derivation, and the signing
478    /// equation.
479    const KEY: &str = "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
480                       96d5724e4c70a825f872c9ea60d2edf5";
481
482    fn key_bytes() -> Vec<u8> {
483        unhex(&KEY.replace(char::is_whitespace, "")).unwrap()
484    }
485
486    #[test]
487    fn rfc6979_public_key() {
488        let mut pk = [0u8; 97];
489        EcdsaP384Sha384::public_key(&key_bytes(), &mut pk).unwrap();
490        assert_eq!(pk[0], 0x04);
491        assert_eq!(
492            hex(&pk[1..49]),
493            "ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64\
494             def8f0ea9055866064a254515480bc13"
495                .replace(char::is_whitespace, ""),
496            "Ux"
497        );
498        assert_eq!(
499            hex(&pk[49..]),
500            "8015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1\
501             288b231c3ae0d4fe7344fd2533264720"
502                .replace(char::is_whitespace, ""),
503            "Uy"
504        );
505    }
506
507    #[test]
508    fn rfc6979_sample_vector() {
509        let mut sig = [0u8; 96];
510        EcdsaP384Sha384::sign(&key_bytes(), b"sample", &mut sig).unwrap();
511        assert_eq!(
512            hex(&sig[..48]),
513            "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c\
514             81a648152e44acf96e36dd1e80fabe46"
515                .replace(char::is_whitespace, ""),
516            "r"
517        );
518        assert_eq!(
519            hex(&sig[48..]),
520            "99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94f\
521             a329c145786e679e7b82c71a38628ac8"
522                .replace(char::is_whitespace, ""),
523            "s"
524        );
525    }
526
527    #[test]
528    fn rfc6979_test_vector() {
529        let mut sig = [0u8; 96];
530        EcdsaP384Sha384::sign(&key_bytes(), b"test", &mut sig).unwrap();
531        assert_eq!(
532            hex(&sig[..48]),
533            "8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36ab775d509d7a5feb\
534             0542a7f0812998da8f1dd3ca3cf023db"
535                .replace(char::is_whitespace, ""),
536            "r"
537        );
538        assert_eq!(
539            hex(&sig[48..]),
540            "ddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13173923e0\
541             6a739f040649a667bf3b828246baa5a5"
542                .replace(char::is_whitespace, ""),
543            "s"
544        );
545    }
546
547    #[test]
548    fn signing_is_deterministic_and_message_bound() {
549        let key = key_bytes();
550        let mut a = [0u8; 96];
551        let mut b = [0u8; 96];
552        EcdsaP384Sha384::sign(&key, b"same", &mut a).unwrap();
553        EcdsaP384Sha384::sign(&key, b"same", &mut b).unwrap();
554        assert_eq!(a, b);
555        EcdsaP384Sha384::sign(&key, b"other", &mut b).unwrap();
556        assert_ne!(&a[..48], &b[..48]);
557    }
558
559    #[test]
560    fn sign_and_verify_round_trip() {
561        let key = key_bytes();
562        let mut pk = [0u8; 97];
563        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
564        for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
565            let mut sig = [0u8; 96];
566            EcdsaP384Sha384::sign(&key, message, &mut sig).unwrap();
567            EcdsaP384Sha384::verify(&pk, message, &sig).unwrap();
568        }
569    }
570
571    #[test]
572    fn verification_rejects_tampering() {
573        let key = key_bytes();
574        let mut pk = [0u8; 97];
575        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
576        let mut sig = [0u8; 96];
577        EcdsaP384Sha384::sign(&key, b"authentic", &mut sig).unwrap();
578
579        assert!(EcdsaP384Sha384::verify(&pk, b"forged", &sig).is_err());
580        let mut bad = sig;
581        bad[0] ^= 1;
582        assert!(EcdsaP384Sha384::verify(&pk, b"authentic", &bad).is_err());
583        let mut bad = sig;
584        bad[95] ^= 1;
585        assert!(EcdsaP384Sha384::verify(&pk, b"authentic", &bad).is_err());
586
587        let mut other = [0u8; 97];
588        EcdsaP384Sha384::public_key(&[0x11u8; 48], &mut other).unwrap();
589        assert!(EcdsaP384Sha384::verify(&other, b"authentic", &sig).is_err());
590    }
591
592    #[test]
593    fn signing_rejects_invalid_private_keys() {
594        let mut sig = [0u8; 96];
595        assert!(
596            EcdsaP384Sha384::sign(&[0u8; 48], b"m", &mut sig).is_err(),
597            "zero"
598        );
599        assert!(
600            EcdsaP384Sha384::sign(&[0xffu8; 48], b"m", &mut sig).is_err(),
601            ">= n"
602        );
603        assert!(
604            EcdsaP384Sha384::sign(&[1u8; 32], b"m", &mut sig).is_err(),
605            "P-256 sized"
606        );
607    }
608
609    #[test]
610    fn malleability_and_normalization() {
611        let key = key_bytes();
612        let mut pk = [0u8; 97];
613        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
614        let mut sig = [0u8; 96];
615        EcdsaP384Sha384::sign(&key, b"sample", &mut sig).unwrap();
616
617        let mut normalized = sig;
618        EcdsaP384Sha384::normalize_s(&mut normalized).unwrap();
619        assert!(EcdsaP384Sha384::has_low_s(&normalized).unwrap());
620        // Both forms verify: that is the malleability.
621        EcdsaP384Sha384::verify(&pk, b"sample", &normalized).unwrap();
622        EcdsaP384Sha384::verify(&pk, b"sample", &sig).unwrap();
623
624        let mut twice = normalized;
625        EcdsaP384Sha384::normalize_s(&mut twice).unwrap();
626        assert_eq!(twice, normalized, "normalization must be idempotent");
627    }
628
629    #[test]
630    fn ecdsa_self_test_passes() {
631        EcdsaP384Sha384::self_test().unwrap();
632    }
633
634    // -- ECDH -------------------------------------------------------------
635
636    #[test]
637    fn both_parties_derive_the_same_secret() {
638        let (alice, bob) = ([0x11u8; 48], [0x22u8; 48]);
639        let mut alice_pk = [0u8; 97];
640        let mut bob_pk = [0u8; 97];
641        EcdhP384::public_key(&alice, &mut alice_pk).unwrap();
642        EcdhP384::public_key(&bob, &mut bob_pk).unwrap();
643
644        let mut z1 = [0u8; 48];
645        let mut z2 = [0u8; 48];
646        EcdhP384::agree(&alice, &bob_pk, &mut z1).unwrap();
647        EcdhP384::agree(&bob, &alice_pk, &mut z2).unwrap();
648        assert_eq!(z1, z2);
649        assert_ne!(z1, [0u8; 48]);
650    }
651
652    #[test]
653    fn compressed_and_uncompressed_peers_agree() {
654        let (alice, bob) = ([0x33u8; 48], [0x44u8; 48]);
655        let mut unc = [0u8; 97];
656        let mut comp = [0u8; 49];
657        EcdhP384::public_key(&bob, &mut unc).unwrap();
658        EcdhP384::public_key_compressed(&bob, &mut comp).unwrap();
659
660        let mut z1 = [0u8; 48];
661        let mut z2 = [0u8; 48];
662        EcdhP384::agree(&alice, &unc, &mut z1).unwrap();
663        EcdhP384::agree(&alice, &comp, &mut z2).unwrap();
664        assert_eq!(z1, z2);
665    }
666
667    #[test]
668    fn ecdh_rejects_invalid_inputs() {
669        let alice = [0x11u8; 48];
670        let mut z = [0u8; 48];
671        assert!(EcdhP384::agree(&alice, &[0u8; 97], &mut z).is_err());
672        assert!(EcdhP384::agree(&alice, &[], &mut z).is_err());
673
674        let mut bob_pk = [0u8; 97];
675        EcdhP384::public_key(&[0x22u8; 48], &mut bob_pk).unwrap();
676        bob_pk[96] ^= 1;
677        assert!(
678            EcdhP384::agree(&alice, &bob_pk, &mut z).is_err(),
679            "off curve"
680        );
681    }
682
683    #[test]
684    fn ecdh_self_test_passes() {
685        EcdhP384::self_test().unwrap();
686    }
687}