//! High-level curve API: [`Curve`] trait + per-curve unit structs.
//!
//! This module exposes the user-facing dispatch layer shared by ECDSA
//! and ECDH over short Weierstrass curves. A curve key pair is
//! primitive-agnostic -- a [`SecretKey`] is just a scalar, a
//! [`PublicKey`] is just a point -- so the same unit struct
//! (`P256`, `P384`, ...) dispatches ECDSA sign/verify **and** ECDH
//! derive to the underlying LIMBS-generic implementations in
//! [`super::ecdsa`].
//!
//! Supported curves:
//!
//! | Wrapper             | Curve              | Canonical hash (ECDSA) |
//! |---------------------|--------------------|------------------------|
//! | [`P256`]            | NIST P-256         | SHA-256                |
//! | [`P384`]            | NIST P-384         | SHA-384                |
//! | [`P521`]            | NIST P-521         | SHA-512                |
//! | [`Secp256k1`]       | secp256k1 (SECG)   | SHA-256                |
//! | [`BrainpoolP256r1`] | brainpoolP256r1    | SHA-256                |
//! | [`BrainpoolP384r1`] | brainpoolP384r1    | SHA-384                |
//! | [`BrainpoolP512r1`] | brainpoolP512r1    | SHA-512                |

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;

// ============================================================================
// Shared ECDSA / ECDH key types
// ============================================================================

/// A simple RNG trait for randomized nonce generation and key
/// generation.
///
/// Implementors fill the supplied buffer with cryptographically
/// strong pseudo-random bytes. The trait is deliberately minimal
/// (no `fn try_fill_bytes` returning `Result`, no error type) to
/// keep the public surface small. Callers running on platforms
/// where the underlying entropy source can fail should provide a
/// wrapper that panics or aborts on failure.
pub trait CryptoRng {
    /// Fill `dest` with cryptographically random bytes.
    fn fill_bytes(&mut self, dest: &mut [u8]);
}

/// Public key on a short Weierstrass curve: an SEC1-encoded point
/// (uncompressed `0x04 || X || Y` or compressed `0x02/0x03 || X`).
///
/// Shared between ECDSA and ECDH on the same curve: there is no
/// "ECDH-only" or "ECDSA-only" key type.
#[derive(Clone, Debug)]
pub struct PublicKey {
    /// SEC1-encoded curve point.
    pub bytes: Vec<u8>,
}

/// Secret key on a short Weierstrass curve: a scalar in `[1, n-1]`
/// encoded as `felem_bytes` big-endian octets (per the curve's SEC1
/// octet length).
///
/// Shared between ECDSA and ECDH on the same curve.
#[derive(Clone)]
pub struct SecretKey {
    /// Big-endian encoding of the secret scalar `d`.
    pub bytes: Vec<u8>,
}

// ============================================================================
// Curve trait
// ============================================================================
//
// Public API. Two axes are exposed orthogonally:
//
//   * The curve  -- one unit struct per curve, implementing `Curve`.
//   * The hash   -- a generic type parameter on the methods that need it
//                   (RFC 6979 internal HMAC; the message-input convenience
//                   wrappers that hash before signing).
//
// `sign_random` and `verify` do **not** carry an `H` parameter: they
// consume the digest as opaque bytes (interpreted via `bits2int`) and
// have no algebraic dependency on which hash produced it. The asymmetry
// reflects the actual algorithm.
//
// Naming convention: the **digest-input** form is the canonical, short
// name (this is the form that maps directly to OpenSSL `ECDSA_sign`,
// PKCS#11, HSM offload paths, X.509/CMS pipelines, ...). The **message
// input** convenience form has a `_msg` suffix.
//
// Per-curve unit structs (`P256`, `P384`, ...) carry no data; they exist
// only to dispatch the trait methods to the LIMBS-generic internals with
// the right `params()` and the right `LIMBS` const.

