1use ed25519_dalek::{Signature, Signer, Verifier, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH};
63use crate::IronShieldChallenge;
64use base64::{Engine, engine::general_purpose::STANDARD};
65use std::env;
66
67macro_rules! debug_log {
69 ($($arg:tt)*) => {
70 #[cfg(all(target_arch = "wasm32", feature = "wasm-logging"))]
71 {
72 let msg = format!($($arg)*);
73 web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&msg));
74 }
75 #[cfg(not(target_arch = "wasm32"))]
76 eprintln!($($arg)*);
77 #[cfg(all(target_arch = "wasm32", not(feature = "wasm-logging")))]
78 {
79 let _ = format!($($arg)*);
81 }
82 };
83}
84
85#[derive(Debug, Clone)]
87pub enum CryptoError {
88 MissingEnvironmentVariable(String),
90 InvalidKeyFormat(String),
92 SigningFailed(String),
94 VerificationFailed(String),
96 Base64DecodingFailed(String),
98 PgpParsingFailed(String),
100}
101
102impl std::fmt::Display for CryptoError {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 CryptoError::MissingEnvironmentVariable(var) => write!(f, "Missing environment variable: {}", var),
106 CryptoError::InvalidKeyFormat(msg) => write!(f, "Invalid key format: {}", msg),
107 CryptoError::SigningFailed(msg) => write!(f, "Signing failed: {}", msg),
108 CryptoError::VerificationFailed(msg) => write!(f, "Verification failed: {}", msg),
109 CryptoError::Base64DecodingFailed(msg) => write!(f, "Base64 decoding failed: {}", msg),
110 CryptoError::PgpParsingFailed(msg) => write!(f, "PGP parsing failed: {}", msg),
111 }
112 }
113}
114
115impl std::error::Error for CryptoError {}
116
117fn parse_key_simple(key_data: &str, is_private: bool) -> Result<[u8; 32], CryptoError> {
130 let cleaned_data = key_data
132 .chars()
133 .filter(|c| !c.is_whitespace()) .collect::<String>();
135
136 debug_log!("🔑 Parsing key data: {} chars → {} chars after cleaning", key_data.len(), cleaned_data.len());
137
138 let invalid_chars: Vec<char> = cleaned_data
140 .chars()
141 .filter(|&c| !matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
142 .collect();
143
144 if !invalid_chars.is_empty() {
145 debug_log!("🔧 Fixing {} invalid base64 characters", invalid_chars.len());
146
147 let fixed_data = cleaned_data
149 .chars()
150 .filter(|&c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '+' | '/' | '='))
151 .collect::<String>();
152
153 debug_log!("🔧 Fixed data length: {}", fixed_data.len());
154
155 match STANDARD.decode(&fixed_data) {
157 Ok(key_bytes) => {
158 debug_log!("✅ Fixed data decoded to {} bytes", key_bytes.len());
159 return try_extract_ed25519_key(&key_bytes, is_private);
160 }
161 Err(e) => {
162 debug_log!("⚠️ Fixed data decode failed: {}", e);
163 }
164 }
165 }
166
167 let key_bytes = match STANDARD.decode(&cleaned_data) {
169 Ok(bytes) => {
170 debug_log!("✅ Base64 decoded to {} bytes", bytes.len());
171 bytes
172 }
173 Err(e) => {
174 debug_log!("⚠️ Base64 decode failed: {}", e);
175
176 let mut test_data = cleaned_data.clone();
178 while !test_data.is_empty() {
179 if let Ok(bytes) = STANDARD.decode(&test_data) {
180 debug_log!("✅ Successful decode after trimming to {} chars → {} bytes", test_data.len(), bytes.len());
181 return try_extract_ed25519_key(&bytes, is_private);
182 }
183 test_data.pop();
184 }
185
186 return Err(CryptoError::Base64DecodingFailed(format!("Failed to decode cleaned key data: {}", e)));
187 }
188 };
189
190 try_extract_ed25519_key(&key_bytes, is_private)
191}
192
193fn try_extract_ed25519_key(key_bytes: &[u8], is_private: bool) -> Result<[u8; 32], CryptoError> {
195 debug_log!("🔑 Extracting Ed25519 key from {} bytes", key_bytes.len());
196
197 if key_bytes.len() == 32 {
199 let mut key_array = [0u8; 32];
200 key_array.copy_from_slice(&key_bytes);
201
202 if is_private {
204 let _signing_key = SigningKey::from_bytes(&key_array);
205 debug_log!("✅ Raw Ed25519 private key validated");
206 } else {
207 let _verifying_key = VerifyingKey::from_bytes(&key_array)
208 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid raw public key: {}", e)))?;
209 debug_log!("✅ Raw Ed25519 public key validated");
210 }
211
212 return Ok(key_array);
213 }
214
215 if key_bytes.len() >= 32 {
217 debug_log!("🔍 Scanning PGP data for Ed25519 key...");
218
219 for window_start in 0..key_bytes.len().saturating_sub(32) {
222 let potential_key = &key_bytes[window_start..window_start + 32];
223
224 if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
226 continue;
227 }
228
229 let mut key_array = [0u8; 32];
231 key_array.copy_from_slice(potential_key);
232
233 if is_private {
234 let signing_key = SigningKey::from_bytes(&key_array);
236 let derived_public = signing_key.verifying_key();
237
238 let public_bytes = derived_public.to_bytes();
240
241 let search_start = window_start + 32;
243 if search_start < key_bytes.len() {
244 let remaining_data = &key_bytes[search_start..];
245 if remaining_data.windows(32).any(|window| window == public_bytes) {
246 debug_log!("✅ Private key found at offset {} (with matching public key)", window_start);
247 return Ok(key_array);
248 }
249 }
250
251 if window_start >= 20 && window_start <= 200 {
253 debug_log!("✅ Private key found at offset {}", window_start);
254 return Ok(key_array);
255 }
256 } else {
257 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
259 if window_start >= 10 && window_start <= 100 {
261 debug_log!("✅ Public key found at offset {}", window_start);
262 return Ok(key_array);
263 }
264 }
265 }
266 }
267
268 for (i, &byte) in key_bytes.iter().enumerate() {
270 if byte == 0x16 && i + 33 < key_bytes.len() { let key_start = i + 1;
272 if key_start + 32 <= key_bytes.len() {
273 let potential_key = &key_bytes[key_start..key_start + 32];
274 let mut key_array = [0u8; 32];
275 key_array.copy_from_slice(potential_key);
276
277 if is_private {
279 let _signing_key = SigningKey::from_bytes(&key_array);
280 debug_log!("✅ Private key found via algorithm ID at offset {}", key_start);
281 return Ok(key_array);
282 } else {
283 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
284 debug_log!("✅ Public key found via algorithm ID at offset {}", key_start);
285 return Ok(key_array);
286 }
287 }
288 }
289 }
290 }
291
292 let common_offsets = [
294 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,
295 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160
296 ];
297
298 for &offset in &common_offsets {
299 if offset + 32 <= key_bytes.len() {
300 let potential_key = &key_bytes[offset..offset + 32];
301
302 if potential_key == &[0u8; 32] || potential_key == &[0xFFu8; 32] {
304 continue;
305 }
306
307 let mut key_array = [0u8; 32];
308 key_array.copy_from_slice(potential_key);
309
310 if is_private {
311 let _signing_key = SigningKey::from_bytes(&key_array);
312 debug_log!("✅ Private key found at common offset {}", offset);
313 return Ok(key_array);
314 } else {
315 if let Ok(_verifying_key) = VerifyingKey::from_bytes(&key_array) {
316 debug_log!("✅ Public key found at common offset {}", offset);
317 return Ok(key_array);
318 }
319 }
320 }
321 }
322 }
323
324 Err(CryptoError::PgpParsingFailed(format!(
325 "Could not find valid Ed25519 key material in {} bytes of PGP data using multiple strategies",
326 key_bytes.len()
327 )))
328}
329
330pub fn load_private_key_from_env() -> Result<SigningKey, CryptoError> {
342 let key_str: String = env::var("IRONSHIELD_PRIVATE_KEY")
343 .map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PRIVATE_KEY".to_string()))?;
344
345 match parse_key_simple(&key_str, true) {
347 Ok(key_array) => {
348 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
349 return Ok(signing_key);
350 }
351 Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
352 }
354 Err(e) => return Err(e), }
356
357 let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
359 .map_err(|e| CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e)))?;
360
361 if key_bytes.len() != SECRET_KEY_LENGTH {
363 return Err(CryptoError::InvalidKeyFormat(
364 format!("Private key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
365 SECRET_KEY_LENGTH, key_bytes.len())
366 ));
367 }
368
369 let key_array: [u8; SECRET_KEY_LENGTH] = key_bytes.try_into()
371 .map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert private key bytes".to_string()))?;
372
373 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
374 Ok(signing_key)
375}
376
377pub fn load_public_key_from_env() -> Result<VerifyingKey, CryptoError> {
389 let key_str: String = env::var("IRONSHIELD_PUBLIC_KEY")
390 .map_err(|_| CryptoError::MissingEnvironmentVariable("IRONSHIELD_PUBLIC_KEY".to_string()))?;
391
392 match parse_key_simple(&key_str, false) {
394 Ok(key_array) => {
395 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
396 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
397 return Ok(verifying_key);
398 }
399 Err(CryptoError::PgpParsingFailed(_)) | Err(CryptoError::Base64DecodingFailed(_)) => {
400 }
402 Err(e) => return Err(e), }
404
405 let key_bytes: Vec<u8> = STANDARD.decode(key_str.trim())
407 .map_err(|e| CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e)))?;
408
409 if key_bytes.len() != PUBLIC_KEY_LENGTH {
411 return Err(CryptoError::InvalidKeyFormat(
412 format!("Public key must be {} bytes (raw Ed25519) or valid PGP format, got {} bytes",
413 PUBLIC_KEY_LENGTH, key_bytes.len())
414 ));
415 }
416
417 let key_array: [u8; PUBLIC_KEY_LENGTH] = key_bytes.try_into()
419 .map_err(|_| CryptoError::InvalidKeyFormat("Failed to convert public key bytes".to_string()))?;
420
421 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&key_array)
422 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
423
424 Ok(verifying_key)
425}
426
427pub fn create_signing_message(
444 random_nonce: &str,
445 created_time: i64,
446 expiration_time: i64,
447 website_id: &str,
448 challenge_param: &[u8; 32],
449 public_key: &[u8; 32]
450) -> String {
451 format!(
452 "{}|{}|{}|{}|{}|{}",
453 random_nonce,
454 created_time,
455 expiration_time,
456 website_id,
457 hex::encode(challenge_param),
458 hex::encode(public_key)
459 )
460}
461
462pub fn generate_signature(signing_key: &SigningKey, message: &str) -> Result<[u8; 64], CryptoError> {
483 let signature: Signature = signing_key.sign(message.as_bytes());
484 Ok(signature.to_bytes())
485}
486
487pub fn sign_challenge(challenge: &IronShieldChallenge) -> Result<[u8; 64], CryptoError> {
515 let signing_key: SigningKey = load_private_key_from_env()?;
516 let message: String = create_signing_message(
517 &challenge.random_nonce,
518 challenge.created_time,
519 challenge.expiration_time,
520 &challenge.website_id,
521 &challenge.challenge_param,
522 &challenge.public_key
523 );
524 generate_signature(&signing_key, &message)
525}
526
527pub fn verify_challenge_signature(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
555 let verifying_key: VerifyingKey = load_public_key_from_env()?;
556
557 let message: String = create_signing_message(
558 &challenge.random_nonce,
559 challenge.created_time,
560 challenge.expiration_time,
561 &challenge.website_id,
562 &challenge.challenge_param,
563 &challenge.public_key
564 );
565 let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
566 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
567
568 verifying_key.verify(message.as_bytes(), &signature)
569 .map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
570
571 Ok(())
572}
573
574pub fn verify_challenge_signature_with_key(
587 challenge: &IronShieldChallenge,
588 public_key_bytes: &[u8; 32]
589) -> Result<(), CryptoError> {
590 let verifying_key: VerifyingKey = VerifyingKey::from_bytes(public_key_bytes)
591 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key: {}", e)))?;
592
593 let message: String = create_signing_message(
594 &challenge.random_nonce,
595 challenge.created_time,
596 challenge.expiration_time,
597 &challenge.website_id,
598 &challenge.challenge_param,
599 &challenge.public_key
600 );
601 let signature: Signature = Signature::from_slice(&challenge.challenge_signature)
602 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid signature format: {}", e)))?;
603
604 verifying_key.verify(message.as_bytes(), &signature)
605 .map_err(|e| CryptoError::VerificationFailed(format!("Signature verification failed: {}", e)))?;
606
607 Ok(())
608}
609
610pub fn generate_test_keypair() -> (String, String) {
627 use rand_core::OsRng;
628
629 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
630 let verifying_key: VerifyingKey = signing_key.verifying_key();
631
632 let private_key_b64: String = STANDARD.encode(signing_key.to_bytes());
633 let public_key_b64: String = STANDARD.encode(verifying_key.to_bytes());
634
635 (private_key_b64, public_key_b64)
636}
637
638pub fn validate_challenge(challenge: &IronShieldChallenge) -> Result<(), CryptoError> {
651 verify_challenge_signature(challenge)?;
653
654 if challenge.is_expired() {
656 return Err(CryptoError::VerificationFailed("Challenge has expired".to_string()));
657 }
658
659 if challenge.website_id.is_empty() {
660 return Err(CryptoError::VerificationFailed("Empty website_id".to_string()));
661 }
662
663 Ok(())
664}
665
666pub fn load_private_key_from_data(key_data: &str) -> Result<SigningKey, CryptoError> {
677 match parse_key_simple(key_data, true) {
679 Ok(key_array) => {
680 let signing_key: SigningKey = SigningKey::from_bytes(&key_array);
681 return Ok(signing_key);
682 }
683 Err(CryptoError::PgpParsingFailed(msg)) => {
684 }
686 Err(CryptoError::Base64DecodingFailed(msg)) => {
687 }
689 Err(e) => {
690 return Err(e); }
692 }
693
694 let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
696 .map_err(|e| {
697 CryptoError::Base64DecodingFailed(format!("Private key (legacy fallback): {}", e))
698 })?;
699
700 if key_bytes.len() != SECRET_KEY_LENGTH {
702 let error_msg = format!(
703 "Invalid key length: expected {} bytes for Ed25519 private key, got {} bytes",
704 SECRET_KEY_LENGTH,
705 key_bytes.len()
706 );
707 return Err(CryptoError::InvalidKeyFormat(error_msg));
708 }
709
710 let mut key_array = [0u8; SECRET_KEY_LENGTH];
711 key_array.copy_from_slice(&key_bytes);
712
713 Ok(SigningKey::from_bytes(&key_array))
714}
715
716pub fn load_public_key_from_data(key_data: &str) -> Result<VerifyingKey, CryptoError> {
727 match parse_key_simple(key_data, false) {
729 Ok(key_array) => {
730 let verifying_key = VerifyingKey::from_bytes(&key_array)
731 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid public key from PGP: {}", e)))?;
732 return Ok(verifying_key);
733 }
734 Err(CryptoError::PgpParsingFailed(msg)) => {
735 }
737 Err(CryptoError::Base64DecodingFailed(msg)) => {
738 }
740 Err(e) => {
741 return Err(e); }
743 }
744
745 let key_bytes: Vec<u8> = STANDARD.decode(key_data.trim())
747 .map_err(|e| {
748 CryptoError::Base64DecodingFailed(format!("Public key (legacy fallback): {}", e))
749 })?;
750
751 if key_bytes.len() != PUBLIC_KEY_LENGTH {
753 let error_msg = format!(
754 "Invalid key length: expected {} bytes for Ed25519 public key, got {} bytes",
755 PUBLIC_KEY_LENGTH,
756 key_bytes.len()
757 );
758 return Err(CryptoError::InvalidKeyFormat(error_msg));
759 }
760
761 let mut key_array = [0u8; PUBLIC_KEY_LENGTH];
762 key_array.copy_from_slice(&key_bytes);
763
764 let verifying_key = VerifyingKey::from_bytes(&key_array)
765 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid Ed25519 public key: {}", e)))?;
766
767 Ok(verifying_key)
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773 use std::env;
774 use std::sync::Mutex;
775
776 static ENV_MUTEX: Mutex<()> = Mutex::new(());
778
779 fn setup_isolated_test_keys() -> (SigningKey, VerifyingKey) {
780 use rand_core::OsRng;
781
782 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
783 let verifying_key: VerifyingKey = signing_key.verifying_key();
784
785 let private_key: String = STANDARD.encode(signing_key.to_bytes());
786 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
787
788 let _lock = ENV_MUTEX.lock().unwrap();
790 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
791 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
792
793 (signing_key, verifying_key)
794 }
795
796 #[test]
797 fn test_basic_ed25519_signing() {
798 use rand_core::OsRng;
799
800 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
802 let verifying_key: VerifyingKey = signing_key.verifying_key();
803
804 let message = b"Hello, world!";
805 let signature: Signature = signing_key.sign(message);
806
807 let result = verifying_key.verify(message, &signature);
809 assert!(result.is_ok(), "Basic Ed25519 signing should work");
810 }
811
812 #[test]
813 fn test_crypto_integration_without_env() {
814 use rand_core::OsRng;
815
816 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
818 let verifying_key: VerifyingKey = signing_key.verifying_key();
819
820 let challenge = IronShieldChallenge::new(
822 "example.com".to_string(),
823 [0xAB; 32],
824 signing_key.clone(),
825 verifying_key.to_bytes(),
826 );
827
828 let signing_message = create_signing_message(
830 &challenge.random_nonce,
831 challenge.created_time,
832 challenge.expiration_time,
833 &challenge.website_id,
834 &challenge.challenge_param,
835 &challenge.public_key
836 );
837 println!("Signing message: {}", signing_message);
838
839 let verification_message = create_signing_message(
841 &challenge.random_nonce,
842 challenge.created_time,
843 challenge.expiration_time,
844 &challenge.website_id,
845 &challenge.challenge_param,
846 &challenge.public_key
847 );
848 assert_eq!(signing_message, verification_message, "Signing message should be consistent");
849
850 let signature_from_bytes = Signature::from_slice(&challenge.challenge_signature)
851 .expect("Should be able to recreate signature from bytes");
852
853 let verification_result = verifying_key.verify(verification_message.as_bytes(), &signature_from_bytes);
854 assert!(verification_result.is_ok(), "Manual verification should succeed");
855
856 let verify_result = verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes());
858 assert!(verify_result.is_ok(), "verify_challenge_signature_with_key should succeed");
859 }
860
861 #[test]
862 fn test_generate_test_keypair() {
863 let (private_key, public_key) = generate_test_keypair();
864
865 assert!(STANDARD.decode(&private_key).is_ok());
867 assert!(STANDARD.decode(&public_key).is_ok());
868
869 let private_bytes = STANDARD.decode(&private_key).unwrap();
871 let public_bytes = STANDARD.decode(&public_key).unwrap();
872 assert_eq!(private_bytes.len(), SECRET_KEY_LENGTH);
873 assert_eq!(public_bytes.len(), PUBLIC_KEY_LENGTH);
874 }
875
876 #[test]
877 fn test_load_keys_from_env() {
878 let _lock = ENV_MUTEX.lock().unwrap();
879
880 let (signing_key, verifying_key) = {
881 use rand_core::OsRng;
882
883 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
884 let verifying_key: VerifyingKey = signing_key.verifying_key();
885
886 let private_key: String = STANDARD.encode(signing_key.to_bytes());
887 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
888
889 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
890 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
891
892 (signing_key, verifying_key)
893 };
894
895 let loaded_signing_key = load_private_key_from_env().unwrap();
897 let loaded_verifying_key = load_public_key_from_env().unwrap();
898
899 assert_eq!(signing_key.to_bytes(), loaded_signing_key.to_bytes());
901 assert_eq!(verifying_key.to_bytes(), loaded_verifying_key.to_bytes());
902 }
903
904 #[test]
905 fn test_missing_environment_variables() {
906 let _lock = ENV_MUTEX.lock().unwrap();
907
908 env::remove_var("IRONSHIELD_PRIVATE_KEY");
910 env::remove_var("IRONSHIELD_PUBLIC_KEY");
911
912 let private_result = load_private_key_from_env();
914 assert!(private_result.is_err());
915 assert!(matches!(private_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
916
917 let public_result = load_public_key_from_env();
918 assert!(public_result.is_err());
919 assert!(matches!(public_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
920 }
921
922 #[test]
923 fn test_invalid_key_format() {
924 let _lock = ENV_MUTEX.lock().unwrap();
925
926 env::set_var("IRONSHIELD_PRIVATE_KEY", "invalid-base64!");
928 env::set_var("IRONSHIELD_PUBLIC_KEY", "invalid-base64!");
929
930 let private_result = load_private_key_from_env();
931 assert!(private_result.is_err());
932 assert!(matches!(private_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
933
934 let public_result = load_public_key_from_env();
935 assert!(public_result.is_err());
936 assert!(matches!(public_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
937 }
938
939 #[test]
940 fn test_challenge_signing_and_verification() {
941 let _lock = ENV_MUTEX.lock().unwrap();
942
943 let (signing_key, verifying_key) = {
944 use rand_core::OsRng;
945
946 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
947 let verifying_key: VerifyingKey = signing_key.verifying_key();
948
949 let private_key: String = STANDARD.encode(signing_key.to_bytes());
950 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
951
952 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
953 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
954
955 (signing_key, verifying_key)
956 };
957
958 let challenge = IronShieldChallenge::new(
960 "test_website".to_string(),
961 [0x12; 32],
962 signing_key.clone(),
963 verifying_key.to_bytes(),
964 );
965
966 verify_challenge_signature(&challenge).unwrap();
968
969 verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes()).unwrap();
971
972 assert_eq!(challenge.public_key, verifying_key.to_bytes());
974 }
975
976 #[test]
977 fn test_tampered_challenge_detection() {
978 let _lock = ENV_MUTEX.lock().unwrap();
979
980 let (signing_key, verifying_key) = {
981 use rand_core::OsRng;
982
983 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
984 let verifying_key: VerifyingKey = signing_key.verifying_key();
985
986 let private_key: String = STANDARD.encode(signing_key.to_bytes());
987 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
988
989 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
990 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
991
992 (signing_key, verifying_key)
993 };
994
995 let mut challenge = IronShieldChallenge::new(
997 "test_website".to_string(),
998 [0x12; 32],
999 signing_key.clone(),
1000 verifying_key.to_bytes(),
1001 );
1002
1003 verify_challenge_signature(&challenge).unwrap();
1005
1006 challenge.random_nonce = "tampered".to_string();
1008
1009 let result = verify_challenge_signature(&challenge);
1011 assert!(result.is_err());
1012 assert!(matches!(result.unwrap_err(), CryptoError::VerificationFailed(_)));
1013 }
1014
1015 #[test]
1016 fn test_invalid_signature_format() {
1017 let _lock = ENV_MUTEX.lock().unwrap();
1018
1019 {
1020 use rand_core::OsRng;
1021
1022 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1023 let verifying_key: VerifyingKey = signing_key.verifying_key();
1024
1025 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1026 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1027
1028 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1029 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1030 }
1031
1032 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1034 let mut challenge = IronShieldChallenge::new(
1035 "test_website".to_string(),
1036 [0x12; 32],
1037 dummy_key,
1038 [0x34; 32],
1039 );
1040
1041 challenge.challenge_signature = [0xFF; 64]; let result = verify_challenge_signature(&challenge);
1046 assert!(result.is_err());
1047 }
1048
1049 #[test]
1050 fn test_signing_message_creation() {
1051 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1052 let challenge = IronShieldChallenge::new(
1053 "test_website".to_string(),
1054 [0x12; 32],
1055 dummy_key,
1056 [0x34; 32],
1057 );
1058
1059 let message = create_signing_message(
1060 &challenge.random_nonce,
1061 challenge.created_time,
1062 challenge.expiration_time,
1063 &challenge.website_id,
1064 &challenge.challenge_param,
1065 &challenge.public_key
1066 );
1067
1068 assert!(message.contains("test_website"));
1070 assert!(message.contains(&hex::encode([0x12; 32])));
1071 assert!(message.contains(&hex::encode([0x34; 32])));
1072 assert!(!message.contains(&hex::encode(challenge.challenge_signature)));
1074 assert_eq!(message.matches('|').count(), 5);
1076 }
1077
1078 #[test]
1079 fn test_sign_challenge_uses_generate_signature() {
1080 let _lock = ENV_MUTEX.lock().unwrap();
1081
1082 let (signing_key, verifying_key) = {
1083 use rand_core::OsRng;
1084
1085 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1086 let verifying_key: VerifyingKey = signing_key.verifying_key();
1087
1088 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1089 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1090
1091 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1092 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1093
1094 (signing_key, verifying_key)
1095 };
1096
1097 let challenge = IronShieldChallenge::new(
1099 "test_website".to_string(),
1100 [0x12; 32],
1101 signing_key.clone(),
1102 verifying_key.to_bytes(),
1103 );
1104
1105 let sign_challenge_result = sign_challenge(&challenge).unwrap();
1107
1108 let message = create_signing_message(
1109 &challenge.random_nonce,
1110 challenge.created_time,
1111 challenge.expiration_time,
1112 &challenge.website_id,
1113 &challenge.challenge_param,
1114 &challenge.public_key
1115 );
1116 let manual_signature = generate_signature(&signing_key, &message).unwrap();
1117
1118 assert_eq!(sign_challenge_result, manual_signature,
1119 "sign_challenge should produce the same result as manual generate_signature");
1120 }
1121}