#![deny(unsafe_code)]
#![deny(missing_docs)]
use ed25519_dalek::{Signer, Verifier};
use thiserror::Error;
pub use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VerifyError {
#[error("Fix: signature does not match certificate + public key. Check cert bytes are unmodified and public key matches the signing key.")]
BadSignature,
}
#[must_use]
pub fn sign(cert_bytes: &[u8], key: &SigningKey) -> Signature {
key.sign(cert_bytes)
}
pub fn verify(
cert_bytes: &[u8],
sig: &Signature,
pubkey: &VerifyingKey,
) -> Result<(), VerifyError> {
pubkey
.verify(cert_bytes, sig)
.map_err(|_| VerifyError::BadSignature)
}
#[must_use]
pub fn canonical_digest(cert_bytes: &[u8]) -> [u8; 32] {
*blake3::hash(cert_bytes).as_bytes()
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_key() -> SigningKey {
let seed: [u8; 32] = [7u8; 32];
SigningKey::from_bytes(&seed)
}
#[test]
fn round_trip_signs_and_verifies() {
let key = sample_key();
let cert = b"example certificate bytes";
let sig = sign(cert, &key);
verify(cert, &sig, &key.verifying_key()).expect("Fix: signature should verify");
}
#[test]
fn tampered_cert_fails_verify() {
let key = sample_key();
let cert = b"original bytes";
let sig = sign(cert, &key);
let tampered = b"tampered bytes";
let err = verify(tampered, &sig, &key.verifying_key()).unwrap_err();
assert!(matches!(err, VerifyError::BadSignature));
}
#[test]
fn canonical_digest_is_stable() {
let a = canonical_digest(b"same input");
let b = canonical_digest(b"same input");
assert_eq!(a, b);
}
}