Download frontend/wasm/src/crypto_engine.rs from Snapkitty/sov-kernel-monster: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/frontend/wasm/src/crypto_engine.rs
- Command line
-
hf download hf://Snapkitty/sov-kernel-monster/frontend/wasm/src/crypto_engine.rs
-
curl -L -o crypto_engine.rs https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/frontend/wasm/src/crypto_engine.rs
10.3 kB
| /// Crypto Engine — deterministic cryptographic operations with timing-safe comparison | |
| /// Provides SHA-512, Blake3, HMAC, and cryptographic utilities for secure operations. | |
| /// All functions are deterministic (same input = same output) and timing-safe. | |
| use wasm_bindgen::prelude::*; | |
| use sha2::{Sha512, Digest}; | |
| use hmac::{Hmac, Mac}; | |
| /// SHA-512 hash | |
| /// | |
| /// # Arguments | |
| /// * `input` - UTF-8 string to hash | |
| /// | |
| /// # Returns | |
| /// SHA-512 hash as 128 hexadecimal characters (512 bits / 4 bits per char) | |
| pub fn sha512(input: &str) -> String { | |
| let mut hasher = Sha512::new(); | |
| hasher.update(input.as_bytes()); | |
| let result = hasher.finalize(); | |
| format!("{:x}", result) | |
| } | |
| /// Verify SHA-512 hash | |
| /// | |
| /// # Arguments | |
| /// * `input` - Original UTF-8 string | |
| /// * `hash` - Expected SHA-512 hash (128 hex characters) | |
| /// | |
| /// # Returns | |
| /// true if computed hash matches provided hash (constant-time comparison) | |
| pub fn verify_sha512(input: &str, hash: &str) -> bool { | |
| let computed = sha512(input); | |
| constant_time_compare(&computed, hash) | |
| } | |
| /// Blake3 hash | |
| /// | |
| /// # Arguments | |
| /// * `input` - UTF-8 string to hash | |
| /// | |
| /// # Returns | |
| /// Blake3 hash as 64 hexadecimal characters (256 bits / 4 bits per char) | |
| pub fn blake3(input: &str) -> String { | |
| let hash = blake3::hash(input.as_bytes()); | |
| hash.to_hex().to_string() | |
| } | |
| /// Verify Blake3 hash | |
| /// | |
| /// # Arguments | |
| /// * `input` - Original UTF-8 string | |
| /// * `hash` - Expected Blake3 hash (64 hex characters) | |
| /// | |
| /// # Returns | |
| /// true if computed hash matches provided hash (constant-time comparison) | |
| pub fn verify_blake3(input: &str, hash: &str) -> bool { | |
| let computed = blake3(input); | |
| constant_time_compare(&computed, hash) | |
| } | |
| /// HMAC-SHA512 | |
| /// | |
| /// # Arguments | |
| /// * `key` - HMAC key as UTF-8 string | |
| /// * `message` - Message to authenticate | |
| /// | |
| /// # Returns | |
| /// HMAC-SHA512 as 128 hexadecimal characters | |
| pub fn hmac_sha512(key: &str, message: &str) -> String { | |
| type HmacSha512 = Hmac<Sha512>; | |
| let mut mac = HmacSha512::new_from_slice(key.as_bytes()) | |
| .expect("HMAC key can be any length"); | |
| mac.update(message.as_bytes()); | |
| let result = mac.finalize(); | |
| format!("{:x}", result.into_bytes()) | |
| } | |
| /// Validate SHA-512 hex string format | |
| /// | |
| /// SHA-512 must be exactly 128 hexadecimal characters (512 bits) | |
| pub fn is_valid_sha512_hex(s: &str) -> bool { | |
| if s.len() != 128 { | |
| return false; | |
| } | |
| s.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f' | 'A'..='F')) | |
| } | |
| /// Validate Blake3 hex string format | |
| /// | |
| /// Blake3 must be exactly 64 hexadecimal characters (256 bits) | |
| pub fn is_valid_blake3_hex(s: &str) -> bool { | |
| if s.len() != 64 { | |
| return false; | |
| } | |
| s.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f' | 'A'..='F')) | |
| } | |
| /// Timing-safe constant-time string comparison | |
| /// | |
| /// Compares two strings in constant time to prevent timing side-channel attacks. | |
| /// Returns true only if strings are identical and same length. | |
| /// | |
| /// # Arguments | |
| /// * `a` - First string | |
| /// * `b` - Second string | |
| /// | |
| /// # Returns | |
| /// true if strings are equal (constant-time, safe for cryptographic comparison) | |
| pub fn constant_time_compare(a: &str, b: &str) -> bool { | |
| // First, compare lengths without early exit (constant-time length check) | |
| let len_match = a.len() == b.len(); | |
| // Always compare full byte sequences to prevent timing leaks | |
| let a_bytes = a.as_bytes(); | |
| let b_bytes = b.as_bytes(); | |
| let mut result = 0u8; | |
| // Compare each byte, accumulating differences | |
| // This runs in constant time regardless of where differences occur | |
| for i in 0..a_bytes.len().max(b_bytes.len()) { | |
| let byte_a = if i < a_bytes.len() { a_bytes[i] } else { 0 }; | |
| let byte_b = if i < b_bytes.len() { b_bytes[i] } else { 0 }; | |
| result |= byte_a ^ byte_b; | |
| } | |
| // result is 0 if all bytes matched | |
| len_match && result == 0 | |
| } | |
| mod tests { | |
| use super::*; | |
| fn test_sha512_deterministic() { | |
| let input = "hello"; | |
| let hash1 = sha512(input); | |
| let hash2 = sha512(input); | |
| assert_eq!(hash1, hash2); | |
| } | |
| fn test_sha512_known_value() { | |
| // Known SHA-512 hash of "hello" | |
| let input = "hello"; | |
| let hash = sha512(input); | |
| // Note: just verify it's 128 chars and deterministic | |
| assert_eq!(hash.len(), 128); | |
| } | |
| fn test_sha512_empty_string() { | |
| let hash = sha512(""); | |
| assert_eq!(hash.len(), 128); | |
| } | |
| fn test_sha512_long_input() { | |
| let input = "a".repeat(10000); | |
| let hash = sha512(&input); | |
| assert_eq!(hash.len(), 128); | |
| } | |
| fn test_verify_sha512_correct() { | |
| let input = "test"; | |
| let hash = sha512(input); | |
| assert!(verify_sha512(input, &hash)); | |
| } | |
| fn test_verify_sha512_incorrect() { | |
| let input = "test"; | |
| let wrong_hash = "0".repeat(128); | |
| assert!(!verify_sha512(input, &wrong_hash)); | |
| } | |
| fn test_blake3_deterministic() { | |
| let input = "hello"; | |
| let hash1 = blake3(input); | |
| let hash2 = blake3(input); | |
| assert_eq!(hash1, hash2); | |
| } | |
| fn test_blake3_format() { | |
| let hash = blake3("test"); | |
| assert_eq!(hash.len(), 64); | |
| assert!(hash.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f'))); | |
| } | |
| fn test_blake3_empty_string() { | |
| let hash = blake3(""); | |
| assert_eq!(hash.len(), 64); | |
| } | |
| fn test_verify_blake3_correct() { | |
| let input = "test"; | |
| let hash = blake3(input); | |
| assert!(verify_blake3(input, &hash)); | |
| } | |
| fn test_verify_blake3_incorrect() { | |
| let input = "test"; | |
| let wrong_hash = "0".repeat(64); | |
| assert!(!verify_blake3(input, &wrong_hash)); | |
| } | |
| fn test_hmac_sha512_deterministic() { | |
| let key = "secret_key"; | |
| let message = "message"; | |
| let hmac1 = hmac_sha512(key, message); | |
| let hmac2 = hmac_sha512(key, message); | |
| assert_eq!(hmac1, hmac2); | |
| } | |
| fn test_hmac_sha512_format() { | |
| let hmac = hmac_sha512("key", "message"); | |
| assert_eq!(hmac.len(), 128); | |
| assert!(hmac.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f'))); | |
| } | |
| fn test_hmac_sha512_key_sensitivity() { | |
| let message = "message"; | |
| let hmac1 = hmac_sha512("key1", message); | |
| let hmac2 = hmac_sha512("key2", message); | |
| assert_ne!(hmac1, hmac2); | |
| } | |
| fn test_hmac_sha512_message_sensitivity() { | |
| let key = "key"; | |
| let hmac1 = hmac_sha512(key, "message1"); | |
| let hmac2 = hmac_sha512(key, "message2"); | |
| assert_ne!(hmac1, hmac2); | |
| } | |
| fn test_hmac_sha512_empty_key() { | |
| let hmac = hmac_sha512("", "message"); | |
| assert_eq!(hmac.len(), 128); | |
| } | |
| fn test_hmac_sha512_empty_message() { | |
| let hmac = hmac_sha512("key", ""); | |
| assert_eq!(hmac.len(), 128); | |
| } | |
| fn test_is_valid_sha512_hex_correct() { | |
| let valid = "0".repeat(128); | |
| assert!(is_valid_sha512_hex(&valid)); | |
| } | |
| fn test_is_valid_sha512_hex_mixed_case() { | |
| // 128 chars = 32 * 4 char pattern | |
| let pattern = "aAbBcCdD"; // 8 chars | |
| let valid = pattern.repeat(16); // 8 * 16 = 128 chars | |
| assert_eq!(valid.len(), 128); | |
| assert!(is_valid_sha512_hex(&valid)); | |
| } | |
| fn test_is_valid_sha512_hex_too_short() { | |
| assert!(!is_valid_sha512_hex(&"0".repeat(127))); | |
| } | |
| fn test_is_valid_sha512_hex_too_long() { | |
| assert!(!is_valid_sha512_hex(&"0".repeat(129))); | |
| } | |
| fn test_is_valid_sha512_hex_invalid_chars() { | |
| let invalid = "0".repeat(127) + "g"; // 'g' is not hex | |
| assert!(!is_valid_sha512_hex(&invalid)); | |
| } | |
| fn test_is_valid_blake3_hex_correct() { | |
| let valid = "0".repeat(64); | |
| assert!(is_valid_blake3_hex(&valid)); | |
| } | |
| fn test_is_valid_blake3_hex_mixed_case() { | |
| // 64 chars = 8 * 8 char pattern | |
| let pattern = "aAbBcCdD"; // 8 chars | |
| let valid = pattern.repeat(8); // 8 * 8 = 64 chars | |
| assert_eq!(valid.len(), 64); | |
| assert!(is_valid_blake3_hex(&valid)); | |
| } | |
| fn test_is_valid_blake3_hex_too_short() { | |
| assert!(!is_valid_blake3_hex(&"0".repeat(63))); | |
| } | |
| fn test_is_valid_blake3_hex_too_long() { | |
| assert!(!is_valid_blake3_hex(&"0".repeat(65))); | |
| } | |
| fn test_is_valid_blake3_hex_invalid_chars() { | |
| let invalid = "0".repeat(63) + "g"; | |
| assert!(!is_valid_blake3_hex(&invalid)); | |
| } | |
| fn test_constant_time_compare_equal() { | |
| assert!(constant_time_compare("hello", "hello")); | |
| assert!(constant_time_compare("", "")); | |
| } | |
| fn test_constant_time_compare_not_equal() { | |
| assert!(!constant_time_compare("hello", "world")); | |
| assert!(!constant_time_compare("a", "b")); | |
| } | |
| fn test_constant_time_compare_different_length() { | |
| assert!(!constant_time_compare("hello", "hello world")); | |
| assert!(!constant_time_compare("", "a")); | |
| } | |
| fn test_constant_time_compare_case_sensitive() { | |
| assert!(!constant_time_compare("Hello", "hello")); | |
| } | |
| fn test_constant_time_compare_unicode() { | |
| assert!(constant_time_compare("café", "café")); | |
| assert!(!constant_time_compare("cafe", "café")); | |
| } | |
| fn test_constant_time_compare_long_strings() { | |
| let s1 = "a".repeat(10000); | |
| let s2 = "a".repeat(10000); | |
| assert!(constant_time_compare(&s1, &s2)); | |
| let s3 = "a".repeat(9999) + "b"; | |
| assert!(!constant_time_compare(&s1, &s3)); | |
| } | |
| fn test_constant_time_compare_hex_strings() { | |
| let hex1 = "abc123def456"; | |
| let hex2 = "abc123def456"; | |
| assert!(constant_time_compare(hex1, hex2)); | |
| let hex3 = "abc123def457"; | |
| assert!(!constant_time_compare(hex1, hex3)); | |
| } | |
| } | |