use serde::Serialize;
use sha2::{Digest, Sha256};
#[derive(Serialize)]
struct Envelope<'a> {
domain: &'static str,
request_sha256: &'a str,
host_sha256: &'a str,
installation_sha256: &'a str,
config_sha256: &'a str,
executable_sha256: &'a str,
}
pub fn operation_key(
request: &str,
host: &str,
installation: &str,
config: &str,
executable: &str,
) -> Result<String, &'static str> {
let bytes = envelope(request, host, installation, config, executable)?;
Ok(format!("{:x}", Sha256::digest(bytes)))
}
fn envelope(
request: &str,
host: &str,
installation: &str,
config: &str,
executable: &str,
) -> Result<Vec<u8>, &'static str> {
for hash in [request, host, installation, config, executable] {
if hash.len() != 64
|| !hash
.bytes()
.all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
{
return Err("capture binding digest rejected");
}
}
serde_json::to_vec(&Envelope {
domain: "gate-runtime/podman-capture-key-v1",
request_sha256: request,
host_sha256: host,
installation_sha256: installation,
config_sha256: config,
executable_sha256: executable,
})
.map_err(|_| "binding encoding")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn golden_key_and_all_snapshot_changes() {
let hashes: Vec<String> = (0..5).map(|i| format!("{i}").repeat(64)).collect();
let key =
operation_key(&hashes[0], &hashes[1], &hashes[2], &hashes[3], &hashes[4]).unwrap();
assert_eq!(
key,
"5071ab535b718e57e663e2de6da7fdece81bab385f413773a00aa7c537d5de90"
);
for i in 0..5 {
let mut changed = hashes.clone();
changed[i] = "f".repeat(64);
assert_ne!(
key,
operation_key(
&changed[0],
&changed[1],
&changed[2],
&changed[3],
&changed[4]
)
.unwrap()
);
}
assert!(operation_key("bad", &hashes[1], &hashes[2], &hashes[3], &hashes[4]).is_err());
}
}