trazaeo 0.5.2

Open-source provenance SDK and specification for verifiable EO and climate data workflows
Documentation
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()
}