1use 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
19pub(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
29const 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#[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#[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#[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#[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 let t = x255.square_n(33).mul(&x32);
126 let t = t.square_n(94).mul(&x30);
128 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#[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#[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#[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#[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#[derive(Debug, Clone, Copy)]
196pub struct P384;
197
198crate::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 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 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 #[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 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 #[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#[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
374pub 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 const PUBLIC_KEY_LEN: usize = 97;
386 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 pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
411 ecdsa::verify_prehash::<P384>(public_key, digest, signature)
412 }
413
414 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
416 ecdsa::public_key_compressed::<P384>(private_key, out)
417 }
418
419 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
428 ecdsa::normalize_s::<P384>(signature)
429 }
430
431 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 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 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
468pub 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 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 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 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
518pub type Point = crate::nist::point::Point<P384>;
520pub type AffinePoint = crate::nist::point::AffinePoint<P384>;
522pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P384>;
529
530#[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 #[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 #[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 #[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 #[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 #[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 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 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 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 #[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}