From ebee65a7da625d067cdfeb69b41692d8852435c0 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Fri, 9 Jan 2026 15:41:59 +0100 Subject: [PATCH] feat(cli): add transformation attestation support Integrate wsc-attestation crate for supply chain security audit trails. When optimizing WASM modules, LOOM now embeds a cryptographic attestation in a custom section containing: - Input/output SHA256 hashes for integrity verification - Tool name, version, and optimization parameters - Timestamp and unique attestation ID - Z3 verification status (when --verify is used) - Instruction count metadata Usage: loom optimize input.wasm -o output.wasm # attestation on (default) loom optimize input.wasm -o output.wasm --attestation=false # disable The attestation follows the wsc-attestation format and is stored in the "wsc.transformation.attestation" custom section, enabling verifiers to trace optimized modules back to their original signed sources. Closes #43 --- loom-cli/Cargo.toml | 10 +++- loom-cli/src/main.rs | 117 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 4 deletions(-) diff --git a/loom-cli/Cargo.toml b/loom-cli/Cargo.toml index c462199..7bf2d37 100644 --- a/loom-cli/Cargo.toml +++ b/loom-cli/Cargo.toml @@ -19,11 +19,17 @@ clap = { workspace = true } # Error handling anyhow = { workspace = true } +# Attestation support (optional) +wsc-attestation = { version = "0.4", optional = true } +serde_json = { version = "1.0", optional = true } + [features] -# Default features - verification enabled for production safety -default = ["verification"] +# Default features - verification and attestation enabled for production safety +default = ["verification", "attestation"] # Enable Z3-based formal verification verification = ["loom-core/verification"] +# Enable transformation attestation support +attestation = ["wsc-attestation", "serde_json"] [[bin]] name = "loom" diff --git a/loom-cli/src/main.rs b/loom-cli/src/main.rs index 8ada164..b9cee70 100644 --- a/loom-cli/src/main.rs +++ b/loom-cli/src/main.rs @@ -42,6 +42,13 @@ enum Commands { #[arg(long)] verify: bool, + /// Add transformation attestation to output (for supply chain security) + /// Embeds a cryptographic audit trail in a custom section + /// Enabled by default; use --no-attestation to disable + #[arg(long, default_value_t = true, action = clap::ArgAction::Set)] + #[cfg(feature = "attestation")] + attestation: bool, + /// Select specific optimization passes (comma-separated) /// Available: inline,precompute,constant-folding,cse,advanced,branches,dce,merge-blocks,vacuum,simplify-locals /// Example: --passes inline,constant-folding,dce @@ -178,6 +185,60 @@ fn count_constant_folds(module: &loom_core::Module) -> usize { count } +/// Build transformation attestation for the optimization +#[cfg(feature = "attestation")] +fn build_attestation( + input_bytes: &[u8], + input_name: &str, + optimized_module: &loom_core::Module, + enabled_passes: &Option>, + verification_status: Option, +) -> String { + use wsc_attestation::TransformationAttestationBuilder; + + // Encode the optimized module to get its bytes for hashing + let output_bytes = loom_core::encode::encode_wasm(optimized_module).unwrap_or_default(); + + // Build the attestation + let mut builder = + TransformationAttestationBuilder::new_optimization("loom", env!("CARGO_PKG_VERSION")) + .add_input_unsigned(input_bytes, input_name); + + // Add optimization parameters + let passes_str = enabled_passes + .as_ref() + .map(|p| p.join(",")) + .unwrap_or_else(|| "all".to_string()); + builder = builder.add_parameter("passes", serde_json::json!(passes_str)); + + // Add verification status if available + if let Some(verified) = verification_status { + builder = builder.add_parameter("z3_verified", serde_json::json!(verified)); + } + + // Add metadata about the optimization + builder = builder.add_metadata( + "instructions_before", + serde_json::json!(count_instructions_from_bytes(input_bytes)), + ); + builder = builder.add_metadata( + "instructions_after", + serde_json::json!(count_instructions(optimized_module)), + ); + + // Build and serialize + let attestation = builder.build(&output_bytes, "output.wasm"); + attestation.to_json().unwrap_or_else(|_| "{}".to_string()) +} + +/// Count instructions from raw WASM bytes (for attestation) +#[cfg(feature = "attestation")] +fn count_instructions_from_bytes(bytes: &[u8]) -> usize { + loom_core::parse::parse_wasm(bytes) + .map(|m| count_instructions(&m)) + .unwrap_or(0) +} + /// Optimize command implementation fn optimize_command( input: String, @@ -185,6 +246,7 @@ fn optimize_command( output_wat: bool, show_stats: bool, run_verify: bool, + add_attestation: bool, passes: Option>, ) -> Result<()> { println!("🔧 LOOM Optimizer v{}", env!("CARGO_PKG_VERSION")); @@ -417,6 +479,47 @@ fn optimize_command( // Collect statistics after optimization stats.instructions_after = count_instructions(&module); + // Add transformation attestation if enabled + #[cfg(feature = "attestation")] + let verification_status = if run_verify { + #[cfg(feature = "verification")] + { + // Check if Z3 verification passed + match loom_core::verify::verify_optimization( + original_module.as_ref().unwrap_or(&module), + &module, + ) { + Ok(true) => Some(true), + Ok(false) => Some(false), + Err(_) => None, + } + } + #[cfg(not(feature = "verification"))] + None + } else { + None + }; + + #[cfg(feature = "attestation")] + if add_attestation && !output_wat { + println!("🔏 Adding transformation attestation..."); + let attestation = build_attestation( + &input_bytes, + &input, + &module, + &enabled_passes, + verification_status, + ); + // Add attestation to custom sections + module.custom_sections.push(( + wsc_attestation::TRANSFORMATION_ATTESTATION_SECTION.to_string(), + attestation.into_bytes(), + )); + println!("✓ Attestation added to custom section"); + } + #[cfg(not(feature = "attestation"))] + let _ = add_attestation; // Suppress unused warning + // Encode output let output_bytes = if output_wat { println!("📝 Encoding to WAT format..."); @@ -563,9 +666,15 @@ fn main() -> Result<()> { wat, stats, verify, + #[cfg(feature = "attestation")] + attestation, passes, }) => { - optimize_command(input, output, wat, stats, verify, passes)?; + #[cfg(feature = "attestation")] + let add_attestation = attestation; + #[cfg(not(feature = "attestation"))] + let add_attestation = false; + optimize_command(input, output, wat, stats, verify, add_attestation, passes)?; } Some(Commands::Verify { isle_file }) => { @@ -637,6 +746,7 @@ mod tests { false, false, false, + false, // attestation None, ); @@ -672,6 +782,7 @@ mod tests { false, true, // Enable stats false, + false, // attestation None, ); @@ -703,7 +814,8 @@ mod tests { Some(output_path.to_string_lossy().to_string()), false, false, - true, // Enable verification + true, // Enable verification + false, // attestation None, ); @@ -739,6 +851,7 @@ mod tests { true, // WAT output false, false, + false, // attestation (disabled for WAT) None, );