use super::curve::*;
use super::ecdsa::{
Signature, compress_pubkey_internal, decompress_pubkey_internal, ecdh_internal, keygen_internal,
sign_random_internal, sign_rfc6979_internal, verify_internal,
};
use crate::Hasher;
pub trait CryptoRng {
fn fill_bytes(&mut self, dest: &mut [u8]);
}
#[derive(Clone, Debug)]
pub struct PublicKey {
pub bytes: Vec<u8>,
}
#[derive(Clone)]
pub struct SecretKey {
pub bytes: Vec<u8>,
}
pub trait Curve: Sized {
fn keygen(rng: &mut dyn CryptoRng) -> (PublicKey, SecretKey);
fn compress_pubkey(pk: &PublicKey) -> Option<Vec<u8>>;
fn decompress_pubkey(compressed: &[u8]) -> Option<PublicKey>;
fn ecdh(sk: &SecretKey, peer_pk: &PublicKey) -> Option<Vec<u8>>;
fn sign_rfc6979<H: Hasher>(sk: &SecretKey, digest: &[u8]) -> Signature;
fn sign_random(sk: &SecretKey, digest: &[u8], rng: &mut dyn CryptoRng) -> Signature;
fn verify(pk: &PublicKey, digest: &[u8], sig: &Signature) -> bool;
fn sign_rfc6979_msg<H: Hasher>(sk: &SecretKey, msg: &[u8]) -> Signature {
let digest = H::hash(msg);
Self::sign_rfc6979::<H>(sk, &digest)
}
fn sign_random_msg<H: Hasher>(sk: &SecretKey, msg: &[u8], rng: &mut dyn CryptoRng) -> Signature {
let digest = H::hash(msg);
Self::sign_random(sk, &digest, rng)
}
fn verify_msg<H: Hasher>(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool {
let digest = H::hash(msg);
Self::verify(pk, &digest, sig)
}
}
macro_rules! curve_dispatch {
($name:ident, $params_fn:path, $limbs:expr, $doc:literal) => {
#[doc = $doc]
pub struct $name;
impl Curve for $name {
fn keygen(rng: &mut dyn CryptoRng) -> (PublicKey, SecretKey) {
keygen_internal::<$limbs>(&$params_fn(), rng)
}
fn ecdh(sk: &SecretKey, peer_pk: &PublicKey) -> Option<Vec<u8>> {
ecdh_internal::<$limbs>(&$params_fn(), sk, peer_pk)
}
fn compress_pubkey(pk: &PublicKey) -> Option<Vec<u8>> {
compress_pubkey_internal::<$limbs>(&$params_fn(), pk)
}
fn decompress_pubkey(compressed: &[u8]) -> Option<PublicKey> {
decompress_pubkey_internal::<$limbs>(&$params_fn(), compressed)
}
fn sign_rfc6979<H: Hasher>(sk: &SecretKey, digest: &[u8]) -> Signature {
sign_rfc6979_internal::<H, $limbs>(&$params_fn(), sk, digest)
}
fn sign_random(sk: &SecretKey, digest: &[u8], rng: &mut dyn CryptoRng) -> Signature {
sign_random_internal::<$limbs>(&$params_fn(), sk, digest, rng)
}
fn verify(pk: &PublicKey, digest: &[u8], sig: &Signature) -> bool {
verify_internal::<$limbs>(&$params_fn(), pk, digest, sig)
}
}
};
}
curve_dispatch!(P256, p256_params, 4, "NIST P-256 (secp256r1).");
curve_dispatch!(P384, p384_params, 6, "NIST P-384 (secp384r1).");
curve_dispatch!(Secp256k1, secp256k1_params, 4, "secp256k1 (SECG / Bitcoin / Ethereum).");
curve_dispatch!(
BrainpoolP256r1,
brainpoolp256r1_params,
4,
"brainpoolP256r1 (BSI / RFC 5639)."
);
curve_dispatch!(
BrainpoolP384r1,
brainpoolp384r1_params,
6,
"brainpoolP384r1 (BSI / RFC 5639)."
);
curve_dispatch!(
BrainpoolP512r1,
brainpoolp512r1_params,
8,
"brainpoolP512r1 (BSI / RFC 5639)."
);
curve_dispatch!(
P521,
secp521r1_params,
9,
"NIST P-521 (secp521r1). Uses LIMBS=9; qlen=521 is not a multiple \
of 8, so all RFC 6979 byte-length arithmetic uses rlen_bytes=66 \
(not LIMBS*8=72). The canonical hash pairing is SHA-512."
);
#[cfg(test)]
mod tests {
use super::super::ecdsa::fe_to_felem_bytes;
use super::super::field::FieldElement;
use super::*;
use crate::hash::sha256::Sha256;
use crate::hash::sha384::Sha384;
use crate::hash::sha512::Sha512;
fn hex_to_bytes(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
fn d1_keypair<const LIMBS: usize>(params: &CurveParams<LIMBS>) -> (SecretKey, PublicKey) {
let felem = params.felem_bytes;
let mut sk_bytes = vec![0u8; felem];
sk_bytes[felem - 1] = 1;
let mut pk_bytes = Vec::with_capacity(1 + 2 * felem);
pk_bytes.push(0x04);
pk_bytes.extend_from_slice(&fe_to_felem_bytes(¶ms.gx, felem));
pk_bytes.extend_from_slice(&fe_to_felem_bytes(¶ms.gy, felem));
(SecretKey { bytes: sk_bytes }, PublicKey { bytes: pk_bytes })
}
struct TestRng {
state: u64,
}
impl TestRng {
fn new(seed: u64) -> Self {
Self {
state: if seed == 0 { 0xdeadbeefcafef00d } else { seed },
}
}
}
impl CryptoRng for TestRng {
fn fill_bytes(&mut self, dest: &mut [u8]) {
for chunk in dest.chunks_mut(8) {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
for (i, b) in chunk.iter_mut().enumerate() {
*b = (x >> (8 * i)) as u8;
}
}
}
}
#[test]
fn test_ecdsa_p256_sign_verify_rfc6979() {
let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
let sk = SecretKey {
bytes: sk_bytes.clone(),
};
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let d = FieldElement::<4>::from_bytes_be(&sk_bytes);
let q = scalar_mul_point(&d, &g, ¶ms);
let (qx, qy) = q.to_affine(¶ms.p).unwrap();
let mut pk_bytes = vec![0x04];
pk_bytes.extend_from_slice(&qx.to_bytes_be());
pk_bytes.extend_from_slice(&qy.to_bytes_be());
let pk = PublicKey { bytes: pk_bytes };
let msg = b"sample";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let expected_r = hex_to_bytes("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716");
let expected_s = hex_to_bytes("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
assert_eq!(sig.r, expected_r, "Signature r mismatch");
assert_eq!(sig.s, expected_s, "Signature s mismatch");
assert!(
P256::verify_msg::<Sha256>(&pk, msg, &sig),
"Signature verification failed"
);
}
#[test]
fn test_ecdsa_p256_verify_rejects_bad_sig() {
let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
let sk = SecretKey {
bytes: sk_bytes.clone(),
};
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let d = FieldElement::<4>::from_bytes_be(&sk_bytes);
let q = scalar_mul_point(&d, &g, ¶ms);
let (qx, qy) = q.to_affine(¶ms.p).unwrap();
let mut pk_bytes = vec![0x04];
pk_bytes.extend_from_slice(&qx.to_bytes_be());
pk_bytes.extend_from_slice(&qy.to_bytes_be());
let pk = PublicKey { bytes: pk_bytes };
let msg = b"sample";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert!(!P256::verify(&pk, b"tampered", &sig), "Should reject tampered message");
let mut bad_sig = sig.clone();
bad_sig.r[0] ^= 0x01;
assert!(!P256::verify(&pk, msg, &bad_sig), "Should reject modified signature");
}
#[test]
fn test_ecdsa_p256_sign_verify_roundtrip() {
let sk_bytes = hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001");
let sk = SecretKey {
bytes: sk_bytes.clone(),
};
let params = p256_params();
let mut pk_bytes = vec![0x04];
pk_bytes.extend_from_slice(¶ms.gx.to_bytes_be());
pk_bytes.extend_from_slice(¶ms.gy.to_bytes_be());
let pk = PublicKey { bytes: pk_bytes };
let msg = b"test message for ECDSA P-256";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert!(
P256::verify_msg::<Sha256>(&pk, msg, &sig),
"Roundtrip verification failed"
);
}
#[test]
fn test_ecdsa_secp256k1_sign_verify_roundtrip() {
let (sk, pk) = d1_keypair(&secp256k1_params());
let msg = b"hello secp256k1";
let sig = Secp256k1::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert!(Secp256k1::verify_msg::<Sha256>(&pk, msg, &sig));
assert!(!Secp256k1::verify(&pk, b"tampered", &sig));
let mut bad = sig.clone();
bad.s[0] ^= 0x01;
assert!(!Secp256k1::verify(&pk, msg, &bad));
}
#[test]
fn test_ecdsa_brainpoolp256r1_sign_verify_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
let msg = b"hello brainpoolP256r1";
let sig = BrainpoolP256r1::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert!(BrainpoolP256r1::verify_msg::<Sha256>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_brainpoolp384r1_sign_verify_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp384r1_params());
let msg = b"hello brainpoolP384r1";
let sig = BrainpoolP384r1::sign_rfc6979_msg::<Sha384>(&sk, msg);
assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_brainpoolp512r1_sign_verify_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp512r1_params());
let msg = b"hello brainpoolP512r1";
let sig = BrainpoolP512r1::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert!(BrainpoolP512r1::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_p256_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&p256_params());
let mut rng = TestRng::new(1);
let msg = b"random nonce P-256";
let sig = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_p384_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&p384_params());
let mut rng = TestRng::new(11);
let msg = b"random nonce P-384";
let sig = P384::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
assert!(P384::verify_msg::<Sha384>(&pk, msg, &sig));
}
#[test]
fn test_p384_random_seed_sweep() {
let (sk, pk) = d1_keypair(&p384_params());
let msg = b"sweep";
for seed in 1u64..=20 {
let mut rng = TestRng::new(seed);
let sig = P384::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
assert!(
P384::verify_msg::<Sha384>(&pk, msg, &sig),
"verify failed for P-384 seed {}",
seed,
);
}
}
#[test]
fn test_ecdsa_p384_sign_rfc6979_roundtrip() {
let (sk, pk) = d1_keypair(&p384_params());
let msg = b"P-384 RFC 6979 baseline";
let sig = P384::sign_rfc6979_msg::<Sha384>(&sk, msg);
assert!(P384::verify_msg::<Sha384>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_secp256k1_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&secp256k1_params());
let mut rng = TestRng::new(3);
let msg = b"random nonce secp256k1";
let sig = Secp256k1::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
assert!(Secp256k1::verify_msg::<Sha256>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_brainpoolp256r1_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
let mut rng = TestRng::new(4);
let msg = b"random nonce brainpoolP256r1";
let sig = BrainpoolP256r1::sign_random_msg::<Sha256>(&sk, msg, &mut rng);
assert!(BrainpoolP256r1::verify_msg::<Sha256>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_brainpoolp384r1_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp384r1_params());
let mut rng = TestRng::new(5);
let msg = b"random nonce brainpoolP384r1";
let sig = BrainpoolP384r1::sign_random_msg::<Sha384>(&sk, msg, &mut rng);
assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
}
#[test]
fn test_ecdsa_brainpoolp512r1_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp512r1_params());
let mut rng = TestRng::new(6);
let msg = b"random nonce brainpoolP512r1";
let sig = BrainpoolP512r1::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
assert!(BrainpoolP512r1::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_sign_random_is_nondeterministic() {
let (sk, pk) = d1_keypair(&p256_params());
let msg = b"same message, different rng";
let mut rng1 = TestRng::new(0xAA11);
let mut rng2 = TestRng::new(0xBB22);
let sig1 = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng1);
let sig2 = P256::sign_random_msg::<Sha256>(&sk, msg, &mut rng2);
assert_ne!(sig1.r, sig2.r, "random sigs should differ on r");
assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig1));
assert!(P256::verify_msg::<Sha256>(&pk, msg, &sig2));
}
#[test]
fn test_sign_rfc6979_is_deterministic() {
let (sk, _pk) = d1_keypair(&p256_params());
let msg = b"same message, no rng";
let sig1 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let sig2 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert_eq!(sig1.r, sig2.r);
assert_eq!(sig1.s, sig2.s);
}
#[test]
fn test_digest_vs_msg_forms_agree() {
let (sk, _pk) = d1_keypair(&p256_params());
let msg = b"agreement";
let from_msg = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let digest = Sha256::hash(msg);
let from_digest = P256::sign_rfc6979::<Sha256>(&sk, &digest);
assert_eq!(from_msg.r, from_digest.r);
assert_eq!(from_msg.s, from_digest.s);
}
fn fresh_p256_signed() -> (SecretKey, PublicKey, Vec<u8>, Signature) {
let (sk, pk) = d1_keypair(&p256_params());
let msg = b"verify hardening sample";
let digest = Sha256::hash(msg);
let sig = P256::sign_rfc6979::<Sha256>(&sk, &digest);
assert!(P256::verify(&pk, &digest, &sig));
(sk, pk, digest, sig)
}
#[test]
fn test_verify_rejects_wrong_length_pubkey() {
let (_sk, mut pk, digest, sig) = fresh_p256_signed();
pk.bytes.pop();
assert!(!P256::verify(&pk, &digest, &sig));
}
#[test]
fn test_verify_rejects_wrong_tag_pubkey() {
let (_sk, mut pk, digest, sig) = fresh_p256_signed();
pk.bytes[0] = 0x02;
assert!(!P256::verify(&pk, &digest, &sig));
}
#[test]
fn test_verify_rejects_off_curve_pubkey() {
let (_sk, mut pk, digest, sig) = fresh_p256_signed();
let last = pk.bytes.len() - 1;
pk.bytes[last] ^= 0x01;
assert!(!P256::verify(&pk, &digest, &sig), "off-curve pubkey must be rejected");
}
#[test]
fn test_verify_rejects_infinity_pubkey() {
let (_sk, _real_pk, digest, sig) = fresh_p256_signed();
let mut bytes = vec![0u8; 65];
bytes[0] = 0x04;
let bad_pk = PublicKey { bytes };
assert!(!P256::verify(&bad_pk, &digest, &sig));
}
#[test]
fn test_verify_rejects_off_curve_pubkey_brainpoolp384r1() {
let (sk, mut pk) = d1_keypair(&brainpoolp384r1_params());
let msg = b"bp384 hardening";
let sig = BrainpoolP384r1::sign_rfc6979_msg::<Sha384>(&sk, msg);
assert!(BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
let last = pk.bytes.len() - 1;
pk.bytes[last] ^= 0x01;
assert!(!BrainpoolP384r1::verify_msg::<Sha384>(&pk, msg, &sig));
}
#[test]
fn test_p256_sha512_pairing_roundtrip() {
let (sk, pk) = d1_keypair(&p256_params());
let msg = b"P-256 paired with SHA-512";
let sig_det = P256::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert!(P256::verify_msg::<Sha512>(&pk, msg, &sig_det));
let mut rng = TestRng::new(0xC001);
let sig_rand = P256::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
assert!(P256::verify_msg::<Sha512>(&pk, msg, &sig_rand));
let sig_det2 = P256::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert_eq!(sig_det.r, sig_det2.r);
assert_eq!(sig_det.s, sig_det2.s);
let sig_sha256 = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
assert_ne!(
sig_det.r, sig_sha256.r,
"P-256+SHA-512 and P-256+SHA-256 must differ on r"
);
}
#[test]
fn test_brainpoolp256r1_sha512_pairing_roundtrip() {
let (sk, pk) = d1_keypair(&brainpoolp256r1_params());
let msg = b"brainpoolP256r1 + SHA-512";
let sig = BrainpoolP256r1::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert!(BrainpoolP256r1::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_secp256k1_sha512_pairing_roundtrip() {
let (sk, pk) = d1_keypair(&secp256k1_params());
let msg = b"secp256k1 + SHA-512";
let sig = Secp256k1::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert!(Secp256k1::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_p521_sign_rfc6979_roundtrip() {
let (sk, pk) = d1_keypair(&secp521r1_params());
assert_eq!(sk.bytes.len(), 66);
assert_eq!(pk.bytes.len(), 1 + 2 * 66);
let msg = b"P-521 RFC 6979 baseline";
let sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert_eq!(sig.r.len(), 66);
assert_eq!(sig.s.len(), 66);
assert!(P521::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_p521_sign_random_roundtrip() {
let (sk, pk) = d1_keypair(&secp521r1_params());
let mut rng = TestRng::new(0x521);
let msg = b"P-521 random nonce";
let sig = P521::sign_random_msg::<Sha512>(&sk, msg, &mut rng);
assert!(P521::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_p521_rfc6979_is_deterministic() {
let (sk, _pk) = d1_keypair(&secp521r1_params());
let msg = b"determinism";
let sig1 = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
let sig2 = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
assert_eq!(sig1.r, sig2.r);
assert_eq!(sig1.s, sig2.s);
}
#[test]
fn test_p521_sha512_der_roundtrip() {
let (sk, pk) = d1_keypair(&secp521r1_params());
let msg = b"P-521 DER";
let sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
let der = sig.to_der();
let parsed = Signature::from_der(&der).expect("from_der");
assert!(P521::verify_msg::<Sha512>(&pk, msg, &parsed));
}
#[test]
fn test_p521_sec1_compressed_roundtrip() {
let (_sk, pk) = d1_keypair(&secp521r1_params());
assert_eq!(pk.bytes.len(), 133);
let compressed = P521::compress_pubkey(&pk).expect("compress");
assert_eq!(compressed.len(), 67);
assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
let decompressed = P521::decompress_pubkey(&compressed).expect("decompress");
assert_eq!(decompressed.bytes.len(), 133);
assert_eq!(decompressed.bytes, pk.bytes);
}
#[test]
fn test_p521_ecdh_roundtrip() {
let mut rng = TestRng::new(0x5EC5);
let (pk_a, sk_a) = P521::keygen(&mut rng);
let (pk_b, sk_b) = P521::keygen(&mut rng);
assert_eq!(pk_a.bytes.len(), 133);
assert_eq!(sk_a.bytes.len(), 66);
let s_ab = P521::ecdh(&sk_a, &pk_b).expect("alice ecdh");
let s_ba = P521::ecdh(&sk_b, &pk_a).expect("bob ecdh");
assert_eq!(s_ab, s_ba);
assert_eq!(s_ab.len(), 66);
}
#[test]
fn test_p521_sec1_pinned_interop() {
let compressed_g = hex_to_bytes(
"0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
);
assert_eq!(compressed_g.len(), 67);
let uncompressed_g = hex_to_bytes(
"04\
00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66\
011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
);
assert_eq!(uncompressed_g.len(), 133);
let decompressed = P521::decompress_pubkey(&compressed_g).expect("decompress");
assert_eq!(decompressed.bytes, uncompressed_g);
let pk = PublicKey {
bytes: uncompressed_g.clone(),
};
let recompressed = P521::compress_pubkey(&pk).expect("compress");
assert_eq!(recompressed, compressed_g);
let dummy_digest = [0u8; 64];
let bogus_sig = Signature {
r: vec![0x01],
s: vec![0x01],
};
let _ = P521::verify(&pk, &dummy_digest, &bogus_sig);
}
#[test]
fn test_p521_verify_rejects_tampered() {
let (sk, pk) = d1_keypair(&secp521r1_params());
let msg = b"tamper";
let mut sig = P521::sign_rfc6979_msg::<Sha512>(&sk, msg);
sig.r[0] ^= 0x01;
assert!(!P521::verify_msg::<Sha512>(&pk, msg, &sig));
}
#[test]
fn test_sec1_compressed_roundtrip_p256() {
let (_sk, pk) = d1_keypair(&p256_params());
assert_eq!(pk.bytes.len(), 65);
assert_eq!(pk.bytes[0], 0x04);
let compressed = P256::compress_pubkey(&pk).expect("compress");
assert_eq!(compressed.len(), 33);
assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
let decompressed = P256::decompress_pubkey(&compressed).expect("decompress");
assert_eq!(decompressed.bytes, pk.bytes);
}
#[test]
fn test_sec1_compressed_roundtrip_all_curves() {
fn rt<C: Curve>() -> Option<()> {
let mut rng = TestRng::new(0x5EC1);
let (pk, _sk) = C::keygen(&mut rng);
let compressed = C::compress_pubkey(&pk)?;
let decompressed = C::decompress_pubkey(&compressed)?;
assert_eq!(decompressed.bytes, pk.bytes);
Some(())
}
assert!(rt::<P256>().is_some());
assert!(rt::<P384>().is_some());
assert!(rt::<Secp256k1>().is_some());
assert!(rt::<BrainpoolP256r1>().is_some());
assert!(rt::<BrainpoolP384r1>().is_some());
assert!(rt::<BrainpoolP512r1>().is_some());
assert!(rt::<P521>().is_some());
}
#[test]
fn test_verify_accepts_compressed_pubkey() {
let (sk, pk) = d1_keypair(&p256_params());
let msg = b"compressed pk end-to-end";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let compressed = P256::compress_pubkey(&pk).unwrap();
let compressed_pk = PublicKey { bytes: compressed };
assert!(P256::verify_msg::<Sha256>(&compressed_pk, msg, &sig));
}
#[test]
fn test_ecdh_accepts_compressed_pubkey() {
let mut rng = TestRng::new(0x5EC2);
let (pk_a, sk_a) = P256::keygen(&mut rng);
let (pk_b, sk_b) = P256::keygen(&mut rng);
let pk_a_c = PublicKey {
bytes: P256::compress_pubkey(&pk_a).unwrap(),
};
let pk_b_c = PublicKey {
bytes: P256::compress_pubkey(&pk_b).unwrap(),
};
let s_ab = P256::ecdh(&sk_a, &pk_b_c).unwrap();
let s_ba = P256::ecdh(&sk_b, &pk_a_c).unwrap();
assert_eq!(s_ab, s_ba);
}
#[test]
fn test_sec1_compressed_parity_bit() {
let mut rng = TestRng::new(0xBEEF);
for _ in 0..20 {
let (pk, _sk) = P256::keygen(&mut rng);
let compressed = P256::compress_pubkey(&pk).unwrap();
let y_lsb = pk.bytes[64] & 1;
let tag = compressed[0];
assert!(tag == 0x02 || tag == 0x03);
assert_eq!((tag & 1), y_lsb, "compressed tag parity must match Y LSB");
}
}
#[test]
fn test_decompress_rejects_wrong_length() {
assert!(P256::decompress_pubkey(&[0x02; 32]).is_none());
assert!(P256::decompress_pubkey(&[0x02; 34]).is_none());
assert!(P256::decompress_pubkey(&[]).is_none());
}
#[test]
fn test_decompress_rejects_unknown_tag() {
let mut bytes = vec![0u8; 33];
bytes[0] = 0x05;
assert!(P256::decompress_pubkey(&bytes).is_none());
}
#[test]
fn test_der_sig_roundtrip_verifies() {
let (sk, pk) = d1_keypair(&p256_params());
let msg = b"DER round-trip";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let der = sig.to_der();
let parsed = Signature::from_der(&der).expect("from_der");
assert!(P256::verify_msg::<Sha256>(&pk, msg, &parsed));
}
#[test]
fn test_der_idempotent_on_der_side() {
let (sk, _pk) = d1_keypair(&p256_params());
let msg = b"idempotent";
let sig = P256::sign_rfc6979_msg::<Sha256>(&sk, msg);
let der1 = sig.to_der();
let parsed = Signature::from_der(&der1).unwrap();
let der2 = parsed.to_der();
assert_eq!(der1, der2);
}
}