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
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#[derive(Debug, Clone, Copy)]
51pub struct P384;
52
53crate::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 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 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 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
133pub 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 const PUBLIC_KEY_LEN: usize = 97;
145 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 pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
170 ecdsa::verify_prehash::<P384>(public_key, digest, signature)
171 }
172
173 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
175 ecdsa::public_key_compressed::<P384>(private_key, out)
176 }
177
178 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
187 ecdsa::normalize_s::<P384>(signature)
188 }
189
190 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 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 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
227pub 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 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 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 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
277pub type Point = crate::nist::point::Point<P384>;
279pub 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 #[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 #[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 #[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 #[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 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 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 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 #[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}