use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleSignatureData {
pub author_key: [u8; 32],
pub signature: Vec<u8>,
pub signed_at: u64,
}
impl ModuleSignatureData {
pub fn sign(manifest_hash: &[u8; 32], signing_key: &SigningKey) -> Self {
let signature = signing_key.sign(manifest_hash);
let author_key = signing_key.verifying_key().to_bytes();
Self {
author_key,
signature: signature.to_bytes().to_vec(),
signed_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
}
}
pub fn verify(&self, manifest_hash: &[u8; 32]) -> bool {
let Ok(verifying_key) = VerifyingKey::from_bytes(&self.author_key) else {
return false;
};
let Ok(sig_bytes): Result<[u8; 64], _> = self.signature.as_slice().try_into() else {
return false;
};
let signature = Signature::from_bytes(&sig_bytes);
verifying_key.verify(manifest_hash, &signature).is_ok()
}
}
pub fn sign_manifest_hash(
manifest_hash: &[u8; 32],
secret_key_bytes: &[u8; 32],
) -> ModuleSignatureData {
let signing_key = SigningKey::from_bytes(secret_key_bytes);
ModuleSignatureData::sign(manifest_hash, &signing_key)
}
pub fn public_key_from_secret(secret_key_bytes: &[u8; 32]) -> [u8; 32] {
let signing_key = SigningKey::from_bytes(secret_key_bytes);
signing_key.verifying_key().to_bytes()
}
pub fn generate_keypair() -> (SigningKey, VerifyingKey) {
let mut secret = [0u8; 32];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut secret);
let signing_key = SigningKey::from_bytes(&secret);
let verifying_key = signing_key.verifying_key();
(signing_key, verifying_key)
}
pub fn generate_keypair_bytes() -> ([u8; 32], [u8; 32]) {
let (signing, verifying) = generate_keypair();
(signing.to_bytes(), verifying.to_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sign_and_verify() {
let (signing_key, _) = generate_keypair();
let manifest_hash = [42u8; 32];
let sig = ModuleSignatureData::sign(&manifest_hash, &signing_key);
assert!(sig.verify(&manifest_hash));
}
#[test]
fn test_verify_fails_with_wrong_hash() {
let (signing_key, _) = generate_keypair();
let manifest_hash = [42u8; 32];
let sig = ModuleSignatureData::sign(&manifest_hash, &signing_key);
let wrong_hash = [99u8; 32];
assert!(!sig.verify(&wrong_hash));
}
#[test]
fn test_verify_fails_with_corrupt_signature() {
let (signing_key, _) = generate_keypair();
let manifest_hash = [42u8; 32];
let mut sig = ModuleSignatureData::sign(&manifest_hash, &signing_key);
sig.signature[0] ^= 0xFF;
assert!(!sig.verify(&manifest_hash));
}
#[test]
fn test_verify_fails_with_wrong_key() {
let (signing_key, _) = generate_keypair();
let (other_key, _) = generate_keypair();
let manifest_hash = [42u8; 32];
let mut sig = ModuleSignatureData::sign(&manifest_hash, &signing_key);
sig.author_key = other_key.verifying_key().to_bytes();
assert!(!sig.verify(&manifest_hash));
}
#[test]
fn test_signed_at_is_nonzero() {
let (signing_key, _) = generate_keypair();
let sig = ModuleSignatureData::sign(&[0u8; 32], &signing_key);
assert!(sig.signed_at > 0);
}
#[test]
fn test_serde_roundtrip() {
let (signing_key, _) = generate_keypair();
let manifest_hash = [7u8; 32];
let sig = ModuleSignatureData::sign(&manifest_hash, &signing_key);
let json = serde_json::to_string(&sig).expect("serialize");
let restored: ModuleSignatureData = serde_json::from_str(&json).expect("deserialize");
assert_eq!(restored.author_key, sig.author_key);
assert_eq!(restored.signature, sig.signature);
assert_eq!(restored.signed_at, sig.signed_at);
assert!(restored.verify(&manifest_hash));
}
}