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