/// Operations on a short Weierstrass curve: keygen, ECDSA sign / verify,
/// and ECDH key agreement.
///
/// Implementations are unit structs ([`P256`], [`P384`], [`P521`],
/// [`Secp256k1`], [`BrainpoolP256r1`], [`BrainpoolP384r1`],
/// [`BrainpoolP512r1`]) and the methods are called as e.g.
/// `P256::sign_rfc6979::<Sha256>(&sk, &digest)` or
/// `P256::ecdh(&sk, &peer_pk)`.
///
/// # Method overview
///
/// | Method                                       | Input         | `H` needed?                |
/// |----------------------------------------------|---------------|----------------------------|
/// | [`keygen`](Self::keygen)                     | --            | no                         |
/// | [`ecdh`](Self::ecdh)                         | peer SEC1 pk  | no                         |
/// | [`sign_rfc6979`](Self::sign_rfc6979)         | digest        | yes (HMAC inside RFC 6979) |
/// | [`sign_random`](Self::sign_random)           | digest        | no                         |
/// | [`verify`](Self::verify)                     | digest        | no                         |
/// | [`sign_rfc6979_msg`](Self::sign_rfc6979_msg) | message       | yes (hash + HMAC)          |
/// | [`sign_random_msg`](Self::sign_random_msg)   | message       | yes (hash)                 |
/// | [`verify_msg`](Self::verify_msg)             | message       | yes (hash)                 |
///
/// Key pairs returned by [`keygen`](Self::keygen) are interchangeable
/// across ECDSA and ECDH on the same curve -- a [`SecretKey`] is just a
/// scalar, a [`PublicKey`] is just a point. There is no "ECDH-only" or
/// "ECDSA-only" key type.
///
/// # Hash choice (ECDSA)
///
/// Any [`Hasher`] implementation may be paired with any curve.
/// The standard pairings (P-256+SHA-256, P-384+SHA-384, brainpoolP512r1
/// +SHA-512, etc.) are common, but **not** mandated by this crate. If you
/// need P-256 + SHA-512 for an exotic protocol, that works too -- the
/// `bits2int` step will truncate the digest to the curve's qlen as
/// specified in RFC 6979 §2.3.2.
///
/// Per FIPS 186-5 §6.4.2, the hash output should provide at least the
/// security level of the curve (e.g. don't pair P-384 with SHA-1). The
/// crate does not enforce this; document and test your protocol's choice.
pub trait Curve: Sized {
    /// Generate a key pair on this curve.
    fn keygen(rng: &mut dyn CryptoRng) -> (PublicKey, SecretKey);

    /// Compress a public key from SEC1 uncompressed (`0x04 || X || Y`)
    /// to SEC1 compressed (`0x02/0x03 || X`). If the input is already
    /// compressed, returns a validated clone. Returns `None` for
    /// malformed or off-curve input.
    fn compress_pubkey(pk: &PublicKey) -> Option<Vec<u8>>;

    /// Decompress a SEC1 compressed public key (`0x02/0x03 || X`) to
    /// uncompressed form (`0x04 || X || Y`), recovering Y via the
    /// field square-root. If the input is already uncompressed, acts
    /// as a validate-and-clone. Returns `None` if the input is
    /// malformed, if X is not a valid x-coordinate on the curve, or
    /// if the decompressed point fails the on-curve check.
    fn decompress_pubkey(compressed: &[u8]) -> Option<PublicKey>;

    /// **ECDH** key agreement: derive the shared secret from our secret
    /// key and the peer's SEC1 uncompressed public key.
    ///
    /// Returns the **raw X coordinate** of `sk * peer_pk` as `LIMBS * 8`
    /// big-endian bytes (matches NIST SP 800-56A §5.7.1.2 "ECC CDH
    /// Primitive" and TLS / IKE conventions).
    ///
    /// Returns `None` if any of the following holds:
    /// - peer's pk has the wrong SEC1 length / tag
    /// - peer's pk is not on the curve (defends against invalid-curve
    ///   attacks; see [`super::curve::is_on_curve`])
    /// - our secret scalar is not in `[1, n-1]`
    /// - the resulting shared point is the point at infinity
    ///
    /// Higher-level KDFs (HKDF, X9.63 KDF, ...) are out of scope for this
    /// layer -- callers feed the raw X bytes into their KDF of choice.
    fn ecdh(sk: &SecretKey, peer_pk: &PublicKey) -> Option<Vec<u8>>;

    /// Sign a precomputed digest with the deterministic RFC 6979 nonce.
    ///
    /// `H` is the hash that produced `digest`; it is required because RFC
    /// 6979 derives the nonce via HMAC-`H` internally. Two calls with the
    /// same `(sk, digest, H)` produce byte-identical signatures.
    fn sign_rfc6979<H: Hasher>(sk: &SecretKey, digest: &[u8]) -> Signature;

    /// Sign a precomputed digest with a uniformly random nonce drawn from
    /// `rng`. The hash function is irrelevant -- only the digest bytes are
    /// consumed (via `bits2int`). **Each call must consume fresh entropy**;
    /// reusing `k` across two signatures with the same key recovers the
    /// secret key.
    fn sign_random(sk: &SecretKey, digest: &[u8], rng: &mut dyn CryptoRng) -> Signature;

    /// Verify a signature against a precomputed digest.
    fn verify(pk: &PublicKey, digest: &[u8], sig: &Signature) -> bool;

    // ----- Convenience: hash the message in-place -----

    /// Convenience: hash `msg` with `H`, then call [`Self::sign_rfc6979`].
    fn sign_rfc6979_msg<H: Hasher>(sk: &SecretKey, msg: &[u8]) -> Signature {
        let digest = H::hash(msg);
        Self::sign_rfc6979::<H>(sk, &digest)
    }

    /// Convenience: hash `msg` with `H`, then call [`Self::sign_random`].
    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)
    }

    /// Convenience: hash `msg` with `H`, then call [`Self::verify`].
    fn verify_msg<H: Hasher>(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool {
        let digest = H::hash(msg);
        Self::verify(pk, &digest, sig)
    }
}

