//! Golden vectors for the P-256 signer.
//!
//! These assertions pin the exact bytes our implementation produces
//! for a fixed `(secret, message)` pair, so future refactors cannot
//! silently change wire output. Signatures are RFC 6979 deterministic,
//! so the pin is stable across runs and across machines.
//!
//! # `WebAuthn` compatibility
//!
//! The last test constructs the exact input a `WebAuthn` authenticator
//! signs — `authenticatorData || sha256(clientDataJSON)` — and
//! verifies that our verifier accepts a signature over it. The
//! signature itself was produced by our own deterministic signer
//! using a fixed secret; what we assert is the *format compatibility*:
//! 64-byte compact `(r, s)`, SHA-256 hashing, SEC1-uncompressed
//! pubkey (`0x04` prefix) as `WebAuthn` authenticators publish. This
//! is an interop contract test, NOT a published browser vector —
//! there is no universally accepted `WebAuthn` golden in the spec
//! because the bytes depend on the authenticator's private key. See
//! the comment above `webauthn_compat_compressed_and_uncompressed`
//! for details.

#![cfg(feature = "algo-p256")]

use mkit_attest::envelope::{self, Envelope, Sig};
use mkit_attest::signer_p256::{self, P256Signer};
use mkit_attest::statement::{self, Statement, Subject};
use mkit_attest::verify::{Reason, Registry, TrustRoot, verify_envelope};
use sha2::{Digest, Sha256};

/// Fixed secret for reproducibility. Chosen for readable bytes; no
/// special cryptographic property.
const SECRET: [u8; 32] = [
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
    0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
];

/// Fixed DSSE PAE-shaped message.
const PAE: &[u8] = b"DSSEv1 28 application/vnd.in-toto+json 2 {}";

// Pin the bytes. If this pair ever changes after a p256 crate upgrade,
// inspect the diff carefully — a change in the deterministic-k
// implementation would be a breaking wire-format change. Values were
// generated by this very file (RFC 6979 is reproducible across runs
// and machines).
const EXPECTED_PK_HEX: &str = "02515c3d6eb9e396b904d3feca7f54fdcd0cc1e997bf375dca515ad0a6c3b4035f";
const EXPECTED_SIG_HEX: &str = "a0075344345ada8f8dd1182e51d0aafcaab7e6c44bc378705cb4b78705faed5c\
     42849f10ff7bf91ea3ac1eda3eb663c289d3dd68c27403acad830a6bde4306c5";

#[test]
fn golden_signature_and_pubkey() {
    let signer = P256Signer::new(SECRET).expect("signer");

    // Compressed SEC1 pubkey (33 bytes).
    let pk_compressed = signer.public_key_sec1();
    assert_eq!(pk_compressed.len(), 33);
    let pk_hex = hex::encode(&pk_compressed);

    let sig = signer.sign_dsse(PAE).expect("sign");
    assert_eq!(sig.len(), 64);
    let sig_hex = hex::encode(&sig);

    // Re-signing must produce the identical bytes: RFC 6979
    // deterministic signing.
    let sig2 = signer.sign_dsse(PAE).expect("sign 2");
    assert_eq!(sig, sig2, "RFC 6979 determinism");

    // Normalise — source literal has whitespace for readability.
    let expected_sig: String = EXPECTED_SIG_HEX
        .chars()
        .filter(|c| !c.is_whitespace())
        .collect();

    assert_eq!(pk_hex, EXPECTED_PK_HEX, "golden compressed pubkey hex");
    assert_eq!(sig_hex, expected_sig, "golden signature hex");

    // And the verifier must accept it.
    signer_p256::verify_p256(&pk_compressed, PAE, &sig).expect("golden verify");
}

