rshr_202_step_301_platform.rs (4116B)
1 use std::fs; 2 use std::path::Path; 3 use std::process::Command; 4 5 use serde_json::{Value, json}; 6 use sha2::{Digest, Sha256}; 7 8 const APPLE_TOOLCHAIN_IDENTITY_SHA256: &str = 9 "fd9bb9af273d0a834c2abff36910edf25f3e5b60c36fcc23b45b738c5c8b2d08"; 10 const PROBE_SOURCE_PATH: &str = 11 "tools/radroots_scripts/src/radroots_scripts/verify/rshr_200_series.py"; 12 const PROBE_SOURCE_SHA256: &str = 13 "add949c6c20a037123808230625dfd09dd6fa6c5afe5a856400227191f5de5b5"; 14 const REQUEST_PATH: &str = ".git/rshr-step-301-platform-request-sha256"; 15 16 fn root() -> &'static Path { 17 Path::new(env!("CARGO_MANIFEST_DIR")) 18 .parent() 19 .and_then(Path::parent) 20 .expect("xtask must remain under tools/xtask") 21 } 22 23 fn canonical(value: &Value) -> Result<Vec<u8>, String> { 24 serde_json::to_vec(value).map_err(|_| "Step 301 platform JSON encoding failed".to_owned()) 25 } 26 27 fn sha256(bytes: &[u8]) -> String { 28 hex::encode(Sha256::digest(bytes)) 29 } 30 31 fn uname(flag: &str) -> Result<String, String> { 32 let output = Command::new("/usr/bin/uname") 33 .arg(flag) 34 .output() 35 .map_err(|_| "Step 301 platform probe could not start uname".to_owned())?; 36 if !output.status.success() || !output.stderr.is_empty() { 37 return Err("Step 301 platform probe uname failed".to_owned()); 38 } 39 let value = std::str::from_utf8(&output.stdout) 40 .map_err(|_| "Step 301 platform probe uname output is not UTF-8".to_owned())? 41 .strip_suffix('\n') 42 .ok_or_else(|| "Step 301 platform probe uname output differs".to_owned())?; 43 if value.is_empty() || value.contains('\n') || value.contains('\r') { 44 return Err("Step 301 platform probe uname output differs".to_owned()); 45 } 46 Ok(value.to_owned()) 47 } 48 49 pub(crate) fn run() -> Result<(), String> { 50 let request_bytes = fs::read(root().join(REQUEST_PATH)) 51 .map_err(|_| "Step 301 platform execution request is unavailable".to_owned())?; 52 let raw_request = std::str::from_utf8(&request_bytes) 53 .map_err(|_| "Step 301 platform execution request is not UTF-8".to_owned())?; 54 let execution_request_sha256 = raw_request.strip_suffix('\n').unwrap_or(raw_request); 55 if execution_request_sha256.len() != 64 56 || !execution_request_sha256 57 .bytes() 58 .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()) 59 { 60 return Err("Step 301 platform execution request differs".to_owned()); 61 } 62 63 let kernel_name = uname("-s")?; 64 let kernel_release = uname("-r")?; 65 let kernel_version = uname("-v")?; 66 if kernel_name != "Darwin" || std::env::consts::ARCH != "aarch64" { 67 return Err("Step 301 platform identity differs".to_owned()); 68 } 69 70 let os_build = json!({ 71 "kernel_name": kernel_name, 72 "kernel_release": kernel_release, 73 "kernel_version": kernel_version, 74 }); 75 let result = json!({ 76 "schema": "radroots.services-hardening.rshr-200-platform-result.v1", 77 "platform": "macos_aarch64", 78 "system": "aarch64-darwin", 79 "os_family": "macos", 80 "architecture": "aarch64", 81 "kernel_name": os_build["kernel_name"], 82 "kernel_release": os_build["kernel_release"], 83 "os_build_sha256": sha256(&canonical(&os_build)?), 84 "runner_kind": "host", 85 "runner_image_sha256": "none", 86 "apple_toolchain_identity_sha256": APPLE_TOOLCHAIN_IDENTITY_SHA256, 87 "probe_source_path": PROBE_SOURCE_PATH, 88 "probe_source_sha256": PROBE_SOURCE_SHA256, 89 "execution_request_sha256": execution_request_sha256, 90 "assertion": [ 91 {"id": "os_family", "result": "pass"}, 92 {"id": "architecture", "result": "pass"}, 93 {"id": "kernel_identity", "result": "pass"}, 94 {"id": "runner_identity", "result": "pass"}, 95 {"id": "apple_identity", "result": "pass"}, 96 ], 97 "result": "available", 98 }); 99 let mut bytes = canonical(&result)?; 100 bytes.push(b'\n'); 101 std::io::Write::write_all(&mut std::io::stdout().lock(), &bytes) 102 .map_err(|_| "Step 301 platform result write failed".to_owned()) 103 }