1use crate::mont_field;
23use crate::nist::arith::Field;
24use crate::nist::point::Curve;
25use crate::nist::{ecdh, ecdsa};
26use ic_core::traits::{Algorithm, KeyAgreement, SelfTest, SignatureScheme};
27use ic_core::{ensure, Result};
28
29mont_field!(
30 Fp,
31 9,
32 66,
33 [
34 0xffff_ffff_ffff_ffff,
35 0xffff_ffff_ffff_ffff,
36 0xffff_ffff_ffff_ffff,
37 0xffff_ffff_ffff_ffff,
38 0xffff_ffff_ffff_ffff,
39 0xffff_ffff_ffff_ffff,
40 0xffff_ffff_ffff_ffff,
41 0xffff_ffff_ffff_ffff,
42 0x0000_0000_0000_01ff,
43 ],
44 "The P-521 coordinate field, GF(p) with p = 2^521 - 1."
45);
46
47mont_field!(
48 Fn,
49 9,
50 66,
51 [
52 0xbb6f_b71e_9138_6409,
53 0x3bb5_c9b8_899c_47ae,
54 0x7fcc_0148_f709_a5d0,
55 0x5186_8783_bf2f_966b,
56 0xffff_ffff_ffff_fffa,
57 0xffff_ffff_ffff_ffff,
58 0xffff_ffff_ffff_ffff,
59 0xffff_ffff_ffff_ffff,
60 0x0000_0000_0000_01ff,
61 ],
62 "The P-521 scalar ring, Z/nZ where n is the order of the base point."
63);
64
65#[derive(Debug, Clone, Copy)]
67pub struct P521;
68
69crate::nist::gentable::generator_table_for!(P521, 8);
72
73impl Curve for P521 {
74 type Field = Fp;
75 type Scalar = Fn;
76
77 const NAME: &'static str = "P-521";
78 const FIELD_BYTES: usize = 66;
81 const SCALAR_BYTES: usize = 66;
82 const ORDER_BITS: usize = 521;
85
86 const B: Fp = Fp::to_mont_const([
89 0xef45_1fd4_6b50_3f00,
90 0x3573_df88_3d2c_34f1,
91 0x1652_c0bd_3bb1_bf07,
92 0x5619_3951_ec7e_937b,
93 0xb8b4_8991_8ef1_09e1,
94 0xa2da_725b_99b3_15f3,
95 0x929a_21a0_b685_40ee,
96 0x953e_b961_8e1c_9a1f,
97 0x0000_0000_0000_0051,
98 ]);
99
100 const GX: Fp = Fp::to_mont_const([
101 0xf97e_7e31_c2e5_bd66,
102 0x3348_b3c1_856a_429b,
103 0xfe1d_c127_a2ff_a8de,
104 0xa14b_5e77_efe7_5928,
105 0xf828_af60_6b4d_3dba,
106 0x9c64_8139_053f_b521,
107 0x9e3e_cb66_2395_b442,
108 0x858e_06b7_0404_e9cd,
109 0x0000_0000_0000_00c6,
110 ]);
111
112 const GY: Fp = Fp::to_mont_const([
113 0x88be_9476_9fd1_6650,
114 0x353c_7086_a272_c240,
115 0xc550_b901_3fad_0761,
116 0x97ee_7299_5ef4_2640,
117 0x17af_bd17_273e_662c,
118 0x98f5_4449_579b_4468,
119 0x5c8a_5fb4_2c7d_1bd9,
120 0x3929_6a78_9a3b_c004,
121 0x0000_0000_0000_0118,
122 ]);
123
124 fn sqrt(x: &Fp) -> Fp {
130 x.square_n(519)
131 }
132
133 fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
134 let mut b = [0u8; 66];
135 if bytes.len() != 66 {
136 return None;
137 }
138 b.copy_from_slice(bytes);
139 Fp::from_bytes(&b)
140 }
141
142 fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
143 let mut b = [0u8; 66];
144 if bytes.len() != 66 {
145 return None;
146 }
147 b.copy_from_slice(bytes);
148 Fn::from_bytes(&b)
149 }
150
151 fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
152 let mut b = [0u8; 66];
153 let n = core::cmp::min(66, bytes.len());
154 b[66 - n..].copy_from_slice(&bytes[..n]);
159 Fn::from_bytes_reduced(&b)
160 }
161}
162
163impl ecdsa::EcdsaCurve for P521 {
164 type Digest = ic_hash::Sha512;
165 type Hmac = ic_mac::HmacSha512;
166}
167
168pub struct EcdsaP521Sha512;
170
171impl Algorithm for EcdsaP521Sha512 {
172 const ID: &'static str = "ecdsa-p521-sha512";
173 const NAME: &'static str = "ECDSA P-521 with SHA-512";
174}
175
176impl SignatureScheme for EcdsaP521Sha512 {
177 const PRIVATE_KEY_LEN: usize = 66;
178 const PUBLIC_KEY_LEN: usize = 133;
180 const SIGNATURE_LEN: usize = 132;
182
183 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
184 ecdsa::public_key::<P521>(private_key, out)
185 }
186
187 fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
188 ecdsa::sign::<P521>(private_key, message, signature)
189 }
190
191 fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
192 ecdsa::verify::<P521>(public_key, message, signature)
193 }
194}
195
196impl EcdsaP521Sha512 {
197 pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
205 ecdsa::verify_prehash::<P521>(public_key, digest, signature)
206 }
207
208 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
210 ecdsa::public_key_compressed::<P521>(private_key, out)
211 }
212
213 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
222 ecdsa::normalize_s::<P521>(signature)
223 }
224
225 pub fn has_low_s(signature: &[u8]) -> Result<bool> {
227 ecdsa::has_low_s::<P521>(signature)
228 }
229}
230
231impl SelfTest for EcdsaP521Sha512 {
232 fn self_test() -> Result<()> {
233 let mut key = [0x2au8; 66];
239 key[0] = 0x00;
240 let mut pk = [0u8; 133];
241 <Self as SignatureScheme>::public_key(&key, &mut pk)?;
242
243 let mut sig = [0u8; 132];
244 <Self as SignatureScheme>::sign(&key, b"self-test", &mut sig)?;
245 <Self as SignatureScheme>::verify(&pk, b"self-test", &sig)?;
246
247 let mut again = [0u8; 132];
249 <Self as SignatureScheme>::sign(&key, b"self-test", &mut again)?;
250 ensure!(
251 ic_core::ct::verify(&sig, &again),
252 SelfTestFailed,
253 "ecdsa-p521-sha512"
254 );
255
256 sig[0] ^= 1;
257 ensure!(
258 <Self as SignatureScheme>::verify(&pk, b"self-test", &sig).is_err(),
259 SelfTestFailed,
260 "ecdsa-p521-sha512"
261 );
262 Ok(())
263 }
264}
265
266pub struct EcdhP521;
268
269impl Algorithm for EcdhP521 {
270 const ID: &'static str = "ecdh-p521";
271 const NAME: &'static str = "ECDH P-521";
272}
273
274impl KeyAgreement for EcdhP521 {
275 const PRIVATE_KEY_LEN: usize = 66;
276 const PUBLIC_KEY_LEN: usize = 133;
277 const SHARED_SECRET_LEN: usize = 66;
278
279 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
280 ecdh::public_key::<P521>(private_key, out)
281 }
282
283 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
284 ecdh::agree::<P521>(private_key, peer_public_key, out)
285 }
286}
287
288impl EcdhP521 {
289 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
291 ecdh::public_key_compressed::<P521>(private_key, out)
292 }
293}
294
295impl SelfTest for EcdhP521 {
296 fn self_test() -> Result<()> {
297 let (mut a, mut b) = ([0x11u8; 66], [0x22u8; 66]);
301 a[0] = 0x00;
302 b[0] = 0x00;
303 let mut a_pk = [0u8; 133];
304 let mut b_pk = [0u8; 133];
305 <Self as KeyAgreement>::public_key(&a, &mut a_pk)?;
306 <Self as KeyAgreement>::public_key(&b, &mut b_pk)?;
307
308 let mut z1 = [0u8; 66];
309 let mut z2 = [0u8; 66];
310 <Self as KeyAgreement>::agree(&a, &b_pk, &mut z1)?;
311 <Self as KeyAgreement>::agree(&b, &a_pk, &mut z2)?;
312 ensure!(ic_core::ct::verify(&z1, &z2), SelfTestFailed, "ecdh-p521");
313 ensure!(z1 != [0u8; 66], SelfTestFailed, "ecdh-p521");
314 Ok(())
315 }
316}
317
318pub type Point = crate::nist::point::Point<P521>;
320pub type AffinePoint = crate::nist::point::AffinePoint<P521>;
322pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P521>;
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
346 fn the_square_root_shortcut_matches_the_generic_exponent() {
347 use crate::nist::arith::sqrt_p3mod4;
348
349 let mut checked = 0;
350 for seed in 1u64..40 {
351 let mut bytes = [0u8; 66];
352 for (i, b) in bytes.iter_mut().enumerate() {
353 *b = (seed.wrapping_mul(i as u64 + 7) & 0xff) as u8;
354 }
355 bytes[0] &= 0x01;
357 let Some(x) = P521::field_from_slice(&bytes) else {
358 continue;
359 };
360 let y2 = x.square();
363
364 let shortcut = <P521 as Curve>::sqrt(&y2);
365 let generic = sqrt_p3mod4(&y2, Fp::MODULUS, |v, e| v.pow(e));
366 assert_eq!(
367 shortcut, generic,
368 "the shortcut disagrees with the generic exponent at seed {seed}"
369 );
370 assert_eq!(shortcut.square(), y2, "and it must actually be a root");
371 checked += 1;
372 }
373 assert!(
374 checked > 20,
375 "the sweep should reach real inputs: {checked}"
376 );
377 }
378
379 #[test]
384 fn a_root_squares_back_to_its_input_on_every_curve() {
385 for seed in 1u8..12 {
386 let mut b = [0u8; 66];
387 b[65] = seed;
388 let x = P521::field_from_slice(&b).unwrap();
389 let y2 = x.square();
390 let root = <P521 as Curve>::sqrt(&y2);
391 assert_eq!(root.square(), y2, "P-521 at seed {seed}");
392 }
393 }
394 use ic_core::codec::hex;
395
396 fn scalar(v: u64) -> Fn {
397 Fn::to_mont([v, 0, 0, 0, 0, 0, 0, 0, 0])
398 }
399
400 fn fp(v: u64) -> Fp {
401 Fp::to_mont([v, 0, 0, 0, 0, 0, 0, 0, 0])
402 }
403
404 #[test]
407 fn montgomery_constants_are_consistent() {
408 assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
409 assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
410 }
411
412 #[test]
415 fn the_modulus_is_two_to_the_521_minus_one() {
416 for (i, limb) in Fp::MODULUS.iter().enumerate().take(8) {
417 assert_eq!(*limb, u64::MAX, "limb {i}");
418 }
419 assert_eq!(Fp::MODULUS[8], 0x1ff);
420 assert_eq!(64 - Fp::MODULUS[8].leading_zeros(), 9);
422 }
423
424 #[test]
425 fn small_arithmetic_matches_integers() {
426 assert_eq!(fp(2).add(&fp(3)), fp(5));
427 assert_eq!(fp(5).sub(&fp(3)), fp(2));
428 assert_eq!(fp(6).mul(&fp(7)), fp(42));
429 assert_eq!(fp(9).square(), fp(81));
430 assert_eq!(fp(5).triple(), fp(15));
431 assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0, 0, 0, 0, 0, 0]);
432 }
433
434 #[test]
435 fn inversion_is_correct() {
436 for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
437 assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
438 assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
439 }
440 assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
441 }
442
443 #[test]
444 fn arithmetic_laws_hold_on_large_values() {
445 let mut a_bytes = [0x3au8; 66];
448 a_bytes[0] = 0x01;
449 let mut b_bytes = [0x91u8; 66];
450 b_bytes[0] = 0x00;
451 let mut c_bytes = [0xc7u8; 66];
452 c_bytes[0] = 0x01;
453 let a = P521::field_from_slice(&a_bytes).unwrap();
454 let b = P521::field_from_slice(&b_bytes).unwrap();
455 let c = P521::field_from_slice(&c_bytes).unwrap();
456 assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
457 assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
458 assert_eq!(
459 a.mul(&b.add(&c)),
460 a.mul(&b).add(&a.mul(&c)),
461 "distributivity"
462 );
463 assert_eq!(a.add(&a.neg()), Fp::ZERO);
464 }
465
466 #[test]
469 fn byte_encoding_round_trips_across_the_partial_top_limb() {
470 let mut bytes = [0x00u8; 66];
472 bytes[0] = 0x01;
473 bytes[1] = 0xff;
474 for (i, b) in bytes[2..].iter_mut().enumerate() {
475 *b = i as u8;
476 }
477 let a = P521::field_from_slice(&bytes).unwrap();
478 assert_eq!(a.to_bytes(), bytes);
479
480 let p_minus_1 = Fp::ZERO.sub(&Fp::ONE);
482 let encoded = p_minus_1.to_bytes();
483 assert_eq!(encoded[0], 0x01, "bit 520 is set");
484 assert_eq!(encoded[1], 0xff);
485 assert_eq!(encoded[65], 0xfe, "and the low bit is clear");
486 assert_eq!(P521::field_from_slice(&encoded).unwrap(), p_minus_1);
487 }
488
489 #[test]
491 fn out_of_range_encodings_are_rejected() {
492 let mut p_bytes = [0xffu8; 66];
494 p_bytes[0] = 0x01;
495 assert!(P521::field_from_slice(&p_bytes).is_none(), "p");
496
497 let too_big = [0xffu8; 66];
499 assert!(P521::field_from_slice(&too_big).is_none(), "2^528 - 1");
500
501 assert!(P521::field_from_slice(&[0u8; 65]).is_none());
503 assert!(P521::field_from_slice(&[0u8; 67]).is_none());
504 }
505
506 #[test]
509 fn square_roots_are_correct() {
510 for v in [1u64, 4, 9, 16, 12345] {
511 let x = fp(v);
512 let root = P521::sqrt(&x.square());
513 assert_eq!(root.square(), x.square(), "sqrt({v}^2)^2");
515 assert!(
516 bool::from(root.ct_eq(&x)) || bool::from(root.ct_eq(&x.neg())),
517 "sqrt({v}^2) is +/-{v}"
518 );
519 }
520 }
521
522 #[test]
527 fn the_base_point_is_on_the_curve() {
528 let g = Point::generator().to_affine().unwrap();
529 assert!(bool::from(g.is_on_curve()));
530 }
531
532 #[test]
535 fn the_base_point_has_order_n() {
536 let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
537 let p = Point::generator().mul_scalar(&n_minus_1);
538 assert!(
539 bool::from(p.ct_eq(&Point::generator().neg())),
540 "[n-1]G == -G"
541 );
542 assert!(
543 bool::from(p.add(&Point::generator()).is_identity()),
544 "[n]G is the identity"
545 );
546 }
547
548 #[test]
549 fn identity_and_negation_behave() {
550 let g = Point::generator();
551 assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
552 assert!(bool::from(Point::identity().double().is_identity()));
553 assert!(bool::from(g.add(&g.neg()).is_identity()));
554 }
555
556 #[test]
557 fn addition_handles_equal_inputs_as_a_doubling() {
558 let g = Point::generator();
559 assert!(bool::from(g.add(&g).ct_eq(&g.double())));
560 }
561
562 #[test]
563 fn scalar_multiplication_matches_repeated_addition() {
564 let g = Point::generator();
565 let mut acc = Point::identity();
566 for k in 1..=8u64 {
567 acc = acc.add(&g);
568 assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
569 }
570 }
571
572 #[test]
573 fn scalar_multiplication_is_linear() {
574 let g = Point::generator();
575 let a = scalar(1_234_567);
576 let b = scalar(7_654_321);
577 assert!(bool::from(
578 g.mul_scalar(&a.add(&b))
579 .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
580 ));
581 }
582
583 #[test]
588 fn two_g_matches_an_independent_computation() {
589 let two_g = Point::generator().double().to_affine().unwrap();
590 assert_eq!(
591 hex(two_g.x.to_bytes().as_ref()),
592 "00433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769\
593 be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d"
594 .replace(char::is_whitespace, "")
595 );
596 assert_eq!(
597 hex(two_g.y.to_bytes().as_ref()),
598 "00f4bb8cc7f86db26700a7f3eceeeed3f0b5c6b5107c4da97740ab21a29906c4\
599 2dbbb3e377de9f251f6b93937fa99a3248f4eafcbe95edc0f4f71be356d661f41b02"
600 .replace(char::is_whitespace, "")
601 );
602 }
603
604 #[test]
607 fn a_large_multiple_matches_an_independent_computation() {
608 let k = scalar(0x0123_4567_89ab_cdef);
609 let p = Point::generator().mul_scalar(&k).to_affine().unwrap();
610 assert_eq!(
611 hex(p.x.to_bytes().as_ref()),
612 "004e54b334cb2a1e40cc9712808f78e4adf7e1cd31acb0bc0d969efdfa82de8f\
613 bada7ca6c3e22ba5d47b5dc024e93ffd8c2cb3f1f88d3224050914a8ad9dcd593a59"
614 .replace(char::is_whitespace, "")
615 );
616 assert_eq!(
617 hex(p.y.to_bytes().as_ref()),
618 "010b8759ce9c47342e92da648fd25aeaadd28c3f6cfad8c5fa1beec990ca9e7f\
619 bf0939bf66c1b8d9918db4795980329872afcf99e0f774f84b144bfa60e5587d7abd"
620 .replace(char::is_whitespace, "")
621 );
622 }
623
624 #[test]
625 fn every_multiple_stays_on_the_curve() {
626 let g = Point::generator();
627 for k in [1u64, 2, 3, 17, 255, 65537, u32::MAX as u64] {
628 let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
629 assert!(bool::from(p.is_on_curve()), "[{k}]G is off the curve");
630 }
631 }
632
633 #[test]
634 fn sec1_round_trips_in_both_forms() {
635 let g = Point::generator();
636 for k in [1u64, 2, 3, 4, 5, 6] {
637 let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
638 let mut unc = [0u8; 133];
639 let mut comp = [0u8; 67];
640 assert!(p.write_uncompressed(&mut unc));
641 assert!(p.write_compressed(&mut comp));
642
643 let a = AffinePoint::from_sec1(&unc).unwrap();
644 let b = AffinePoint::from_sec1(&comp).unwrap();
645 assert_eq!(a.x, p.x);
646 assert_eq!(a.y, p.y);
647 assert_eq!(b.x, p.x);
648 assert_eq!(b.y, p.y, "compressed y for [{k}]G");
649 }
650 }
651
652 #[test]
653 fn decoding_rejects_bad_encodings() {
654 let g = Point::generator().to_affine().unwrap();
655 let mut unc = [0u8; 133];
656 assert!(g.write_uncompressed(&mut unc));
657
658 assert!(AffinePoint::from_sec1(&[0u8; 133]).is_none(), "identity");
659 assert!(AffinePoint::from_sec1(&unc[..132]).is_none(), "truncated");
660 assert!(
662 AffinePoint::from_sec1(&[0x04u8; 97]).is_none(),
663 "wrong width"
664 );
665
666 let mut bad = unc;
667 bad[132] ^= 1;
668 assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
669 }
670
671 #[test]
674 fn sign_and_verify_round_trip() {
675 let mut key = [0u8; 66];
676 key[65] = 7;
677 let mut public = [0u8; 133];
678 EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
679
680 for message in [&b""[..], b"a", b"the quick brown fox", &[0x5au8; 1000][..]] {
681 let mut signature = [0u8; 132];
682 EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
683 EcdsaP521Sha512::verify(&public, message, &signature).unwrap();
684 }
685 }
686
687 #[test]
691 fn normalizing_s_is_idempotent() {
692 let mut key = [0u8; 66];
693 key[65] = 7;
694 let mut public = [0u8; 133];
695 EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
696
697 for message in [&b"a"[..], b"b", b"c", b"d"] {
698 let mut signature = [0u8; 132];
699 EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
700
701 let mut normalized = signature;
702 EcdsaP521Sha512::normalize_s(&mut normalized).unwrap();
703 assert!(EcdsaP521Sha512::has_low_s(&normalized).unwrap());
704 EcdsaP521Sha512::verify(&public, message, &normalized).unwrap();
705
706 let mut twice = normalized;
707 EcdsaP521Sha512::normalize_s(&mut twice).unwrap();
708 assert_eq!(twice, normalized, "normalization is idempotent");
709 }
710 }
711
712 #[test]
728 fn signatures_match_an_independent_rfc6979_implementation() {
729 let mut key = [0u8; 66];
730 key[65] = 7;
731
732 let mut public = [0u8; 133];
734 EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
735 assert_eq!(
736 hex(&public[1..67]),
737 "0056d5d1d99d5b7f6346eeb65fda0b073a0c5f22e0e8f5483228f018d2c2f711 4c5d8c308d0abfc698d8c9a6df30dce3bbc46f953f50fdc2619a01cead882816ecd4"
738 .replace(char::is_whitespace, ""),
739 "public key x"
740 );
741 assert_eq!(
742 hex(&public[67..]),
743 "003d2d1b7d9baaa2a110d1d8317a39d68478b5c582d02824f0dd71dbd98a26cb de556bd0f293cdec9e2b9523a34591ce1a5f9e76712a5ddefc7b5c6b8bc90525251b"
744 .replace(char::is_whitespace, ""),
745 "public key y"
746 );
747
748 let cases: &[(&[u8], &str, &str)] = &[
749 (
750 b"",
751 "018a0314748952a0558e30db613981ac046c21bb434d98e8825ad07d192adcfb 12f0f29c86fee2f59368c77d101e208f289b5b8d563fd0dcb126450a4cf64f33af21",
752 "00c17a5af4890ee28950f4477900ad734ea90aa9985cc98c4e5a9242b1aece0a 19f05ecdfd30e67dab5c0539239913aa82fd19a3d9e250bd6e46f2b30e43d1e47d61",
753 ),
754 (
755 b"a",
756 "01e49d6aaa49524d7d9d0a9724bc96ab5271edff11ccbcb56ad4c7353b5d5e35 d66d7fc592c3039f020cf61388a67a73a9d1dada4fa286357f8fd2f80726383967ca",
757 "0185747858829becbeb6d1ae2a1138a56661658ec1c866d9400ca134e1572254 8ee41e7e0b7852d68c91e5650be30a4da44f72125c6eb2bf382251304ea74109bdf0",
758 ),
759 (
760 b"the quick brown fox",
761 "01e8f7a260a7462706d1a3eeb21b244aad1894084cb39d05f5ecb667086d1087 c9d3d66666aa86b411e81318bf2741120acf0f89ba9494277663dda70ab13e6c645c",
762 "0080157cf57486201170f705525fa22c05fcd8e1bd0dd382935f20a4123c2b59 0f80e15854f33b18a770f1d746218ecff89832af5b62f3bc61e72a013051a2a5d47c",
763 ),
764 ];
765
766 for (message, want_r, want_s) in cases {
767 let mut signature = [0u8; 132];
768 EcdsaP521Sha512::sign(&key, message, &mut signature).unwrap();
769 assert_eq!(
770 hex(&signature[..66]),
771 want_r.replace(char::is_whitespace, ""),
772 "r for {message:?}"
773 );
774 assert_eq!(
775 hex(&signature[66..]),
776 want_s.replace(char::is_whitespace, ""),
777 "s for {message:?}"
778 );
779 EcdsaP521Sha512::verify(&public, message, &signature).unwrap();
780 }
781 }
782
783 #[test]
785 fn signing_is_deterministic() {
786 let mut key = [0u8; 66];
787 key[65] = 9;
788 let mut a = [0u8; 132];
789 let mut b = [0u8; 132];
790 EcdsaP521Sha512::sign(&key, b"determinism", &mut a).unwrap();
791 EcdsaP521Sha512::sign(&key, b"determinism", &mut b).unwrap();
792 assert_eq!(a, b);
793 }
794
795 #[test]
796 fn verification_rejects_tampering() {
797 let mut key = [0u8; 66];
798 key[65] = 11;
799 let mut public = [0u8; 133];
800 EcdsaP521Sha512::public_key(&key, &mut public).unwrap();
801 let mut signature = [0u8; 132];
802 EcdsaP521Sha512::sign(&key, b"message", &mut signature).unwrap();
803
804 assert!(EcdsaP521Sha512::verify(&public, b"messagf", &signature).is_err());
805 for bit in [0usize, 7, 260, 527, 1055] {
806 let mut bad = signature;
807 bad[bit / 8] ^= 1 << (bit % 8);
808 assert!(
809 EcdsaP521Sha512::verify(&public, b"message", &bad).is_err(),
810 "flipped signature bit {bit}"
811 );
812 }
813 assert!(EcdsaP521Sha512::verify(&public, b"message", &signature[..131]).is_err());
814 }
815
816 #[test]
818 fn keys_from_another_curve_are_refused() {
819 let mut signature = [0u8; 132];
820 assert!(EcdsaP521Sha512::sign(&[7u8; 48], b"x", &mut signature).is_err());
821 assert!(EcdsaP521Sha512::verify(&[4u8; 97], b"x", &signature).is_err());
822 }
823
824 #[test]
827 fn ecdh_agrees_in_both_directions() {
828 let mut alice = [0u8; 66];
829 alice[65] = 3;
830 let mut bob = [0u8; 66];
831 bob[65] = 5;
832
833 let mut alice_public = [0u8; 133];
834 let mut bob_public = [0u8; 133];
835 EcdhP521::public_key(&alice, &mut alice_public).unwrap();
836 EcdhP521::public_key(&bob, &mut bob_public).unwrap();
837
838 let mut a = [0u8; 66];
839 let mut b = [0u8; 66];
840 EcdhP521::agree(&alice, &bob_public, &mut a).unwrap();
841 EcdhP521::agree(&bob, &alice_public, &mut b).unwrap();
842 assert_eq!(a, b, "both sides derive the same secret");
843
844 let ab = Point::generator()
846 .mul_scalar(&scalar(15))
847 .to_affine()
848 .unwrap();
849 assert_eq!(a, ab.x.to_bytes());
850 }
851
852 #[test]
853 fn ecdh_rejects_a_malformed_peer_key() {
854 let mut alice = [0u8; 66];
855 alice[65] = 3;
856 let mut out = [0u8; 66];
857 assert!(
858 EcdhP521::agree(&alice, &[0u8; 133], &mut out).is_err(),
859 "identity"
860 );
861 assert!(
862 EcdhP521::agree(&alice, &[0x04u8; 133], &mut out).is_err(),
863 "off curve"
864 );
865 assert!(
866 EcdhP521::agree(&alice, &[0x04u8; 97], &mut out).is_err(),
867 "p-384 width"
868 );
869 }
870
871 #[test]
872 fn compressed_public_keys_match_the_uncompressed_ones() {
873 let mut key = [0u8; 66];
874 key[65] = 13;
875 let mut unc = [0u8; 133];
876 let mut comp = [0u8; 67];
877 EcdsaP521Sha512::public_key(&key, &mut unc).unwrap();
878 EcdsaP521Sha512::public_key_compressed(&key, &mut comp).unwrap();
879 assert_eq!(&comp[1..], &unc[1..67], "the x-coordinates agree");
880 assert_eq!(comp[0], 0x02 | (unc[132] & 1), "the sign bit");
881 }
882}