use ed25519_dalek::pkcs8::DecodePrivateKey;
use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use sha2::{Digest, Sha256};
use std::path::Path;
#[derive(Debug)]
pub enum SigningError {
Io(std::io::Error),
Key(String),
Signature(String),
}
impl std::fmt::Display for SigningError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "I/O error: {}", e),
Self::Key(msg) => write!(f, "Key error: {}", msg),
Self::Signature(msg) => write!(f, "Signature error: {}", msg),
}
}
}
impl std::error::Error for SigningError {}
impl From<std::io::Error> for SigningError {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<SigningError> for String {
fn from(e: SigningError) -> Self {
e.to_string()
}
}
pub fn hash_file(path: &Path) -> Result<String, SigningError> {
let data = std::fs::read(path)?;
let mut hasher = Sha256::new();
hasher.update(&data);
Ok(hex::encode(hasher.finalize()))
}
pub fn load_signing_key(path: &Path) -> Result<SigningKey, SigningError> {
let pem = std::fs::read_to_string(path).map_err(|e| {
SigningError::Key(format!("Cannot read key file '{}': {}", path.display(), e))
})?;
let key = SigningKey::from_pkcs8_pem(&pem).map_err(|e| {
SigningError::Key(format!("Invalid PEM key in '{}': {}", path.display(), e))
})?;
Ok(key)
}
pub fn load_signing_key_from_env(var: &str) -> Result<SigningKey, SigningError> {
let pem = std::env::var(var)
.map_err(|_| SigningError::Key(format!("Environment variable '{}' not set", var)))?;
let key = SigningKey::from_pkcs8_pem(&pem)
.map_err(|e| SigningError::Key(format!("Invalid PEM key in env '{}': {}", var, e)))?;
Ok(key)
}
pub fn sign_payload(key: &SigningKey, payload: &[u8]) -> String {
let sig: Signature = key.sign(payload);
hex::encode(sig.to_bytes())
}
pub fn verify_signature(
public_key: &VerifyingKey,
payload: &[u8],
signature_hex: &str,
) -> Result<(), SigningError> {
let sig_bytes = hex::decode(signature_hex)
.map_err(|_| SigningError::Signature("Invalid hex signature".into()))?;
let sig = Signature::from_slice(&sig_bytes)
.map_err(|_| SigningError::Signature("Invalid signature bytes".into()))?;
public_key
.verify_strict(payload, &sig)
.map_err(|_| SigningError::Signature("Signature verification failed".into()))
}