1use sha2::{Digest, Sha256};
8use std::sync::OnceLock;
9
10pub use crate::format::{CURRENT_WALL_ID as CURRENT_ID, FORMAT_VERSION};
11
12pub const LEAN_TOOLCHAIN: &str = include_str!("../assets/wall/current/lean-toolchain");
13
14pub const CERT_PRELUDE: &str = include_str!("../assets/wall/current/CertPrelude.lean");
15pub const CERT_DECODE: &str = include_str!("../assets/wall/current/CertDecode.lean");
16pub const CERT_SCHEMA: &str = include_str!("../assets/wall/current/Schema.lean");
17pub const CERT_SCHEMA_CORE: &str = include_str!("../assets/wall/current/SchemaCore.lean");
18pub const CERT_PLAN_CHECK: &str = include_str!("../assets/wall/current/PlanCheck.lean");
19pub const CERT_PLAN_LOWER: &str = include_str!("../assets/wall/current/PlanLower.lean");
20pub const CERT_PLAN_BYTES: &str = include_str!("../assets/wall/current/PlanBytes.lean");
21pub const CERT_WASM_SLICE: &str = include_str!("../assets/wall/current/WasmSlice.lean");
22pub const CERT_EXPR_FRAGMENT_ACCEPTED: &str =
23 include_str!("../assets/wall/current/ExprFragmentAccepted.lean");
24pub const CERT_ACCEPTED_ARTIFACT: &str =
25 include_str!("../assets/wall/current/AcceptedArtifact.lean");
26pub const CERT_ACCEPTED_ARTIFACT_CORE: &str =
27 include_str!("../assets/wall/current/AcceptedArtifactCore.lean");
28pub const CERT_CLAIM_AXES: &str = include_str!("../assets/wall/current/ClaimAxes.lean");
29pub const CERT_EXPR_FRAGMENT_SEMANTICS: &str =
30 include_str!("../assets/wall/current/ExprFragmentSemantics.lean");
31pub const CERT_INTERPRETER_SEQUENCING: &str =
32 include_str!("../assets/wall/current/InterpreterSequencing.lean");
33pub const CERT_EXPR_FRAGMENT_SOUNDNESS: &str =
34 include_str!("../assets/wall/current/ExprFragmentSoundness.lean");
35pub const CERT_FIELD_PROJECTION_SOUNDNESS: &str =
36 include_str!("../assets/wall/current/FieldProjectionSoundness.lean");
37pub const CERT_CONSTRUCT_VERBATIM_SOUNDNESS: &str =
38 include_str!("../assets/wall/current/ConstructVerbatimSoundness.lean");
39pub const CERT_INT_DISPATCH_SOUNDNESS: &str =
40 include_str!("../assets/wall/current/IntDispatchSoundness.lean");
41pub const CERT_STRING_SOUNDNESS: &str = include_str!("../assets/wall/current/StringSoundness.lean");
42pub const CERT_STANDARD_FACE: &str = include_str!("../assets/wall/current/StandardFace.lean");
43pub const CERT_RECURSION_SOUNDNESS: &str =
44 include_str!("../assets/wall/current/RecursionSoundness.lean");
45pub const CERT_MUTUAL_RECURSION_SOUNDNESS: &str =
46 include_str!("../assets/wall/current/MutualRecursionSoundness.lean");
47pub const CERT_COMPOSITION_SOUNDNESS: &str =
48 include_str!("../assets/wall/current/CompositionSoundness.lean");
49pub const CERT_ACCEPTANCE_SOUNDNESS_CORE: &str =
50 include_str!("../assets/wall/current/AcceptanceSoundnessCore.lean");
51pub const CERT_DISCHARGE_EXPR_FRAGMENT: &str =
52 include_str!("../assets/wall/current/DischargeExprFragment.lean");
53pub const CERT_DISCHARGE_FIELD_PROJECTION: &str =
54 include_str!("../assets/wall/current/DischargeFieldProjection.lean");
55pub const CERT_DISCHARGE_CONSTRUCT: &str =
56 include_str!("../assets/wall/current/DischargeConstruct.lean");
57pub const CERT_DISCHARGE_VERBATIM: &str =
58 include_str!("../assets/wall/current/DischargeVerbatim.lean");
59pub const CERT_DISCHARGE_STRING: &str = include_str!("../assets/wall/current/DischargeString.lean");
60pub const CERT_DISCHARGE_INT_DISPATCH: &str =
61 include_str!("../assets/wall/current/DischargeIntDispatch.lean");
62pub const CERT_DISCHARGE_RECURSION: &str =
63 include_str!("../assets/wall/current/DischargeRecursion.lean");
64pub const CERT_DISCHARGE_COMPOSITION: &str =
65 include_str!("../assets/wall/current/DischargeComposition.lean");
66pub const CERT_ACCEPTANCE_SOUNDNESS: &str =
67 include_str!("../assets/wall/current/AcceptanceSoundness.lean");
68
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub struct Source {
71 pub name: &'static str,
72 pub contents: &'static str,
73}
74
75pub const SOURCES: [Source; 33] = [
78 Source {
79 name: "AcceptedArtifact.lean",
80 contents: CERT_ACCEPTED_ARTIFACT,
81 },
82 Source {
83 name: "AcceptedArtifactCore.lean",
84 contents: CERT_ACCEPTED_ARTIFACT_CORE,
85 },
86 Source {
87 name: "AcceptanceSoundness.lean",
88 contents: CERT_ACCEPTANCE_SOUNDNESS,
89 },
90 Source {
91 name: "AcceptanceSoundnessCore.lean",
92 contents: CERT_ACCEPTANCE_SOUNDNESS_CORE,
93 },
94 Source {
95 name: "CertDecode.lean",
96 contents: CERT_DECODE,
97 },
98 Source {
99 name: "CertPrelude.lean",
100 contents: CERT_PRELUDE,
101 },
102 Source {
103 name: "ClaimAxes.lean",
104 contents: CERT_CLAIM_AXES,
105 },
106 Source {
107 name: "CompositionSoundness.lean",
108 contents: CERT_COMPOSITION_SOUNDNESS,
109 },
110 Source {
111 name: "ConstructVerbatimSoundness.lean",
112 contents: CERT_CONSTRUCT_VERBATIM_SOUNDNESS,
113 },
114 Source {
115 name: "DischargeComposition.lean",
116 contents: CERT_DISCHARGE_COMPOSITION,
117 },
118 Source {
119 name: "DischargeConstruct.lean",
120 contents: CERT_DISCHARGE_CONSTRUCT,
121 },
122 Source {
123 name: "DischargeExprFragment.lean",
124 contents: CERT_DISCHARGE_EXPR_FRAGMENT,
125 },
126 Source {
127 name: "DischargeFieldProjection.lean",
128 contents: CERT_DISCHARGE_FIELD_PROJECTION,
129 },
130 Source {
131 name: "DischargeIntDispatch.lean",
132 contents: CERT_DISCHARGE_INT_DISPATCH,
133 },
134 Source {
135 name: "DischargeRecursion.lean",
136 contents: CERT_DISCHARGE_RECURSION,
137 },
138 Source {
139 name: "DischargeString.lean",
140 contents: CERT_DISCHARGE_STRING,
141 },
142 Source {
143 name: "DischargeVerbatim.lean",
144 contents: CERT_DISCHARGE_VERBATIM,
145 },
146 Source {
147 name: "ExprFragmentAccepted.lean",
148 contents: CERT_EXPR_FRAGMENT_ACCEPTED,
149 },
150 Source {
151 name: "ExprFragmentSemantics.lean",
152 contents: CERT_EXPR_FRAGMENT_SEMANTICS,
153 },
154 Source {
155 name: "ExprFragmentSoundness.lean",
156 contents: CERT_EXPR_FRAGMENT_SOUNDNESS,
157 },
158 Source {
159 name: "FieldProjectionSoundness.lean",
160 contents: CERT_FIELD_PROJECTION_SOUNDNESS,
161 },
162 Source {
163 name: "IntDispatchSoundness.lean",
164 contents: CERT_INT_DISPATCH_SOUNDNESS,
165 },
166 Source {
167 name: "InterpreterSequencing.lean",
168 contents: CERT_INTERPRETER_SEQUENCING,
169 },
170 Source {
171 name: "MutualRecursionSoundness.lean",
172 contents: CERT_MUTUAL_RECURSION_SOUNDNESS,
173 },
174 Source {
175 name: "PlanBytes.lean",
176 contents: CERT_PLAN_BYTES,
177 },
178 Source {
179 name: "PlanCheck.lean",
180 contents: CERT_PLAN_CHECK,
181 },
182 Source {
183 name: "PlanLower.lean",
184 contents: CERT_PLAN_LOWER,
185 },
186 Source {
187 name: "RecursionSoundness.lean",
188 contents: CERT_RECURSION_SOUNDNESS,
189 },
190 Source {
191 name: "Schema.lean",
192 contents: CERT_SCHEMA,
193 },
194 Source {
195 name: "SchemaCore.lean",
196 contents: CERT_SCHEMA_CORE,
197 },
198 Source {
199 name: "StandardFace.lean",
200 contents: CERT_STANDARD_FACE,
201 },
202 Source {
203 name: "StringSoundness.lean",
204 contents: CERT_STRING_SOUNDNESS,
205 },
206 Source {
207 name: "WasmSlice.lean",
208 contents: CERT_WASM_SLICE,
209 },
210];
211
212pub const PRISTINE_ROOTS: [&str; 31] = [
215 "CertPrelude",
216 "CertDecode",
217 "WasmSlice",
218 "SchemaCore",
219 "PlanCheck",
220 "PlanLower",
221 "PlanBytes",
222 "ExprFragmentAccepted",
223 "AcceptedArtifactCore",
224 "ClaimAxes",
225 "ExprFragmentSemantics",
226 "InterpreterSequencing",
227 "ExprFragmentSoundness",
228 "FieldProjectionSoundness",
229 "ConstructVerbatimSoundness",
230 "IntDispatchSoundness",
231 "StringSoundness",
232 "StandardFace",
233 "RecursionSoundness",
234 "MutualRecursionSoundness",
235 "CompositionSoundness",
236 "AcceptanceSoundnessCore",
237 "DischargeExprFragment",
238 "DischargeFieldProjection",
239 "DischargeConstruct",
240 "DischargeVerbatim",
241 "DischargeString",
242 "DischargeIntDispatch",
243 "DischargeRecursion",
244 "DischargeComposition",
245 "AcceptanceSoundness",
246];
247
248#[derive(Debug)]
249pub struct Wall {
250 pub sources: &'static [Source],
251 pub pristine_roots: &'static [&'static str],
252 pub toolchain: &'static str,
253}
254
255pub static CURRENT: Wall = Wall {
256 sources: &SOURCES,
257 pristine_roots: &PRISTINE_ROOTS,
258 toolchain: LEAN_TOOLCHAIN,
259};
260
261fn compute_id() -> String {
264 let mut files = SOURCES
265 .iter()
266 .map(|source| (source.name, source.contents.as_bytes()))
267 .chain(std::iter::once((
268 "lean-toolchain",
269 LEAN_TOOLCHAIN.as_bytes(),
270 )))
271 .collect::<Vec<_>>();
272 files.sort_unstable_by_key(|(name, _)| *name);
273
274 let mut hash = Sha256::new();
275 hash.update(b"aver-certificate-wall\0v1\0");
276 hash.update((files.len() as u64).to_be_bytes());
277 for (name, contents) in files {
278 hash.update((name.len() as u64).to_be_bytes());
279 hash.update(name.as_bytes());
280 hash.update((contents.len() as u64).to_be_bytes());
281 hash.update(contents);
282 }
283 format!("sha256:{:x}", hash.finalize())
284}
285
286pub fn current_id() -> &'static str {
288 static VERIFIED: OnceLock<()> = OnceLock::new();
289 VERIFIED.get_or_init(|| {
290 assert_eq!(
291 compute_id(),
292 CURRENT_ID,
293 "embedded certificate wall changed without updating CURRENT_ID"
294 );
295 });
296 CURRENT_ID
297}
298
299pub fn resolve(id: &str) -> Option<&'static Wall> {
302 (id == current_id()).then_some(&CURRENT)
303}
304
305pub fn render_artifact_bytes(bytes: &[u8]) -> String {
309 let numeral = if bytes.is_empty() {
310 "0".to_string()
311 } else {
312 let mut numeral = String::with_capacity(2 + bytes.len() * 2);
313 numeral.push_str("0x");
314 for byte in bytes.iter().rev() {
315 numeral.push_str(&format!("{byte:02x}"));
316 }
317 numeral
318 };
319 format!(
320 "import WasmSlice\n\nset_option maxRecDepth 200000\n\nnamespace AverCert.ArtifactBytes\n\ndef modBytes : Nat := {numeral}\ndef modLen : Nat := {}\n\nend AverCert.ArtifactBytes\n",
321 bytes.len()
322 )
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn wall_sources_have_unique_plain_filenames() {
331 let mut names = SOURCES.iter().map(|source| source.name).collect::<Vec<_>>();
332 names.sort_unstable();
333 names.dedup();
334 assert_eq!(names.len(), SOURCES.len());
335 assert!(
336 names
337 .iter()
338 .all(|name| name.ends_with(".lean") && !name.contains('/'))
339 );
340 }
341
342 #[test]
343 fn current_wall_resolves_only_by_exact_id() {
344 assert_eq!(compute_id(), CURRENT_ID);
345 assert!(std::ptr::eq(resolve(current_id()).unwrap(), &CURRENT));
346 assert!(resolve("sha256:deadbeef").is_none());
347 }
348}