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 #[allow(dead_code)]
780 fn setup_isolated_test_keys() -> (SigningKey, VerifyingKey) {
781 use rand_core::OsRng;
782
783 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
784 let verifying_key: VerifyingKey = signing_key.verifying_key();
785
786 let private_key: String = STANDARD.encode(signing_key.to_bytes());
787 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
788
789 let _lock = ENV_MUTEX.lock().unwrap();
791 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
792 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
793
794 (signing_key, verifying_key)
795 }
796
797 #[test]
798 fn test_basic_ed25519_signing() {
799 use rand_core::OsRng;
800
801 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
803 let verifying_key: VerifyingKey = signing_key.verifying_key();
804
805 let message = b"Hello, world!";
806 let signature: Signature = signing_key.sign(message);
807
808 let result = verifying_key.verify(message, &signature);
810 assert!(result.is_ok(), "Basic Ed25519 signing should work");
811 }
812
813 #[test]
814 fn test_crypto_integration_without_env() {
815 use rand_core::OsRng;
816
817 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
819 let verifying_key: VerifyingKey = signing_key.verifying_key();
820
821 let challenge = IronShieldChallenge::new(
823 "example.com".to_string(),
824 100_000,
825 signing_key.clone(),
826 verifying_key.to_bytes(),
827 );
828
829 let signing_message = create_signing_message(
831 &challenge.random_nonce,
832 challenge.created_time,
833 challenge.expiration_time,
834 &challenge.website_id,
835 &challenge.challenge_param,
836 &challenge.public_key
837 );
838 println!("Signing message: {}", signing_message);
839
840 let verification_message = create_signing_message(
842 &challenge.random_nonce,
843 challenge.created_time,
844 challenge.expiration_time,
845 &challenge.website_id,
846 &challenge.challenge_param,
847 &challenge.public_key
848 );
849 assert_eq!(signing_message, verification_message, "Signing message should be consistent");
850
851 let signature_from_bytes = Signature::from_slice(&challenge.challenge_signature)
852 .expect("Should be able to recreate signature from bytes");
853
854 let verification_result = verifying_key.verify(verification_message.as_bytes(), &signature_from_bytes);
855 assert!(verification_result.is_ok(), "Manual verification should succeed");
856
857 let verify_result = verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes());
859 assert!(verify_result.is_ok(), "verify_challenge_signature_with_key should succeed");
860 }
861
862 #[test]
863 fn test_generate_test_keypair() {
864 let (private_key, public_key) = generate_test_keypair();
865
866 assert!(STANDARD.decode(&private_key).is_ok());
868 assert!(STANDARD.decode(&public_key).is_ok());
869
870 let private_bytes = STANDARD.decode(&private_key).unwrap();
872 let public_bytes = STANDARD.decode(&public_key).unwrap();
873 assert_eq!(private_bytes.len(), SECRET_KEY_LENGTH);
874 assert_eq!(public_bytes.len(), PUBLIC_KEY_LENGTH);
875 }
876
877 #[test]
878 fn test_load_keys_from_env() {
879 let _lock = ENV_MUTEX.lock().unwrap();
880
881 let (signing_key, verifying_key) = {
882 use rand_core::OsRng;
883
884 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
885 let verifying_key: VerifyingKey = signing_key.verifying_key();
886
887 let private_key: String = STANDARD.encode(signing_key.to_bytes());
888 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
889
890 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
891 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
892
893 (signing_key, verifying_key)
894 };
895
896 let loaded_signing_key = load_private_key_from_env().unwrap();
898 let loaded_verifying_key = load_public_key_from_env().unwrap();
899
900 assert_eq!(signing_key.to_bytes(), loaded_signing_key.to_bytes());
902 assert_eq!(verifying_key.to_bytes(), loaded_verifying_key.to_bytes());
903 }
904
905 #[test]
906 fn test_missing_environment_variables() {
907 let _lock = ENV_MUTEX.lock().unwrap();
908
909 env::remove_var("IRONSHIELD_PRIVATE_KEY");
911 env::remove_var("IRONSHIELD_PUBLIC_KEY");
912
913 let private_result = load_private_key_from_env();
915 assert!(private_result.is_err());
916 assert!(matches!(private_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
917
918 let public_result = load_public_key_from_env();
919 assert!(public_result.is_err());
920 assert!(matches!(public_result.unwrap_err(), CryptoError::MissingEnvironmentVariable(_)));
921 }
922
923 #[test]
924 fn test_invalid_key_format() {
925 let _lock = ENV_MUTEX.lock().unwrap();
926
927 env::set_var("IRONSHIELD_PRIVATE_KEY", "invalid-base64!");
929 env::set_var("IRONSHIELD_PUBLIC_KEY", "invalid-base64!");
930
931 let private_result = load_private_key_from_env();
932 assert!(private_result.is_err());
933 assert!(matches!(private_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
934
935 let public_result = load_public_key_from_env();
936 assert!(public_result.is_err());
937 assert!(matches!(public_result.unwrap_err(), CryptoError::Base64DecodingFailed(_)));
938 }
939
940 #[test]
941 fn test_challenge_signing_and_verification() {
942 let _lock = ENV_MUTEX.lock().unwrap();
943
944 let (signing_key, verifying_key) = {
945 use rand_core::OsRng;
946
947 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
948 let verifying_key: VerifyingKey = signing_key.verifying_key();
949
950 let private_key: String = STANDARD.encode(signing_key.to_bytes());
951 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
952
953 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
954 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
955
956 (signing_key, verifying_key)
957 };
958
959 let challenge = IronShieldChallenge::new(
961 "test_website".to_string(),
962 100_000,
963 signing_key.clone(),
964 verifying_key.to_bytes(),
965 );
966
967 verify_challenge_signature(&challenge).unwrap();
969
970 verify_challenge_signature_with_key(&challenge, &verifying_key.to_bytes()).unwrap();
972
973 assert_eq!(challenge.public_key, verifying_key.to_bytes());
975 }
976
977 #[test]
978 fn test_tampered_challenge_detection() {
979 let _lock = ENV_MUTEX.lock().unwrap();
980
981 let (signing_key, verifying_key) = {
982 use rand_core::OsRng;
983
984 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
985 let verifying_key: VerifyingKey = signing_key.verifying_key();
986
987 let private_key: String = STANDARD.encode(signing_key.to_bytes());
988 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
989
990 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
991 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
992
993 (signing_key, verifying_key)
994 };
995
996 let mut challenge = IronShieldChallenge::new(
998 "test_website".to_string(),
999 100_000,
1000 signing_key.clone(),
1001 verifying_key.to_bytes(),
1002 );
1003
1004 verify_challenge_signature(&challenge).unwrap();
1006
1007 challenge.random_nonce = "tampered".to_string();
1009
1010 let result = verify_challenge_signature(&challenge);
1012 assert!(result.is_err());
1013 assert!(matches!(result.unwrap_err(), CryptoError::VerificationFailed(_)));
1014 }
1015
1016 #[test]
1017 fn test_invalid_signature_format() {
1018 let _lock = ENV_MUTEX.lock().unwrap();
1019
1020 {
1021 use rand_core::OsRng;
1022
1023 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1024 let verifying_key: VerifyingKey = signing_key.verifying_key();
1025
1026 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1027 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1028
1029 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1030 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1031 }
1032
1033 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1035 let mut challenge = IronShieldChallenge::new(
1036 "test_website".to_string(),
1037 100_000,
1038 dummy_key,
1039 [0x34; 32],
1040 );
1041
1042 challenge.challenge_signature = [0xFF; 64]; let result = verify_challenge_signature(&challenge);
1047 assert!(result.is_err());
1048 }
1049
1050 #[test]
1051 fn test_signing_message_creation() {
1052 let dummy_key = SigningKey::from_bytes(&[0u8; 32]);
1053 let challenge = IronShieldChallenge::new(
1054 "test_website".to_string(),
1055 100_000,
1056 dummy_key,
1057 [0x34; 32],
1058 );
1059
1060 let message = create_signing_message(
1061 &challenge.random_nonce,
1062 challenge.created_time,
1063 challenge.expiration_time,
1064 &challenge.website_id,
1065 &challenge.challenge_param,
1066 &challenge.public_key
1067 );
1068
1069 let expected_prefix = format!(
1071 "{}|{}|{}|{}|",
1072 challenge.random_nonce,
1073 challenge.created_time,
1074 challenge.expiration_time,
1075 challenge.website_id
1076 );
1077 assert!(message.starts_with(&expected_prefix));
1078 assert!(message.ends_with(&hex::encode(challenge.public_key)));
1079 }
1080
1081 #[test]
1082 fn test_sign_challenge_uses_generate_signature() {
1083 let _lock = ENV_MUTEX.lock().unwrap();
1084
1085 let (signing_key, verifying_key) = {
1086 use rand_core::OsRng;
1087
1088 let signing_key: SigningKey = SigningKey::generate(&mut OsRng);
1089 let verifying_key: VerifyingKey = signing_key.verifying_key();
1090
1091 let private_key: String = STANDARD.encode(signing_key.to_bytes());
1092 let public_key: String = STANDARD.encode(verifying_key.to_bytes());
1093
1094 env::set_var("IRONSHIELD_PRIVATE_KEY", &private_key);
1095 env::set_var("IRONSHIELD_PUBLIC_KEY", &public_key);
1096
1097 (signing_key, verifying_key)
1098 };
1099
1100 let challenge = IronShieldChallenge::new(
1102 "test_website".to_string(),
1103 100_000,
1104 signing_key.clone(),
1105 verifying_key.to_bytes(),
1106 );
1107
1108 let sign_challenge_result = sign_challenge(&challenge).unwrap();
1110
1111 let message = create_signing_message(
1112 &challenge.random_nonce,
1113 challenge.created_time,
1114 challenge.expiration_time,
1115 &challenge.website_id,
1116 &challenge.challenge_param,
1117 &challenge.public_key
1118 );
1119 let manual_signature = generate_signature(&signing_key, &message).unwrap();
1120
1121 assert_eq!(sign_challenge_result, manual_signature,
1122 "sign_challenge should produce the same result as manual generate_signature");
1123 }
1124}