use crate::error::{TrazaeoError, TrazaeoResult};
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct Attestation {
pub signer_id: String,
pub key_id: String,
pub signature: String,
pub signed_at: String,
}
fn decode_signing_key_hex(signing_key_hex: &str) -> TrazaeoResult<SigningKey> {
let bytes = hex::decode(signing_key_hex).map_err(|_| {
TrazaeoError::invalid_input("decode signing key", "invalid signing key hex")
})?;
let key_bytes: [u8; 32] = bytes.as_slice().try_into().map_err(|_| {
TrazaeoError::invalid_input("decode signing key", "signing key must be 32 bytes")
})?;
Ok(SigningKey::from_bytes(&key_bytes))
}
fn decode_verifying_key_hex(verifying_key_hex: &str) -> Option<VerifyingKey> {
let bytes = hex::decode(verifying_key_hex).ok()?;
let key_bytes: [u8; 32] = bytes.as_slice().try_into().ok()?;
VerifyingKey::from_bytes(&key_bytes).ok()
}
pub fn compute_attestation_signature(
signing_key_hex: &str,
payload: &[u8],
) -> TrazaeoResult<String> {
let signing_key = decode_signing_key_hex(signing_key_hex)?;
Ok(hex::encode(signing_key.sign(payload).to_bytes()))
}
pub fn make_attestation(
signer_id: &str,
signing_key_hex: &str,
signed_at: &str,
payload: &[u8],
) -> TrazaeoResult<Attestation> {
let signing_key = decode_signing_key_hex(signing_key_hex)?;
let signature = signing_key.sign(payload);
Ok(Attestation {
signer_id: signer_id.to_string(),
key_id: hex::encode(signing_key.verifying_key().to_bytes()),
signature: hex::encode(signature.to_bytes()),
signed_at: signed_at.to_string(),
})
}
pub fn verify_attestation(attestation: &Attestation, payload: &[u8]) -> bool {
let Some(verifying_key) = decode_verifying_key_hex(&attestation.key_id) else {
return false;
};
let Ok(signature_bytes) = hex::decode(&attestation.signature) else {
return false;
};
let Ok(signature_array) = <[u8; 64]>::try_from(signature_bytes.as_slice()) else {
return false;
};
let signature = Signature::from_bytes(&signature_array);
verifying_key.verify(payload, &signature).is_ok()
}