use p384::ecdsa::signature::hazmat::PrehashVerifier;
use p384::ecdsa::{
Signature, SigningKey, VerifyingKey,
signature::{Signer, Verifier},
};
use zeroize::Zeroizing;
use crate::primitives::resource_limits::validate_signature_size;
use crate::unified_api::error::{CoreError, Result};
use crate::unified_api::zero_trust::SecurityMode;
pub const ECDSA_P384_SIGNATURE_LEN: usize = 96;
fn verifying_key(public_key: &[u8]) -> Result<VerifyingKey> {
VerifyingKey::from_sec1_bytes(public_key)
.map_err(|_e| CoreError::InvalidKey("invalid P-384 public key (expected SEC1)".to_string()))
}
pub fn generate_ecdsa_p384_keypair(mode: SecurityMode) -> Result<(Vec<u8>, Zeroizing<Vec<u8>>)> {
mode.validate()?;
let sk = SigningKey::random(&mut rand_core_0_6::OsRng);
let public_key = VerifyingKey::from(&sk).to_encoded_point(false).as_bytes().to_vec();
let secret_key = Zeroizing::new(sk.to_bytes().to_vec());
Ok((public_key, secret_key))
}
pub fn sign_ecdsa_p384(data: &[u8], secret_key: &[u8], mode: SecurityMode) -> Result<Vec<u8>> {
mode.validate()?;
validate_signature_size(data.len())
.map_err(|_e| CoreError::ResourceExceeded("message exceeds resource limit".to_string()))?;
let sk = SigningKey::from_slice(secret_key)
.map_err(|_e| CoreError::InvalidKey("invalid P-384 secret key".to_string()))?;
let signature: Signature = sk.sign(data);
Ok(signature.to_bytes().to_vec())
}
pub fn verify_ecdsa_p384(
data: &[u8],
signature: &[u8],
public_key: &[u8],
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
validate_signature_size(data.len())
.map_err(|_e| CoreError::ResourceExceeded("message exceeds resource limit".to_string()))?;
let vk = verifying_key(public_key)?;
Ok(Signature::from_slice(signature).ok().is_some_and(|sig| vk.verify(data, &sig).is_ok()))
}
pub fn verify_ecdsa_p384_prehash(
prehash: &[u8],
signature: &[u8],
public_key: &[u8],
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
let vk = verifying_key(public_key)?;
Ok(Signature::from_slice(signature)
.ok()
.is_some_and(|sig| vk.verify_prehash(prehash, &sig).is_ok()))
}
pub fn verify_ecdsa_p384_prehash_der(
prehash: &[u8],
signature_der: &[u8],
public_key: &[u8],
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
let vk = verifying_key(public_key)?;
Ok(Signature::from_der(signature_der)
.ok()
.is_some_and(|sig| vk.verify_prehash(prehash, &sig).is_ok()))
}
#[cfg(test)]
mod tests {
use super::*;
fn keypair() -> Option<(Vec<u8>, Zeroizing<Vec<u8>>)> {
generate_ecdsa_p384_keypair(SecurityMode::Unverified).ok()
}
#[test]
fn sign_verify_roundtrip() {
let Some((pk, sk)) = keypair() else { return };
let msg = b"latticearc p384 facade roundtrip";
let Ok(sig) = sign_ecdsa_p384(msg, &sk, SecurityMode::Unverified) else {
return;
};
assert_eq!(sig.len(), ECDSA_P384_SIGNATURE_LEN);
assert_eq!(verify_ecdsa_p384(msg, &sig, &pk, SecurityMode::Unverified).ok(), Some(true));
}
#[test]
fn rejects_tampered_message() {
let Some((pk, sk)) = keypair() else { return };
let Ok(sig) = sign_ecdsa_p384(b"original", &sk, SecurityMode::Unverified) else {
return;
};
assert_eq!(
verify_ecdsa_p384(b"tampered", &sig, &pk, SecurityMode::Unverified).ok(),
Some(false)
);
}
#[test]
fn prehash_verifies_sha384_digest() {
use sha2::{Digest, Sha384};
let Some((pk, sk)) = keypair() else { return };
let msg = b"prehash payload";
let Ok(sig) = sign_ecdsa_p384(msg, &sk, SecurityMode::Unverified) else {
return;
};
let digest = Sha384::digest(msg);
assert_eq!(
verify_ecdsa_p384_prehash(&digest, &sig, &pk, SecurityMode::Unverified).ok(),
Some(true)
);
}
#[test]
fn prehash_der_verifies_x509_style_signature() {
use sha2::{Digest, Sha384};
let Some((pk, sk)) = keypair() else { return };
let tbs = b"to-be-signed certificate bytes";
let Ok(raw) = sign_ecdsa_p384(tbs, &sk, SecurityMode::Unverified) else {
return;
};
let Some(sig) = Signature::from_slice(&raw).ok() else {
return;
};
let der = sig.to_der();
let digest = Sha384::digest(tbs);
assert_eq!(
verify_ecdsa_p384_prehash_der(&digest, der.as_bytes(), &pk, SecurityMode::Unverified)
.ok(),
Some(true)
);
}
#[test]
fn prehash_der_rejects_raw_signature() {
let Some((pk, sk)) = keypair() else { return };
let Ok(raw) = sign_ecdsa_p384(b"m", &sk, SecurityMode::Unverified) else {
return;
};
assert_eq!(
verify_ecdsa_p384_prehash_der(&[0u8; 48], &raw, &pk, SecurityMode::Unverified).ok(),
Some(false)
);
}
#[test]
fn malformed_signature_is_false_not_error() {
let Some((pk, _sk)) = keypair() else { return };
assert_eq!(
verify_ecdsa_p384(b"m", &[0u8; 96], &pk, SecurityMode::Unverified).ok(),
Some(false)
);
}
#[test]
fn invalid_public_key_errors() {
assert!(verify_ecdsa_p384(b"m", &[0u8; 96], &[0u8; 10], SecurityMode::Unverified).is_err());
}
}