use crate::seqstring::global_string;
use crate::stack::{Stack, pop, push};
use crate::value::Value;
use aes_gcm::{
Aes256Gcm, Nonce,
aead::{Aead, KeyInit as AesKeyInit, OsRng, rand_core::RngCore as AeadRngCore},
};
use base64::{Engine, engine::general_purpose::STANDARD};
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use hmac::{Hmac, Mac};
use rand::thread_rng;
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;
use uuid::Uuid;
const AES_NONCE_SIZE: usize = 12;
const AES_KEY_SIZE: usize = 32;
const AES_GCM_TAG_SIZE: usize = 16;
const MIN_PBKDF2_ITERATIONS: i64 = 1_000;
type HmacSha256 = Hmac<Sha256>;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_sha256(stack: Stack) -> Stack {
assert!(!stack.is_null(), "sha256: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => {
let mut hasher = Sha256::new();
hasher.update(s.as_str().as_bytes());
let result = hasher.finalize();
let hex_digest = hex::encode(result);
unsafe { push(stack, Value::String(global_string(hex_digest))) }
}
_ => panic!("sha256: expected String on stack, got {:?}", value),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_hmac_sha256(stack: Stack) -> Stack {
assert!(!stack.is_null(), "hmac-sha256: stack is empty");
let (stack, key_value) = unsafe { pop(stack) };
let (stack, msg_value) = unsafe { pop(stack) };
match (msg_value, key_value) {
(Value::String(msg), Value::String(key)) => {
let mut mac = <HmacSha256 as Mac>::new_from_slice(key.as_str().as_bytes())
.expect("HMAC can take any key");
mac.update(msg.as_str().as_bytes());
let result = mac.finalize();
let hex_sig = hex::encode(result.into_bytes());
unsafe { push(stack, Value::String(global_string(hex_sig))) }
}
(msg, key) => panic!(
"hmac-sha256: expected (String, String) on stack, got ({:?}, {:?})",
msg, key
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_constant_time_eq(stack: Stack) -> Stack {
assert!(!stack.is_null(), "constant-time-eq: stack is empty");
let (stack, b_value) = unsafe { pop(stack) };
let (stack, a_value) = unsafe { pop(stack) };
match (a_value, b_value) {
(Value::String(a), Value::String(b)) => {
let a_bytes = a.as_str().as_bytes();
let b_bytes = b.as_str().as_bytes();
let eq = a_bytes.ct_eq(b_bytes);
unsafe { push(stack, Value::Bool(bool::from(eq))) }
}
(a, b) => panic!(
"constant-time-eq: expected (String, String) on stack, got ({:?}, {:?})",
a, b
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_random_bytes(stack: Stack) -> Stack {
assert!(!stack.is_null(), "random-bytes: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::Int(n) => {
if n < 0 {
panic!("random-bytes: byte count must be non-negative, got {}", n);
}
if n > 1024 {
panic!("random-bytes: byte count too large (max 1024), got {}", n);
}
let mut bytes = vec![0u8; n as usize];
thread_rng().fill_bytes(&mut bytes);
let hex_str = hex::encode(&bytes);
unsafe { push(stack, Value::String(global_string(hex_str))) }
}
_ => panic!("random-bytes: expected Int on stack, got {:?}", value),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_uuid4(stack: Stack) -> Stack {
assert!(!stack.is_null(), "uuid4: stack is empty");
let uuid = Uuid::new_v4();
unsafe { push(stack, Value::String(global_string(uuid.to_string()))) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_random_int(stack: Stack) -> Stack {
assert!(!stack.is_null(), "random-int: stack is empty");
let (stack, max_val) = unsafe { pop(stack) };
let (stack, min_val) = unsafe { pop(stack) };
match (min_val, max_val) {
(Value::Int(min), Value::Int(max)) => {
let result = if min >= max {
min } else {
random_int_range(min, max)
};
unsafe { push(stack, Value::Int(result)) }
}
(min, max) => panic!(
"random-int: expected (Int, Int) on stack, got ({:?}, {:?})",
min, max
),
}
}
fn random_int_range(min: i64, max: i64) -> i64 {
let range = (max as u64).wrapping_sub(min as u64);
if range == 0 {
return min;
}
let threshold = u64::MAX - (u64::MAX % range);
loop {
let mut bytes = [0u8; 8];
thread_rng().fill_bytes(&mut bytes);
let val = u64::from_le_bytes(bytes);
if val < threshold {
let result = (min as u64).wrapping_add(val % range);
return result as i64;
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_aes_gcm_encrypt(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.aes-gcm-encrypt: stack is null");
let (stack, key_val) = unsafe { pop(stack) };
let (stack, plaintext_val) = unsafe { pop(stack) };
match (plaintext_val, key_val) {
(Value::String(plaintext), Value::String(key_hex)) => {
match aes_gcm_encrypt(plaintext.as_str(), key_hex.as_str()) {
Some(ciphertext) => {
let stack = unsafe { push(stack, Value::String(global_string(ciphertext))) };
unsafe { push(stack, Value::Bool(true)) }
}
None => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
}
}
_ => panic!("crypto.aes-gcm-encrypt: expected two Strings on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_aes_gcm_decrypt(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.aes-gcm-decrypt: stack is null");
let (stack, key_val) = unsafe { pop(stack) };
let (stack, ciphertext_val) = unsafe { pop(stack) };
match (ciphertext_val, key_val) {
(Value::String(ciphertext), Value::String(key_hex)) => {
match aes_gcm_decrypt(ciphertext.as_str(), key_hex.as_str()) {
Some(plaintext) => {
let stack = unsafe { push(stack, Value::String(global_string(plaintext))) };
unsafe { push(stack, Value::Bool(true)) }
}
None => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
}
}
_ => panic!("crypto.aes-gcm-decrypt: expected two Strings on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_pbkdf2_sha256(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.pbkdf2-sha256: stack is null");
let (stack, iterations_val) = unsafe { pop(stack) };
let (stack, salt_val) = unsafe { pop(stack) };
let (stack, password_val) = unsafe { pop(stack) };
match (password_val, salt_val, iterations_val) {
(Value::String(password), Value::String(salt), Value::Int(iterations)) => {
if iterations < MIN_PBKDF2_ITERATIONS {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
return unsafe { push(stack, Value::Bool(false)) };
}
let key = derive_key_pbkdf2(password.as_str(), salt.as_str(), iterations as u32);
let key_hex = hex::encode(key);
let stack = unsafe { push(stack, Value::String(global_string(key_hex))) };
unsafe { push(stack, Value::Bool(true)) }
}
_ => panic!("crypto.pbkdf2-sha256: expected String, String, Int on stack"),
}
}
fn aes_gcm_encrypt(plaintext: &str, key_hex: &str) -> Option<String> {
let key_bytes = hex::decode(key_hex).ok()?;
if key_bytes.len() != AES_KEY_SIZE {
return None;
}
let cipher = Aes256Gcm::new_from_slice(&key_bytes).ok()?;
let mut nonce_bytes = [0u8; AES_NONCE_SIZE];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes()).ok()?;
let mut combined = Vec::with_capacity(AES_NONCE_SIZE + ciphertext.len());
combined.extend_from_slice(&nonce_bytes);
combined.extend_from_slice(&ciphertext);
Some(STANDARD.encode(&combined))
}
fn aes_gcm_decrypt(ciphertext_b64: &str, key_hex: &str) -> Option<String> {
let combined = STANDARD.decode(ciphertext_b64).ok()?;
if combined.len() < AES_NONCE_SIZE + AES_GCM_TAG_SIZE {
return None;
}
let key_bytes = hex::decode(key_hex).ok()?;
if key_bytes.len() != AES_KEY_SIZE {
return None;
}
let (nonce_bytes, ciphertext) = combined.split_at(AES_NONCE_SIZE);
let nonce = Nonce::from_slice(nonce_bytes);
let cipher = Aes256Gcm::new_from_slice(&key_bytes).ok()?;
let plaintext_bytes = cipher.decrypt(nonce, ciphertext).ok()?;
String::from_utf8(plaintext_bytes).ok()
}
fn derive_key_pbkdf2(password: &str, salt: &str, iterations: u32) -> [u8; AES_KEY_SIZE] {
let mut key = [0u8; AES_KEY_SIZE];
pbkdf2::pbkdf2_hmac::<Sha256>(password.as_bytes(), salt.as_bytes(), iterations, &mut key);
key
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_ed25519_keypair(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.ed25519-keypair: stack is null");
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_hex = hex::encode(signing_key.to_bytes());
let public_hex = hex::encode(verifying_key.to_bytes());
let stack = unsafe { push(stack, Value::String(global_string(public_hex))) };
unsafe { push(stack, Value::String(global_string(private_hex))) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_ed25519_sign(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.ed25519-sign: stack is null");
let (stack, key_val) = unsafe { pop(stack) };
let (stack, msg_val) = unsafe { pop(stack) };
match (msg_val, key_val) {
(Value::String(message), Value::String(private_key_hex)) => {
match ed25519_sign(message.as_str(), private_key_hex.as_str()) {
Some(signature) => {
let stack = unsafe { push(stack, Value::String(global_string(signature))) };
unsafe { push(stack, Value::Bool(true)) }
}
None => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
}
}
_ => panic!("crypto.ed25519-sign: expected String, String on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_crypto_ed25519_verify(stack: Stack) -> Stack {
assert!(!stack.is_null(), "crypto.ed25519-verify: stack is null");
let (stack, pubkey_val) = unsafe { pop(stack) };
let (stack, sig_val) = unsafe { pop(stack) };
let (stack, msg_val) = unsafe { pop(stack) };
match (msg_val, sig_val, pubkey_val) {
(Value::String(message), Value::String(signature_hex), Value::String(public_key_hex)) => {
let valid = ed25519_verify(
message.as_str(),
signature_hex.as_str(),
public_key_hex.as_str(),
);
unsafe { push(stack, Value::Bool(valid)) }
}
_ => panic!("crypto.ed25519-verify: expected String, String, String on stack"),
}
}
fn ed25519_sign(message: &str, private_key_hex: &str) -> Option<String> {
let key_bytes = hex::decode(private_key_hex).ok()?;
if key_bytes.len() != 32 {
return None;
}
let key_array: [u8; 32] = key_bytes.try_into().ok()?;
let signing_key = SigningKey::from_bytes(&key_array);
let signature = signing_key.sign(message.as_bytes());
Some(hex::encode(signature.to_bytes()))
}
fn ed25519_verify(message: &str, signature_hex: &str, public_key_hex: &str) -> bool {
let verify_inner = || -> Option<bool> {
let sig_bytes = hex::decode(signature_hex).ok()?;
if sig_bytes.len() != 64 {
return Some(false);
}
let pubkey_bytes = hex::decode(public_key_hex).ok()?;
if pubkey_bytes.len() != 32 {
return Some(false);
}
let sig_array: [u8; 64] = sig_bytes.try_into().ok()?;
let pubkey_array: [u8; 32] = pubkey_bytes.try_into().ok()?;
let signature = Signature::from_bytes(&sig_array);
let verifying_key = VerifyingKey::from_bytes(&pubkey_array).ok()?;
Some(verifying_key.verify(message.as_bytes(), &signature).is_ok())
};
verify_inner().unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stack::pop;
#[test]
fn test_sha256() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = patch_seq_sha256(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(
s.as_str(),
"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
);
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_sha256_empty() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string(String::new())));
let stack = patch_seq_sha256(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(
s.as_str(),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_hmac_sha256() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("message".to_string())));
let stack = push(stack, Value::String(global_string("secret".to_string())));
let stack = patch_seq_hmac_sha256(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(
s.as_str(),
"8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"
);
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_constant_time_eq_equal() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = patch_seq_constant_time_eq(stack);
let (_, value) = pop(stack);
match value {
Value::Bool(b) => assert!(b),
_ => panic!("Expected Bool"),
}
}
}
#[test]
fn test_constant_time_eq_different() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = push(stack, Value::String(global_string("world".to_string())));
let stack = patch_seq_constant_time_eq(stack);
let (_, value) = pop(stack);
match value {
Value::Bool(b) => assert!(!b),
_ => panic!("Expected Bool"),
}
}
}
#[test]
fn test_constant_time_eq_different_lengths() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = push(stack, Value::String(global_string("hi".to_string())));
let stack = patch_seq_constant_time_eq(stack);
let (_, value) = pop(stack);
match value {
Value::Bool(b) => assert!(!b),
_ => panic!("Expected Bool"),
}
}
}
#[test]
fn test_random_bytes() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(16));
let stack = patch_seq_random_bytes(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(s.as_str().len(), 32);
assert!(hex::decode(s.as_str()).is_ok());
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_random_bytes_zero() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(0));
let stack = patch_seq_random_bytes(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(s.as_str(), "");
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_uuid4() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = patch_seq_uuid4(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(s.as_str().len(), 36);
assert_eq!(s.as_str().chars().filter(|c| *c == '-').count(), 4);
assert_eq!(s.as_str().chars().nth(14), Some('4'));
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_uuid4_unique() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = patch_seq_uuid4(stack);
let (stack, value1) = pop(stack);
let stack = patch_seq_uuid4(stack);
let (_, value2) = pop(stack);
match (value1, value2) {
(Value::String(s1), Value::String(s2)) => {
assert_ne!(s1.as_str(), s2.as_str());
}
_ => panic!("Expected Strings"),
}
}
}
#[test]
fn test_random_bytes_max_limit() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(1024)); let stack = patch_seq_random_bytes(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert_eq!(s.as_str().len(), 2048);
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_aes_gcm_roundtrip() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let key_hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let stack = push(
stack,
Value::String(global_string("hello world".to_string())),
);
let stack = push(stack, Value::String(global_string(key_hex.to_string())));
let stack = patch_seq_crypto_aes_gcm_encrypt(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let stack = push(stack, Value::String(global_string(key_hex.to_string())));
let stack = patch_seq_crypto_aes_gcm_decrypt(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_, result) = pop(stack);
if let Value::String(s) = result {
assert_eq!(s.as_str(), "hello world");
} else {
panic!("expected string");
}
}
}
#[test]
fn test_aes_gcm_wrong_key() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let key1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let key2 = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
let stack = push(
stack,
Value::String(global_string("secret message".to_string())),
);
let stack = push(stack, Value::String(global_string(key1.to_string())));
let stack = patch_seq_crypto_aes_gcm_encrypt(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let stack = push(stack, Value::String(global_string(key2.to_string())));
let stack = patch_seq_crypto_aes_gcm_decrypt(stack);
let (_, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
}
}
#[test]
fn test_aes_gcm_invalid_key_length() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let short_key = "0123456789abcdef";
let stack = push(stack, Value::String(global_string("test data".to_string())));
let stack = push(stack, Value::String(global_string(short_key.to_string())));
let stack = patch_seq_crypto_aes_gcm_encrypt(stack);
let (_, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
}
}
#[test]
fn test_aes_gcm_invalid_ciphertext() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let stack = push(
stack,
Value::String(global_string("not-valid-base64!!!".to_string())),
);
let stack = push(stack, Value::String(global_string(key.to_string())));
let stack = patch_seq_crypto_aes_gcm_decrypt(stack);
let (_, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
}
}
#[test]
fn test_aes_gcm_empty_plaintext() {
let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let ciphertext = aes_gcm_encrypt("", key).unwrap();
let decrypted = aes_gcm_decrypt(&ciphertext, key).unwrap();
assert_eq!(decrypted, "");
}
#[test]
fn test_aes_gcm_special_characters() {
let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let plaintext = "Hello\nWorld\tTab\"Quote\\Backslash";
let ciphertext = aes_gcm_encrypt(plaintext, key).unwrap();
let decrypted = aes_gcm_decrypt(&ciphertext, key).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_pbkdf2_sha256() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(
stack,
Value::String(global_string("my-password".to_string())),
);
let stack = push(
stack,
Value::String(global_string("random-salt".to_string())),
);
let stack = push(stack, Value::Int(10000));
let stack = patch_seq_crypto_pbkdf2_sha256(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_, result) = pop(stack);
if let Value::String(s) = result {
assert_eq!(s.as_str().len(), 64);
assert!(hex::decode(s.as_str()).is_ok());
} else {
panic!("expected string");
}
}
}
#[test]
fn test_pbkdf2_deterministic() {
let key1 = derive_key_pbkdf2("password", "salt", 10000);
let key2 = derive_key_pbkdf2("password", "salt", 10000);
assert_eq!(key1, key2);
let key3 = derive_key_pbkdf2("password", "different-salt", 10000);
assert_ne!(key1, key3);
}
#[test]
fn test_pbkdf2_invalid_iterations() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("password".to_string())));
let stack = push(stack, Value::String(global_string("salt".to_string())));
let stack = push(stack, Value::Int(0));
let stack = patch_seq_crypto_pbkdf2_sha256(stack);
let (_, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
}
}
#[test]
fn test_encrypt_decrypt_with_derived_key() {
let password = "user-secret-password";
let salt = "unique-user-salt";
let iterations = 10000u32;
let key = derive_key_pbkdf2(password, salt, iterations);
let key_hex = hex::encode(key);
let plaintext = "sensitive data to protect";
let ciphertext = aes_gcm_encrypt(plaintext, &key_hex).unwrap();
let decrypted = aes_gcm_decrypt(&ciphertext, &key_hex).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_ed25519_sign_verify() {
let message = "Hello, World!";
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_hex = hex::encode(signing_key.to_bytes());
let public_hex = hex::encode(verifying_key.to_bytes());
let signature = ed25519_sign(message, &private_hex).unwrap();
assert_eq!(signature.len(), 128);
assert!(ed25519_verify(message, &signature, &public_hex));
}
#[test]
fn test_ed25519_wrong_message() {
let message = "Original message";
let wrong_message = "Wrong message";
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_hex = hex::encode(signing_key.to_bytes());
let public_hex = hex::encode(verifying_key.to_bytes());
let signature = ed25519_sign(message, &private_hex).unwrap();
assert!(!ed25519_verify(wrong_message, &signature, &public_hex));
}
#[test]
fn test_ed25519_wrong_key() {
let message = "Test message";
let signing_key1 = SigningKey::generate(&mut OsRng);
let signing_key2 = SigningKey::generate(&mut OsRng);
let private_hex = hex::encode(signing_key1.to_bytes());
let wrong_public_hex = hex::encode(signing_key2.verifying_key().to_bytes());
let signature = ed25519_sign(message, &private_hex).unwrap();
assert!(!ed25519_verify(message, &signature, &wrong_public_hex));
}
#[test]
fn test_ed25519_invalid_key_length() {
let message = "Test message";
let invalid_key = "tooshort";
assert!(ed25519_sign(message, invalid_key).is_none());
}
#[test]
fn test_ed25519_invalid_signature() {
let message = "Test message";
let signing_key = SigningKey::generate(&mut OsRng);
let public_hex = hex::encode(signing_key.verifying_key().to_bytes());
let invalid_signature = "0".repeat(128);
assert!(!ed25519_verify(message, &invalid_signature, &public_hex));
}
#[test]
fn test_ed25519_empty_message() {
let message = "";
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_hex = hex::encode(signing_key.to_bytes());
let public_hex = hex::encode(verifying_key.to_bytes());
let signature = ed25519_sign(message, &private_hex).unwrap();
assert!(ed25519_verify(message, &signature, &public_hex));
}
#[test]
fn test_ed25519_keypair_ffi() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = patch_seq_crypto_ed25519_keypair(stack);
let (stack, private_key) = pop(stack);
let (_, public_key) = pop(stack);
if let Value::String(pk) = public_key {
assert_eq!(pk.as_str().len(), 64);
} else {
panic!("Expected String for public key");
}
if let Value::String(sk) = private_key {
assert_eq!(sk.as_str().len(), 64);
} else {
panic!("Expected String for private key");
}
}
}
#[test]
fn test_ed25519_sign_ffi() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let signing_key = SigningKey::generate(&mut OsRng);
let private_hex = hex::encode(signing_key.to_bytes());
let stack = push(
stack,
Value::String(global_string("Test message".to_string())),
);
let stack = push(stack, Value::String(global_string(private_hex)));
let stack = patch_seq_crypto_ed25519_sign(stack);
let (stack, success) = pop(stack);
let (_, signature) = pop(stack);
assert_eq!(success, Value::Bool(true));
if let Value::String(sig) = signature {
assert_eq!(sig.as_str().len(), 128); } else {
panic!("Expected String for signature");
}
}
}
#[test]
fn test_ed25519_verify_ffi() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_hex = hex::encode(signing_key.to_bytes());
let public_hex = hex::encode(verifying_key.to_bytes());
let message = "Verify this message";
let signature = ed25519_sign(message, &private_hex).unwrap();
let stack = push(stack, Value::String(global_string(message.to_string())));
let stack = push(stack, Value::String(global_string(signature)));
let stack = push(stack, Value::String(global_string(public_hex)));
let stack = patch_seq_crypto_ed25519_verify(stack);
let (_, valid) = pop(stack);
assert_eq!(valid, Value::Bool(true));
}
}
#[test]
fn test_random_int_basic() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(1));
let stack = push(stack, Value::Int(100));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => {
assert!((1..100).contains(&n), "Expected 1 <= {} < 100", n);
}
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_same_min_max() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(5));
let stack = push(stack, Value::Int(5));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => assert_eq!(n, 5),
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_inverted_range() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(10));
let stack = push(stack, Value::Int(5));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => assert_eq!(n, 10), _ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_small_range() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(0));
let stack = push(stack, Value::Int(2));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => assert!((0..2).contains(&n), "Expected 0 <= {} < 2", n),
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_negative_range() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(-10));
let stack = push(stack, Value::Int(10));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => assert!((-10..10).contains(&n), "Expected -10 <= {} < 10", n),
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_large_range() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(0));
let stack = push(stack, Value::Int(i64::MAX));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(n) => assert!(n >= 0, "Expected {} >= 0", n),
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_extreme_range() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(i64::MIN));
let stack = push(stack, Value::Int(i64::MAX));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
match value {
Value::Int(_) => {} _ => panic!("Expected Int"),
}
}
}
#[test]
fn test_random_int_uniformity() {
let mut buckets = [0u32; 10];
let samples = 10000;
unsafe {
for _ in 0..samples {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(0));
let stack = push(stack, Value::Int(10));
let stack = patch_seq_random_int(stack);
let (_, value) = pop(stack);
if let Value::Int(n) = value {
buckets[n as usize] += 1;
}
}
}
let expected = samples as u32 / 10;
let tolerance = expected * 30 / 100;
for (i, &count) in buckets.iter().enumerate() {
assert!(
count >= expected - tolerance && count <= expected + tolerance,
"Bucket {} has {} samples, expected {} ± {} (uniformity test)",
i,
count,
expected,
tolerance
);
}
}
}