//! ES256 signing and verifying keys on P-256.
//!
//! The private half ([`SigningKey`]) is held only by an issuer or a holder's
//! device; verification needs just the public [`VerifyingKey`]. Keys serialize
//! as PKCS#8 PEM so an issuer key can live in a sops secret and a holder key in
//! a device keystore. Signing uses RFC 6979 deterministic ECDSA, so it needs no
//! randomness and produces the fixed 64-byte `r || s` JOSE signature.

use crate::error::Error;
use crate::jwk::Jwk;
use p256::ecdsa::signature::{Signer as _, Verifier as _};
use p256::ecdsa::{Signature, SigningKey as P256SigningKey, VerifyingKey};
use p256::pkcs8::{DecodePrivateKey as _, EncodePrivateKey as _, LineEnding};
use rand_core::OsRng;

/// An ES256 private key for signing credentials or key-binding proofs.
#[derive(Clone)]
pub struct SigningKey(P256SigningKey);

impl SigningKey {
    /// Generate a fresh key from the operating system's CSPRNG.
    #[must_use]
    pub fn generate() -> Self {
        Self(P256SigningKey::random(&mut OsRng))
    }

    /// The public half, for distribution to verifiers.
    #[must_use]
    pub fn verifying_key(&self) -> VerifyingKey {
        *self.0.verifying_key()
    }

    /// The public half as a JWK, for a `cnf` claim or issuer metadata.
    #[must_use]
    pub fn public_jwk(&self) -> Jwk {
        Jwk::from_verifying_key(self.0.verifying_key())
    }

    /// Serialize the private key as PKCS#8 PEM.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Pkcs8`] if encoding fails.
    pub fn to_pkcs8_pem(&self) -> Result<String, Error> {
        self.0
            .to_pkcs8_pem(LineEnding::LF)
            .map(|pem| pem.to_string())
            .map_err(|_| Error::Pkcs8)
    }

    /// Parse a private key from PKCS#8 PEM.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Pkcs8`] if the PEM is not a valid P-256 PKCS#8 key.
    pub fn from_pkcs8_pem(pem: &str) -> Result<Self, Error> {
        P256SigningKey::from_pkcs8_pem(pem)
            .map(Self)
            .map_err(|_| Error::Pkcs8)
    }

    /// Sign a message, returning the 64-byte `r || s` ES256 signature.
    pub(crate) fn sign_bytes(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
        let signature: Signature = self.0.try_sign(message).map_err(|_| Error::Signing)?;
        Ok(signature.to_bytes().to_vec())
    }
}

impl core::fmt::Debug for SigningKey {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        // Never print private scalar material.
        f.write_str("SigningKey(P-256, [private])")
    }
}

/// Verify an ES256 signature over `message` with `key`.
pub(crate) fn verify_bytes(
    key: &VerifyingKey,
    message: &[u8],
    signature: &[u8],
    context: &'static str,
) -> Result<(), Error> {
    let signature =
        Signature::from_slice(signature).map_err(|_| Error::BadSignature { context })?;
    key.verify(message, &signature)
        .map_err(|_| Error::BadSignature { context })
}
