fleetforge_trust/
scitt.rs1use 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
10pub 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}