1pub mod error;
26pub mod profile_p6_vendor;
27
28pub use error::*;
29pub use profile_p6_vendor::*;
30
31use crate::error::{require_non_empty, require_non_empty_slice};
32use schemars::JsonSchema;
33use serde::{Deserialize, Serialize};
34use stack_ids::{
35 ArtifactAdmissionPolicyId, AttestationEnvelopeId, ContentDigest, DisclosurePolicyId,
36 TransparencyReceiptId, TrustRootSetId,
37};
38use std::fmt::{Display, Formatter, Result as FmtResult};
39
40pub const ATTESTATION_ENVELOPE_V1_SCHEMA: &str = "attestation_envelope_v1";
41pub const TRUST_ROOT_SET_V1_SCHEMA: &str = "trust_root_set_v1";
42pub const TRANSPARENCY_RECEIPT_V1_SCHEMA: &str = "transparency_receipt_v1";
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
45#[serde(rename_all = "snake_case")]
46pub enum AttestationReplayabilityClassV1 {
47 Replayable,
48 ReplayableWithTicket,
49}
50
51impl Display for AttestationReplayabilityClassV1 {
52 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
53 let value = match self {
54 Self::Replayable => "replayable",
55 Self::ReplayableWithTicket => "replayable_with_ticket",
56 };
57 f.write_str(value)
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
62#[serde(rename_all = "snake_case")]
63pub enum TrustRootExpirationPolicyV1 {
64 #[serde(rename = "365d")]
65 Days365,
66 #[serde(rename = "rotate_every_90_days")]
67 RotateEvery90Days,
68}
69
70impl Display for TrustRootExpirationPolicyV1 {
71 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
72 let value = match self {
73 Self::Days365 => "365d",
74 Self::RotateEvery90Days => "rotate_every_90_days",
75 };
76 f.write_str(value)
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
81#[serde(rename_all = "snake_case")]
82pub enum TrustRootRotationPolicyV1 {
83 PublishedKeyset,
84 Overlap14DaysAndEmitSupersession,
85}
86
87impl Display for TrustRootRotationPolicyV1 {
88 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
89 let value = match self {
90 Self::PublishedKeyset => "published_keyset",
91 Self::Overlap14DaysAndEmitSupersession => "overlap_14_days_and_emit_supersession",
92 };
93 f.write_str(value)
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
98#[serde(rename_all = "snake_case")]
99pub enum TransparencyAdmissibilityJudgmentV1 {
100 Admitted,
101 LoggedAndAdmissible,
102 AdmissibleWithNotedCaveat,
103}
104
105impl Display for TransparencyAdmissibilityJudgmentV1 {
106 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
107 let value = match self {
108 Self::Admitted => "admitted",
109 Self::LoggedAndAdmissible => "logged_and_admissible",
110 Self::AdmissibleWithNotedCaveat => "admissible_with_noted_caveat",
111 };
112 f.write_str(value)
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
117pub struct AttestationEnvelopeV1 {
118 pub schema_version: String,
119 pub attestation_envelope_id: AttestationEnvelopeId,
120 pub artifact_family: String,
121 pub artifact_version: String,
122 pub content_digest: ContentDigest,
123 pub schema_identity: String,
124 pub signer_identity: String,
125 pub signing_time: String,
126 pub trust_root_set_id: TrustRootSetId,
127 pub provenance_summary: String,
128 pub disclosure_policy_id: DisclosurePolicyId,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub artifact_admission_policy_id: Option<ArtifactAdmissionPolicyId>,
131 pub replayability_class: AttestationReplayabilityClassV1,
132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
133 pub revocation_refs: Vec<String>,
134 #[serde(default, skip_serializing_if = "Vec::is_empty")]
135 pub supersession_refs: Vec<String>,
136}
137
138impl AttestationEnvelopeV1 {
139 #[allow(clippy::too_many_arguments)]
140 pub fn new(
141 attestation_envelope_id: AttestationEnvelopeId,
142 artifact_family: impl Into<String>,
143 artifact_version: impl Into<String>,
144 content_digest: ContentDigest,
145 schema_identity: impl Into<String>,
146 signer_identity: impl Into<String>,
147 signing_time: impl Into<String>,
148 trust_root_set_id: TrustRootSetId,
149 provenance_summary: impl Into<String>,
150 disclosure_policy_id: DisclosurePolicyId,
151 artifact_admission_policy_id: Option<ArtifactAdmissionPolicyId>,
152 replayability_class: AttestationReplayabilityClassV1,
153 revocation_refs: Vec<String>,
154 supersession_refs: Vec<String>,
155 ) -> Result<Self, AttestationValidationError> {
156 let value = Self {
157 schema_version: ATTESTATION_ENVELOPE_V1_SCHEMA.to_string(),
158 attestation_envelope_id,
159 artifact_family: artifact_family.into(),
160 artifact_version: artifact_version.into(),
161 content_digest,
162 schema_identity: schema_identity.into(),
163 signer_identity: signer_identity.into(),
164 signing_time: signing_time.into(),
165 trust_root_set_id,
166 provenance_summary: provenance_summary.into(),
167 disclosure_policy_id,
168 artifact_admission_policy_id,
169 replayability_class,
170 revocation_refs,
171 supersession_refs,
172 };
173 value.validate()?;
174 Ok(value)
175 }
176
177 pub fn validate(&self) -> AttestationValidationResult {
178 require_non_empty(
179 self.attestation_envelope_id.as_str(),
180 "attestation_envelope_id",
181 )?;
182 require_non_empty(&self.artifact_family, "artifact_family")?;
183 require_non_empty(&self.artifact_version, "artifact_version")?;
184 require_non_empty(&self.content_digest.0, "content_digest")?;
185 require_non_empty(&self.schema_identity, "schema_identity")?;
186 require_non_empty(&self.signer_identity, "signer_identity")?;
187 require_non_empty(&self.signing_time, "signing_time")?;
188 require_non_empty(self.trust_root_set_id.as_str(), "trust_root_set_id")?;
189 require_non_empty(&self.provenance_summary, "provenance_summary")?;
190 require_non_empty(self.disclosure_policy_id.as_str(), "disclosure_policy_id")?;
191 Ok(())
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
196pub struct TrustRootSetV1 {
197 pub schema_version: String,
198 pub trust_root_set_id: TrustRootSetId,
199 #[serde(default, skip_serializing_if = "Vec::is_empty")]
200 pub trust_root_identities: Vec<String>,
201 #[serde(default, skip_serializing_if = "Vec::is_empty")]
202 pub allowed_signer_classes: Vec<String>,
203 pub expiration_policy: TrustRootExpirationPolicyV1,
204 pub rotation_policy: TrustRootRotationPolicyV1,
205 #[serde(default, skip_serializing_if = "Vec::is_empty")]
206 pub allowed_artifact_families: Vec<String>,
207 #[serde(default, skip_serializing_if = "Vec::is_empty")]
208 pub revocation_sources: Vec<String>,
209 #[serde(default, skip_serializing_if = "Vec::is_empty")]
210 pub policy_owner_refs: Vec<String>,
211}
212
213impl TrustRootSetV1 {
214 #[allow(clippy::too_many_arguments)]
215 pub fn new(
216 trust_root_set_id: TrustRootSetId,
217 trust_root_identities: Vec<String>,
218 allowed_signer_classes: Vec<String>,
219 expiration_policy: TrustRootExpirationPolicyV1,
220 rotation_policy: TrustRootRotationPolicyV1,
221 allowed_artifact_families: Vec<String>,
222 revocation_sources: Vec<String>,
223 policy_owner_refs: Vec<String>,
224 ) -> Result<Self, AttestationValidationError> {
225 let value = Self {
226 schema_version: TRUST_ROOT_SET_V1_SCHEMA.to_string(),
227 trust_root_set_id,
228 trust_root_identities,
229 allowed_signer_classes,
230 expiration_policy,
231 rotation_policy,
232 allowed_artifact_families,
233 revocation_sources,
234 policy_owner_refs,
235 };
236 value.validate()?;
237 Ok(value)
238 }
239
240 pub fn validate(&self) -> AttestationValidationResult {
241 require_non_empty(self.trust_root_set_id.as_str(), "trust_root_set_id")?;
242 require_non_empty_slice(&self.trust_root_identities, "trust_root_identities")?;
243 require_non_empty_slice(&self.allowed_signer_classes, "allowed_signer_classes")?;
244 require_non_empty_slice(&self.allowed_artifact_families, "allowed_artifact_families")?;
245 require_non_empty_slice(&self.revocation_sources, "revocation_sources")?;
246 require_non_empty_slice(&self.policy_owner_refs, "policy_owner_refs")?;
247 Ok(())
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
252pub struct TransparencyReceiptV1 {
253 pub schema_version: String,
254 pub transparency_receipt_id: TransparencyReceiptId,
255 pub attestation_envelope_id: AttestationEnvelopeId,
256 pub registry_identity: String,
257 pub inclusion_material: String,
258 pub recorded_time: String,
259 pub admissibility_judgment: TransparencyAdmissibilityJudgmentV1,
260}
261
262impl TransparencyReceiptV1 {
263 pub fn new(
264 transparency_receipt_id: TransparencyReceiptId,
265 attestation_envelope_id: AttestationEnvelopeId,
266 registry_identity: impl Into<String>,
267 inclusion_material: impl Into<String>,
268 recorded_time: impl Into<String>,
269 admissibility_judgment: TransparencyAdmissibilityJudgmentV1,
270 ) -> Result<Self, AttestationValidationError> {
271 let value = Self {
272 schema_version: TRANSPARENCY_RECEIPT_V1_SCHEMA.to_string(),
273 transparency_receipt_id,
274 attestation_envelope_id,
275 registry_identity: registry_identity.into(),
276 inclusion_material: inclusion_material.into(),
277 recorded_time: recorded_time.into(),
278 admissibility_judgment,
279 };
280 value.validate()?;
281 Ok(value)
282 }
283
284 pub fn validate(&self) -> AttestationValidationResult {
285 require_non_empty(
286 self.transparency_receipt_id.as_str(),
287 "transparency_receipt_id",
288 )?;
289 require_non_empty(
290 self.attestation_envelope_id.as_str(),
291 "attestation_envelope_id",
292 )?;
293 require_non_empty(&self.registry_identity, "registry_identity")?;
294 require_non_empty(&self.inclusion_material, "inclusion_material")?;
295 require_non_empty(&self.recorded_time, "recorded_time")?;
296 Ok(())
297 }
298}