1use base64::{
66 Engine,
67 engine::general_purpose::STANDARD
68};
69use ed25519_dalek::{
70 Signature,
71 Signer,
72 Verifier,
73 SigningKey,
74 VerifyingKey,
75 PUBLIC_KEY_LENGTH,
76 SECRET_KEY_LENGTH
77};
78use rand::rngs::OsRng;
79
80use crate::IronShieldChallenge;
81
82use std::env;
83
84macro_rules! debug_log {
86 ($($arg:tt)*) => {
87 #[cfg(all(target_arch = "wasm32", feature = "wasm-logging"))]
88 {
89 let msg = format!($($arg)*);
90 web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&msg));
91 }
92 #[cfg(not(target_arch = "wasm32"))]
93 eprintln!($($arg)*);
94 #[cfg(all(target_arch = "wasm32", not(feature = "wasm-logging")))]
95 {
96 let _ = format!($($arg)*);
98 }
99 };
100}
101
102#[derive(Debug, Clone)]
103pub enum CryptoError {
104 MissingEnvironmentVariable(String),
105 InvalidKeyFormat(String),
106 SigningFailed(String),
107 VerificationFailed(String),
108 Base64DecodingFailed(String),
109 PgpParsingFailed(String),
110}
111
112impl std::fmt::Display for CryptoError {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 match self {
115 CryptoError::MissingEnvironmentVariable(var) => write!(f, "Missing environment variable: {}", var),
116 CryptoError::InvalidKeyFormat(msg) => write!(f, "Invalid key format: {}", msg),
117 CryptoError::SigningFailed(msg) => write!(f, "Signing failed: {}", msg),
118 CryptoError::VerificationFailed(msg) => write!(f, "Verification failed: {}", msg),
119 CryptoError::Base64DecodingFailed(msg) => write!(f, "Base64 decoding failed: {}", msg),
120 CryptoError::PgpParsingFailed(msg) => write!(f, "PGP parsing failed: {}", msg),
121 }
122 }
123}
124
125impl std::error::Error for CryptoError {}
126
127fn parse_key_simple(key_data: &str, is_private: bool) -> Result<[u8; 32], CryptoError> {
140 let cleaned_data = key_data
142 .chars()
143 .filter(|c| !c.is_whitespace()) .collect::<String>();
145
146 debug_log!("🔑 Parsing key data: {} chars → {} chars after cleaning", key_data.len(), cleaned_data.len());
147
148 let invalid_chars: Vec<char> = cleaned_data
150 .chars()
151 .filter(|&c| !matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
152 .collect();
153
154 if !invalid_chars.is_empty() {
155 debug_log!("🔧 Fixing {} invalid base64 characters", invalid_chars.len());
156
157 let fixed_data = cleaned_data
159 .chars()
160 .filter(|&c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
161 .collect::<String>();
162
163 debug_log!("🔧 Fixed data length: {}", fixed_data.len());
164
165 match STANDARD.decode(&fixed_data) {
167 Ok(key_bytes) => {
168 debug_log!("✅ Fixed data decoded to {} bytes", key_bytes.len());
169 return try_extract_ed25519_key(&key_bytes, is_private);
170 }
171 Err(e) => {
172 debug_log!("⚠️ Fixed data decode failed: {}", e);
173 }
174 }
175 }
176
177 let key_bytes = match STANDARD.decode(&cleaned_data) {
179 Ok(bytes) => {
180 debug_log!("✅ Base64 decoded to {} bytes", bytes.len());
181 bytes
182 }
183 Err(e) => {
184 debug_log!("⚠️ Base64 decode failed: {}", e);
185
186 let mut test_data = cleaned_data.clone();
188 while !test_data.is_empty() {
189 if let Ok(bytes) = STANDARD.decode(&test_data) {
190 debug_log!("✅ Successful decode after trimming to {} chars → {} bytes", test_data.len(), bytes.len());
191 return try_extract_ed25519_key(&bytes, is_private);
192 }
193 test_data.pop();
194 }
195
196 return Err(CryptoError::Base64DecodingFailed(format!("Failed to decode cleaned key data: {}", e)));
197 }
198 };
199
200 try_extract_ed25519_key(&key_bytes, is_private)
201}
202
203fn try_extract_ed25519_key(key_bytes: &[u8], is_private: bool) -> Result<[u8; 32], CryptoError> {
205 debug_log!("🔑 Extracting Ed25519 key from {} bytes", key_bytes.len());
206
207 if key_bytes.len() == 32 {
209 let mut key_array = [0u8; 32];
210 key_array.copy_from_slice(&key_bytes);
211
212 if is_private {
214 let _signing_key = SigningKey::from_bytes(&key_array);
215 debug_log!("✅ Raw Ed25519 private key validated");
216 } else {
217 let _verifying_key = VerifyingKey::from_bytes(&key_array)
218 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid raw public key: {}", e)))?;
219 debug_log!("✅ Raw Ed25519 public key validated");
220 }
221
222 return Ok(key_array);
223 }
224
225 if key_bytes.len() >= 32 {
227 debug_log!("🔍 Scanning PGP data for Ed25519 key...");
228
229 for window_start in 0..key_bytes.len().saturating_sub(32) {
232 let potential_key = &key_bytes[window_start..window_start + 32];
233
234 if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
236 continue;
237 }
238
239 let mut key_array = [0u8; 32];
241 key_array.copy_from_slice(potential_key);
242
243 if is_private {
244 let signing_key = SigningKey::from_bytes(&key_array);
246 let derived_public = signing_key.verifying_key();
247
248 let public_bytes = derived_public.to_bytes();
250
251 let search_start = window_start + 32;
253 if search_start < key_bytes.len() {
254 let remaining_data = &key_bytes[search_start..];
255 if remaining_data.windows(32).any(|window| window == public_bytes) {
256 debug_log!("✅ Private key found at offset {} (with matching public key)", window_start);
257 return Ok(key_array);
258 }
259 }
260
261 if window_start >= 20 && window_start <= 200 {
263 debug_log!("✅ Private key found at offset {}", window_start);
264 return Ok(key_array);
265 }
266 } else {
267 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
269 if window_start >= 10 && window_start <= 100 {
271 debug_log!("✅ Public key found at offset {}", window_start);
272 return Ok(key_array);
273 }
274 }
275 }
276 }
277
278 for (i, &byte) in key_bytes.iter().enumerate() {
280 if byte == 0x16 && i + 33 < key_bytes.len() { let key_start = i + 1;
282 if key_start + 32 <= key_bytes.len() {
283 let potential_key = &key_bytes[key_start..key_start + 32];
284 let mut key_array = [0u8; 32];
285 key_array.copy_from_slice(potential_key);
286
287 if is_private {
289 let _signing_key = SigningKey::from_bytes(&key_array);
290 debug_log!("✅ Private key found via algorithm ID at offset {}", key_start);
291 return Ok(key_array);
292 } else {
293 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
294 debug_log!("✅ Public key found via algorithm ID at offset {}", key_start);
295 return Ok(key_array);
296 }
297 }
298 }
299 }
300 }
301
302 let common_offsets = [
304 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,
305 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160
306 ];
307
308 for &offset in &common_offsets {
309 if offset + 32 <= key_bytes.len() {
310 let potential_key = &key_bytes[offset..offset + 32];
311
312 if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
314 continue;
315 }
316
317 let mut key_array = [0u8; 32];
318 key_array.copy_from_slice(potential_key);
319
320 if is_private {
321 let _signing_key = SigningKey::from_bytes(&key_array);
322 debug_log!("✅ Private key found at common offset {}", offset);
323 return Ok(key_array);
324 } else {
325 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
326 debug_log!("✅ Public key found at common offset {}", offset);
327 return Ok(key_array);
328 }
329 }
330 }
331 }
332 }
333
334 Err(CryptoError::PgpParsingFailed(format!(
335 "Could not find valid Ed25519 key material in {} bytes of PGP data using multiple strategies",
336 key_bytes.len()
337 )))
338}
339
340pub fn load_private_key_from_env() -> Result<SigningKey, CryptoError> {
354 let key_str: String = env::var("IRONSHIELD_PRIVATE_KEY")
355 .map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PRIVATE_KEY".to_string()))?;
356
357 match parse_key_simple(&key_str, true) {
359 Ok(key_array) => {
360 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
361 return Ok(signing_key);
362 }
363 Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
364 }
366 Err(e) => return Err(e), }
368
369 let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
371 .map_err(|e| CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e)))?;
372
373 if key_bytes.len() != SECRET_KEY_LENGTH {
375 return Err(CryptoError::InvalidKeyFormat(
376 format!("Private key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
377 SECRET_KEY_LENGTH, key_bytes.len())
378 ));
379 }
380
381 let key_array: [u8; SECRET_KEY_LENGTH] = key_bytes.try_into()
383 .map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert private key bytes".to_string()))?;
384
385 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
386 Ok(signing_key)
387}
388
389pub fn load_public_key_from_env() -> Result<VerifyingKey, CryptoError> {
403 let key_str: String = env::var("IRONSHIELD_PUBLIC_KEY")
404 .map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PUBLIC_KEY".to_string()))?;
405
406 match parse_key_simple(&key_str, false) {
408 Ok(key_array) => {
409 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
410 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
411 return Ok(verifying_key);
412 }
413 Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
414 }
416 Err(e) => return Err(e), }
418
419 let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
421 .map_err(|e| CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e)))?;
422
423 if key_bytes.len() != PUBLIC_KEY_LENGTH {
425 return Err(CryptoError::InvalidKeyFormat(
426 format!("Public key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
427 PUBLIC_KEY_LENGTH, key_bytes.len())
428 ));
429 }
430
431 let key_array: [u8; PUBLIC_KEY_LENGTH] = key_bytes.try_into()
433 .map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert public key bytes".to_string()))?;
434
435 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
436 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
437
438 Ok(verifying_key)
439}
440
441pub fn create_signing_message(
458 random_nonce: &str,
459 created_time: i64,
460 expiration_time: i64,
461 website_id: &str,
462 challenge_param: &[u8; 32],
463 public_key: &[u8; 32]
464) -> String {
465 format!(
466 "{}|{}|{}|{}|{}|{}",
467 random_nonce,
468 created_time,
469 expiration_time,
470 website_id,
471 hex::encode(challenge_param),
472 hex::encode(public_key)
473 )
474}
475
476pub fn generate_signature(signing_key: &SigningKey, message: &str) -> Result<[u8; 64], CryptoError> {
497 let signature: Signature = signing_key.sign(message.as_bytes());
498 Ok(signature.to_bytes())
499}
500
501pub fn sign_challenge(challenge: &IronShieldChallenge) -> Result<[u8; 64], CryptoError> {
529 let signing_key: SigningKey = load_private_key_from_env()?;
530 let message: String = create_signing_message(
531 &challenge.random_nonce,
532 challenge.created_time,
533 challenge.expiration_time,
534 &challenge.website_id,
535 &challenge.challenge_param,
536 &challenge.public_key
537 );
538 generate_signature(&signing_key, &message)
539}
540
541pub fn verify_challenge_signature(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
569 let verifying_key: VerifyingKey = load_public_key_from_env()?;
570
571 let message: String = create_signing_message(
572 &challenge.random_nonce,
573 challenge.created_time,
574 challenge.expiration_time,
575 &challenge.website_id,
576 &challenge.challenge_param,
577 &challenge.public_key
578 );
579 let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
580 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
581
582 verifying_key.verify(message.as_bytes(), &signature)
583 .map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
584
585 Ok(())
586}
587
588pub fn verify_challenge_signature_with_key(
601 challenge: &IronShieldChallenge,
602 public_key_bytes: &[u8; 32]
603) -> Result<(), CryptoError> {
604 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(public_key_bytes)
605 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
606
607 let message: String = create_signing_message(
608 &challenge.random_nonce,
609 challenge.created_time,
610 challenge.expiration_time,
611 &challenge.website_id,
612 &challenge.challenge_param,
613 &challenge.public_key
614 );
615 let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
616 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
617
618 verifying_key.verify(message.as_bytes(), &signature)
619 .map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
620
621 Ok(())
622}
623
624pub fn generate_test_keypair() -> (String, String) {
641 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
642 let verifying_key: VerifyingKey = signing_key.verifying_key();
643
644 let private_key_b64: String = STANDARD.encode(signing_key.to_bytes());
645 let public_key_b64: String = STANDARD.encode(verifying_key.to_bytes());
646
647 (private_key_b64, public_key_b64)
648}
649
650pub fn validate_challenge(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
663 verify_challenge_signature(challenge)?;
665
666 if challenge.is_expired() {
668 return Err(CryptoError::VerificationFailed("Challenge has expired".to_string()));
669 }
670
671 if challenge.website_id.is_empty() {
672 return Err(CryptoError::VerificationFailed("Empty website_id".to_string()));
673 }
674
675 Ok(())
676}
677
678pub fn load_private_key_from_data(key_data: &str) -> Result<SigningKey, CryptoError> {
689 match parse_key_simple(key_data, true) {
691 Ok(key_array) => {
692 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
693 return Ok(signing_key);
694 }
695 Err(CryptoError::PgpParsingFailed(_msg)) => {
696 }
698 Err(CryptoError::Base64DecodingFailed(_msg)) => {
699 }
701 Err(e) => {
702 return Err(e); }
704 }
705
706 let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
708 .map_err(|e| {
709 CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e))
710 })?;
711
712 if key_bytes.len() != SECRET_KEY_LENGTH {
714 let error_msg = format!(
715 "Invalid key length: expected {} bytes for Ed25519 private key, got {} bytes",
716 SECRET_KEY_LENGTH,
717 key_bytes.len()
718 );
719 return Err(CryptoError::InvalidKeyFormat(error_msg));
720 }
721
722 let mut key_array = [0u8; SECRET_KEY_LENGTH];
723 key_array.copy_from_slice(&key_bytes);
724
725 Ok(SigningKey::from_bytes(&key_array))
726}
727
728pub fn load_public_key_from_data(key_data: &str) -> Result<VerifyingKey, CryptoError> {
739 match parse_key_simple(key_data, false) {
741 Ok(key_array) => {
742 let verifying_key = VerifyingKey::from_bytes(&key_array)
743 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key from PGP: {}", e)))?;
744 return Ok(verifying_key);
745 }
746 Err(CryptoError::PgpParsingFailed(_msg)) => {
747 }
749 Err(CryptoError::Base64DecodingFailed(_msg)) => {
750 }
752 Err(e) => {
753 return Err(e); }
755 }
756
757 let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
759 .map_err(|e| {
760 CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e))
761 })?;
762
763 if key_bytes.len() != PUBLIC_KEY_LENGTH {
765 let error_msg = format!(
766 "Invalid key length: expected {} bytes for Ed25519 public key, got {} bytes",
767 PUBLIC_KEY_LENGTH,
768 key_bytes.len()
769 );
770 return Err(CryptoError::InvalidKeyFormat(error_msg));
771 }
772
773 let mut key_array = [0u8; PUBLIC_KEY_LENGTH];
774 key_array.copy_from_slice(&key_bytes);
775
776 let verifying_key = VerifyingKey::from_bytes(&key_array)
777 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid Ed25519 public key: {}", e)))?;
778
779 Ok(verifying_key)
780}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785 use std::env;
786 use std::sync::Mutex;
787 use rand::rngs::OsRng;
788
789 static ENV_MUTEX: Mutex<()> = Mutex::new(());
791
792 #[allow(dead_code)]
793 fn setup_isolated_test_keys() -> (SigningKey, VerifyingKey) {
794 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
795 let verifying_key: VerifyingKey = signing_key.verifying_key();
796
797 let private_key: String = STANDARD.encode(signing_key.to_bytes());
798 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
799
800 let _lock = ENV_MUTEX.lock().unwrap();
802 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
803 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
804
805 (signing_key, verifying_key)
806 }
807
808 #[test]
809 fn test_basic_ed25519_signing() {
810 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
812 let verifying_key: VerifyingKey = signing_key.verifying_key();
813
814 let message = b"Hello, world!";
815 let signature: Signature = signing_key.sign(message);
816
817 let result = verifying_key.verify(message, &signature);
819 assert!(result.is_ok(), "Basic Ed25519 signing should work");
820 }
821
822 #[test]
823 fn test_crypto_integration_without_env() {
824 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
826 let verifying_key: VerifyingKey = signing_key.verifying_key();
827
828 let challenge = IronShieldChallenge::new(
830 "example.com".to_string(),
831 100_000,
832 signing_key.clone(),
833 verifying_key.to_bytes(),
834 );
835
836 let signing_message = create_signing_message(
838 &challenge.random_nonce,
839 challenge.created_time,
840 challenge.expiration_time,
841 &challenge.website_id,
842 &challenge.challenge_param,
843 &challenge.public_key
844 );
845 println!("Signing message: {}", signing_message);
846
847 let verification_message = create_signing_message(
849 &challenge.random_nonce,
850 challenge.created_time,
851 challenge.expiration_time,
852 &challenge.website_id,
853 &challenge.challenge_param,
854 &challenge.public_key
855 );
856 assert_eq!(signing_message, verification_message, "Signing message should be consistent");
857
858 let signature_from_bytes = Signature::from_slice(&challenge.challenge_signature)
859 .expect("Should be able to recreate signature from bytes");
860
861 let verification_result = verifying_key.verify(verification_message.as_bytes(), &signature_from_bytes);
862 assert!(verification_result.is_ok(), "Manual verification should succeed");
863
864 let verify_result = verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes());
866 assert!(verify_result.is_ok(), "verify_challenge_signature_with_key should succeed");
867 }
868
869 #[test]
870 fn test_generate_test_keypair() {
871 let (private_key, public_key) = generate_test_keypair();
872
873 assert!(STANDARD.decode(&private_key).is_ok());
875 assert!(STANDARD.decode(&public_key).is_ok());
876
877 let private_bytes = STANDARD.decode(&private_key).unwrap();
879 let public_bytes = STANDARD.decode(&public_key).unwrap();
880 assert_eq!(private_bytes.len(), SECRET_KEY_LENGTH);
881 assert_eq!(public_bytes.len(), PUBLIC_KEY_LENGTH);
882 }
883
884 #[test]
885 fn test_load_keys_from_env() {
886 let _lock = ENV_MUTEX.lock().unwrap();
887
888 let (signing_key, verifying_key) = {
889 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
890 let verifying_key: VerifyingKey = signing_key.verifying_key();
891
892 let private_key: String = STANDARD.encode(signing_key.to_bytes());
893 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
894
895 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
896 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
897
898 (signing_key, verifying_key)
899 };
900
901 let loaded_signing_key = load_private_key_from_env().unwrap();
903 let loaded_verifying_key = load_public_key_from_env().unwrap();
904
905 assert_eq!(signing_key.to_bytes(), loaded_signing_key.to_bytes());
907 assert_eq!(verifying_key.to_bytes(), loaded_verifying_key.to_bytes());
908 }
909
910 #[test]
911 fn test_missing_environment_variables() {
912 let _lock = ENV_MUTEX.lock().unwrap();
913
914 env::remove_var("IRONSHIELD_PRIVATE_KEY");
916 env::remove_var("IRONSHIELD_PUBLIC_KEY");
917
918 let private_result = load_private_key_from_env();
920 assert!(private_result.is_err());
921 assert!(matches!(private_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
922
923 let public_result = load_public_key_from_env();
924 assert!(public_result.is_err());
925 assert!(matches!(public_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
926 }
927
928 #[test]
929 fn test_invalid_key_format() {
930 let _lock = ENV_MUTEX.lock().unwrap();
931
932 env::set_var("IRONSHIELD_PRIVATE_KEY", "invalid-base64!");
934 env::set_var("IRONSHIELD_PUBLIC_KEY", "invalid-base64!");
935
936 let private_result = load_private_key_from_env();
937 assert!(private_result.is_err());
938 assert!(matches!(private_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
939
940 let public_result = load_public_key_from_env();
941 assert!(public_result.is_err());
942 assert!(matches!(public_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
943 }
944
945 #[test]
946 fn test_challenge_signing_and_verification() {
947 let _lock = ENV_MUTEX.lock().unwrap();
948
949 let (signing_key, verifying_key) = {
950 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
951 let verifying_key: VerifyingKey = signing_key.verifying_key();
952
953 let private_key: String = STANDARD.encode(signing_key.to_bytes());
954 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
955
956 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
957 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
958
959 (signing_key, verifying_key)
960 };
961
962 let challenge = IronShieldChallenge::new(
964 "test_website".to_string(),
965 100_000,
966 signing_key.clone(),
967 verifying_key.to_bytes(),
968 );
969
970 verify_challenge_signature(&challenge).unwrap();
972
973 verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes()).unwrap();
975
976 assert_eq!(challenge.public_key, verifying_key.to_bytes());
978 }
979
980 #[test]
981 fn test_tampered_challenge_detection() {
982 let _lock = ENV_MUTEX.lock().unwrap();
983
984 let (signing_key, verifying_key) = {
985 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
986 let verifying_key: VerifyingKey = signing_key.verifying_key();
987
988 let private_key: String = STANDARD.encode(signing_key.to_bytes());
989 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
990
991 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
992 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
993
994 (signing_key, verifying_key)
995 };
996
997 let mut challenge = IronShieldChallenge::new(
999 "test_website".to_string(),
1000 100_000,
1001 signing_key.clone(),
1002 verifying_key.to_bytes(),
1003 );
1004
1005 verify_challenge_signature(&challenge).unwrap();
1007
1008 challenge.random_nonce = "tampered".to_string();
1010
1011 let result = verify_challenge_signature(&challenge);
1013 assert!(result.is_err());
1014 assert!(matches!(result.unwrap_err(), CryptoError::VerificationFailed(_)));
1015 }
1016
1017 #[test]
1018 fn test_invalid_signature_format() {
1019 let _lock = ENV_MUTEX.lock().unwrap();
1020 {
1021 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1022 let verifying_key: VerifyingKey = signing_key.verifying_key();
1023
1024 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1025 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1026
1027 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1028 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1029 }
1030
1031 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1033 let mut challenge = IronShieldChallenge::new(
1034 "test_website".to_string(),
1035 100_000,
1036 dummy_key,
1037 [0x34; 32],
1038 );
1039
1040 challenge.challenge_signature = [0xFF; 64]; let result = verify_challenge_signature(&challenge);
1045 assert!(result.is_err());
1046 }
1047
1048 #[test]
1049 fn test_signing_message_creation() {
1050 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1051 let challenge = IronShieldChallenge::new(
1052 "test_website".to_string(),
1053 100_000,
1054 dummy_key,
1055 [0x34; 32],
1056 );
1057
1058 let message = create_signing_message(
1059 &challenge.random_nonce,
1060 challenge.created_time,
1061 challenge.expiration_time,
1062 &challenge.website_id,
1063 &challenge.challenge_param,
1064 &challenge.public_key
1065 );
1066
1067 let expected_prefix = format!(
1069 "{}|{}|{}|{}|",
1070 challenge.random_nonce,
1071 challenge.created_time,
1072 challenge.expiration_time,
1073 challenge.website_id
1074 );
1075 assert!(message.starts_with(&expected_prefix));
1076 assert!(message.ends_with(&hex::encode(challenge.public_key)));
1077 }
1078
1079 #[test]
1080 fn test_sign_challenge_uses_generate_signature() {
1081 let _lock = ENV_MUTEX.lock().unwrap();
1082
1083 let (signing_key, verifying_key) = {
1084 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1085 let verifying_key: VerifyingKey = signing_key.verifying_key();
1086
1087 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1088 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1089
1090 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1091 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1092
1093 (signing_key, verifying_key)
1094 };
1095
1096 let challenge = IronShieldChallenge::new(
1098 "test_website".to_string(),
1099 100_000,
1100 signing_key.clone(),
1101 verifying_key.to_bytes(),
1102 );
1103
1104 let sign_challenge_result = sign_challenge(&challenge).unwrap();
1106
1107 let message = create_signing_message(
1108 &challenge.random_nonce,
1109 challenge.created_time,
1110 challenge.expiration_time,
1111 &challenge.website_id,
1112 &challenge.challenge_param,
1113 &challenge.public_key
1114 );
1115 let manual_signature = generate_signature(&signing_key, &message).unwrap();
1116
1117 assert_eq!(sign_challenge_result, manual_signature,
1118 "sign_challenge should produce the same result as manual generate_signature");
1119 }
1120}