use ed25519_dalek::{Signer, SigningKey};
use super::{
ATTESTATION_FORMAT, ATTESTATION_VERSION, Attestation, AttestationError, AttestedOperation,
SessionLog, params_digest, seal_bytes, to_hex,
};
#[derive(Debug, Clone)]
pub struct SealMetadata {
pub started_at_ms: u64,
pub ended_at_ms: u64,
pub bytes_egressed: u64,
pub bytes_ingressed: u64,
pub policy_json: String,
pub app_name: String,
pub app_version: String,
}
pub struct SessionSigner(SigningKey);
impl SessionSigner {
pub fn generate() -> Result<Self, AttestationError> {
let mut seed = [0u8; 32];
getrandom::fill(&mut seed).map_err(|err| AttestationError::Rng(err.to_string()))?;
Ok(Self(SigningKey::from_bytes(&seed)))
}
pub fn from_seed(seed: [u8; 32]) -> Self {
Self(SigningKey::from_bytes(&seed))
}
pub fn public_key_bytes(&self) -> [u8; 32] {
self.0.verifying_key().to_bytes()
}
pub fn seal(
&self,
log: &SessionLog,
meta: &SealMetadata,
) -> Result<Attestation, AttestationError> {
let session_id = log.session_id_bytes();
let merkle_root = log.merkle_root();
let head_hash = log.head_hash();
let entry_count = log.entries().len() as u64;
let policy_digest = params_digest(&meta.policy_json);
let message = seal_bytes(
ATTESTATION_VERSION,
&session_id,
meta.started_at_ms,
meta.ended_at_ms,
entry_count,
&merkle_root,
&head_hash,
meta.bytes_egressed,
meta.bytes_ingressed,
&policy_digest,
);
let signature = self.0.sign(&message);
let operations = log
.entries()
.iter()
.map(|entry| AttestedOperation {
seq: entry.seq,
ts_ms: entry.ts_ms,
op: entry.op.clone(),
params: entry.params.clone(),
prev_hash: to_hex(&entry.prev_hash),
entry_hash: to_hex(&entry.entry_hash),
})
.collect();
Ok(Attestation {
format: ATTESTATION_FORMAT.to_string(),
version: ATTESTATION_VERSION,
session_id: to_hex(&session_id),
app_name: meta.app_name.clone(),
app_version: meta.app_version.clone(),
started_at_ms: meta.started_at_ms,
ended_at_ms: meta.ended_at_ms,
policy: meta.policy_json.clone(),
bytes_egressed: meta.bytes_egressed,
bytes_ingressed: meta.bytes_ingressed,
operations,
merkle_root: to_hex(&merkle_root),
head_hash: to_hex(&head_hash),
public_key: to_hex(&self.public_key_bytes()),
signature: to_hex(&signature.to_bytes()),
})
}
}