1use std::sync::atomic::{AtomicU64, Ordering};
21
22use bytes::Bytes;
23use zeroize::{Zeroize, ZeroizeOnDrop};
24
25use super::crypto::{CryptoError, CryptoProvider};
26use super::{AuthProtocol, PrivProtocol};
27
28#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum PrivacyError {
34 InvalidPrivParamsLength { expected: usize, actual: usize },
36 InvalidCiphertextLength { length: usize, block_size: usize },
38 Crypto(CryptoError),
40}
41
42impl From<CryptoError> for PrivacyError {
43 fn from(e: CryptoError) -> Self {
44 Self::Crypto(e)
45 }
46}
47
48impl std::fmt::Display for PrivacyError {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Self::InvalidPrivParamsLength { expected, actual } => {
52 write!(
53 f,
54 "invalid privParameters length: expected {expected}, got {actual}"
55 )
56 }
57 Self::InvalidCiphertextLength { length, block_size } => {
58 write!(
59 f,
60 "ciphertext length {length} not multiple of block size {block_size}"
61 )
62 }
63 Self::Crypto(e) => write!(f, "{e}"),
64 }
65 }
66}
67
68impl std::error::Error for PrivacyError {
69 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
70 match self {
71 Self::Crypto(e) => Some(e),
72 _ => None,
73 }
74 }
75}
76
77pub type PrivacyResult<T> = std::result::Result<T, PrivacyError>;
79
80fn random_nonzero_u64() -> super::crypto::CryptoResult<u64> {
84 let mut buf = [0u8; 8];
85 loop {
86 getrandom::fill(&mut buf).map_err(|_| super::crypto::CryptoError::RandomSource)?;
87 let val = u64::from_ne_bytes(buf);
88 if val != 0 {
89 return Ok(val);
90 }
91 }
93}
94
95#[derive(Zeroize, ZeroizeOnDrop)]
107pub struct PrivKey {
108 key: Vec<u8>,
110 #[zeroize(skip)]
112 protocol: PrivProtocol,
113 #[zeroize(skip)]
116 salt_counter: AtomicU64,
117}
118
119pub struct SaltCounter(AtomicU64);
121
122impl SaltCounter {
123 #[must_use]
128 pub fn new() -> Self {
129 Self(AtomicU64::new(
130 random_nonzero_u64().expect("OS random source unavailable"),
131 ))
132 }
133
134 #[must_use]
138 pub fn from_value(value: u64) -> Self {
139 Self(AtomicU64::new(value))
140 }
141
142 pub fn next(&self) -> u64 {
151 let old = self.0.fetch_add(1, Ordering::SeqCst);
152 let val = old.wrapping_add(1);
153 if val != 0 {
154 return val;
155 }
156 let _ = self
159 .0
160 .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst);
161 1
162 }
163}
164
165impl Default for SaltCounter {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171impl PrivKey {
172 pub fn from_password(
209 auth_protocol: AuthProtocol,
210 priv_protocol: PrivProtocol,
211 password: &[u8],
212 engine_id: &[u8],
213 ) -> super::crypto::CryptoResult<Self> {
214 use super::MasterKey;
215
216 let master = MasterKey::from_password(auth_protocol, password)?;
217 Self::from_master_key(&master, priv_protocol, engine_id)
218 }
219
220 pub fn from_master_key(
242 master: &super::MasterKey,
243 priv_protocol: PrivProtocol,
244 engine_id: &[u8],
245 ) -> super::crypto::CryptoResult<Self> {
246 use super::{
247 KeyExtension,
248 auth::{extend_key, extend_key_reeder},
249 };
250
251 let auth_protocol = master.protocol();
252 let key_extension = priv_protocol.key_extension_for(auth_protocol);
253
254 let localized = master.localize(engine_id)?;
256 let key_bytes = localized.as_bytes();
257
258 let key = match key_extension {
259 KeyExtension::None => key_bytes.to_vec(),
260 KeyExtension::Blumenthal => {
261 extend_key(auth_protocol, key_bytes, priv_protocol.key_len())?
262 }
263 KeyExtension::Reeder => {
264 extend_key_reeder(auth_protocol, key_bytes, engine_id, priv_protocol.key_len())?
265 }
266 };
267
268 Ok(Self {
269 key,
270 protocol: priv_protocol,
271 salt_counter: Self::init_salt()?,
272 })
273 }
274
275 pub fn from_bytes(
277 protocol: PrivProtocol,
278 key: impl Into<Vec<u8>>,
279 ) -> super::crypto::CryptoResult<Self> {
280 Ok(Self {
281 key: key.into(),
282 protocol,
283 salt_counter: Self::init_salt()?,
284 })
285 }
286
287 fn init_salt() -> super::crypto::CryptoResult<AtomicU64> {
291 Ok(AtomicU64::new(random_nonzero_u64()?))
292 }
293
294 pub fn protocol(&self) -> PrivProtocol {
296 self.protocol
297 }
298
299 pub fn encryption_key(&self) -> &[u8] {
301 match self.protocol {
302 PrivProtocol::Des => &self.key[..8],
303 PrivProtocol::Des3 => &self.key[..24],
304 PrivProtocol::Aes128 => &self.key[..16],
305 PrivProtocol::Aes192 => &self.key[..24],
306 PrivProtocol::Aes256 => &self.key[..32],
307 }
308 }
309
310 pub fn encrypt(
322 &self,
323 plaintext: &[u8],
324 engine_boots: u32,
325 engine_time: u32,
326 salt_counter: Option<&SaltCounter>,
327 ) -> PrivacyResult<(Bytes, Bytes)> {
328 let salt = salt_counter.map_or_else(
329 || {
330 let val = self.salt_counter.fetch_add(1, Ordering::Relaxed);
332 if val != 0 {
333 return val;
334 }
335 self.salt_counter.fetch_add(1, Ordering::Relaxed)
337 },
338 SaltCounter::next,
339 );
340
341 match self.protocol {
342 PrivProtocol::Des => self.encrypt_des(plaintext, engine_boots, salt),
343 PrivProtocol::Des3 => self.encrypt_des3(plaintext, engine_boots, salt),
344 PrivProtocol::Aes128 => {
345 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 16)
346 }
347 PrivProtocol::Aes192 => {
348 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 24)
349 }
350 PrivProtocol::Aes256 => {
351 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 32)
352 }
353 }
354 }
355
356 pub fn decrypt(
368 &self,
369 ciphertext: &[u8],
370 engine_boots: u32,
371 engine_time: u32,
372 priv_params: &[u8],
373 ) -> PrivacyResult<Bytes> {
374 if priv_params.len() != 8 {
375 tracing::debug!(target: "async_snmp::crypto", { expected = 8, actual = priv_params.len() }, "invalid privParameters length");
376 return Err(PrivacyError::InvalidPrivParamsLength {
377 expected: 8,
378 actual: priv_params.len(),
379 });
380 }
381
382 match self.protocol {
383 PrivProtocol::Des => self.decrypt_des(ciphertext, priv_params),
384 PrivProtocol::Des3 => self.decrypt_des3(ciphertext, priv_params),
385 PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
386 self.decrypt_aes(ciphertext, engine_boots, engine_time, priv_params)
387 }
388 }
389 }
390
391 fn encrypt_des(
393 &self,
394 plaintext: &[u8],
395 engine_boots: u32,
396 salt_int: u64,
397 ) -> PrivacyResult<(Bytes, Bytes)> {
398 let key = &self.key[..8];
400 let pre_iv = &self.key[8..16];
402
403 let mut salt = [0u8; 8];
406 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
407 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
408
409 let mut iv = [0u8; 8];
411 for i in 0..8 {
412 iv[i] = pre_iv[i] ^ salt[i];
413 }
414
415 let mut buffer = plaintext.to_vec();
416 super::crypto::provider().encrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
417
418 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
419 }
420
421 fn decrypt_des(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
423 if !ciphertext.len().is_multiple_of(8) {
424 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "DES decryption failed: invalid ciphertext length");
425 return Err(PrivacyError::InvalidCiphertextLength {
426 length: ciphertext.len(),
427 block_size: 8,
428 });
429 }
430
431 let key = &self.key[..8];
433 let pre_iv = &self.key[8..16];
435
436 let salt = priv_params;
438
439 let mut iv = [0u8; 8];
441 for i in 0..8 {
442 iv[i] = pre_iv[i] ^ salt[i];
443 }
444
445 let mut buffer = ciphertext.to_vec();
446 super::crypto::provider().decrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
447
448 Ok(Bytes::from(buffer))
449 }
450
451 fn encrypt_des3(
453 &self,
454 plaintext: &[u8],
455 engine_boots: u32,
456 salt_int: u64,
457 ) -> PrivacyResult<(Bytes, Bytes)> {
458 let key = &self.key[..24];
460 let pre_iv = &self.key[24..32];
462
463 let mut salt = [0u8; 8];
465 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
466 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
467
468 let mut iv = [0u8; 8];
470 for i in 0..8 {
471 iv[i] = pre_iv[i] ^ salt[i];
472 }
473
474 let mut buffer = plaintext.to_vec();
475 super::crypto::provider().encrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
476
477 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
478 }
479
480 fn decrypt_des3(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
482 if !ciphertext.len().is_multiple_of(8) {
483 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "3DES decryption failed: invalid ciphertext length");
484 return Err(PrivacyError::InvalidCiphertextLength {
485 length: ciphertext.len(),
486 block_size: 8,
487 });
488 }
489
490 let key = &self.key[..24];
492 let pre_iv = &self.key[24..32];
494
495 let salt = priv_params;
497
498 let mut iv = [0u8; 8];
500 for i in 0..8 {
501 iv[i] = pre_iv[i] ^ salt[i];
502 }
503
504 let mut buffer = ciphertext.to_vec();
505 super::crypto::provider().decrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
506
507 Ok(Bytes::from(buffer))
508 }
509
510 fn encrypt_aes(
512 &self,
513 plaintext: &[u8],
514 engine_boots: u32,
515 engine_time: u32,
516 salt: u64,
517 key_len: usize,
518 ) -> PrivacyResult<(Bytes, Bytes)> {
519 let key = &self.key[..key_len];
521
522 let salt_bytes = salt.to_be_bytes();
524
525 let mut iv = [0u8; 16];
528 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
529 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
530 iv[8..].copy_from_slice(&salt_bytes);
531
532 let mut buffer = plaintext.to_vec();
533 super::crypto::provider().encrypt(self.protocol, key, &iv, &mut buffer)?;
534
535 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt_bytes)))
536 }
537
538 fn decrypt_aes(
540 &self,
541 ciphertext: &[u8],
542 engine_boots: u32,
543 engine_time: u32,
544 priv_params: &[u8],
545 ) -> PrivacyResult<Bytes> {
546 let key_len = match self.protocol {
547 PrivProtocol::Aes128 => 16,
548 PrivProtocol::Aes192 => 24,
549 PrivProtocol::Aes256 => 32,
550 _ => unreachable!(),
551 };
552
553 let key = &self.key[..key_len];
555
556 let mut iv = [0u8; 16];
558 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
559 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
560 iv[8..].copy_from_slice(priv_params);
561
562 let mut buffer = ciphertext.to_vec();
563 super::crypto::provider().decrypt(self.protocol, key, &iv, &mut buffer)?;
564
565 Ok(Bytes::from(buffer))
566 }
567}
568
569impl std::fmt::Debug for PrivKey {
570 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571 f.debug_struct("PrivKey")
572 .field("protocol", &self.protocol)
573 .field("key", &"[REDACTED]")
574 .field("salt_counter", &"[REDACTED]")
575 .finish()
576 }
577}
578
579impl Clone for PrivKey {
580 fn clone(&self) -> Self {
581 Self {
582 key: self.key.clone(),
583 protocol: self.protocol,
584 salt_counter: Self::init_salt().expect("OS random source unavailable"),
586 }
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use super::*;
593 use crate::format::hex::decode as decode_hex;
594
595 #[cfg(feature = "crypto-rustcrypto")]
596 #[test]
597 fn test_des_encrypt_decrypt_roundtrip() {
598 let key = vec![
600 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, ];
603 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
604
605 let plaintext = b"Hello, SNMPv3 World!";
606 let engine_boots = 100u32;
607 let engine_time = 12345u32;
608
609 let (ciphertext, priv_params) = priv_key
610 .encrypt(plaintext, engine_boots, engine_time, None)
611 .expect("encryption failed");
612
613 assert_ne!(ciphertext.as_ref(), plaintext);
615 assert_eq!(priv_params.len(), 8);
617
618 let decrypted = priv_key
620 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
621 .expect("decryption failed");
622
623 assert!(decrypted.len() >= plaintext.len());
625 assert_eq!(&decrypted[..plaintext.len()], plaintext);
626 }
627
628 #[cfg(feature = "crypto-rustcrypto")]
629 #[test]
630 fn test_des3_encrypt_decrypt_roundtrip() {
631 let key = vec![
633 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, ];
638 let priv_key = PrivKey::from_bytes(PrivProtocol::Des3, key).unwrap();
639
640 let plaintext = b"Hello, SNMPv3 World with 3DES!";
641 let engine_boots = 100u32;
642 let engine_time = 12345u32;
643
644 let (ciphertext, priv_params) = priv_key
645 .encrypt(plaintext, engine_boots, engine_time, None)
646 .expect("encryption failed");
647
648 assert_ne!(ciphertext.as_ref(), plaintext);
650 assert_eq!(priv_params.len(), 8);
652
653 let decrypted = priv_key
655 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
656 .expect("decryption failed");
657
658 assert!(decrypted.len() >= plaintext.len());
660 assert_eq!(&decrypted[..plaintext.len()], plaintext);
661 }
662
663 #[test]
664 fn test_aes128_encrypt_decrypt_roundtrip() {
665 let key = vec![
667 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
668 0x0f, 0x10,
669 ];
670 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
671
672 let plaintext = b"Hello, SNMPv3 AES World!";
673 let engine_boots = 200u32;
674 let engine_time = 54321u32;
675
676 let (ciphertext, priv_params) = priv_key
677 .encrypt(plaintext, engine_boots, engine_time, None)
678 .expect("encryption failed");
679
680 assert_ne!(ciphertext.as_ref(), plaintext);
682 assert_eq!(priv_params.len(), 8);
684
685 let decrypted = priv_key
687 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
688 .expect("decryption failed");
689
690 assert_eq!(decrypted.len(), plaintext.len());
692 assert_eq!(decrypted.as_ref(), plaintext);
693 }
694
695 #[cfg(feature = "crypto-rustcrypto")]
696 #[test]
697 fn test_des_invalid_ciphertext_length() {
698 let key = vec![0u8; 16];
699 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
700
701 let ciphertext = [0u8; 13];
703 let priv_params = [0u8; 8];
704
705 let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
706 assert!(result.is_err());
707 }
708
709 #[test]
710 fn test_invalid_priv_params_length() {
711 let key = vec![0u8; 16];
712 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
713
714 let ciphertext = [0u8; 16];
716 let priv_params = [0u8; 4]; let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
719 assert!(result.is_err());
720 }
721
722 #[test]
723 fn test_salt_counter() {
724 let counter = SaltCounter::new();
725 let s1 = counter.next();
726 let s2 = counter.next();
727 let s3 = counter.next();
728
729 assert_eq!(s2, s1.wrapping_add(1));
731 assert_eq!(s3, s2.wrapping_add(1));
732 }
733
734 #[test]
739 fn test_salt_counter_skips_zero() {
740 let counter = SaltCounter::from_value(u64::MAX - 1);
746
747 let s1 = counter.next();
748 assert_eq!(s1, u64::MAX);
749
750 let s2 = counter.next();
752 assert_ne!(s2, 0, "SaltCounter should never return zero");
753 assert_eq!(s2, 1, "SaltCounter should skip 0 and return 1");
754
755 let s3 = counter.next();
757 assert_eq!(s3, 2);
758 }
759
760 #[test]
765 fn test_priv_key_internal_salt_skips_zero() {
766 let key = vec![0u8; 16];
767 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
768
769 priv_key.salt_counter.store(u64::MAX, Ordering::Relaxed);
771
772 let plaintext = b"test";
773
774 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
776 assert_eq!(
777 u64::from_be_bytes(salt1.as_ref().try_into().unwrap()),
778 u64::MAX
779 );
780
781 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
783 let salt2_value = u64::from_be_bytes(salt2.as_ref().try_into().unwrap());
784 assert_ne!(salt2_value, 0, "Salt should never be zero");
785 assert_eq!(salt2_value, 1, "Salt should skip 0 and be 1");
786
787 let (_, salt3) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
789 let salt3_value = u64::from_be_bytes(salt3.as_ref().try_into().unwrap());
790 assert_eq!(salt3_value, 2);
791 }
792
793 #[test]
794 fn test_multiple_encryptions_different_salt() {
795 let key = vec![0u8; 16];
796 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
797
798 let plaintext = b"test data";
799
800 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
801 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
802
803 assert_ne!(salt1, salt2);
805 }
806
807 #[test]
808 fn test_from_password() {
809 let password = b"maplesyrup";
811 let engine_id = decode_hex("000000000000000000000002").unwrap();
812
813 let priv_key = PrivKey::from_password(
814 AuthProtocol::Sha1,
815 PrivProtocol::Aes128,
816 password,
817 &engine_id,
818 )
819 .unwrap();
820
821 let plaintext = b"test message";
823 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
824 let decrypted = priv_key
825 .decrypt(&ciphertext, 100, 200, &priv_params)
826 .unwrap();
827
828 assert_eq!(decrypted.as_ref(), plaintext);
829 }
830
831 #[test]
832 fn test_aes192_encrypt_decrypt_roundtrip() {
833 let key = vec![
835 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
836 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
837 ];
838 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, key).unwrap();
839
840 let plaintext = b"Hello, SNMPv3 AES-192 World!";
841 let engine_boots = 300u32;
842 let engine_time = 67890u32;
843
844 let (ciphertext, priv_params) = priv_key
845 .encrypt(plaintext, engine_boots, engine_time, None)
846 .expect("AES-192 encryption failed");
847
848 assert_ne!(ciphertext.as_ref(), plaintext);
850 assert_eq!(priv_params.len(), 8);
852
853 let decrypted = priv_key
855 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
856 .expect("AES-192 decryption failed");
857
858 assert_eq!(decrypted.len(), plaintext.len());
860 assert_eq!(decrypted.as_ref(), plaintext);
861 }
862
863 #[test]
864 fn test_aes256_encrypt_decrypt_roundtrip() {
865 let key = vec![
867 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
868 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
869 0x1d, 0x1e, 0x1f, 0x20,
870 ];
871 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, key).unwrap();
872
873 let plaintext = b"Hello, SNMPv3 AES-256 World!";
874 let engine_boots = 400u32;
875 let engine_time = 11111u32;
876
877 let (ciphertext, priv_params) = priv_key
878 .encrypt(plaintext, engine_boots, engine_time, None)
879 .expect("AES-256 encryption failed");
880
881 assert_ne!(ciphertext.as_ref(), plaintext);
883 assert_eq!(priv_params.len(), 8);
885
886 let decrypted = priv_key
888 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
889 .expect("AES-256 decryption failed");
890
891 assert_eq!(decrypted.len(), plaintext.len());
893 assert_eq!(decrypted.as_ref(), plaintext);
894 }
895
896 #[test]
897 fn test_aes192_from_password() {
898 let password = b"longpassword123";
900 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
901
902 let priv_key = PrivKey::from_password(
903 AuthProtocol::Sha256, PrivProtocol::Aes192,
905 password,
906 &engine_id,
907 )
908 .unwrap();
909
910 let plaintext = b"test message for AES-192";
911 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
912 let decrypted = priv_key
913 .decrypt(&ciphertext, 100, 200, &priv_params)
914 .unwrap();
915
916 assert_eq!(decrypted.as_ref(), plaintext);
917 }
918
919 #[test]
920 fn test_aes256_from_password() {
921 let password = b"anotherlongpassword456";
923 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
924
925 let priv_key = PrivKey::from_password(
926 AuthProtocol::Sha256, PrivProtocol::Aes256,
928 password,
929 &engine_id,
930 )
931 .unwrap();
932
933 let plaintext = b"test message for AES-256";
934 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
935 let decrypted = priv_key
936 .decrypt(&ciphertext, 100, 200, &priv_params)
937 .unwrap();
938
939 assert_eq!(decrypted.as_ref(), plaintext);
940 }
941
942 #[cfg(feature = "crypto-rustcrypto")]
952 #[test]
953 fn test_des_wrong_key_produces_garbage() {
954 let correct_key = vec![
956 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
957 0x17, 0x18,
958 ];
959 let wrong_key = vec![
961 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2,
962 0xE1, 0xE0,
963 ];
964
965 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Des, correct_key).unwrap();
966 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Des, wrong_key).unwrap();
967
968 let plaintext = b"Secret SNMPv3 message data!";
969 let engine_boots = 100u32;
970 let engine_time = 12345u32;
971
972 let (ciphertext, priv_params) = correct_priv_key
974 .encrypt(plaintext, engine_boots, engine_time, None)
975 .expect("encryption failed");
976
977 let wrong_decrypted = wrong_priv_key
979 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
980 .expect("decryption should succeed cryptographically");
981
982 assert_ne!(
984 &wrong_decrypted[..plaintext.len()],
985 plaintext,
986 "wrong key should NOT produce the original plaintext"
987 );
988
989 let correct_decrypted = correct_priv_key
991 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
992 .expect("correct key decryption failed");
993 assert_eq!(
994 &correct_decrypted[..plaintext.len()],
995 plaintext,
996 "correct key should produce the original plaintext"
997 );
998 }
999
1000 #[test]
1001 fn test_aes128_wrong_key_produces_garbage() {
1002 let correct_key = vec![
1003 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1004 0x0f, 0x10,
1005 ];
1006 let wrong_key = vec![
1007 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1008 0xF1, 0xF0,
1009 ];
1010
1011 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, correct_key).unwrap();
1012 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, wrong_key).unwrap();
1013
1014 let plaintext = b"Secret AES-128 message data!";
1015 let engine_boots = 200u32;
1016 let engine_time = 54321u32;
1017
1018 let (ciphertext, priv_params) = correct_priv_key
1020 .encrypt(plaintext, engine_boots, engine_time, None)
1021 .expect("encryption failed");
1022
1023 let wrong_decrypted = wrong_priv_key
1025 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1026 .expect("decryption should succeed cryptographically");
1027
1028 assert_ne!(
1030 wrong_decrypted.as_ref(),
1031 plaintext,
1032 "wrong key should NOT produce the original plaintext"
1033 );
1034
1035 let correct_decrypted = correct_priv_key
1037 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1038 .expect("correct key decryption failed");
1039 assert_eq!(correct_decrypted.as_ref(), plaintext);
1040 }
1041
1042 #[test]
1043 fn test_aes192_wrong_key_produces_garbage() {
1044 let correct_key = vec![
1045 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1046 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1047 ];
1048 let wrong_key = vec![
1049 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1050 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8,
1051 ];
1052
1053 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, correct_key).unwrap();
1054 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, wrong_key).unwrap();
1055
1056 let plaintext = b"Secret AES-192 message data!";
1057 let engine_boots = 300u32;
1058 let engine_time = 67890u32;
1059
1060 let (ciphertext, priv_params) = correct_priv_key
1061 .encrypt(plaintext, engine_boots, engine_time, None)
1062 .expect("encryption failed");
1063
1064 let wrong_decrypted = wrong_priv_key
1065 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1066 .expect("decryption should succeed cryptographically");
1067
1068 assert_ne!(
1069 wrong_decrypted.as_ref(),
1070 plaintext,
1071 "wrong key should NOT produce the original plaintext"
1072 );
1073 }
1074
1075 #[test]
1076 fn test_aes256_wrong_key_produces_garbage() {
1077 let correct_key = vec![
1078 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1079 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1080 0x1d, 0x1e, 0x1f, 0x20,
1081 ];
1082 let wrong_key = vec![
1083 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1084 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4,
1085 0xE3, 0xE2, 0xE1, 0xE0,
1086 ];
1087
1088 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, correct_key).unwrap();
1089 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, wrong_key).unwrap();
1090
1091 let plaintext = b"Secret AES-256 message data!";
1092 let engine_boots = 400u32;
1093 let engine_time = 11111u32;
1094
1095 let (ciphertext, priv_params) = correct_priv_key
1096 .encrypt(plaintext, engine_boots, engine_time, None)
1097 .expect("encryption failed");
1098
1099 let wrong_decrypted = wrong_priv_key
1100 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1101 .expect("decryption should succeed cryptographically");
1102
1103 assert_ne!(
1104 wrong_decrypted.as_ref(),
1105 plaintext,
1106 "wrong key should NOT produce the original plaintext"
1107 );
1108 }
1109
1110 #[cfg(feature = "crypto-rustcrypto")]
1111 #[test]
1112 fn test_des_wrong_priv_params_produces_garbage() {
1113 let key = vec![
1116 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1117 0x17, 0x18,
1118 ];
1119
1120 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1121
1122 let plaintext = b"DES test message";
1123 let engine_boots = 100u32;
1124 let engine_time = 12345u32;
1125
1126 let (ciphertext, correct_priv_params) = priv_key
1127 .encrypt(plaintext, engine_boots, engine_time, None)
1128 .expect("encryption failed");
1129
1130 let wrong_priv_params = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
1132
1133 let wrong_decrypted = priv_key
1134 .decrypt(&ciphertext, engine_boots, engine_time, &wrong_priv_params)
1135 .expect("decryption should succeed cryptographically");
1136
1137 assert_ne!(
1139 &wrong_decrypted[..plaintext.len()],
1140 plaintext,
1141 "wrong priv_params should NOT produce the original plaintext"
1142 );
1143
1144 let correct_decrypted = priv_key
1146 .decrypt(&ciphertext, engine_boots, engine_time, &correct_priv_params)
1147 .expect("correct decryption failed");
1148 assert_eq!(&correct_decrypted[..plaintext.len()], plaintext);
1149 }
1150
1151 #[test]
1156 fn test_salt_counter_no_duplicates_concurrent() {
1157 use std::collections::HashSet;
1158 use std::sync::{Arc, Mutex};
1159 use std::thread;
1160
1161 let counter = Arc::new(SaltCounter::new());
1162 let results = Arc::new(Mutex::new(HashSet::new()));
1163 let iterations = 10_000usize;
1164 let threads = 8usize;
1165
1166 let handles: Vec<_> = (0..threads)
1167 .map(|_| {
1168 let counter = Arc::clone(&counter);
1169 let results = Arc::clone(&results);
1170 thread::spawn(move || {
1171 for _ in 0..iterations {
1172 let salt = counter.next();
1173 assert_ne!(salt, 0, "SaltCounter must never return zero");
1174 let mut set = results.lock().unwrap();
1175 assert!(set.insert(salt), "SaltCounter emitted duplicate: {salt}");
1176 }
1177 })
1178 })
1179 .collect();
1180
1181 for h in handles {
1182 h.join().expect("thread panicked");
1183 }
1184 }
1185
1186 #[test]
1191 fn test_priv_key_clone_independent_salts() {
1192 let key = vec![0u8; 16];
1193 let original = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1194 let cloned = original.clone();
1195
1196 let plaintext = b"test";
1197
1198 let (_, salt_orig) = original.encrypt(plaintext, 0, 0, None).unwrap();
1200 let (_, salt_clone) = cloned.encrypt(plaintext, 0, 0, None).unwrap();
1201
1202 assert_ne!(
1203 salt_orig, salt_clone,
1204 "cloned PrivKey must start with an independent salt counter"
1205 );
1206 }
1207
1208 #[test]
1209 fn test_aes_wrong_engine_time_produces_garbage() {
1210 let key = vec![
1213 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1214 0x0f, 0x10,
1215 ];
1216
1217 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1218
1219 let plaintext = b"AES test message";
1220 let engine_boots = 200u32;
1221 let engine_time = 54321u32;
1222
1223 let (ciphertext, priv_params) = priv_key
1224 .encrypt(plaintext, engine_boots, engine_time, None)
1225 .expect("encryption failed");
1226
1227 let wrong_decrypted = priv_key
1229 .decrypt(&ciphertext, engine_boots, engine_time + 1, &priv_params)
1230 .expect("decryption should succeed cryptographically");
1231
1232 assert_ne!(
1233 wrong_decrypted.as_ref(),
1234 plaintext,
1235 "wrong engine_time should NOT produce the original plaintext"
1236 );
1237
1238 let wrong_decrypted2 = priv_key
1240 .decrypt(&ciphertext, engine_boots + 1, engine_time, &priv_params)
1241 .expect("decryption should succeed cryptographically");
1242
1243 assert_ne!(
1244 wrong_decrypted2.as_ref(),
1245 plaintext,
1246 "wrong engine_boots should NOT produce the original plaintext"
1247 );
1248 }
1249}