1use ed25519_dalek::Signer;
16
17use crate::artifact::TrustedArtifact;
18use crate::bundle::{AnchorRoot, TrustAnchorBundle};
19use crate::graph::{AuthorityKind, AuthorityNode, DelegationEdge, TrustGraph};
20use crate::registry::{DimensionTag, Registry};
21use crate::scope::ScopeDimensions;
22
23#[derive(Debug, Clone)]
27pub struct Fixture {
28 pub artifact: TrustedArtifact,
30 pub bundle: TrustAnchorBundle,
32 pub graph: TrustGraph,
34 pub registry: Registry,
36 pub root_secret: ed25519_dalek::SigningKey,
38 pub end_secret: ed25519_dalek::SigningKey,
40}
41
42impl Fixture {
43 pub fn valid() -> Fixture {
45 let mut seed = [0u8; 32];
46 rand_core::RngCore::fill_bytes(&mut rand_core::OsRng, &mut seed);
47 let root_secret = ed25519_dalek::SigningKey::from_bytes(&seed);
48 let mut seed = [0u8; 32];
49 rand_core::RngCore::fill_bytes(&mut rand_core::OsRng, &mut seed);
50 let end_secret = ed25519_dalek::SigningKey::from_bytes(&seed);
51
52 let registry = Registry::with_initial_values();
53 let root = AuthorityNode {
54 id: "root".into(),
55 kind: AuthorityKind::Root,
56 public_key: root_secret.verifying_key().as_bytes().to_vec(),
57 quorum: None,
58 scope: ScopeDimensions::unconstrained(),
59 };
60 let end = AuthorityNode {
61 id: "end".into(),
62 kind: AuthorityKind::EndCertificate,
63 public_key: end_secret.verifying_key().as_bytes().to_vec(),
64 quorum: None,
65 scope: ScopeDimensions::unconstrained(),
66 };
67 let mut graph = TrustGraph::new();
68 graph.add_node(root.clone());
69 graph.add_node(end.clone());
70 graph
71 .add_delegation(DelegationEdge {
72 parent: "root".into(),
73 child: "end".into(),
74 signature: root_secret
75 .sign(&end.binding_bytes().unwrap())
76 .to_bytes()
77 .to_vec(),
78 })
79 .unwrap();
80
81 let bundle = Self::sign_bundle(
82 root.public_key.clone(),
83 &root_secret,
84 chrono::Utc::now() - chrono::Duration::hours(1),
85 chrono::Utc::now() + chrono::Duration::days(30),
86 );
87
88 let mut artifact = TrustedArtifact::new(
89 crate::artifact::ArtifactVersion { major: 1, minor: 0 },
90 "fixture-1",
91 serde_json::json!({"dose": 500}),
92 None,
93 )
94 .unwrap();
95 artifact
96 .sign(
97 DimensionTag::data(),
98 "Ed25519",
99 "end",
100 end_secret.verifying_key().as_bytes().to_vec(),
101 "root",
102 &|m| end_secret.sign(m).to_bytes().to_vec(),
103 ®istry,
104 )
105 .unwrap();
106
107 Fixture {
108 artifact,
109 bundle,
110 graph,
111 registry,
112 root_secret,
113 end_secret,
114 }
115 }
116
117 fn sign_bundle(
118 root_key: Vec<u8>,
119 root_secret: &ed25519_dalek::SigningKey,
120 valid_from: chrono::DateTime<chrono::Utc>,
121 valid_until: chrono::DateTime<chrono::Utc>,
122 ) -> TrustAnchorBundle {
123 let mut bundle = TrustAnchorBundle {
124 bundle_version: "1".into(),
125 valid_from,
126 valid_until,
127 roots: vec![AnchorRoot {
128 name: "root".into(),
129 aggregate_key: root_key.clone(),
130 fingerprint: hex::encode(&root_key),
131 quorum: None,
132 }],
133 transparency_logs: vec![],
134 update_log: None,
135 bundle_signature: vec![],
136 };
137 let msg = bundle.signing_bytes().unwrap();
138 bundle.bundle_signature = root_secret.sign(&msg).to_bytes().to_vec();
139 bundle
140 }
141
142 pub fn tampered_artifact(&self) -> TrustedArtifact {
145 let mut value = serde_json::to_value(&self.artifact).unwrap();
146 value["payload"]["dose"] = serde_json::json!(999);
147 serde_json::from_value(value).unwrap()
148 }
149
150 pub fn expired_bundle(&self) -> TrustAnchorBundle {
153 Self::sign_bundle(
154 self.root_secret.verifying_key().as_bytes().to_vec(),
155 &self.root_secret,
156 chrono::Utc::now() - chrono::Duration::days(30),
157 chrono::Utc::now() - chrono::Duration::hours(1),
158 )
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use crate::verify::{VerifyOptions, verify_trusted_artifact};
166
167 #[test]
168 fn valid_specimen_ladders() {
169 let f = Fixture::valid();
170 let options = VerifyOptions {
171 transparency_included: true,
172 time_anchored: true,
173 time_attested_at: Some(chrono::Utc::now().to_rfc3339()),
174 accepted_labels: vec!["verified".into()],
175 ..VerifyOptions::default()
176 };
177 let verdict =
178 verify_trusted_artifact(&f.artifact, &f.bundle, &f.graph, &f.registry, &options)
179 .unwrap();
180 assert_eq!(verdict.label, "verified");
181 assert!(verdict.accept);
182 }
183
184 #[test]
185 fn tampered_specimen_hard_fails() {
186 let f = Fixture::valid();
187 let err = verify_trusted_artifact(
188 &f.tampered_artifact(),
189 &f.bundle,
190 &f.graph,
191 &f.registry,
192 &VerifyOptions::default(),
193 )
194 .unwrap_err();
195 assert!(format!("{err}").contains("signature_validity"), "{err}");
196 }
197
198 #[test]
199 fn expired_specimen_hard_fails() {
200 let f = Fixture::valid();
201 let err = verify_trusted_artifact(
202 &f.artifact,
203 &f.expired_bundle(),
204 &f.graph,
205 &f.registry,
206 &VerifyOptions::default(),
207 )
208 .unwrap_err();
209 assert!(format!("{err}").contains("bundle"), "{err}");
210 }
211}