/// `WebAuthn` compatibility — our verifier accepts the format an
/// authenticator produces.
///
/// A `WebAuthn` ECDSA assertion is defined by the spec as:
///
/// ```text
/// signature = ECDSA_P256_SHA256(sk, authenticatorData || sha256(clientDataJSON))
/// ```
///
/// with the assertion response carrying the SEC1-uncompressed public
/// key (`0x04` prefix, 65 bytes) in the attestation's `COSE_Key`. The
/// signature is a *DER-encoded* ECDSA signature on the wire.
///
/// What we test: the underlying raw 64-byte compact form that DSSE
/// uses. Our verifier accepts both compressed and uncompressed SEC1
/// pubkeys over SHA-256 of the message, with low-S enforcement —
/// exactly matching the crypto a `WebAuthn` browser stack runs. We
/// do NOT re-implement DER sig decoding here; DSSE is
/// compact-sig-first, and `WebAuthn` DER-to-compact conversion would
/// be a separate serialisation step outside this crate's Phase 1
/// scope.
///
/// Reference: <https://www.w3.org/TR/webauthn-2/#sctn-fido-u2f-sig-format-compat>
///
/// Note — searching RFC 6979 Appendix A.2.5 (P-256 test vectors) and
/// the `WebAuthn` test-vector corpus, no fixed (`authenticatorData`,
/// `clientDataJSON`, signature) triple is published as a normative
/// golden — authenticator secrets are per-device. We therefore drive
/// this test from our own deterministic signer and assert the *shape*
/// (uncompressed pubkey is 65 bytes, starts with `0x04`, verifies).
/// The RFC 6979 side-channel is covered in the determinism check
/// above; adding a true W3C-published triple is a follow-up once an
/// authenticator emulator is in the workspace.
#[test]
fn webauthn_compat_compressed_and_uncompressed() {
    let signer = P256Signer::new(SECRET).expect("signer");

    // Emulate a WebAuthn signing input: authenticatorData || sha256(clientDataJSON).
    // The 37-byte authenticatorData below is: rpIdHash (32 bytes of
    // 0xAA), flags=0x01 (user present), signCount=0x00000000.
    let mut auth_data = [0u8; 37];
    auth_data[..32].fill(0xAA);
    auth_data[32] = 0x01;
    // signCount = 0 — bytes 33..37 already zero.

    // clientDataJSON → sha256.
    let client_data =
        br#"{"type":"webauthn.get","challenge":"Y2hhbGxlbmdl","origin":"https://example.com"}"#;
    let mut h = Sha256::new();
    h.update(client_data);
    let client_hash = h.finalize();

    let mut msg = Vec::with_capacity(auth_data.len() + 32);
    msg.extend_from_slice(&auth_data);
    msg.extend_from_slice(&client_hash);

    // Sign and verify — this is the exact crypto WebAuthn does.
    let sig = signer.sign_dsse(&msg).expect("sign webauthn msg");

    // WebAuthn authenticators emit the 65-byte uncompressed form.
    let pk_uncompressed = signer.public_key_sec1_uncompressed();
    assert_eq!(pk_uncompressed.len(), 65);
    assert_eq!(pk_uncompressed[0], 0x04, "SEC1 uncompressed prefix");
    signer_p256::verify_p256(&pk_uncompressed, &msg, &sig)
        .expect("verify with WebAuthn-shaped (uncompressed) pubkey");

    // Our own signer publishes the compressed form; same sig must
    // verify against it too. Both shapes must work.
    let pk_compressed = signer.public_key_sec1();
    assert_eq!(pk_compressed.len(), 33);
    assert!(
        pk_compressed[0] == 0x02 || pk_compressed[0] == 0x03,
        "SEC1 compressed prefix"
    );
    signer_p256::verify_p256(&pk_compressed, &msg, &sig).expect("verify with compressed pubkey");
}

/// Verifier reached via the `verify::Registry` surface with a
/// `TrustRoot::P256PubKeySec1` entry — the end-to-end shape that the
/// rest of mkit talks to.
#[test]
fn registry_dispatch_p256() {
    let signer = P256Signer::new(SECRET).unwrap();
    let keyid = signer.keyid();
    let commit_hex = "0011223344556677889900112233445566778899001122334455667788990011";

    let stmt = statement::encode(&Statement {
        subjects: vec![Subject {
            name: Some("commit".into()),
            digest_blake3_hex: commit_hex.into(),
        }],
        predicate_type: "https://example.com/predicate/v1".into(),
        predicate_jcs: b"{}",
    })
    .unwrap();

    let pae = envelope::pae_of(envelope::PAYLOAD_TYPE_IN_TOTO, stmt.as_bytes());
    let sig = signer.sign_dsse(&pae).unwrap();

    let env = Envelope {
        payload_type: envelope::PAYLOAD_TYPE_IN_TOTO.into(),
        payload: stmt.into_bytes(),
        signatures: vec![Sig {
            keyid: keyid.clone(),
            sig,
        }],
    };
    let bytes = env.encode().unwrap();

    let mut reg = Registry::new();
    reg.add(&keyid, TrustRoot::P256PubKeySec1(signer.public_key_sec1()));

    let r = verify_envelope(bytes.as_bytes(), &reg).unwrap();
    assert!(r.any_verified);
    assert_eq!(r.signatures[0].reason, Reason::Ok);

    // Registry with an uncompressed-form trust root also verifies.
    let mut reg2 = Registry::new();
    reg2.add(
        &keyid,
        TrustRoot::P256PubKeySec1(signer.public_key_sec1_uncompressed()),
    );
    let r2 = verify_envelope(bytes.as_bytes(), &reg2).unwrap();
    assert!(r2.any_verified);
    assert_eq!(r2.signatures[0].reason, Reason::Ok);
}