// ============================================================================
// Per-curve dispatch
// ============================================================================

/// Generate the per-curve unit struct + `Curve` impl for one curve. The
/// macro pins `LIMBS` and the `params_fn`; everything else flows through
/// the LIMBS-generic internals in [`super::ecdsa`].
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."
);

// ============================================================================
// Tests
// ============================================================================

#[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()
    }

    /// Build (sk, pk) for d=1 on a curve, given its params. Both `sk`
    /// and `pk` use the SEC1 `felem_bytes` external width (same as what
    /// `keygen_internal` emits), so for P-521 this returns a 66-byte
    /// secret key and a 133-byte uncompressed public key, not
    /// 72 / 145.
    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; // d = 1 (big-endian)

        let mut pk_bytes = Vec::with_capacity(1 + 2 * felem);
        pk_bytes.push(0x04);
        pk_bytes.extend_from_slice(&fe_to_felem_bytes(&params.gx, felem));
        pk_bytes.extend_from_slice(&fe_to_felem_bytes(&params.gy, felem));

        (SecretKey { bytes: sk_bytes }, PublicKey { bytes: pk_bytes })
    }

    /// Tiny deterministic xorshift64 RNG, for tests only. Not cryptographic --
    /// the goal is just to feed `sign_random` a known, reproducible byte
    /// stream so that test failures are debuggable.
    struct TestRng {
        state: u64,
    }

    impl TestRng {
        fn new(seed: u64) -> Self {
            // xorshift state must be non-zero.
            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;
                }
            }
        }
    }

    // ----------------------------------------------------------------------
    // ECDSA sign/verify -- P-256 canonical roundtrip
    // ----------------------------------------------------------------------

    #[test]
    fn test_ecdsa_p256_sign_verify_rfc6979() {
        let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
        let sk = SecretKey {
            bytes: sk_bytes.clone(),
        };

        // Compute public key from secret key.
        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, &params);
        let (qx, qy) = q.to_affine(&params.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");

        // Verify the signature.
        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, &params);
        let (qx, qy) = q.to_affine(&params.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);

        // Modify message.
        assert!(!P256::verify(&pk, b"tampered", &sig), "Should reject tampered message");

        // Modify signature.
        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(),
        };

        // Public key for d=1 is just G.
        let params = p256_params();
        let mut pk_bytes = vec![0x04];
        pk_bytes.extend_from_slice(&params.gx.to_bytes_be());
        pk_bytes.extend_from_slice(&params.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"
        );
    }

    // ----------------------------------------------------------------------
    // Roundtrip tests for the new curves.
    //
    // Strategy: use d = 1 so the public key is exactly the generator G --
    // avoids re-doing scalar multiplication in the test setup. Sign with
    // RFC 6979 (deterministic, no rng needed), then verify.
    // ----------------------------------------------------------------------

    #[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));
    }

    // ----------------------------------------------------------------------
    // sign_random tests: exercise the random-nonce path.
    // ----------------------------------------------------------------------

    #[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));
    }

    /// Diagnostic: sweep many seeds and confirm each random signature
    /// verifies.
    #[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));
    }

    /// Two `sign_random` calls on the same `(sk, msg)` with different rng
    /// streams must produce different signatures (different `r`, since the
    /// nonce differs). Both must still verify.
    #[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));
    }

    /// Conversely, two `sign_rfc6979` calls on the same `(sk, msg)` MUST
    /// produce identical signatures.
    #[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);
    }

    /// The digest-input form and the message-input convenience form must
    /// produce byte-identical signatures when the caller pre-hashes the
    /// message themselves.
    #[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);
    }

    // ----------------------------------------------------------------------
    // Public-key validation tests for verify_internal
    // ----------------------------------------------------------------------

    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));
    }

    // ----------------------------------------------------------------------
    // Non-canonical (curve, hash) pairings
    // ----------------------------------------------------------------------

    #[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));
    }

    // ----------------------------------------------------------------------
    // P-521 (secp521r1) end-to-end
    // ----------------------------------------------------------------------

    #[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);
    }

    /// **SEC1 interop pin**: decompress the known SEC1 compressed
    /// encoding of the P-521 generator G and verify it matches the
    /// official (Gx, Gy) from FIPS 186-5 Appendix C.2.3 byte-for-byte.
    #[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));
    }

    // ----------------------------------------------------------------------
    // SEC1 compressed public keys
    // ----------------------------------------------------------------------

    #[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());
    }

    // ----------------------------------------------------------------------
    // DER roundtrip via Curve::sign_rfc6979 / verify
    // ----------------------------------------------------------------------

    /// DER round-trip on a freshly-signed signature: sig -> DER -> sig ->
    /// verify. Value-level round-trip.
    #[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));
    }

    /// DER bytes round-trip: from_der(to_der(sig)) gives the same bytes
    /// when re-encoded. Idempotence on the DER side.
    #[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);
    }
}
