fleetforge_trust/
scitt.rs

1use base64::engine::general_purpose::STANDARD as BASE64;
2use base64::Engine;
3use chrono::{SecondsFormat, Utc};
4use serde_json::{json, Value};
5use uuid::Uuid;
6
7use crate::{digest_json, Signer};
8use anyhow::Result;
9
10/// Constructs a SCITT transparency entry linking change evidence to attestations.
11pub fn build_scitt_entry(
12    change_id: &str,
13    attestation_ids: &[Uuid],
14    artifact_sha256: &str,
15    metadata: &Value,
16    signer: &dyn Signer,
17) -> Result<Value> {
18    let issued_at = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
19    let mut body = json!({
20        "id": Uuid::new_v4().to_string(),
21        "change_id": change_id,
22        "artifact_sha256": artifact_sha256,
23        "attestations": attestation_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>(),
24        "metadata": metadata,
25        "issued_at": issued_at,
26    });
27
28    let payload_digest = digest_json(&body);
29    if let Some(obj) = body.as_object_mut() {
30        obj.insert(
31            "payload_digest".into(),
32            json!({
33                "algorithm": "sha256",
34                "value": payload_digest,
35            }),
36        );
37    }
38
39    let envelope = signer.sign_json(&body)?;
40    if let Some(obj) = body.as_object_mut() {
41        obj.insert(
42            "signature".into(),
43            json!({
44                "algorithm": envelope.algorithm.as_str(),
45                "key_id": envelope.key_id,
46                "value": BASE64.encode(envelope.signature),
47            }),
48        );
49    }
50
51    Ok(body)
52}