pub const FORMAT: &str = WRITE.format;
pub const NOTES_REF: &str = WRITE.notes_ref;
pub const INPUTS_REF: &str = WRITE.inputs_ref;
pub const LOCAL_PREFIX: &str = "attest-local/";
pub const NAMESPACE: &str = WRITE.namespace;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Names {
pub format: &'static str,
pub notes_ref: &'static str,
pub inputs_ref: &'static str,
pub namespace: &'static str,
pub input_domain: &'static str,
pub canon_domain: &'static str,
pub producer: &'static str,
}
pub const LEGACY: Names = Names {
format: "amont-attest-v2",
notes_ref: "amont-attest",
inputs_ref: "amont-attest-inputs",
namespace: "amont-attest",
input_domain: "amont-attest-input",
canon_domain: "amont-attest-canon",
producer: "amont",
};
pub const GREEN: Names = Names {
format: "greengate-attest-v2",
notes_ref: "greengate-attest",
inputs_ref: "greengate-attest-inputs",
namespace: "greengate-attest",
input_domain: "greengate-attest-input",
canon_domain: "greengate-attest-canon",
producer: "greengate",
};
pub const WRITE: Names = LEGACY;
pub const MAX_BLOCKS: usize = 32;
pub(crate) const BEGIN: &str = "-----BEGIN SSH SIGNATURE-----";
pub(crate) const END: &str = "-----END SSH SIGNATURE-----";
pub fn platform() -> String {
format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)
}
pub fn bound_platform(principal: &str) -> Option<&str> {
const ARCHES: [&str; 2] = ["x86_64", "aarch64"];
const SYSTEMS: [&str; 3] = ["linux", "macos", "windows"];
let (_, suffix) = principal.rsplit_once('@')?;
let (arch, system) = suffix.split_once('-')?;
(ARCHES.contains(&arch) && SYSTEMS.contains(&system)).then_some(suffix)
}
#[derive(Debug, PartialEq)]
pub struct Block {
pub payload: String,
pub signature: String,
}
pub fn split_blocks(body: &str) -> (Vec<Block>, bool) {
enum State {
Between,
Payload,
WaitingForSignature,
Signature,
}
let mut blocks = Vec::new();
let mut state = State::Between;
let mut payload = String::new();
let mut signature = String::new();
let close = |blocks: &mut Vec<Block>, payload: &str, signature: &str| -> bool {
if blocks.len() == MAX_BLOCKS {
return true;
}
blocks.push(Block {
payload: payload.to_string(),
signature: signature.to_string(),
});
false
};
for line in body.split('\n') {
match state {
State::Between => {
if line.is_empty() {
continue;
}
payload.clear();
payload.push_str(line);
payload.push('\n');
state = State::Payload;
}
State::Payload => {
if line.is_empty() {
state = State::WaitingForSignature;
} else {
payload.push_str(line);
payload.push('\n');
}
}
State::WaitingForSignature => {
if line.is_empty() {
continue;
}
if line != BEGIN {
return (blocks, false);
}
signature.clear();
signature.push_str(line);
signature.push('\n');
state = State::Signature;
}
State::Signature => {
signature.push_str(line);
signature.push('\n');
if line == END {
if close(&mut blocks, &payload, &signature) {
return (blocks, true);
}
state = State::Between;
}
}
}
}
if let State::Signature = state {
if close(&mut blocks, &payload, &signature) {
return (blocks, true);
}
}
(blocks, false)
}
pub fn split_note(body: &str) -> Option<(String, String)> {
split_blocks(body)
.0
.into_iter()
.next()
.map(|b| (b.payload, b.signature))
}
pub fn platform_matches(want: Option<&str>, ran_on: &str) -> bool {
match want {
None => true,
Some(want) if want.contains('-') => want == ran_on,
Some(want) => ran_on.rsplit_once('-').is_some_and(|(_, os)| os == want),
}
}
pub fn field<'a>(payload: &'a str, name: &str) -> Option<&'a str> {
payload
.split('\n')
.find_map(|l| l.strip_prefix(name).and_then(|r| r.strip_prefix(' ')))
.map(|v| v.trim_start_matches(' '))
}
pub fn is_oid(s: &str) -> bool {
(s.len() == 40 || s.len() == 64)
&& s.bytes()
.all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
}
pub fn input_fp<'a>(payload: &'a str, gate: &str) -> Option<&'a str> {
payload.split('\n').find_map(|line| {
let f: Vec<&str> = line.split([' ', '\t']).filter(|t| !t.is_empty()).collect();
(f.len() == 3 && f[0] == "input" && f[1] == gate && is_oid(f[2])).then(|| f[2])
})
}
pub fn canon_claim(payload: &str) -> Option<&str> {
payload.split('\n').find_map(|line| {
let f: Vec<&str> = line.split([' ', '\t']).filter(|t| !t.is_empty()).collect();
(f.len() == 2 && f[0] == "canon" && is_oid(f[1])).then(|| f[1])
})
}
pub fn payload(
tree: &str,
gates: &str,
platform: &str,
inputs: &[(String, String)],
canon: Option<&str>,
producer: &str,
) -> String {
let mut out = format!("{FORMAT}\ntree {tree}\ngates {gates}\nplatform {platform}\n");
for (gate, fp) in inputs {
out.push_str(&format!("input {gate} {fp}\n"));
}
if let Some(c) = canon {
out.push_str(&format!("canon {c}\n"));
}
out.push_str(&format!("{} {producer}\n", WRITE.producer));
out
}
pub fn block(payload: &str, signature: &str) -> String {
format!(
"{}\n\n{}",
payload.trim_end_matches('\n'),
signature.trim_end_matches('\n')
)
}