Files
archy/scripts/tests/os_ota_crypto_fixture.rs

70 lines
3.4 KiB
Rust

//! ISOLATED FIXTURE ONLY: native production crypto with a compile-time TEST anchor.
//! Not an Archipelago candidate executable; never installed on a real node.
use anyhow::{bail, ensure, Context, Result};
use ed25519_dalek::SigningKey;
use serde_json::{json, Value};
use std::{fs, path::Path, process::Command, thread, time::Duration};
mod trust {
pub mod anchor;
pub mod canonical;
pub mod did;
pub mod signed_doc;
}
fn guest_only() -> Result<()> {
ensure!(fs::read_to_string("/sys/class/dmi/id/sys_vendor")?.contains("QEMU"), "private QEMU fixture only");
ensure!(Path::new("/opt/os-ota-crypto-fixture.json").is_file(), "fixture appointment absent");
Ok(())
}
fn main() -> Result<()> {
guest_only()?;
let args: Vec<String> = std::env::args().collect();
if args.len() == 4 && args[1..3] == ["ceremony", "verify"] {
let doc: Value = serde_json::from_slice(&fs::read(&args[3])?)?;
match trust::signed_doc::verify_detached(&doc)? {
trust::signed_doc::SignatureStatus::Verified { signer_did, anchored: true } => {
println!("{}", json!({"fixture_only":true,"anchored":true,"signed_by":signer_did}));
return Ok(());
}
_ => bail!("unsigned or unanchored fixture document refused"),
}
}
if args.len() == 5 && args[1] == "fixture-sign" {
let key_path = Path::new(&args[2]);
ensure!(key_path.parent() == Some(Path::new("/root/os-ota-crypto-fixture")), "only disposable fixture key directory");
let bytes: [u8;32] = fs::read(key_path)?.try_into().map_err(|_| anyhow::anyhow!("exact 32-byte disposable seed required"))?;
let key = SigningKey::from_bytes(&bytes);
let mut doc: Value = serde_json::from_slice(&fs::read(&args[3])?)?;
let (signature, signed_by) = trust::signed_doc::sign_detached(&key, &doc)?;
let object = doc.as_object_mut().context("object required")?;
object.insert("signature".into(), json!(signature));
object.insert("signed_by".into(), json!(signed_by));
fs::write(&args[4], serde_json::to_vec_pretty(&doc)?)?;
println!("EPHEMERAL_FIXTURE_SIGNATURE_NOT_RELEASE_CEREMONY");
return Ok(());
}
if args.len() == 2 && args[1] == "--version" {
println!("ISOLATED_OS_OTA_CRYPTO_FIXTURE_NOT_CANDIDATE v1");
return Ok(());
}
ensure!(args.len() == 1, "unsupported fixture operation");
// Inert native requester, so PID1's actual executable matches the appointed
// verifier/prerequisite. Every mutation goes through the shipped D09 client.
let control = Path::new("/run/d09-fixture-client");
loop {
for entry in fs::read_dir(control)? {
let path = entry?.path();
let name = path.file_name().unwrap().to_string_lossy();
if !name.starts_with("request-") || !name.ends_with(".json") { continue; }
let output = control.join(name.replacen("request-", "result-", 1));
if output.exists() { continue; }
let request: Vec<String> = serde_json::from_slice(&fs::read(&path)?)?;
let result = Command::new("/usr/libexec/archipelago-d09ctl").args(request).output()?;
fs::write(output, serde_json::to_vec(&json!({"exit":result.status.code().unwrap_or(128),
"stdout":String::from_utf8_lossy(&result.stdout),"stderr":String::from_utf8_lossy(&result.stderr)}))?)?;
}
thread::sleep(Duration::from_millis(100));
}
}