canic_host/deployment_truth/promotion/materialization/
evidence.rs1use crate::deployment_truth::{
2 BuildMaterializationEvidenceV1, BuildMaterializationInputV1, BuildMaterializationResultV1,
3 BuildRecipeIdentityV1, DEPLOYMENT_TRUTH_SCHEMA_VERSION,
4};
5
6use super::super::digest::{
7 build_materialization_evidence_digest, build_materialization_input_digest,
8};
9use super::super::ensure::{
10 ensure_materialization_field, ensure_materialization_link, ensure_materialization_sha256,
11};
12use super::super::error::PromotionMaterializationIdentityError;
13use super::super::request::BuildMaterializationEvidenceRequest;
14
15pub fn build_materialization_evidence(
16 request: BuildMaterializationEvidenceRequest,
17) -> Result<BuildMaterializationEvidenceV1, PromotionMaterializationIdentityError> {
18 ensure_materialization_field("evidence_id", &request.evidence_id)?;
19 validate_build_recipe_identity(&request.recipe)?;
20 validate_build_materialization_input(&request.materialization_input)?;
21 validate_build_materialization_result(&request.materialization_result)?;
22 let computed_materialization_input_digest =
23 build_materialization_input_digest(&request.materialization_input);
24 let mut evidence = BuildMaterializationEvidenceV1 {
25 schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
26 evidence_id: request.evidence_id,
27 materialization_evidence_digest: String::new(),
28 recipe_id_matches_input: request.recipe.recipe_id
29 == request.materialization_input.build_recipe_id,
30 recipe_id_matches_result: request.recipe.recipe_id
31 == request.materialization_result.build_recipe_id,
32 materialization_input_digest_matches_result: computed_materialization_input_digest
33 == request.materialization_result.materialization_input_digest,
34 computed_materialization_input_digest,
35 recipe: request.recipe,
36 materialization_input: request.materialization_input,
37 materialization_result: request.materialization_result,
38 };
39 evidence.materialization_evidence_digest = build_materialization_evidence_digest(&evidence);
40 validate_build_materialization_evidence(&evidence)?;
41 Ok(evidence)
42}
43
44pub fn validate_build_materialization_evidence(
45 evidence: &BuildMaterializationEvidenceV1,
46) -> Result<(), PromotionMaterializationIdentityError> {
47 if evidence.schema_version != DEPLOYMENT_TRUTH_SCHEMA_VERSION {
48 return Err(
49 PromotionMaterializationIdentityError::SchemaVersionMismatch {
50 expected: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
51 found: evidence.schema_version,
52 },
53 );
54 }
55 ensure_materialization_field("evidence_id", &evidence.evidence_id)?;
56 ensure_materialization_sha256(
57 "materialization_evidence_digest",
58 &evidence.materialization_evidence_digest,
59 )?;
60 validate_build_recipe_identity(&evidence.recipe)?;
61 validate_build_materialization_input(&evidence.materialization_input)?;
62 validate_build_materialization_result(&evidence.materialization_result)?;
63 ensure_materialization_sha256(
64 "computed_materialization_input_digest",
65 &evidence.computed_materialization_input_digest,
66 )?;
67 ensure_materialization_link(
68 "recipe_id_matches_input",
69 evidence.recipe_id_matches_input
70 == (evidence.recipe.recipe_id == evidence.materialization_input.build_recipe_id),
71 )?;
72 ensure_materialization_link("recipe_id_matches_input", evidence.recipe_id_matches_input)?;
73 ensure_materialization_link(
74 "recipe_id_matches_result",
75 evidence.recipe_id_matches_result
76 == (evidence.recipe.recipe_id == evidence.materialization_result.build_recipe_id),
77 )?;
78 ensure_materialization_link(
79 "recipe_id_matches_result",
80 evidence.recipe_id_matches_result,
81 )?;
82 let computed = build_materialization_input_digest(&evidence.materialization_input);
83 if computed != evidence.computed_materialization_input_digest {
84 return Err(PromotionMaterializationIdentityError::DigestMismatch {
85 field: "computed_materialization_input_digest",
86 expected: computed,
87 found: evidence.computed_materialization_input_digest.clone(),
88 });
89 }
90 ensure_materialization_link(
91 "materialization_input_digest_matches_result",
92 evidence.materialization_input_digest_matches_result
93 == (evidence.computed_materialization_input_digest
94 == evidence.materialization_result.materialization_input_digest),
95 )?;
96 ensure_materialization_link(
97 "materialization_input_digest_matches_result",
98 evidence.materialization_input_digest_matches_result,
99 )?;
100 if evidence.materialization_evidence_digest != build_materialization_evidence_digest(evidence) {
101 return Err(PromotionMaterializationIdentityError::LinkageMismatch {
102 field: "materialization_evidence_digest",
103 });
104 }
105 Ok(())
106}
107
108pub fn validate_build_recipe_identity(
109 recipe: &BuildRecipeIdentityV1,
110) -> Result<(), PromotionMaterializationIdentityError> {
111 ensure_materialization_field("recipe_id", &recipe.recipe_id)?;
112 ensure_materialization_field("source_revision", &recipe.source_revision)?;
113 ensure_materialization_field("package_or_role_selector", &recipe.package_or_role_selector)?;
114 ensure_materialization_field("cargo_profile", &recipe.cargo_profile)?;
115 ensure_materialization_sha256("cargo_features_digest", &recipe.cargo_features_digest)?;
116 ensure_materialization_sha256("cargo_lock_digest", &recipe.cargo_lock_digest)?;
117 ensure_materialization_field("rust_toolchain", &recipe.rust_toolchain)?;
118 ensure_materialization_field("builder_version", &recipe.builder_version)?;
119 ensure_materialization_field("target_triple", &recipe.target_triple)?;
120 ensure_materialization_field("linker_identity", &recipe.linker_identity)?;
121 ensure_materialization_field("deterministic_build_mode", &recipe.deterministic_build_mode)?;
122 ensure_materialization_field("wasm_opt_version", &recipe.wasm_opt_version)?;
123 ensure_materialization_field("compression_identity", &recipe.compression_identity)?;
124 Ok(())
125}
126
127pub fn validate_build_materialization_input(
128 input: &BuildMaterializationInputV1,
129) -> Result<(), PromotionMaterializationIdentityError> {
130 ensure_materialization_field("materialization_input_id", &input.materialization_input_id)?;
131 ensure_materialization_field("build_recipe_id", &input.build_recipe_id)?;
132 ensure_materialization_sha256(
133 "canonical_embedded_config_sha256",
134 &input.canonical_embedded_config_sha256,
135 )?;
136 ensure_materialization_field("network", &input.network)?;
137 ensure_materialization_field("root_trust_anchor", &input.root_trust_anchor)?;
138 ensure_materialization_field("runtime_variant", &input.runtime_variant)?;
139 Ok(())
140}
141
142pub fn validate_build_materialization_result(
143 result: &BuildMaterializationResultV1,
144) -> Result<(), PromotionMaterializationIdentityError> {
145 ensure_materialization_field(
146 "materialization_result_id",
147 &result.materialization_result_id,
148 )?;
149 ensure_materialization_field("build_recipe_id", &result.build_recipe_id)?;
150 ensure_materialization_sha256(
151 "materialization_input_digest",
152 &result.materialization_input_digest,
153 )?;
154 ensure_materialization_sha256("wasm_sha256", &result.wasm_sha256)?;
155 ensure_materialization_sha256("wasm_gz_sha256", &result.wasm_gz_sha256)?;
156 ensure_materialization_sha256("installed_module_hash", &result.installed_module_hash)?;
157 ensure_materialization_sha256("candid_sha256", &result.candid_sha256)?;
158 Ok(())
159}