use base64::{
Engine,
engine::general_purpose::STANDARD
};
use ed25519_dalek::{
Signature,
Signer,
Verifier,
SigningKey,
VerifyingKey,
PUBLIC_KEY_LENGTH,
SECRET_KEY_LENGTH
};
use rand::rngs::OsRng;
use crate::IronShieldChallenge;
use std::env;
macro_rules! debug_log {
($($arg:tt)*) => {
#[cfg(all(target_arch = "wasm32", feature = "wasm-logging"))]
{
let msg = format!($($arg)*);
web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&msg));
}
#[cfg(not(target_arch = "wasm32"))]
eprintln!($($arg)*);
#[cfg(all(target_arch = "wasm32", not(feature = "wasm-logging")))]
{
let _ = format!($($arg)*);
}
};
}
#[derive(Debug, Clone)]
pub enum CryptoError {
MissingEnvironmentVariable(String),
InvalidKeyFormat(String),
SigningFailed(String),
VerificationFailed(String),
Base64DecodingFailed(String),
PgpParsingFailed(String),
}
impl std::fmt::Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CryptoError::MissingEnvironmentVariable(var) => write!(f, "Missing environment variable: {}", var),
CryptoError::InvalidKeyFormat(msg) => write!(f, "Invalid key format: {}", msg),
CryptoError::SigningFailed(msg) => write!(f, "Signing failed: {}", msg),
CryptoError::VerificationFailed(msg) => write!(f, "Verification failed: {}", msg),
CryptoError::Base64DecodingFailed(msg) => write!(f, "Base64 decoding failed: {}", msg),
CryptoError::PgpParsingFailed(msg) => write!(f, "PGP parsing failed: {}", msg),
}
}
}
impl std::error::Error for CryptoError {}
fn parse_key_simple(key_data: &str, is_private: bool) -> Result<[u8; 32], CryptoError> {
let cleaned_data = key_data
.chars()
.filter(|c| !c.is_whitespace()) .collect::<String>();
debug_log!("🔑 Parsing key data: {} chars → {} chars after cleaning", key_data.len(), cleaned_data.len());
let invalid_chars: Vec<char> = cleaned_data
.chars()
.filter(|&c| !matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
.collect();
if !invalid_chars.is_empty() {
debug_log!("🔧 Fixing {} invalid base64 characters", invalid_chars.len());
let fixed_data = cleaned_data
.chars()
.filter(|&c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
.collect::<String>();
debug_log!("🔧 Fixed data length: {}", fixed_data.len());
match STANDARD.decode(&fixed_data) {
Ok(key_bytes) => {
debug_log!("✅ Fixed data decoded to {} bytes", key_bytes.len());
return try_extract_ed25519_key(&key_bytes, is_private);
}
Err(e) => {
debug_log!("⚠️ Fixed data decode failed: {}", e);
}
}
}
let key_bytes = match STANDARD.decode(&cleaned_data) {
Ok(bytes) => {
debug_log!("✅ Base64 decoded to {} bytes", bytes.len());
bytes
}
Err(e) => {
debug_log!("⚠️ Base64 decode failed: {}", e);
let mut test_data = cleaned_data.clone();
while !test_data.is_empty() {
if let Ok(bytes) = STANDARD.decode(&test_data) {
debug_log!("✅ Successful decode after trimming to {} chars → {} bytes", test_data.len(), bytes.len());
return try_extract_ed25519_key(&bytes, is_private);
}
test_data.pop();
}
return Err(CryptoError::Base64DecodingFailed(format!("Failed to decode cleaned key data: {}", e)));
}
};
try_extract_ed25519_key(&key_bytes, is_private)
}
fn try_extract_ed25519_key(key_bytes: &[u8], is_private: bool) -> Result<[u8; 32], CryptoError> {
debug_log!("🔑 Extracting Ed25519 key from {} bytes", key_bytes.len());
if key_bytes.len() == 32 {
let mut key_array = [0u8; 32];
key_array.copy_from_slice(&key_bytes);
if is_private {
let _signing_key = SigningKey::from_bytes(&key_array);
debug_log!("✅ Raw Ed25519 private key validated");
} else {
let _verifying_key = VerifyingKey::from_bytes(&key_array)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid raw public key: {}", e)))?;
debug_log!("✅ Raw Ed25519 public key validated");
}
return Ok(key_array);
}
if key_bytes.len() >= 32 {
debug_log!("🔍 Scanning PGP data for Ed25519 key...");
for window_start in 0..key_bytes.len().saturating_sub(32) {
let potential_key = &key_bytes[window_start..window_start + 32];
if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
continue;
}
let mut key_array = [0u8; 32];
key_array.copy_from_slice(potential_key);
if is_private {
let signing_key = SigningKey::from_bytes(&key_array);
let derived_public = signing_key.verifying_key();
let public_bytes = derived_public.to_bytes();
let search_start = window_start + 32;
if search_start < key_bytes.len() {
let remaining_data = &key_bytes[search_start..];
if remaining_data.windows(32).any(|window| window == public_bytes) {
debug_log!("✅ Private key found at offset {} (with matching public key)", window_start);
return Ok(key_array);
}
}
if window_start >= 20 && window_start <= 200 {
debug_log!("✅ Private key found at offset {}", window_start);
return Ok(key_array);
}
} else {
if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
if window_start >= 10 && window_start <= 100 {
debug_log!("✅ Public key found at offset {}", window_start);
return Ok(key_array);
}
}
}
}
for (i, &byte) in key_bytes.iter().enumerate() {
if byte == 0x16 && i + 33 < key_bytes.len() { let key_start = i + 1;
if key_start + 32 <= key_bytes.len() {
let potential_key = &key_bytes[key_start..key_start + 32];
let mut key_array = [0u8; 32];
key_array.copy_from_slice(potential_key);
if is_private {
let _signing_key = SigningKey::from_bytes(&key_array);
debug_log!("✅ Private key found via algorithm ID at offset {}", key_start);
return Ok(key_array);
} else {
if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
debug_log!("✅ Public key found via algorithm ID at offset {}", key_start);
return Ok(key_array);
}
}
}
}
}
let common_offsets = [
32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,
104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160
];
for &offset in &common_offsets {
if offset + 32 <= key_bytes.len() {
let potential_key = &key_bytes[offset..offset + 32];
if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
continue;
}
let mut key_array = [0u8; 32];
key_array.copy_from_slice(potential_key);
if is_private {
let _signing_key = SigningKey::from_bytes(&key_array);
debug_log!("✅ Private key found at common offset {}", offset);
return Ok(key_array);
} else {
if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
debug_log!("✅ Public key found at common offset {}", offset);
return Ok(key_array);
}
}
}
}
}
Err(CryptoError::PgpParsingFailed(format!(
"Could not find valid Ed25519 key material in {} bytes of PGP data using multiple strategies",
key_bytes.len()
)))
}
pub fn load_private_key_from_env() -> Result<SigningKey, CryptoError> {
let key_str: String = env::var("IRONSHIELD_PRIVATE_KEY")
.map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PRIVATE_KEY".to_string()))?;
match parse_key_simple(&key_str, true) {
Ok(key_array) => {
let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
return Ok(signing_key);
}
Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
}
Err(e) => return Err(e), }
let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
.map_err(|e| CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e)))?;
if key_bytes.len() != SECRET_KEY_LENGTH {
return Err(CryptoError::InvalidKeyFormat(
format!("Private key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
SECRET_KEY_LENGTH, key_bytes.len())
));
}
let key_array: [u8; SECRET_KEY_LENGTH] = key_bytes.try_into()
.map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert private key bytes".to_string()))?;
let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
Ok(signing_key)
}
pub fn load_public_key_from_env() -> Result<VerifyingKey, CryptoError> {
let key_str: String = env::var("IRONSHIELD_PUBLIC_KEY")
.map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PUBLIC_KEY".to_string()))?;
match parse_key_simple(&key_str, false) {
Ok(key_array) => {
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
return Ok(verifying_key);
}
Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
}
Err(e) => return Err(e), }
let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
.map_err(|e| CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e)))?;
if key_bytes.len() != PUBLIC_KEY_LENGTH {
return Err(CryptoError::InvalidKeyFormat(
format!("Public key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
PUBLIC_KEY_LENGTH, key_bytes.len())
));
}
let key_array: [u8; PUBLIC_KEY_LENGTH] = key_bytes.try_into()
.map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert public key bytes".to_string()))?;
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
Ok(verifying_key)
}
pub fn create_signing_message(
random_nonce: &str,
created_time: i64,
expiration_time: i64,
website_id: &str,
challenge_param: &[u8; 32],
public_key: &[u8; 32]
) -> String {
format!(
"{}|{}|{}|{}|{}|{}",
random_nonce,
created_time,
expiration_time,
website_id,
hex::encode(challenge_param),
hex::encode(public_key)
)
}
pub fn generate_signature(signing_key: &SigningKey, message: &str) -> Result<[u8; 64], CryptoError> {
let signature: Signature = signing_key.sign(message.as_bytes());
Ok(signature.to_bytes())
}
pub fn sign_challenge(challenge: &IronShieldChallenge) -> Result<[u8; 64], CryptoError> {
let signing_key: SigningKey = load_private_key_from_env()?;
let message: String = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
generate_signature(&signing_key, &message)
}
pub fn verify_challenge_signature(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
let verifying_key: VerifyingKey = load_public_key_from_env()?;
let message: String = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
verifying_key.verify(message.as_bytes(), &signature)
.map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
Ok(())
}
pub fn verify_challenge_signature_with_key(
challenge: &IronShieldChallenge,
public_key_bytes: &[u8; 32]
) -> Result<(), CryptoError> {
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(public_key_bytes)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
let message: String = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
verifying_key.verify(message.as_bytes(), &signature)
.map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
Ok(())
}
pub fn generate_test_keypair() -> (String, String) {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key_b64: String = STANDARD.encode(signing_key.to_bytes());
let public_key_b64: String = STANDARD.encode(verifying_key.to_bytes());
(private_key_b64, public_key_b64)
}
pub fn validate_challenge(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
verify_challenge_signature(challenge)?;
if challenge.is_expired() {
return Err(CryptoError::VerificationFailed("Challenge has expired".to_string()));
}
if challenge.website_id.is_empty() {
return Err(CryptoError::VerificationFailed("Empty website_id".to_string()));
}
Ok(())
}
pub fn load_private_key_from_data(key_data: &str) -> Result<SigningKey, CryptoError> {
match parse_key_simple(key_data, true) {
Ok(key_array) => {
let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
return Ok(signing_key);
}
Err(CryptoError::PgpParsingFailed(_msg)) => {
}
Err(CryptoError::Base64DecodingFailed(_msg)) => {
}
Err(e) => {
return Err(e); }
}
let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
.map_err(|e| {
CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e))
})?;
if key_bytes.len() != SECRET_KEY_LENGTH {
let error_msg = format!(
"Invalid key length: expected {} bytes for Ed25519 private key, got {} bytes",
SECRET_KEY_LENGTH,
key_bytes.len()
);
return Err(CryptoError::InvalidKeyFormat(error_msg));
}
let mut key_array = [0u8; SECRET_KEY_LENGTH];
key_array.copy_from_slice(&key_bytes);
Ok(SigningKey::from_bytes(&key_array))
}
pub fn load_public_key_from_data(key_data: &str) -> Result<VerifyingKey, CryptoError> {
match parse_key_simple(key_data, false) {
Ok(key_array) => {
let verifying_key = VerifyingKey::from_bytes(&key_array)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key from PGP: {}", e)))?;
return Ok(verifying_key);
}
Err(CryptoError::PgpParsingFailed(_msg)) => {
}
Err(CryptoError::Base64DecodingFailed(_msg)) => {
}
Err(e) => {
return Err(e); }
}
let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
.map_err(|e| {
CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e))
})?;
if key_bytes.len() != PUBLIC_KEY_LENGTH {
let error_msg = format!(
"Invalid key length: expected {} bytes for Ed25519 public key, got {} bytes",
PUBLIC_KEY_LENGTH,
key_bytes.len()
);
return Err(CryptoError::InvalidKeyFormat(error_msg));
}
let mut key_array = [0u8; PUBLIC_KEY_LENGTH];
key_array.copy_from_slice(&key_bytes);
let verifying_key = VerifyingKey::from_bytes(&key_array)
.map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid Ed25519 public key: {}", e)))?;
Ok(verifying_key)
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::sync::Mutex;
use rand::rngs::OsRng;
static ENV_MUTEX: Mutex<()> = Mutex::new(());
#[allow(dead_code)]
fn setup_isolated_test_keys() -> (SigningKey, VerifyingKey) {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
let _lock = ENV_MUTEX.lock().unwrap();
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
(signing_key, verifying_key)
}
#[test]
fn test_basic_ed25519_signing() {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let message = b"Hello, world!";
let signature: Signature = signing_key.sign(message);
let result = verifying_key.verify(message, &signature);
assert!(result.is_ok(), "Basic Ed25519 signing should work");
}
#[test]
fn test_crypto_integration_without_env() {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let challenge = IronShieldChallenge::new(
"example.com".to_string(),
100_000,
signing_key.clone(),
verifying_key.to_bytes(),
);
let signing_message = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
println!("Signing message: {}", signing_message);
let verification_message = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
assert_eq!(signing_message, verification_message, "Signing message should be consistent");
let signature_from_bytes = Signature::from_slice(&challenge.challenge_signature)
.expect("Should be able to recreate signature from bytes");
let verification_result = verifying_key.verify(verification_message.as_bytes(), &signature_from_bytes);
assert!(verification_result.is_ok(), "Manual verification should succeed");
let verify_result = verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes());
assert!(verify_result.is_ok(), "verify_challenge_signature_with_key should succeed");
}
#[test]
fn test_generate_test_keypair() {
let (private_key, public_key) = generate_test_keypair();
assert!(STANDARD.decode(&private_key).is_ok());
assert!(STANDARD.decode(&public_key).is_ok());
let private_bytes = STANDARD.decode(&private_key).unwrap();
let public_bytes = STANDARD.decode(&public_key).unwrap();
assert_eq!(private_bytes.len(), SECRET_KEY_LENGTH);
assert_eq!(public_bytes.len(), PUBLIC_KEY_LENGTH);
}
#[test]
fn test_load_keys_from_env() {
let _lock = ENV_MUTEX.lock().unwrap();
let (signing_key, verifying_key) = {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
(signing_key, verifying_key)
};
let loaded_signing_key = load_private_key_from_env().unwrap();
let loaded_verifying_key = load_public_key_from_env().unwrap();
assert_eq!(signing_key.to_bytes(), loaded_signing_key.to_bytes());
assert_eq!(verifying_key.to_bytes(), loaded_verifying_key.to_bytes());
}
#[test]
fn test_missing_environment_variables() {
let _lock = ENV_MUTEX.lock().unwrap();
env::remove_var("IRONSHIELD_PRIVATE_KEY");
env::remove_var("IRONSHIELD_PUBLIC_KEY");
let private_result = load_private_key_from_env();
assert!(private_result.is_err());
assert!(matches!(private_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
let public_result = load_public_key_from_env();
assert!(public_result.is_err());
assert!(matches!(public_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
}
#[test]
fn test_invalid_key_format() {
let _lock = ENV_MUTEX.lock().unwrap();
env::set_var("IRONSHIELD_PRIVATE_KEY", "invalid-base64!");
env::set_var("IRONSHIELD_PUBLIC_KEY", "invalid-base64!");
let private_result = load_private_key_from_env();
assert!(private_result.is_err());
assert!(matches!(private_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
let public_result = load_public_key_from_env();
assert!(public_result.is_err());
assert!(matches!(public_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
}
#[test]
fn test_challenge_signing_and_verification() {
let _lock = ENV_MUTEX.lock().unwrap();
let (signing_key, verifying_key) = {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
(signing_key, verifying_key)
};
let challenge = IronShieldChallenge::new(
"test_website".to_string(),
100_000,
signing_key.clone(),
verifying_key.to_bytes(),
);
verify_challenge_signature(&challenge).unwrap();
verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes()).unwrap();
assert_eq!(challenge.public_key, verifying_key.to_bytes());
}
#[test]
fn test_tampered_challenge_detection() {
let _lock = ENV_MUTEX.lock().unwrap();
let (signing_key, verifying_key) = {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
(signing_key, verifying_key)
};
let mut challenge = IronShieldChallenge::new(
"test_website".to_string(),
100_000,
signing_key.clone(),
verifying_key.to_bytes(),
);
verify_challenge_signature(&challenge).unwrap();
challenge.random_nonce = "tampered".to_string();
let result = verify_challenge_signature(&challenge);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), CryptoError::VerificationFailed(_)));
}
#[test]
fn test_invalid_signature_format() {
let _lock = ENV_MUTEX.lock().unwrap();
{
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
}
let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
let mut challenge = IronShieldChallenge::new(
"test_website".to_string(),
100_000,
dummy_key,
[0x34; 32],
);
challenge.challenge_signature = [0xFF; 64];
let result = verify_challenge_signature(&challenge);
assert!(result.is_err());
}
#[test]
fn test_signing_message_creation() {
let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
let challenge = IronShieldChallenge::new(
"test_website".to_string(),
100_000,
dummy_key,
[0x34; 32],
);
let message = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
let expected_prefix = format!(
"{}|{}|{}|{}|",
challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
challenge.website_id
);
assert!(message.starts_with(&expected_prefix));
assert!(message.ends_with(&hex::encode(challenge.public_key)));
}
#[test]
fn test_sign_challenge_uses_generate_signature() {
let _lock = ENV_MUTEX.lock().unwrap();
let (signing_key, verifying_key) = {
let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let private_key: String = STANDARD.encode(signing_key.to_bytes());
let public_key: String = STANDARD.encode(verifying_key.to_bytes());
env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
(signing_key, verifying_key)
};
let challenge = IronShieldChallenge::new(
"test_website".to_string(),
100_000,
signing_key.clone(),
verifying_key.to_bytes(),
);
let sign_challenge_result = sign_challenge(&challenge).unwrap();
let message = create_signing_message(
&challenge.random_nonce,
challenge.created_time,
challenge.expiration_time,
&challenge.website_id,
&challenge.challenge_param,
&challenge.public_key
);
let manual_signature = generate_signature(&signing_key, &message).unwrap();
assert_eq!(sign_challenge_result, manual_signature,
"sign_challenge should produce the same result as manual generate_signature");
}
}