|
|
|
|
|
|
|
|
| use wasm_bindgen::prelude::*;
|
|
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| #[derive(Debug)]
|
| pub struct Blake3VerificationResult {
|
| pub valid: bool,
|
| pub error_code: u8,
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn blake3_hash(input: &[u8]) -> Vec<u8> {
|
|
|
| let mut hasher = blake3::Hasher::new();
|
| hasher.update(input);
|
| hasher.finalize().as_bytes().to_vec()
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn blake3_verify_wasm(
|
| payload: &[u8],
|
| expected_digest: &[u8],
|
| ) -> Blake3VerificationResult {
|
|
|
| if payload.is_empty() || expected_digest.is_empty() {
|
| return Blake3VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| };
|
| }
|
|
|
| if expected_digest.len() != 32 {
|
| return Blake3VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| };
|
| }
|
|
|
|
|
| let mut hasher = blake3::Hasher::new();
|
| hasher.update(payload);
|
| let computed = hasher.finalize();
|
|
|
|
|
| let mut match_flag = true;
|
| for (computed_byte, expected_byte) in computed.as_bytes().iter().zip(expected_digest) {
|
| if computed_byte != expected_byte {
|
| match_flag = false;
|
| }
|
| }
|
|
|
| Blake3VerificationResult {
|
| valid: match_flag,
|
| error_code: if match_flag { 0 } else { 1 },
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| #[derive(Debug)]
|
| pub struct Ed25519VerificationResult {
|
| pub valid: bool,
|
| pub error_code: u8,
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn ed25519_verify_wasm(
|
| message: &[u8],
|
| signature: &[u8],
|
| public_key: &[u8],
|
| ) -> Ed25519VerificationResult {
|
|
|
| if message.is_empty() {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| };
|
| }
|
|
|
| if signature.len() != 64 {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| };
|
| }
|
|
|
| if public_key.len() != 32 {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| };
|
| }
|
|
|
|
|
| let sig_bytes: [u8; 64] = match signature.try_into() {
|
| Ok(b) => b,
|
| Err(_) => {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| }
|
| }
|
| };
|
|
|
| let pk_bytes: [u8; 32] = match public_key.try_into() {
|
| Ok(b) => b,
|
| Err(_) => {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| }
|
| }
|
| };
|
|
|
|
|
| let signature = ed25519_zebra::Signature::from(sig_bytes);
|
|
|
| let public_key: ed25519_zebra::VerificationKey = match ed25519_zebra::VerificationKeyBytes::from(pk_bytes)
|
| .try_into()
|
| {
|
| Ok(pk) => pk,
|
| Err(_) => {
|
| return Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 2,
|
| }
|
| }
|
| };
|
|
|
|
|
| match public_key.verify(&signature, message) {
|
| Ok(()) => Ed25519VerificationResult {
|
| valid: true,
|
| error_code: 0,
|
| },
|
| Err(_) => Ed25519VerificationResult {
|
| valid: false,
|
| error_code: 1,
|
| },
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct MutationValidationResult {
|
| pub valid: bool,
|
| pub error_code: u8,
|
| }
|
|
|
| impl MutationValidationResult {
|
| pub fn with_message(valid: bool, error_code: u8) -> Self {
|
| MutationValidationResult { valid, error_code }
|
| }
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn validate_mutation_wasm(
|
| event_id: u32,
|
| generation: u32,
|
| source_hash: &[u8],
|
| bytecode_hash: &[u8],
|
| native_code_hash: &[u8],
|
| actor_signature: &[u8],
|
| ) -> MutationValidationResult {
|
| let mut errors = Vec::new();
|
|
|
|
|
| if event_id == 0 {
|
| errors.push("Event ID must be non-zero");
|
| }
|
|
|
|
|
| if generation > 0x7FFFFFFF {
|
| errors.push("Generation counter overflow");
|
| }
|
|
|
|
|
| if source_hash.len() != 32 {
|
| errors.push("Source hash must be 32 bytes");
|
| }
|
|
|
|
|
| if bytecode_hash.len() != 32 {
|
| errors.push("Bytecode hash must be 32 bytes");
|
| }
|
|
|
|
|
| if native_code_hash.len() != 32 {
|
| errors.push("Native code hash must be 32 bytes");
|
| }
|
|
|
|
|
| if actor_signature.len() != 64 {
|
| errors.push("Actor signature must be 64 bytes");
|
| }
|
|
|
|
|
| if source_hash == bytecode_hash || bytecode_hash == native_code_hash {
|
| errors.push("Hash aliasing detected (hashes must be distinct)");
|
| }
|
|
|
|
|
| if source_hash.iter().all(|&b| b == 0) {
|
| errors.push("Source hash is all-zeros (invalid)");
|
| }
|
|
|
| let valid = errors.is_empty();
|
|
|
| MutationValidationResult {
|
| valid,
|
| error_code: if valid { 0 } else { 1 },
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct ProofCertificateValidationResult {
|
| pub valid: bool,
|
| pub theorem_id: u32,
|
| pub theorems_covered: u32,
|
| pub error_code: u8,
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn validate_proof_certificate_wasm(
|
| cert_bytes: &[u8],
|
| ) -> ProofCertificateValidationResult {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| let mut errors = Vec::new();
|
|
|
|
|
| if cert_bytes.len() != 157 {
|
| return ProofCertificateValidationResult {
|
| valid: false,
|
| theorem_id: 0,
|
| theorems_covered: 0,
|
| error_code: 2,
|
| };
|
| }
|
|
|
|
|
| let theorem_id = u32::from_le_bytes([
|
| cert_bytes[0],
|
| cert_bytes[1],
|
| cert_bytes[2],
|
| cert_bytes[3],
|
| ]);
|
|
|
| let theorems_covered = u32::from_le_bytes([
|
| cert_bytes[36],
|
| cert_bytes[37],
|
| cert_bytes[38],
|
| cert_bytes[39],
|
| ]);
|
|
|
| let machine_state_inv = u32::from_le_bytes([
|
| cert_bytes[40],
|
| cert_bytes[41],
|
| cert_bytes[42],
|
| cert_bytes[43],
|
| ]);
|
|
|
| let opt_level = cert_bytes[60];
|
|
|
|
|
| if theorem_id < 0x0001 || theorem_id > 0x000B {
|
| errors.push(format!("Theorem ID out of range: 0x{:04X}", theorem_id));
|
| }
|
|
|
|
|
| if theorems_covered == 0 {
|
| errors.push("No theorems covered (bitmask is zero)".to_string());
|
| }
|
|
|
|
|
| if machine_state_inv == 0 {
|
| errors.push("Machine state invariants must be non-zero".to_string());
|
| }
|
|
|
|
|
| if opt_level > 2 {
|
| errors.push(format!("Optimization level out of range: {}", opt_level));
|
| }
|
|
|
|
|
| let backend_bytes = &cert_bytes[44..60];
|
| let backend_str = std::str::from_utf8(backend_bytes)
|
| .unwrap_or("")
|
| .trim_end_matches('\0');
|
|
|
| if !["x86_64", "aarch64", "wasm32"].contains(&backend_str) {
|
| errors.push(format!("Unknown backend: {}", backend_str));
|
| }
|
|
|
| let valid = errors.is_empty();
|
|
|
| ProofCertificateValidationResult {
|
| valid,
|
| theorem_id,
|
| theorems_covered,
|
| error_code: if valid { 0 } else { 1 },
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen(start)]
|
| pub fn init_wasm() {
|
|
|
| #[cfg(feature = "console_error_panic_hook")]
|
| console_error_panic_hook::set_once();
|
| }
|
|
|
|
|
|
|
|
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
|
|
| #[test]
|
| fn test_blake3_hash() {
|
| let input = b"test payload";
|
| let hash = blake3_hash(input);
|
| assert_eq!(hash.len(), 32);
|
| }
|
|
|
| #[test]
|
| fn test_blake3_verify_valid() {
|
| let input = b"test payload";
|
| let digest = blake3_hash(input);
|
| let result = blake3_verify_wasm(input, &digest);
|
| assert!(result.valid);
|
| assert_eq!(result.error_code, 0);
|
| }
|
|
|
| #[test]
|
| fn test_blake3_verify_invalid() {
|
| let input = b"test payload";
|
| let digest = blake3_hash(input);
|
| let mut bad_digest = digest.clone();
|
| bad_digest[0] ^= 0xFF;
|
| let result = blake3_verify_wasm(input, &bad_digest);
|
| assert!(!result.valid);
|
| assert_eq!(result.error_code, 1);
|
| }
|
|
|
| #[test]
|
| fn test_mutation_validation_valid() {
|
| let result = validate_mutation_wasm(
|
| 1,
|
| 5,
|
| &[0x01; 32],
|
| &[0x02; 32],
|
| &[0x03; 32],
|
| &[0x04; 64],
|
| );
|
| assert!(result.valid);
|
| assert_eq!(result.error_code, 0);
|
| }
|
|
|
| #[test]
|
| fn test_mutation_validation_invalid_event_id() {
|
| let result = validate_mutation_wasm(
|
| 0,
|
| 5,
|
| &[0x01; 32],
|
| &[0x02; 32],
|
| &[0x03; 32],
|
| &[0x04; 64],
|
| );
|
| assert!(!result.valid);
|
| }
|
|
|
| #[test]
|
| fn test_proof_certificate_validation() {
|
| let mut cert = vec![0u8; 157];
|
|
|
| cert[0] = 0x01;
|
| cert[1] = 0x00;
|
|
|
| cert[36] = 0x0F;
|
| cert[37] = 0x00;
|
|
|
| cert[40] = 0x07;
|
| cert[41] = 0x00;
|
|
|
| for (i, byte) in b"x86_64".iter().enumerate() {
|
| cert[44 + i] = *byte;
|
| }
|
|
|
| cert[60] = 2;
|
|
|
| let result = validate_proof_certificate_wasm(&cert);
|
| assert!(result.valid);
|
| assert_eq!(result.theorem_id, 0x0001);
|
| assert_eq!(result.theorems_covered, 0x000F);
|
| }
|
| }
|
|
|