gatehouse_runtime_protocol/lifecycle_v2/
capture_binding.rs1use serde::Serialize;
3use sha2::{Digest, Sha256};
4#[derive(Serialize)]
5struct Envelope<'a> {
6 domain: &'static str,
7 request_sha256: &'a str,
8 host_sha256: &'a str,
9 installation_sha256: &'a str,
10 config_sha256: &'a str,
11 executable_sha256: &'a str,
12}
13pub fn operation_key(
17 request: &str,
18 host: &str,
19 installation: &str,
20 config: &str,
21 executable: &str,
22) -> Result<String, &'static str> {
23 let bytes = envelope(request, host, installation, config, executable)?;
24 Ok(format!("{:x}", Sha256::digest(bytes)))
25}
26fn envelope(
27 request: &str,
28 host: &str,
29 installation: &str,
30 config: &str,
31 executable: &str,
32) -> Result<Vec<u8>, &'static str> {
33 for hash in [request, host, installation, config, executable] {
34 if hash.len() != 64
35 || !hash
36 .bytes()
37 .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
38 {
39 return Err("capture binding digest rejected");
40 }
41 }
42 serde_json::to_vec(&Envelope {
43 domain: "gate-runtime/podman-capture-key-v1",
44 request_sha256: request,
45 host_sha256: host,
46 installation_sha256: installation,
47 config_sha256: config,
48 executable_sha256: executable,
49 })
50 .map_err(|_| "binding encoding")
51}
52#[cfg(test)]
53mod tests {
54 use super::*;
55 #[test]
56 fn golden_key_and_all_snapshot_changes() {
57 let hashes: Vec<String> = (0..5).map(|i| format!("{i}").repeat(64)).collect();
58 let key =
59 operation_key(&hashes[0], &hashes[1], &hashes[2], &hashes[3], &hashes[4]).unwrap();
60 assert_eq!(
61 key,
62 "5071ab535b718e57e663e2de6da7fdece81bab385f413773a00aa7c537d5de90"
63 );
64 for i in 0..5 {
65 let mut changed = hashes.clone();
66 changed[i] = "f".repeat(64);
67 assert_ne!(
68 key,
69 operation_key(
70 &changed[0],
71 &changed[1],
72 &changed[2],
73 &changed[3],
74 &changed[4]
75 )
76 .unwrap()
77 );
78 }
79 assert!(operation_key("bad", &hashes[1], &hashes[2], &hashes[3], &hashes[4]).is_err());
80 }
81}