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
19/// The prime `p`, little-endian.
20pub(crate) const P: [u64; 6] = [
21    0x0000_0000_ffff_ffff,
22    0xffff_ffff_0000_0000,
23    0xffff_ffff_ffff_fffe,
24    0xffff_ffff_ffff_ffff,
25    0xffff_ffff_ffff_ffff,
26    0xffff_ffff_ffff_ffff,
27];
28
29/// The group order `n`, little-endian.
30const N: [u64; 6] = [
31    0xecec_196a_ccc5_2973,
32    0x581a_0db2_48b0_a77a,
33    0xc763_4d81_f437_2ddf,
34    0xffff_ffff_ffff_ffff,
35    0xffff_ffff_ffff_ffff,
36    0xffff_ffff_ffff_ffff,
37];
38
39// On x86-64 both rings multiply in assembly on MULX/ADX where the CPU has it,
40// and by the generic word loop otherwise: the field in assembly specialised
41// to p (`nist::p384_x86_point`), with squaring of its own; the scalar ring
42// generic in the modulus (`nist::mont_x86`). The field inverts by divsteps
43// on 64-bit targets, by an addition chain on 32-bit ones.
44#[cfg(target_arch = "x86_64")]
45mont_field!(
46    Fp,
47    6,
48    48,
49    P,
50    "The P-384 coordinate field, GF(p) with p = 2^384 - 2^128 - 2^96 + 2^32 - 1.",
51    mul = fp_mul,
52    square = fp_sqr,
53    invert = fp_invert,
54    add = Self::add_raw,
55    sub = Self::sub_raw
56);
57
58#[cfg(not(target_arch = "x86_64"))]
59mont_field!(
60    Fp,
61    6,
62    48,
63    P,
64    "The P-384 coordinate field, GF(p) with p = 2^384 - 2^128 - 2^96 + 2^32 - 1.",
65    mul = Self::mont_mul_raw,
66    square = Self::mont_square_raw,
67    invert = fp_invert,
68    add = Self::add_raw,
69    sub = Self::sub_raw
70);
71
72/// `a^-1` in the coordinate field, Montgomery form in and out: by divsteps
73/// on 64-bit targets (`nist::safegcd`), by [`fp_invert_chain`] elsewhere.
74///
75/// The divsteps invert the integer they are given, so a Montgomery `a * R`
76/// comes back as `a^-1 * R^-1`; one Montgomery multiplication by `R^3`
77/// makes that `a^-1 * R`.
78#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
79fn fp_invert(a: [u64; 6]) -> [u64; 6] {
80    const R3: [u64; 6] = Fp::mont_mul_const(Fp::R2, Fp::R2);
81    Fp(crate::nist::safegcd::invert_384::<FpModulus>(&a))
82        .mul(&Fp(R3))
83        .0
84}
85
86/// The prime, for the divsteps.
87#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
88pub(crate) struct FpModulus;
89
90#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
91impl crate::nist::safegcd::Modulus<7> for FpModulus {
92    const INFO: crate::nist::safegcd::ModInfo<7> = crate::nist::safegcd::ModInfo::new(P);
93}
94
95#[cfg(not(all(target_pointer_width = "64", not(ic_limb32))))]
96fn fp_invert(a: [u64; 6]) -> [u64; 6] {
97    fp_invert_chain(a)
98}
99
100/// `a^(p-2)`, the inverse by Fermat, by an addition chain: 385 squarings
101/// and 14 multiplications, where [`Fp::pow`]'s four-bit window takes 380
102/// squarings, 109 multiplications and 96 table scans. What 32-bit targets
103/// use, where the divsteps' 128-bit products would be library calls; on
104/// 64-bit targets the tests' reference for the divsteps.
105///
106/// `p - 2` is, from the top, 255 ones, a zero, 32 ones, 64 zeros, 30 ones,
107/// a zero and a one. `x_k` below is `a^(2^k - 1)`, a run of `k` ones.
108/// Nothing depends on `a` but the values: the chain is a constant sequence
109/// of squarings and multiplications. The tests hold it to `Fp::pow`.
110#[cfg(any(test, not(all(target_pointer_width = "64", not(ic_limb32)))))]
111fn fp_invert_chain(a: [u64; 6]) -> [u64; 6] {
112    let x1 = Fp(a);
113    let x2 = x1.square().mul(&x1);
114    let x3 = x2.square().mul(&x1);
115    let x6 = x3.square_n(3).mul(&x3);
116    let x12 = x6.square_n(6).mul(&x6);
117    let x15 = x12.square_n(3).mul(&x3);
118    let x30 = x15.square_n(15).mul(&x15);
119    let x32 = x30.square_n(2).mul(&x2);
120    let x60 = x30.square_n(30).mul(&x30);
121    let x120 = x60.square_n(60).mul(&x60);
122    let x240 = x120.square_n(120).mul(&x120);
123    let x255 = x240.square_n(15).mul(&x15);
124    // 255 ones, then a zero, then 32 ones.
125    let t = x255.square_n(33).mul(&x32);
126    // 64 zeros, then 30 ones.
127    let t = t.square_n(94).mul(&x30);
128    // A zero and a one.
129    t.square_n(2).mul(&x1).0
130}
131
132#[cfg(target_arch = "x86_64")]
133mont_field!(
134    Fn,
135    6,
136    48,
137    N,
138    "The P-384 scalar ring, Z/nZ where n is the order of the base point.",
139    mul = fn_mul,
140    square = |a| fn_mul(a, a),
141    invert = Self::invert_raw,
142    add = Self::add_raw,
143    sub = Self::sub_raw
144);
145
146#[cfg(not(target_arch = "x86_64"))]
147mont_field!(
148    Fn,
149    6,
150    48,
151    N,
152    "The P-384 scalar ring, Z/nZ where n is the order of the base point."
153);
154
155/// The last step of Montgomery multiplication: `t` below `2m`, with its top
156/// word, made canonical by one subtraction chosen behind the barrier.
157#[cfg(target_arch = "x86_64")]
158#[inline(always)]
159fn canonical(t: [u64; 6], top: u64, m: [u64; 6]) -> [u64; 6] {
160    let (reduced, borrow) = crate::nist::arith::sbb(t, m);
161    crate::nist::arith::select_ct((top | (1 - borrow)).wrapping_neg(), reduced, t)
162}
163
164/// `a * b * 2^-384 mod p`.
165#[cfg(target_arch = "x86_64")]
166#[inline(always)]
167fn fp_mul(a: [u64; 6], b: [u64; 6]) -> [u64; 6] {
168    match crate::nist::p384_x86::mul_p(&a, &b) {
169        Some(r) => r,
170        None => Fp::mont_mul_raw(a, b),
171    }
172}
173
174/// `a^2 * 2^-384 mod p`.
175#[cfg(target_arch = "x86_64")]
176#[inline(always)]
177fn fp_sqr(a: [u64; 6]) -> [u64; 6] {
178    match crate::nist::p384_x86::sqr_p(&a) {
179        Some(r) => r,
180        None => Fp::mont_mul_raw(a, a),
181    }
182}
183
184/// `a * b * 2^-384 mod n`.
185#[cfg(target_arch = "x86_64")]
186#[inline(always)]
187fn fn_mul(a: [u64; 6], b: [u64; 6]) -> [u64; 6] {
188    match crate::nist::p384_x86::mul_n(&a, &b) {
189        Some((t, top)) => canonical(t, top, N),
190        None => Fn::mont_mul_raw(a, b),
191    }
192}
193
194/// The P-384 curve.
195#[derive(Debug, Clone, Copy)]
196pub struct P384;
197
198// Its own generator table, with its own storage; see the macro, which
199// emits the table under `std` and the windowed multiplication without it.
200crate::nist::gentable::generator_table_for!(P384, 16);
201
202impl Curve for P384 {
203    type Field = Fp;
204    type Scalar = Fn;
205
206    const NAME: &'static str = "P-384";
207    const FIELD_BYTES: usize = 48;
208    const SCALAR_BYTES: usize = 48;
209    const ORDER_BITS: usize = 384;
210
211    /// `b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a`
212    ///     `c656398d8a2ed19d2a85c8edd3ec2aef`
213    const B: Fp = Fp::to_mont_const([
214        0x2a85_c8ed_d3ec_2aef,
215        0xc656_398d_8a2e_d19d,
216        0x0314_088f_5013_875a,
217        0x181d_9c6e_fe81_4112,
218        0x988e_056b_e3f8_2d19,
219        0xb331_2fa7_e23e_e7e4,
220    ]);
221
222    const GX: Fp = Fp::to_mont_const([
223        0x3a54_5e38_7276_0ab7,
224        0x5502_f25d_bf55_296c,
225        0x59f7_41e0_8254_2a38,
226        0x6e1d_3b62_8ba7_9b98,
227        0x8eb1_c71e_f320_ad74,
228        0xaa87_ca22_be8b_0537,
229    ]);
230
231    const GY: Fp = Fp::to_mont_const([
232        0x7a43_1d7c_90ea_0e5f,
233        0x0a60_b1ce_1d7e_819d,
234        0xe9da_3113_b5f0_b8c0,
235        0xf8f4_1dbd_289a_147c,
236        0x5d9e_98bf_9292_dc29,
237        0x3617_de4a_9626_2c6f,
238    ]);
239
240    /// `p = 3 mod 4`, so a square root is `x^((p+1)/4)`.
241    fn sqrt(x: &Fp) -> Fp {
242        sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
243    }
244
245    fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
246        let mut b = [0u8; 48];
247        if bytes.len() != 48 {
248            return None;
249        }
250        b.copy_from_slice(bytes);
251        Fp::from_bytes(&b)
252    }
253
254    fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
255        let mut b = [0u8; 48];
256        if bytes.len() != 48 {
257            return None;
258        }
259        b.copy_from_slice(bytes);
260        Fn::from_bytes(&b)
261    }
262
263    #[cfg(target_arch = "x86_64")]
264    #[inline(always)]
265    fn double_fast(x: &Fp, y: &Fp, z: &Fp) -> Option<[Fp; 3]> {
266        crate::nist::p384_x86::point_double([&x.0, &y.0, &z.0])
267            .map(|[x, y, z]| [Fp(x), Fp(y), Fp(z)])
268    }
269
270    #[cfg(target_arch = "x86_64")]
271    #[inline(always)]
272    fn scan_affine(
273        entries: &[crate::nist::point::AffinePoint<P384>],
274        masks: &[u64],
275    ) -> Option<crate::nist::point::AffinePoint<P384>> {
276        Some(crate::nist::p384_x86::scan_affine(entries, masks))
277    }
278
279    #[cfg(target_arch = "x86_64")]
280    #[inline(always)]
281    fn scan_point(entries: &[crate::nist::point::Point<P384>], masks: &[u64]) -> Option<[Fp; 3]> {
282        let [x, y, z] = crate::nist::p384_x86::scan_point(entries, masks);
283        Some([Fp(x), Fp(y), Fp(z)])
284    }
285
286    #[cfg(target_arch = "x86_64")]
287    #[inline(always)]
288    fn add_fast(p: [&Fp; 3], q: [&Fp; 3]) -> Option<([Fp; 3], Fp, Fp)> {
289        crate::nist::p384_x86::point_add([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0, &q[2].0])
290            .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
291    }
292
293    #[cfg(target_arch = "x86_64")]
294    #[inline(always)]
295    fn add_affine_fast(p: [&Fp; 3], q: [&Fp; 2]) -> Option<([Fp; 3], Fp, Fp)> {
296        crate::nist::p384_x86::point_add_affine([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0])
297            .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
298    }
299
300    #[cfg(target_arch = "x86_64")]
301    #[inline(always)]
302    fn mul_scalar_fast(p: &Point, scalar: &Fn) -> Option<Point> {
303        match crate::nist::ifma::Ifma::get() {
304            Some(t) => Some(crate::nist::ifma::mul_scalar(t, p, scalar)),
305            None => crate::nist::p384_x86::mul_scalar(p, scalar),
306        }
307    }
308
309    #[cfg(target_arch = "x86_64")]
310    #[inline(always)]
311    fn comb_mul_fast(
312        windows: &[crate::nist::gentable::AffineWindow<Self, { crate::nist::gentable::ENTRIES }>;
313             crate::nist::gentable::TEETH],
314        scalar: &Fn,
315    ) -> Option<Point> {
316        crate::nist::ifma::Ifma::get().map(|t| crate::nist::ifma::comb_mul(t, windows, scalar))
317    }
318
319    #[cfg(all(target_arch = "x86_64", feature = "std"))]
320    #[inline(always)]
321    fn table_mul_fast<const E: usize>(
322        windows: &crate::nist::gentable::Windows<Self, E>,
323        scalar: &Fn,
324    ) -> Option<Point> {
325        crate::nist::ifma::Ifma::get().map(|t| crate::nist::ifma::table_mul(t, windows, scalar))
326    }
327
328    /// Verification's joint chain on the formulas' assembly: public inputs
329    /// only, never a secret path (`nist::verify_x86`).
330    #[cfg(target_arch = "x86_64")]
331    #[inline(always)]
332    fn mul_double_vartime_fast(
333        g: &[crate::nist::point::AffinePoint<P384>],
334        q: &[crate::nist::point::AffinePoint<P384>],
335        g_naf: &[i8],
336        q_naf: &[i8],
337    ) -> Option<Point> {
338        crate::nist::verify_x86::p384(g, q, g_naf, q_naf)
339    }
340
341    fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
342        let mut b = [0u8; 48];
343        let n = core::cmp::min(48, bytes.len());
344        // Take the leftmost bytes, which is what bits2int does when the input
345        // is at least as wide as the group order.
346        b[48 - n..].copy_from_slice(&bytes[..n]);
347        Fn::from_bytes_reduced(&b)
348    }
349}
350
351impl ecdsa::EcdsaCurve for P384 {
352    type Digest = ic_hash::Sha384;
353    type Hmac = ic_mac::HmacSha384;
354
355    /// By divsteps, as the field inverts, on 64-bit targets: 13 us by Fermat
356    /// in the generic ring against about 2 here (Zen 5, MULX/ADX). Constant
357    /// time, though verification's `s` does not need it to be.
358    #[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
359    fn invert_public_scalar(s: &Fn) -> Fn {
360        const R3: [u64; 6] = Fn::mont_mul_const(Fn::R2, Fn::R2);
361        Fn(crate::nist::safegcd::invert_384::<FnModulus>(&s.0)).mul(&Fn(R3))
362    }
363}
364
365/// The order, for verification's divsteps.
366#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
367pub(crate) struct FnModulus;
368
369#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
370impl crate::nist::safegcd::Modulus<7> for FnModulus {
371    const INFO: crate::nist::safegcd::ModInfo<7> = crate::nist::safegcd::ModInfo::new(N);
372}
373
374/// ECDSA over P-384 with SHA-384.
375pub struct EcdsaP384Sha384;
376
377impl Algorithm for EcdsaP384Sha384 {
378    const ID: &'static str = "ecdsa-p384-sha384";
379    const NAME: &'static str = "ECDSA P-384 with SHA-384";
380}
381
382impl SignatureScheme for EcdsaP384Sha384 {
383    const PRIVATE_KEY_LEN: usize = 48;
384    /// SEC1 uncompressed: `0x04 || X || Y`.
385    const PUBLIC_KEY_LEN: usize = 97;
386    /// Fixed-width `r || s`.
387    const SIGNATURE_LEN: usize = 96;
388
389    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
390        ecdsa::public_key::<P384>(private_key, out)
391    }
392
393    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
394        ecdsa::sign::<P384>(private_key, message, signature)
395    }
396
397    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
398        ecdsa::verify::<P384>(public_key, message, signature)
399    }
400}
401
402impl EcdsaP384Sha384 {
403    /// Verify a signature over a digest the caller computed, for signatures
404    /// made with a hash other than SHA-384.
405    ///
406    /// `digest` must be one of [`crate::PREHASH_LENS`] and at least 48
407    /// bytes, so that the hash is at least as strong as the curve; narrower is
408    /// refused with `InvalidLength`. Which hash produced it is the caller's to
409    /// establish: nothing here can tell.
410    pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
411        ecdsa::verify_prehash::<P384>(public_key, digest, signature)
412    }
413
414    /// Compute the public key in SEC1 compressed form (49 bytes).
415    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
416        ecdsa::public_key_compressed::<P384>(private_key, out)
417    }
418
419    /// Rewrite a signature to its low-`s` form, if it is not already.
420    ///
421    /// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the
422    /// same message, so a signature is not a unique identifier unless one form
423    /// is chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs
424    /// and verifies per the standard, so normalization is offered rather than
425    /// imposed -- apply it when a signature doubles as a database key or a
426    /// transaction id.
427    pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
428        ecdsa::normalize_s::<P384>(signature)
429    }
430
431    /// Whether a signature is already in low-`s` form.
432    pub fn has_low_s(signature: &[u8]) -> Result<bool> {
433        ecdsa::has_low_s::<P384>(signature)
434    }
435}
436
437impl SelfTest for EcdsaP384Sha384 {
438    fn self_test() -> Result<()> {
439        // Round-trip plus tamper rejection. The published RFC 6979 vector is
440        // asserted by the unit tests; this CAST is the startup integrity check.
441        let key = [0x2au8; 48];
442        let mut pk = [0u8; 97];
443        <Self as SignatureScheme>::public_key(&key, &mut pk)?;
444
445        let mut sig = [0u8; 96];
446        <Self as SignatureScheme>::sign(&key, b"self-test", &mut sig)?;
447        <Self as SignatureScheme>::verify(&pk, b"self-test", &sig)?;
448
449        // Signing is deterministic, so a repeat must agree exactly.
450        let mut again = [0u8; 96];
451        <Self as SignatureScheme>::sign(&key, b"self-test", &mut again)?;
452        ensure!(
453            ic_core::ct::verify(&sig, &again),
454            SelfTestFailed,
455            "ecdsa-p384-sha384"
456        );
457
458        sig[0] ^= 1;
459        ensure!(
460            <Self as SignatureScheme>::verify(&pk, b"self-test", &sig).is_err(),
461            SelfTestFailed,
462            "ecdsa-p384-sha384"
463        );
464        Ok(())
465    }
466}
467
468/// ECDH over P-384.
469pub struct EcdhP384;
470
471impl Algorithm for EcdhP384 {
472    const ID: &'static str = "ecdh-p384";
473    const NAME: &'static str = "ECDH P-384";
474}
475
476impl KeyAgreement for EcdhP384 {
477    const PRIVATE_KEY_LEN: usize = 48;
478    /// SEC1 uncompressed: `0x04 || X || Y`.
479    const PUBLIC_KEY_LEN: usize = 97;
480    const SHARED_SECRET_LEN: usize = 48;
481
482    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
483        ecdh::public_key::<P384>(private_key, out)
484    }
485
486    fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
487        ecdh::agree::<P384>(private_key, peer_public_key, out)
488    }
489}
490
491impl EcdhP384 {
492    /// Compute the public key in SEC1 compressed form (49 bytes).
493    pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
494        ecdh::public_key_compressed::<P384>(private_key, out)
495    }
496}
497
498impl SelfTest for EcdhP384 {
499    fn self_test() -> Result<()> {
500        // Both sides of an exchange must agree, and the result must not be the
501        // trivial one.
502        let (a, b) = ([0x11u8; 48], [0x22u8; 48]);
503        let mut a_pk = [0u8; 97];
504        let mut b_pk = [0u8; 97];
505        <Self as KeyAgreement>::public_key(&a, &mut a_pk)?;
506        <Self as KeyAgreement>::public_key(&b, &mut b_pk)?;
507
508        let mut z1 = [0u8; 48];
509        let mut z2 = [0u8; 48];
510        <Self as KeyAgreement>::agree(&a, &b_pk, &mut z1)?;
511        <Self as KeyAgreement>::agree(&b, &a_pk, &mut z2)?;
512        ensure!(ic_core::ct::verify(&z1, &z2), SelfTestFailed, "ecdh-p384");
513        ensure!(z1 != [0u8; 48], SelfTestFailed, "ecdh-p384");
514        Ok(())
515    }
516}
517
518/// A P-384 point in Jacobian coordinates.
519pub type Point = crate::nist::point::Point<P384>;
520/// A P-384 point in affine coordinates.
521pub type AffinePoint = crate::nist::point::AffinePoint<P384>;
522/// A peer's P-384 public key for [`EcdhP384`], decoded, validated and
523/// expanded once: `new` takes its SEC1 encoding and refuses what
524/// [`EcdhP384`]'s `agree` would refuse; `agree` takes a private key and
525/// writes the 48-byte shared secret, doing only the scalar multiplication
526/// in about 40 percent of the time of an unprepared agreement. About 6 KiB, not
527/// allocated.
528pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P384>;
529
530// Bounded proofs for Kani; see docs/PROOFS.md.
531#[cfg(kani)]
532mod proofs;
533
534#[cfg(test)]
535mod tests {
536    use super::*;
537    use ic_core::codec::{hex, unhex};
538
539    fn scalar(v: u64) -> Fn {
540        Fn::to_mont([v, 0, 0, 0, 0, 0])
541    }
542
543    fn fp(v: u64) -> Fp {
544        Fp::to_mont([v, 0, 0, 0, 0, 0])
545    }
546
547    // -- field ------------------------------------------------------------
548
549    #[test]
550    fn montgomery_constants_are_consistent() {
551        assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
552        assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
553    }
554
555    #[test]
556    fn small_arithmetic_matches_integers() {
557        assert_eq!(fp(2).add(&fp(3)), fp(5));
558        assert_eq!(fp(5).sub(&fp(3)), fp(2));
559        assert_eq!(fp(6).mul(&fp(7)), fp(42));
560        assert_eq!(fp(9).square(), fp(81));
561        assert_eq!(fp(5).triple(), fp(15));
562        assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0, 0, 0]);
563    }
564
565    /// The field's inversion -- divsteps on 64-bit targets -- and its
566    /// addition chain, each against `a^(p-2)` by the generic windowed
567    /// exponentiation, which shares nothing with either but the
568    /// multiplication: on zero, one, `-1`, `-2`, powers of two across the
569    /// width, and two thousand pseudo-random elements; and against `a * a^-1
570    /// = 1` on all but zero.
571    #[test]
572    fn the_inversion_agrees_with_exponentiation() {
573        let mut exp = P;
574        exp[0] -= 2;
575        let mut values = std::vec![Fp::ZERO, Fp::ONE, Fp::ONE.neg(), Fp::ONE.double().neg()];
576        for k in [1usize, 31, 32, 63, 64, 95, 96, 127, 128, 200, 255, 256, 383] {
577            let mut x = [0u64; 6];
578            x[k / 64] = 1 << (k % 64);
579            values.push(Fp::to_mont(x));
580            values.push(Fp::to_mont(x).neg());
581        }
582        let mut state = 0x384u64;
583        for _ in 0..2000 {
584            let mut bytes = [0u8; 48];
585            for chunk in bytes.chunks_mut(8) {
586                state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
587                let mut z = state;
588                z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
589                z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
590                chunk.copy_from_slice(&(z ^ (z >> 31)).to_be_bytes());
591            }
592            values.push(Fp::from_bytes_reduced(&bytes));
593        }
594        for v in values {
595            let inv = v.invert();
596            assert_eq!(inv, v.pow(&exp), "{v:x?}");
597            assert_eq!(Fp(fp_invert_chain(v.0)), inv, "{v:x?}");
598            if v != Fp::ZERO {
599                assert_eq!(v.mul(&inv), Fp::ONE, "{v:x?}");
600            }
601        }
602    }
603
604    #[test]
605    fn inversion_is_correct() {
606        for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
607            assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
608            assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
609        }
610        assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
611    }
612
613    #[test]
614    fn arithmetic_laws_hold_on_large_values() {
615        let a = P384::field_from_slice(&[0x3a; 48]).unwrap();
616        let b = P384::field_from_slice(&[0x91; 48]).unwrap();
617        let c = P384::field_from_slice(&[0xc7; 48]).unwrap();
618        assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
619        assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
620        assert_eq!(
621            a.mul(&b.add(&c)),
622            a.mul(&b).add(&a.mul(&c)),
623            "distributivity"
624        );
625        assert_eq!(a.add(&a.neg()), Fp::ZERO);
626    }
627
628    #[test]
629    fn byte_encoding_round_trips() {
630        let bytes = [0x7fu8; 48];
631        let a = P384::field_from_slice(&bytes).unwrap();
632        assert_eq!(a.to_bytes(), bytes);
633    }
634
635    // -- group law --------------------------------------------------------
636
637    /// Validates B, GX, GY and the curve equation together: if any of the four
638    /// constants were mistranscribed, the base point would not satisfy it.
639    #[test]
640    fn the_base_point_is_on_the_curve() {
641        let g = Point::generator().to_affine().unwrap();
642        assert!(bool::from(g.is_on_curve()));
643    }
644
645    /// Validates the group order n against the base point. Together with the
646    /// test above this pins down every curve constant.
647    #[test]
648    fn the_base_point_has_order_n() {
649        let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
650        let p = Point::generator().mul_scalar(&n_minus_1);
651        assert!(
652            bool::from(p.ct_eq(&Point::generator().neg())),
653            "[n-1]G == -G"
654        );
655        assert!(
656            bool::from(p.add(&Point::generator()).is_identity()),
657            "[n]G is the identity"
658        );
659    }
660
661    #[test]
662    fn identity_and_negation_behave() {
663        let g = Point::generator();
664        assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
665        assert!(bool::from(Point::identity().double().is_identity()));
666        assert!(bool::from(g.add(&g.neg()).is_identity()));
667    }
668
669    #[test]
670    fn addition_handles_equal_inputs_as_a_doubling() {
671        let g = Point::generator();
672        assert!(bool::from(g.add(&g).ct_eq(&g.double())));
673    }
674
675    #[test]
676    fn scalar_multiplication_matches_repeated_addition() {
677        let g = Point::generator();
678        let mut acc = Point::identity();
679        for k in 1..=8u64 {
680            acc = acc.add(&g);
681            assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
682        }
683    }
684
685    #[test]
686    fn scalar_multiplication_is_linear() {
687        let g = Point::generator();
688        let a = scalar(1_234_567);
689        let b = scalar(7_654_321);
690        assert!(bool::from(
691            g.mul_scalar(&a.add(&b))
692                .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
693        ));
694    }
695
696    /// The published `[2]G`, an independent check on the group law rather than
697    /// on self-consistency.
698    #[test]
699    fn two_g_matches_the_published_value() {
700        let two_g = Point::generator().double().to_affine().unwrap();
701        assert_eq!(
702            hex(two_g.x.to_bytes().as_ref()),
703            "08d999057ba3d2d969260045c55b97f089025959a6f434d651d207d19fb96e9e\
704             4fe0e86ebe0e64f85b96a9c75295df61"
705                .replace(char::is_whitespace, "")
706        );
707        assert_eq!(
708            hex(two_g.y.to_bytes().as_ref()),
709            "8e80f1fa5b1b3cedb7bfe8dffd6dba74b275d875bc6cc43e904e505f256ab425\
710             5ffd43e94d39e22d61501e700a940e80"
711                .replace(char::is_whitespace, "")
712        );
713    }
714
715    #[test]
716    fn every_multiple_stays_on_the_curve() {
717        let g = Point::generator();
718        for k in [1u64, 2, 3, 17, 255, 65537] {
719            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
720            assert!(bool::from(p.is_on_curve()), "[{k}]G is off the curve");
721        }
722    }
723
724    #[test]
725    fn sec1_round_trips_in_both_forms() {
726        let g = Point::generator();
727        for k in [1u64, 2, 3, 4, 5, 6] {
728            let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
729            let mut unc = [0u8; 97];
730            let mut comp = [0u8; 49];
731            assert!(p.write_uncompressed(&mut unc));
732            assert!(p.write_compressed(&mut comp));
733
734            let a = AffinePoint::from_sec1(&unc).unwrap();
735            let b = AffinePoint::from_sec1(&comp).unwrap();
736            assert_eq!(a.x, p.x);
737            assert_eq!(a.y, p.y);
738            assert_eq!(b.x, p.x);
739            assert_eq!(b.y, p.y, "compressed y for [{k}]G");
740        }
741    }
742
743    #[test]
744    fn decoding_rejects_bad_encodings() {
745        let g = Point::generator().to_affine().unwrap();
746        let mut unc = [0u8; 97];
747        assert!(g.write_uncompressed(&mut unc));
748
749        assert!(AffinePoint::from_sec1(&[0u8; 97]).is_none(), "identity");
750        assert!(AffinePoint::from_sec1(&unc[..96]).is_none(), "truncated");
751        // A P-256-sized encoding must not be accepted here.
752        assert!(
753            AffinePoint::from_sec1(&[0x04u8; 65]).is_none(),
754            "wrong width"
755        );
756
757        let mut bad = unc;
758        bad[96] ^= 1;
759        assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
760    }
761
762    // -- ECDSA ------------------------------------------------------------
763
764    /// RFC 6979 A.2.6: P-384 with SHA-384.
765    ///
766    /// The private key, public key and both message signatures are published
767    /// together, so matching them exercises the whole stack: the 6-limb field,
768    /// the group law, the scalar ring, the nonce derivation, and the signing
769    /// equation.
770    const KEY: &str = "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
771                       96d5724e4c70a825f872c9ea60d2edf5";
772
773    fn key_bytes() -> Vec<u8> {
774        unhex(&KEY.replace(char::is_whitespace, "")).unwrap()
775    }
776
777    #[test]
778    fn rfc6979_public_key() {
779        let mut pk = [0u8; 97];
780        EcdsaP384Sha384::public_key(&key_bytes(), &mut pk).unwrap();
781        assert_eq!(pk[0], 0x04);
782        assert_eq!(
783            hex(&pk[1..49]),
784            "ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64\
785             def8f0ea9055866064a254515480bc13"
786                .replace(char::is_whitespace, ""),
787            "Ux"
788        );
789        assert_eq!(
790            hex(&pk[49..]),
791            "8015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1\
792             288b231c3ae0d4fe7344fd2533264720"
793                .replace(char::is_whitespace, ""),
794            "Uy"
795        );
796    }
797
798    #[test]
799    fn rfc6979_sample_vector() {
800        let mut sig = [0u8; 96];
801        EcdsaP384Sha384::sign(&key_bytes(), b"sample", &mut sig).unwrap();
802        assert_eq!(
803            hex(&sig[..48]),
804            "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c\
805             81a648152e44acf96e36dd1e80fabe46"
806                .replace(char::is_whitespace, ""),
807            "r"
808        );
809        assert_eq!(
810            hex(&sig[48..]),
811            "99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94f\
812             a329c145786e679e7b82c71a38628ac8"
813                .replace(char::is_whitespace, ""),
814            "s"
815        );
816    }
817
818    #[test]
819    fn rfc6979_test_vector() {
820        let mut sig = [0u8; 96];
821        EcdsaP384Sha384::sign(&key_bytes(), b"test", &mut sig).unwrap();
822        assert_eq!(
823            hex(&sig[..48]),
824            "8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36ab775d509d7a5feb\
825             0542a7f0812998da8f1dd3ca3cf023db"
826                .replace(char::is_whitespace, ""),
827            "r"
828        );
829        assert_eq!(
830            hex(&sig[48..]),
831            "ddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13173923e0\
832             6a739f040649a667bf3b828246baa5a5"
833                .replace(char::is_whitespace, ""),
834            "s"
835        );
836    }
837
838    #[test]
839    fn signing_is_deterministic_and_message_bound() {
840        let key = key_bytes();
841        let mut a = [0u8; 96];
842        let mut b = [0u8; 96];
843        EcdsaP384Sha384::sign(&key, b"same", &mut a).unwrap();
844        EcdsaP384Sha384::sign(&key, b"same", &mut b).unwrap();
845        assert_eq!(a, b);
846        EcdsaP384Sha384::sign(&key, b"other", &mut b).unwrap();
847        assert_ne!(&a[..48], &b[..48]);
848    }
849
850    #[test]
851    fn sign_and_verify_round_trip() {
852        let key = key_bytes();
853        let mut pk = [0u8; 97];
854        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
855        for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
856            let mut sig = [0u8; 96];
857            EcdsaP384Sha384::sign(&key, message, &mut sig).unwrap();
858            EcdsaP384Sha384::verify(&pk, message, &sig).unwrap();
859        }
860    }
861
862    #[test]
863    fn verification_rejects_tampering() {
864        let key = key_bytes();
865        let mut pk = [0u8; 97];
866        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
867        let mut sig = [0u8; 96];
868        EcdsaP384Sha384::sign(&key, b"authentic", &mut sig).unwrap();
869
870        assert!(EcdsaP384Sha384::verify(&pk, b"forged", &sig).is_err());
871        let mut bad = sig;
872        bad[0] ^= 1;
873        assert!(EcdsaP384Sha384::verify(&pk, b"authentic", &bad).is_err());
874        let mut bad = sig;
875        bad[95] ^= 1;
876        assert!(EcdsaP384Sha384::verify(&pk, b"authentic", &bad).is_err());
877
878        let mut other = [0u8; 97];
879        EcdsaP384Sha384::public_key(&[0x11u8; 48], &mut other).unwrap();
880        assert!(EcdsaP384Sha384::verify(&other, b"authentic", &sig).is_err());
881    }
882
883    #[test]
884    fn signing_rejects_invalid_private_keys() {
885        let mut sig = [0u8; 96];
886        assert!(
887            EcdsaP384Sha384::sign(&[0u8; 48], b"m", &mut sig).is_err(),
888            "zero"
889        );
890        assert!(
891            EcdsaP384Sha384::sign(&[0xffu8; 48], b"m", &mut sig).is_err(),
892            ">= n"
893        );
894        assert!(
895            EcdsaP384Sha384::sign(&[1u8; 32], b"m", &mut sig).is_err(),
896            "P-256 sized"
897        );
898    }
899
900    #[test]
901    fn malleability_and_normalization() {
902        let key = key_bytes();
903        let mut pk = [0u8; 97];
904        EcdsaP384Sha384::public_key(&key, &mut pk).unwrap();
905        let mut sig = [0u8; 96];
906        EcdsaP384Sha384::sign(&key, b"sample", &mut sig).unwrap();
907
908        let mut normalized = sig;
909        EcdsaP384Sha384::normalize_s(&mut normalized).unwrap();
910        assert!(EcdsaP384Sha384::has_low_s(&normalized).unwrap());
911        // Both forms verify: that is the malleability.
912        EcdsaP384Sha384::verify(&pk, b"sample", &normalized).unwrap();
913        EcdsaP384Sha384::verify(&pk, b"sample", &sig).unwrap();
914
915        let mut twice = normalized;
916        EcdsaP384Sha384::normalize_s(&mut twice).unwrap();
917        assert_eq!(twice, normalized, "normalization must be idempotent");
918    }
919
920    #[test]
921    fn ecdsa_self_test_passes() {
922        EcdsaP384Sha384::self_test().unwrap();
923    }
924
925    // -- ECDH -------------------------------------------------------------
926
927    #[test]
928    fn both_parties_derive_the_same_secret() {
929        let (alice, bob) = ([0x11u8; 48], [0x22u8; 48]);
930        let mut alice_pk = [0u8; 97];
931        let mut bob_pk = [0u8; 97];
932        EcdhP384::public_key(&alice, &mut alice_pk).unwrap();
933        EcdhP384::public_key(&bob, &mut bob_pk).unwrap();
934
935        let mut z1 = [0u8; 48];
936        let mut z2 = [0u8; 48];
937        EcdhP384::agree(&alice, &bob_pk, &mut z1).unwrap();
938        EcdhP384::agree(&bob, &alice_pk, &mut z2).unwrap();
939        assert_eq!(z1, z2);
940        assert_ne!(z1, [0u8; 48]);
941    }
942
943    #[test]
944    fn compressed_and_uncompressed_peers_agree() {
945        let (alice, bob) = ([0x33u8; 48], [0x44u8; 48]);
946        let mut unc = [0u8; 97];
947        let mut comp = [0u8; 49];
948        EcdhP384::public_key(&bob, &mut unc).unwrap();
949        EcdhP384::public_key_compressed(&bob, &mut comp).unwrap();
950
951        let mut z1 = [0u8; 48];
952        let mut z2 = [0u8; 48];
953        EcdhP384::agree(&alice, &unc, &mut z1).unwrap();
954        EcdhP384::agree(&alice, &comp, &mut z2).unwrap();
955        assert_eq!(z1, z2);
956    }
957
958    #[test]
959    fn ecdh_rejects_invalid_inputs() {
960        let alice = [0x11u8; 48];
961        let mut z = [0u8; 48];
962        assert!(EcdhP384::agree(&alice, &[0u8; 97], &mut z).is_err());
963        assert!(EcdhP384::agree(&alice, &[], &mut z).is_err());
964
965        let mut bob_pk = [0u8; 97];
966        EcdhP384::public_key(&[0x22u8; 48], &mut bob_pk).unwrap();
967        bob_pk[96] ^= 1;
968        assert!(
969            EcdhP384::agree(&alice, &bob_pk, &mut z).is_err(),
970            "off curve"
971        );
972    }
973
974    #[test]
975    fn ecdh_self_test_passes() {
976        EcdhP384::self_test().unwrap();
977    }
978}