1use super::curve::*;
27use super::ecdsa::{
28 Signature, compress_pubkey, decompress_pubkey, ecdh_internal, keygen_internal, sign_random_internal,
29 sign_rfc6979_internal, verify_internal,
30};
31use crate::Hasher;
32
33pub trait CryptoRng {
47 fn fill_bytes(&mut self, dest: &mut [u8]);
49}
50
51#[derive(Clone, Debug)]
57pub struct PublicKey {
58 pub bytes: Vec<u8>,
60}
61
62#[derive(Clone)]
68pub struct SecretKey {
69 pub bytes: Vec<u8>,
71}
72
73pub trait Curve: Sized {
138 fn keygen(rng: &mut dyn CryptoRng) -> (PublicKey, SecretKey);
140
141 fn compress_pubkey(pk: &PublicKey) -> Option<Vec<u8>>;
146
147 fn decompress_pubkey(compressed: &[u8]) -> Option<PublicKey>;
154
155 fn ecdh(sk: &SecretKey, peer_pk: &PublicKey) -> Option<Vec<u8>>;
172
173 fn sign_rfc6979<H: Hasher>(sk: &SecretKey, digest: &[u8]) -> Signature;
179
180 fn sign_random(sk: &SecretKey, digest: &[u8], rng: &mut dyn CryptoRng) -> Signature;
186
187 fn verify(pk: &PublicKey, digest: &[u8], sig: &Signature) -> bool;
189
190 fn sign_rfc6979_msg<H: Hasher>(sk: &SecretKey, msg: &[u8]) -> Signature {
194 let digest = H::hash(msg);
195 Self::sign_rfc6979::<H>(sk, &digest)
196 }
197
198 fn sign_random_msg<H: Hasher>(sk: &SecretKey, msg: &[u8], rng: &mut dyn CryptoRng) -> Signature {
200 let digest = H::hash(msg);
201 Self::sign_random(sk, &digest, rng)
202 }
203
204 fn verify_msg<H: Hasher>(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool {
206 let digest = H::hash(msg);
207 Self::verify(pk, &digest, sig)
208 }
209}
210
211macro_rules! curve_dispatch {
219 ($name:ident, $params_fn:path, $limbs:expr, $doc:literal) => {
220 #[doc = $doc]
221 pub struct $name;
222
223 impl Curve for $name {
224 fn keygen(rng: &mut dyn CryptoRng) -> (PublicKey, SecretKey) {
225 keygen_internal::<$limbs>(&$params_fn(), rng)
226 }
227
228 fn ecdh(sk: &SecretKey, peer_pk: &PublicKey) -> Option<Vec<u8>> {
229 ecdh_internal::<$limbs>(&$params_fn(), sk, peer_pk)
230 }
231
232 fn compress_pubkey(pk: &PublicKey) -> Option<Vec<u8>> {
233 compress_pubkey::<$limbs>(&$params_fn(), pk)
234 }
235
236 fn decompress_pubkey(compressed: &[u8]) -> Option<PublicKey> {
237 decompress_pubkey::<$limbs>(&$params_fn(), compressed)
238 }
239
240 fn sign_rfc6979<H: Hasher>(sk: &SecretKey, digest: &[u8]) -> Signature {
241 sign_rfc6979_internal::<H, $limbs>(&$params_fn(), sk, digest)
242 }
243
244 fn sign_random(sk: &SecretKey, digest: &[u8], rng: &mut dyn CryptoRng) -> Signature {
245 sign_random_internal::<$limbs>(&$params_fn(), sk, digest, rng)
246 }
247
248 fn verify(pk: &PublicKey, digest: &[u8], sig: &Signature) -> bool {
249 verify_internal::<$limbs>(&$params_fn(), pk, digest, sig)
250 }
251 }
252 };
253}
254
255curve_dispatch!(P256, p256_params, 4, "NIST P-256 (secp256r1).");
256curve_dispatch!(P384, p384_params, 6, "NIST P-384 (secp384r1).");
257curve_dispatch!(Secp256k1, secp256k1_params, 4, "secp256k1 (SECG / Bitcoin / Ethereum).");
258curve_dispatch!(
259 BrainpoolP256r1,
260 brainpoolp256r1_params,
261 4,
262 "brainpoolP256r1 (BSI / RFC 5639)."
263);
264curve_dispatch!(
265 BrainpoolP384r1,
266 brainpoolp384r1_params,
267 6,
268 "brainpoolP384r1 (BSI / RFC 5639)."
269);
270curve_dispatch!(
271 BrainpoolP512r1,
272 brainpoolp512r1_params,
273 8,
274 "brainpoolP512r1 (BSI / RFC 5639)."
275);
276curve_dispatch!(
277 P521,
278 secp521r1_params,
279 9,
280 "NIST P-521 (secp521r1). Uses LIMBS=9; qlen=521 is not a multiple \
281 of 8, so all RFC 6979 byte-length arithmetic uses rlen_bytes=66 \
282 (not LIMBS*8=72). The canonical hash pairing is SHA-512."
283);
284
285#[cfg(test)]
290mod tests {
291 use super::super::ecdsa::fe_to_felem_bytes;
292 use super::super::field::FieldElement;
293 use super::*;
294 use crate::hash::sha256::Sha256;
295 use crate::hash::sha384::Sha384;
296 use crate::hash::sha512::Sha512;
297
298 fn hex_to_bytes(hex: &str) -> Vec<u8> {
299 (0..hex.len())
300 .step_by(2)
301 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
302 .collect()
303 }
304
305 fn d1_keypair<const LIMBS: usize>(params: &CurveParams<LIMBS>) -> (SecretKey, PublicKey) {
311 let felem = params.felem_bytes;
312 let mut sk_bytes = vec![0u8; felem];
313 sk_bytes[felem - 1] = 1; let mut pk_bytes = Vec::with_capacity(1 + 2 * felem);
316 pk_bytes.push(0x04);
317 pk_bytes.extend_from_slice(&fe_to_felem_bytes(¶ms.gx, felem));
318 pk_bytes.extend_from_slice(&fe_to_felem_bytes(¶ms.gy, felem));
319
320 (SecretKey { bytes: sk_bytes }, PublicKey { bytes: pk_bytes })
321 }
322
323 struct TestRng {
327 state: u64,
328 }
329
330 impl TestRng {
331 fn new(seed: u64) -> Self {
332 Self {
334 state: if seed == 0 { 0xdeadbeefcafef00d } else { seed },
335 }
336 }
337 }
338
339 impl CryptoRng for TestRng {
340 fn fill_bytes(&mut self, dest: &mut [u8]) {
341 for chunk in dest.chunks_mut(8) {
342 let mut x = self.state;
343 x ^= x << 13;
344 x ^= x >> 7;
345 x ^= x << 17;
346 self.state = x;
347 for (i, b) in chunk.iter_mut().enumerate() {
348 *b = (x >> (8 * i)) as u8;
349 }
350 }
351 }
352 }
353
354 #[test]
359 fn test_ecdsa_p256_sign_verify_rfc6979() {
360 let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
361 let sk = SecretKey {
362 bytes: sk_bytes.clone(),
363 };
364
365 let params = p256_params();
367 let g = JacobianPoint::from_affine(params.gx, params.gy);
368 let d = FieldElement::<4>::from_bytes_be(&sk_bytes);
369 let q = scalar_mul_point(&d, &g, ¶ms);
370 let (qx, qy) = q.to_affine(¶ms.p).unwrap();
371 let mut pk_bytes = vec![0x04];
372 pk_bytes.extend_from_slice(&qx.to_bytes_be());
373 pk_bytes.extend_from_slice(&qy.to_bytes_be());
374 let pk = PublicKey { bytes: pk_bytes };
375
376 let msg = b"sample";
377 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
378
379 let expected_r = hex_to_bytes("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716");
380 let expected_s = hex_to_bytes("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
381
382 assert_eq!(sig.r, expected_r, "Signature r mismatch");
383 assert_eq!(sig.s, expected_s, "Signature s mismatch");
384
385 assert!(
387 P256::verify_msg::<Sha256>(&pk, msg, &sig),
388 "Signature verification failed"
389 );
390 }
391
392 #[test]
393 fn test_ecdsa_p256_verify_rejects_bad_sig() {
394 let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
395 let sk = SecretKey {
396 bytes: sk_bytes.clone(),
397 };
398
399 let params = p256_params();
400 let g = JacobianPoint::from_affine(params.gx, params.gy);
401 let d = FieldElement::<4>::from_bytes_be(&sk_bytes);
402 let q = scalar_mul_point(&d, &g, ¶ms);
403 let (qx, qy) = q.to_affine(¶ms.p).unwrap();
404 let mut pk_bytes = vec![0x04];
405 pk_bytes.extend_from_slice(&qx.to_bytes_be());
406 pk_bytes.extend_from_slice(&qy.to_bytes_be());
407 let pk = PublicKey { bytes: pk_bytes };
408
409 let msg = b"sample";
410 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
411
412 assert!(!P256::verify(&pk, b"tampered", &sig), "Should reject tampered message");
414
415 let mut bad_sig = sig.clone();
417 bad_sig.r[0] ^= 0x01;
418 assert!(!P256::verify(&pk, msg, &bad_sig), "Should reject modified signature");
419 }
420
421 #[test]
422 fn test_ecdsa_p256_sign_verify_roundtrip() {
423 let sk_bytes = hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001");
424 let sk = SecretKey {
425 bytes: sk_bytes.clone(),
426 };
427
428 let params = p256_params();
430 let mut pk_bytes = vec![0x04];
431 pk_bytes.extend_from_slice(¶ms.gx.to_bytes_be());
432 pk_bytes.extend_from_slice(¶ms.gy.to_bytes_be());
433 let pk = PublicKey { bytes: pk_bytes };
434
435 let msg = b"test message for ECDSA P-256";
436 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
437 assert!(
438 P256::verify_msg::<Sha256>(&pk, msg, &sig),
439 "Roundtrip verification failed"
440 );
441 }
442
443 #[test]
452 fn test_ecdsa_secp256k1_sign_verify_roundtrip() {
453 let (sk, pk) = d1_keypair(&secp256k1_params());
454 let msg = b"hello secp256k1";
455 let sig = Secp256k1::sign_rfc6979_msg::<Sha256>(&sk, msg);
456 assert!(Secp256k1::verify_msg::<Sha256>(&pk, msg, &sig));
457 assert!(!Secp256k1::verify(&pk, b"tampered", &sig));
458 let mut bad = sig.clone();
459 bad.s[0] ^= 0x01;
460 assert!(!Secp256k1::verify(&pk, msg, &bad));
461 }
462
463 #[test]
464 fn test_ecdsa_brainpoolp256r1_sign_verify_roundtrip() {
465 let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
466 let msg = b"hello brainpoolP256r1";
467 let sig = BrainpoolP256r1::sign_rfc6979_msg::<Sha256>(&sk, msg);
468 assert!(BrainpoolP256r1::verify_msg::<Sha256>(&pk, msg, &sig));
469 }
470
471 #[test]
472 fn test_ecdsa_brainpoolp384r1_sign_verify_roundtrip() {
473 let (sk, pk) = d1_keypair(&brainpoolp384r1_params());
474 let msg = b"hello brainpoolP384r1";
475 let sig = BrainpoolP384r1::sign_rfc6979_msg::<Sha384>(&sk, msg);
476 assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
477 }
478
479 #[test]
480 fn test_ecdsa_brainpoolp512r1_sign_verify_roundtrip() {
481 let (sk, pk) = d1_keypair(&brainpoolp512r1_params());
482 let msg = b"hello brainpoolP512r1";
483 let sig = BrainpoolP512r1::sign_rfc6979_msg::<Sha512>(&sk, msg);
484 assert!(BrainpoolP512r1::verify_msg::<Sha512>(&pk, msg, &sig));
485 }
486
487 #[test]
492 fn test_ecdsa_p256_sign_random_roundtrip() {
493 let (sk, pk) = d1_keypair(&p256_params());
494 let mut rng = TestRng::new(1);
495 let msg = b"random nonce P-256";
496 let sig = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
497 assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig));
498 }
499
500 #[test]
501 fn test_ecdsa_p384_sign_random_roundtrip() {
502 let (sk, pk) = d1_keypair(&p384_params());
503 let mut rng = TestRng::new(11);
504 let msg = b"random nonce P-384";
505 let sig = P384::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
506 assert!(P384::verify_msg::<Sha384>(&pk, msg, &sig));
507 }
508
509 #[test]
512 fn test_p384_random_seed_sweep() {
513 let (sk, pk) = d1_keypair(&p384_params());
514 let msg = b"sweep";
515 for seed in 1u64..=20 {
516 let mut rng = TestRng::new(seed);
517 let sig = P384::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
518 assert!(
519 P384::verify_msg::<Sha384>(&pk, msg, &sig),
520 "verify failed for P-384 seed {}",
521 seed,
522 );
523 }
524 }
525
526 #[test]
527 fn test_ecdsa_p384_sign_rfc6979_roundtrip() {
528 let (sk, pk) = d1_keypair(&p384_params());
529 let msg = b"P-384 RFC 6979 baseline";
530 let sig = P384::sign_rfc6979_msg::<Sha384>(&sk, msg);
531 assert!(P384::verify_msg::<Sha384>(&pk, msg, &sig));
532 }
533
534 #[test]
535 fn test_ecdsa_secp256k1_sign_random_roundtrip() {
536 let (sk, pk) = d1_keypair(&secp256k1_params());
537 let mut rng = TestRng::new(3);
538 let msg = b"random nonce secp256k1";
539 let sig = Secp256k1::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
540 assert!(Secp256k1::verify_msg::<Sha256>(&pk, msg, &sig));
541 }
542
543 #[test]
544 fn test_ecdsa_brainpoolp256r1_sign_random_roundtrip() {
545 let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
546 let mut rng = TestRng::new(4);
547 let msg = b"random nonce brainpoolP256r1";
548 let sig = BrainpoolP256r1::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
549 assert!(BrainpoolP256r1::verify_msg::<Sha256>(&pk, msg, &sig));
550 }
551
552 #[test]
553 fn test_ecdsa_brainpoolp384r1_sign_random_roundtrip() {
554 let (sk, pk) = d1_keypair(&brainpoolp384r1_params());
555 let mut rng = TestRng::new(5);
556 let msg = b"random nonce brainpoolP384r1";
557 let sig = BrainpoolP384r1::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
558 assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
559 }
560
561 #[test]
562 fn test_ecdsa_brainpoolp512r1_sign_random_roundtrip() {
563 let (sk, pk) = d1_keypair(&brainpoolp512r1_params());
564 let mut rng = TestRng::new(6);
565 let msg = b"random nonce brainpoolP512r1";
566 let sig = BrainpoolP512r1::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
567 assert!(BrainpoolP512r1::verify_msg::<Sha512>(&pk, msg, &sig));
568 }
569
570 #[test]
574 fn test_sign_random_is_nondeterministic() {
575 let (sk, pk) = d1_keypair(&p256_params());
576 let msg = b"same message, different rng";
577
578 let mut rng1 = TestRng::new(0xAA11);
579 let mut rng2 = TestRng::new(0xBB22);
580 let sig1 = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng1);
581 let sig2 = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng2);
582
583 assert_ne!(sig1.r, sig2.r, "random sigs should differ on r");
584 assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig1));
585 assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig2));
586 }
587
588 #[test]
591 fn test_sign_rfc6979_is_deterministic() {
592 let (sk, _pk) = d1_keypair(&p256_params());
593 let msg = b"same message, no rng";
594
595 let sig1 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
596 let sig2 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
597
598 assert_eq!(sig1.r, sig2.r);
599 assert_eq!(sig1.s, sig2.s);
600 }
601
602 #[test]
606 fn test_digest_vs_msg_forms_agree() {
607 let (sk, _pk) = d1_keypair(&p256_params());
608 let msg = b"agreement";
609
610 let from_msg = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
611 let digest = Sha256::hash(msg);
612 let from_digest = P256::sign_rfc6979::<Sha256>(&sk, &digest);
613
614 assert_eq!(from_msg.r, from_digest.r);
615 assert_eq!(from_msg.s, from_digest.s);
616 }
617
618 fn fresh_p256_signed() -> (SecretKey, PublicKey, Vec<u8>, Signature) {
623 let (sk, pk) = d1_keypair(&p256_params());
624 let msg = b"verify hardening sample";
625 let digest = Sha256::hash(msg);
626 let sig = P256::sign_rfc6979::<Sha256>(&sk, &digest);
627 assert!(P256::verify(&pk, &digest, &sig));
628 (sk, pk, digest, sig)
629 }
630
631 #[test]
632 fn test_verify_rejects_wrong_length_pubkey() {
633 let (_sk, mut pk, digest, sig) = fresh_p256_signed();
634 pk.bytes.pop();
635 assert!(!P256::verify(&pk, &digest, &sig));
636 }
637
638 #[test]
639 fn test_verify_rejects_wrong_tag_pubkey() {
640 let (_sk, mut pk, digest, sig) = fresh_p256_signed();
641 pk.bytes[0] = 0x02;
642 assert!(!P256::verify(&pk, &digest, &sig));
643 }
644
645 #[test]
646 fn test_verify_rejects_off_curve_pubkey() {
647 let (_sk, mut pk, digest, sig) = fresh_p256_signed();
648 let last = pk.bytes.len() - 1;
649 pk.bytes[last] ^= 0x01;
650 assert!(!P256::verify(&pk, &digest, &sig), "off-curve pubkey must be rejected");
651 }
652
653 #[test]
654 fn test_verify_rejects_infinity_pubkey() {
655 let (_sk, _real_pk, digest, sig) = fresh_p256_signed();
656 let mut bytes = vec![0u8; 65];
657 bytes[0] = 0x04;
658 let bad_pk = PublicKey { bytes };
659 assert!(!P256::verify(&bad_pk, &digest, &sig));
660 }
661
662 #[test]
663 fn test_verify_rejects_off_curve_pubkey_brainpoolp384r1() {
664 let (sk, mut pk) = d1_keypair(&brainpoolp384r1_params());
665 let msg = b"bp384 hardening";
666 let sig = BrainpoolP384r1::sign_rfc6979_msg::<Sha384>(&sk, msg);
667 assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
668 let last = pk.bytes.len() - 1;
669 pk.bytes[last] ^= 0x01;
670 assert!(!BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
671 }
672
673 #[test]
678 fn test_p256_sha512_pairing_roundtrip() {
679 let (sk, pk) = d1_keypair(&p256_params());
680 let msg = b"P-256 paired with SHA-512";
681
682 let sig_det = P256::sign_rfc6979_msg::<Sha512>(&sk, msg);
683 assert!(P256::verify_msg::<Sha512>(&pk, msg, &sig_det));
684
685 let mut rng = TestRng::new(0xC001);
686 let sig_rand = P256::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
687 assert!(P256::verify_msg::<Sha512>(&pk, msg, &sig_rand));
688
689 let sig_det2 = P256::sign_rfc6979_msg::<Sha512>(&sk, msg);
690 assert_eq!(sig_det.r, sig_det2.r);
691 assert_eq!(sig_det.s, sig_det2.s);
692
693 let sig_sha256 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
694 assert_ne!(
695 sig_det.r, sig_sha256.r,
696 "P-256+SHA-512 and P-256+SHA-256 must differ on r"
697 );
698 }
699
700 #[test]
701 fn test_brainpoolp256r1_sha512_pairing_roundtrip() {
702 let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
703 let msg = b"brainpoolP256r1 + SHA-512";
704 let sig = BrainpoolP256r1::sign_rfc6979_msg::<Sha512>(&sk, msg);
705 assert!(BrainpoolP256r1::verify_msg::<Sha512>(&pk, msg, &sig));
706 }
707
708 #[test]
709 fn test_secp256k1_sha512_pairing_roundtrip() {
710 let (sk, pk) = d1_keypair(&secp256k1_params());
711 let msg = b"secp256k1 + SHA-512";
712 let sig = Secp256k1::sign_rfc6979_msg::<Sha512>(&sk, msg);
713 assert!(Secp256k1::verify_msg::<Sha512>(&pk, msg, &sig));
714 }
715
716 #[test]
721 fn test_p521_sign_rfc6979_roundtrip() {
722 let (sk, pk) = d1_keypair(&secp521r1_params());
723 assert_eq!(sk.bytes.len(), 66);
724 assert_eq!(pk.bytes.len(), 1 + 2 * 66);
725
726 let msg = b"P-521 RFC 6979 baseline";
727 let sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
728 assert_eq!(sig.r.len(), 66);
729 assert_eq!(sig.s.len(), 66);
730 assert!(P521::verify_msg::<Sha512>(&pk, msg, &sig));
731 }
732
733 #[test]
734 fn test_p521_sign_random_roundtrip() {
735 let (sk, pk) = d1_keypair(&secp521r1_params());
736 let mut rng = TestRng::new(0x521);
737 let msg = b"P-521 random nonce";
738 let sig = P521::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
739 assert!(P521::verify_msg::<Sha512>(&pk, msg, &sig));
740 }
741
742 #[test]
743 fn test_p521_rfc6979_is_deterministic() {
744 let (sk, _pk) = d1_keypair(&secp521r1_params());
745 let msg = b"determinism";
746 let sig1 = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
747 let sig2 = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
748 assert_eq!(sig1.r, sig2.r);
749 assert_eq!(sig1.s, sig2.s);
750 }
751
752 #[test]
753 fn test_p521_sha512_der_roundtrip() {
754 let (sk, pk) = d1_keypair(&secp521r1_params());
755 let msg = b"P-521 DER";
756 let sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
757 let der = sig.to_der();
758 let parsed = Signature::from_der(&der).expect("from_der");
759 assert!(P521::verify_msg::<Sha512>(&pk, msg, &parsed));
760 }
761
762 #[test]
763 fn test_p521_sec1_compressed_roundtrip() {
764 let (_sk, pk) = d1_keypair(&secp521r1_params());
765 assert_eq!(pk.bytes.len(), 133);
766
767 let compressed = P521::compress_pubkey(&pk).expect("compress");
768 assert_eq!(compressed.len(), 67);
769 assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
770
771 let decompressed = P521::decompress_pubkey(&compressed).expect("decompress");
772 assert_eq!(decompressed.bytes.len(), 133);
773 assert_eq!(decompressed.bytes, pk.bytes);
774 }
775
776 #[test]
777 fn test_p521_ecdh_roundtrip() {
778 let mut rng = TestRng::new(0x5EC5);
779 let (pk_a, sk_a) = P521::keygen(&mut rng);
780 let (pk_b, sk_b) = P521::keygen(&mut rng);
781 assert_eq!(pk_a.bytes.len(), 133);
782 assert_eq!(sk_a.bytes.len(), 66);
783 let s_ab = P521::ecdh(&sk_a, &pk_b).expect("alice ecdh");
784 let s_ba = P521::ecdh(&sk_b, &pk_a).expect("bob ecdh");
785 assert_eq!(s_ab, s_ba);
786 assert_eq!(s_ab.len(), 66);
787 }
788
789 #[test]
793 fn test_p521_sec1_pinned_interop() {
794 let compressed_g = hex_to_bytes(
795 "0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
796 );
797 assert_eq!(compressed_g.len(), 67);
798
799 let uncompressed_g = hex_to_bytes(
800 "04\
801 00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66\
802 011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
803 );
804 assert_eq!(uncompressed_g.len(), 133);
805
806 let decompressed = P521::decompress_pubkey(&compressed_g).expect("decompress");
807 assert_eq!(decompressed.bytes, uncompressed_g);
808
809 let pk = PublicKey {
810 bytes: uncompressed_g.clone(),
811 };
812 let recompressed = P521::compress_pubkey(&pk).expect("compress");
813 assert_eq!(recompressed, compressed_g);
814
815 let dummy_digest = [0u8; 64];
816 let bogus_sig = Signature {
817 r: vec![0x01],
818 s: vec![0x01],
819 };
820 let _ = P521::verify(&pk, &dummy_digest, &bogus_sig);
821 }
822
823 #[test]
824 fn test_p521_verify_rejects_tampered() {
825 let (sk, pk) = d1_keypair(&secp521r1_params());
826 let msg = b"tamper";
827 let mut sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
828 sig.r[0] ^= 0x01;
829 assert!(!P521::verify_msg::<Sha512>(&pk, msg, &sig));
830 }
831
832 #[test]
837 fn test_sec1_compressed_roundtrip_p256() {
838 let (_sk, pk) = d1_keypair(&p256_params());
839 assert_eq!(pk.bytes.len(), 65);
840 assert_eq!(pk.bytes[0], 0x04);
841
842 let compressed = P256::compress_pubkey(&pk).expect("compress");
843 assert_eq!(compressed.len(), 33);
844 assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
845
846 let decompressed = P256::decompress_pubkey(&compressed).expect("decompress");
847 assert_eq!(decompressed.bytes, pk.bytes);
848 }
849
850 #[test]
851 fn test_sec1_compressed_roundtrip_all_curves() {
852 fn rt<C: Curve>() -> Option<()> {
853 let mut rng = TestRng::new(0x5EC1);
854 let (pk, _sk) = C::keygen(&mut rng);
855 let compressed = C::compress_pubkey(&pk)?;
856 let decompressed = C::decompress_pubkey(&compressed)?;
857 assert_eq!(decompressed.bytes, pk.bytes);
858 Some(())
859 }
860 assert!(rt::<P256>().is_some());
861 assert!(rt::<P384>().is_some());
862 assert!(rt::<Secp256k1>().is_some());
863 assert!(rt::<BrainpoolP256r1>().is_some());
864 assert!(rt::<BrainpoolP384r1>().is_some());
865 assert!(rt::<BrainpoolP512r1>().is_some());
866 assert!(rt::<P521>().is_some());
867 }
868
869 #[test]
870 fn test_verify_accepts_compressed_pubkey() {
871 let (sk, pk) = d1_keypair(&p256_params());
872 let msg = b"compressed pk end-to-end";
873 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
874
875 let compressed = P256::compress_pubkey(&pk).unwrap();
876 let compressed_pk = PublicKey { bytes: compressed };
877 assert!(P256::verify_msg::<Sha256>(&compressed_pk, msg, &sig));
878 }
879
880 #[test]
881 fn test_ecdh_accepts_compressed_pubkey() {
882 let mut rng = TestRng::new(0x5EC2);
883 let (pk_a, sk_a) = P256::keygen(&mut rng);
884 let (pk_b, sk_b) = P256::keygen(&mut rng);
885
886 let pk_a_c = PublicKey {
887 bytes: P256::compress_pubkey(&pk_a).unwrap(),
888 };
889 let pk_b_c = PublicKey {
890 bytes: P256::compress_pubkey(&pk_b).unwrap(),
891 };
892
893 let s_ab = P256::ecdh(&sk_a, &pk_b_c).unwrap();
894 let s_ba = P256::ecdh(&sk_b, &pk_a_c).unwrap();
895 assert_eq!(s_ab, s_ba);
896 }
897
898 #[test]
899 fn test_sec1_compressed_parity_bit() {
900 let mut rng = TestRng::new(0xBEEF);
901 for _ in 0..20 {
902 let (pk, _sk) = P256::keygen(&mut rng);
903 let compressed = P256::compress_pubkey(&pk).unwrap();
904 let y_lsb = pk.bytes[64] & 1;
905 let tag = compressed[0];
906 assert!(tag == 0x02 || tag == 0x03);
907 assert_eq!((tag & 1), y_lsb, "compressed tag parity must match Y LSB");
908 }
909 }
910
911 #[test]
912 fn test_decompress_rejects_wrong_length() {
913 assert!(P256::decompress_pubkey(&[0x02; 32]).is_none());
914 assert!(P256::decompress_pubkey(&[0x02; 34]).is_none());
915 assert!(P256::decompress_pubkey(&[]).is_none());
916 }
917
918 #[test]
919 fn test_decompress_rejects_unknown_tag() {
920 let mut bytes = vec![0u8; 33];
921 bytes[0] = 0x05;
922 assert!(P256::decompress_pubkey(&bytes).is_none());
923 }
924
925 #[test]
932 fn test_der_sig_roundtrip_verifies() {
933 let (sk, pk) = d1_keypair(&p256_params());
934 let msg = b"DER round-trip";
935 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
936
937 let der = sig.to_der();
938 let parsed = Signature::from_der(&der).expect("from_der");
939 assert!(P256::verify_msg::<Sha256>(&pk, msg, &parsed));
940 }
941
942 #[test]
945 fn test_der_idempotent_on_der_side() {
946 let (sk, _pk) = d1_keypair(&p256_params());
947 let msg = b"idempotent";
948 let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
949
950 let der1 = sig.to_der();
951 let parsed = Signature::from_der(&der1).unwrap();
952 let der2 = parsed.to_der();
953 assert_eq!(der1, der2);
954 }
955}