amiss_wire/controls/
trusted_time.rs1use crate::de::{self, Error, ErrorKind, Obj, fail};
2use crate::digest::{Digest, hj};
3use crate::model::{BranchRef, RepositoryIdentity, UtcInstant};
4
5use super::{
6 decode_branch_ref, decode_digest, decode_instant, decode_provider_id, decode_provider_run_id,
7 decode_repository, root,
8};
9
10const TRUSTED_TIME_STATEMENT_SCHEMA: &str = "amiss/scanner-trusted-time-statement";
11const TRUSTED_TIME_CONTROLLER: &str = "external-required-check-clock";
12
13pub const STATEMENT_TTL_MAX_SECONDS: i64 = 600;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct TrustedTimeStatement {
23 pub digest: Digest,
24 pub repository: RepositoryIdentity,
25 pub ref_name: BranchRef,
26 pub candidate_identity_digest: Digest,
27 pub provider: String,
28 pub provider_run_id: String,
29 pub provider_run_attempt: u64,
30 pub evaluation_instant: UtcInstant,
31 pub valid_until: UtcInstant,
32}
33
34impl TrustedTimeStatement {
35 #[must_use]
36 pub const fn schema(&self) -> &'static str {
37 TRUSTED_TIME_STATEMENT_SCHEMA
38 }
39
40 #[must_use]
41 pub const fn controller(&self) -> &'static str {
42 TRUSTED_TIME_CONTROLLER
43 }
44
45 pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
51 let value = root(bytes)?;
52 let digest = hj(TRUSTED_TIME_STATEMENT_SCHEMA, &value);
53 let mut obj = Obj::new("$", value)?;
54 de::const_str(
55 &obj.field("schema"),
56 obj.take("schema")?,
57 TRUSTED_TIME_STATEMENT_SCHEMA,
58 )?;
59 de::const_str(
60 &obj.field("controller"),
61 obj.take("controller")?,
62 TRUSTED_TIME_CONTROLLER,
63 )?;
64 let repository = decode_repository(&obj.field("repository"), obj.take("repository")?)?;
65 let ref_name = decode_branch_ref(&obj.field("ref"), obj.take("ref")?)?;
66 let candidate_identity_digest = decode_digest(
67 &obj.field("candidate_identity_digest"),
68 obj.take("candidate_identity_digest")?,
69 )?;
70 let provider = decode_provider_id(&obj.field("provider"), obj.take("provider")?)?;
71 let run_id_path = obj.field("provider_run_id");
72 let provider_run_id = decode_provider_run_id(&run_id_path, obj.take("provider_run_id")?)?;
73 let attempt_path = obj.field("provider_run_attempt");
74 let attempt_raw = de::integer(&attempt_path, obj.take("provider_run_attempt")?)?;
75 let provider_run_attempt = u64::try_from(attempt_raw)
76 .ok()
77 .filter(|attempt| *attempt >= 1)
78 .ok_or_else(|| Error::new(&attempt_path, ErrorKind::InvalidValue))?;
79 let evaluation_instant = decode_instant(
80 &obj.field("evaluation_instant"),
81 obj.take("evaluation_instant")?,
82 )?;
83 let until_path = obj.field("valid_until");
84 let valid_until = decode_instant(&until_path, obj.take("valid_until")?)?;
85 obj.finish()?;
86 let lifetime = valid_until
87 .epoch_seconds()
88 .saturating_sub(evaluation_instant.epoch_seconds());
89 if lifetime <= 0 || lifetime > STATEMENT_TTL_MAX_SECONDS {
90 return fail(&until_path, ErrorKind::InvalidValue);
91 }
92 Ok(Self {
93 digest,
94 repository,
95 ref_name,
96 candidate_identity_digest,
97 provider,
98 provider_run_id,
99 provider_run_attempt,
100 evaluation_instant,
101 valid_until,
102 })
103 }
104}