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 {}, got {}",
55 expected, actual
56 )
57 }
58 Self::InvalidCiphertextLength { length, block_size } => {
59 write!(
60 f,
61 "ciphertext length {} not multiple of block size {}",
62 length, block_size
63 )
64 }
65 Self::Crypto(e) => write!(f, "{}", e),
66 }
67 }
68}
69
70impl std::error::Error for PrivacyError {
71 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
72 match self {
73 Self::Crypto(e) => Some(e),
74 _ => None,
75 }
76 }
77}
78
79pub type PrivacyResult<T> = std::result::Result<T, PrivacyError>;
81
82fn random_nonzero_u64() -> super::crypto::CryptoResult<u64> {
86 let mut buf = [0u8; 8];
87 loop {
88 getrandom::fill(&mut buf).map_err(|_| super::crypto::CryptoError::RandomSource)?;
89 let val = u64::from_ne_bytes(buf);
90 if val != 0 {
91 return Ok(val);
92 }
93 }
95}
96
97#[derive(Zeroize, ZeroizeOnDrop)]
109pub struct PrivKey {
110 key: Vec<u8>,
112 #[zeroize(skip)]
114 protocol: PrivProtocol,
115 #[zeroize(skip)]
118 salt_counter: AtomicU64,
119}
120
121pub struct SaltCounter(AtomicU64);
123
124impl SaltCounter {
125 pub fn new() -> Self {
129 Self(AtomicU64::new(
130 random_nonzero_u64().expect("OS random source unavailable"),
131 ))
132 }
133
134 pub fn from_value(value: u64) -> Self {
138 Self(AtomicU64::new(value))
139 }
140
141 pub fn next(&self) -> u64 {
150 let old = self.0.fetch_add(1, Ordering::SeqCst);
151 let val = old.wrapping_add(1);
152 if val != 0 {
153 return val;
154 }
155 let _ = self
158 .0
159 .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst);
160 1
161 }
162}
163
164impl Default for SaltCounter {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl PrivKey {
171 pub fn from_password(
208 auth_protocol: AuthProtocol,
209 priv_protocol: PrivProtocol,
210 password: &[u8],
211 engine_id: &[u8],
212 ) -> super::crypto::CryptoResult<Self> {
213 use super::MasterKey;
214
215 let master = MasterKey::from_password(auth_protocol, password)?;
216 Self::from_master_key(&master, priv_protocol, engine_id)
217 }
218
219 pub fn from_master_key(
241 master: &super::MasterKey,
242 priv_protocol: PrivProtocol,
243 engine_id: &[u8],
244 ) -> super::crypto::CryptoResult<Self> {
245 use super::{
246 KeyExtension,
247 auth::{extend_key, extend_key_reeder},
248 };
249
250 let auth_protocol = master.protocol();
251 let key_extension = priv_protocol.key_extension_for(auth_protocol);
252
253 let localized = master.localize(engine_id)?;
255 let key_bytes = localized.as_bytes();
256
257 let key = match key_extension {
258 KeyExtension::None => key_bytes.to_vec(),
259 KeyExtension::Blumenthal => {
260 extend_key(auth_protocol, key_bytes, priv_protocol.key_len())?
261 }
262 KeyExtension::Reeder => {
263 extend_key_reeder(auth_protocol, key_bytes, engine_id, priv_protocol.key_len())?
264 }
265 };
266
267 Ok(Self {
268 key,
269 protocol: priv_protocol,
270 salt_counter: Self::init_salt()?,
271 })
272 }
273
274 pub fn from_bytes(
276 protocol: PrivProtocol,
277 key: impl Into<Vec<u8>>,
278 ) -> super::crypto::CryptoResult<Self> {
279 Ok(Self {
280 key: key.into(),
281 protocol,
282 salt_counter: Self::init_salt()?,
283 })
284 }
285
286 fn init_salt() -> super::crypto::CryptoResult<AtomicU64> {
290 Ok(AtomicU64::new(random_nonzero_u64()?))
291 }
292
293 pub fn protocol(&self) -> PrivProtocol {
295 self.protocol
296 }
297
298 pub fn encryption_key(&self) -> &[u8] {
300 match self.protocol {
301 PrivProtocol::Des => &self.key[..8],
302 PrivProtocol::Des3 => &self.key[..24],
303 PrivProtocol::Aes128 => &self.key[..16],
304 PrivProtocol::Aes192 => &self.key[..24],
305 PrivProtocol::Aes256 => &self.key[..32],
306 }
307 }
308
309 pub fn encrypt(
321 &self,
322 plaintext: &[u8],
323 engine_boots: u32,
324 engine_time: u32,
325 salt_counter: Option<&SaltCounter>,
326 ) -> PrivacyResult<(Bytes, Bytes)> {
327 let salt = salt_counter.map(|c| c.next()).unwrap_or_else(|| {
328 let val = self.salt_counter.fetch_add(1, Ordering::Relaxed);
330 if val != 0 {
331 return val;
332 }
333 self.salt_counter.fetch_add(1, Ordering::Relaxed)
335 });
336
337 match self.protocol {
338 PrivProtocol::Des => self.encrypt_des(plaintext, engine_boots, salt),
339 PrivProtocol::Des3 => self.encrypt_des3(plaintext, engine_boots, salt),
340 PrivProtocol::Aes128 => {
341 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 16)
342 }
343 PrivProtocol::Aes192 => {
344 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 24)
345 }
346 PrivProtocol::Aes256 => {
347 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 32)
348 }
349 }
350 }
351
352 pub fn decrypt(
364 &self,
365 ciphertext: &[u8],
366 engine_boots: u32,
367 engine_time: u32,
368 priv_params: &[u8],
369 ) -> PrivacyResult<Bytes> {
370 if priv_params.len() != 8 {
371 tracing::debug!(target: "async_snmp::crypto", { expected = 8, actual = priv_params.len() }, "invalid privParameters length");
372 return Err(PrivacyError::InvalidPrivParamsLength {
373 expected: 8,
374 actual: priv_params.len(),
375 });
376 }
377
378 match self.protocol {
379 PrivProtocol::Des => self.decrypt_des(ciphertext, priv_params),
380 PrivProtocol::Des3 => self.decrypt_des3(ciphertext, priv_params),
381 PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
382 self.decrypt_aes(ciphertext, engine_boots, engine_time, priv_params)
383 }
384 }
385 }
386
387 fn encrypt_des(
389 &self,
390 plaintext: &[u8],
391 engine_boots: u32,
392 salt_int: u64,
393 ) -> PrivacyResult<(Bytes, Bytes)> {
394 let key = &self.key[..8];
396 let pre_iv = &self.key[8..16];
398
399 let mut salt = [0u8; 8];
402 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
403 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
404
405 let mut iv = [0u8; 8];
407 for i in 0..8 {
408 iv[i] = pre_iv[i] ^ salt[i];
409 }
410
411 let padded_len = plaintext.len().next_multiple_of(8);
413 let mut buffer = vec![0u8; padded_len];
414 buffer[..plaintext.len()].copy_from_slice(plaintext);
415
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 padded_len = plaintext.len().next_multiple_of(8);
476 let mut buffer = vec![0u8; padded_len];
477 buffer[..plaintext.len()].copy_from_slice(plaintext);
478
479 super::crypto::provider().encrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
480
481 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
482 }
483
484 fn decrypt_des3(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
486 if !ciphertext.len().is_multiple_of(8) {
487 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "3DES decryption failed: invalid ciphertext length");
488 return Err(PrivacyError::InvalidCiphertextLength {
489 length: ciphertext.len(),
490 block_size: 8,
491 });
492 }
493
494 let key = &self.key[..24];
496 let pre_iv = &self.key[24..32];
498
499 let salt = priv_params;
501
502 let mut iv = [0u8; 8];
504 for i in 0..8 {
505 iv[i] = pre_iv[i] ^ salt[i];
506 }
507
508 let mut buffer = ciphertext.to_vec();
509 super::crypto::provider().decrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
510
511 Ok(Bytes::from(buffer))
512 }
513
514 fn encrypt_aes(
516 &self,
517 plaintext: &[u8],
518 engine_boots: u32,
519 engine_time: u32,
520 salt: u64,
521 key_len: usize,
522 ) -> PrivacyResult<(Bytes, Bytes)> {
523 let key = &self.key[..key_len];
525
526 let salt_bytes = salt.to_be_bytes();
528
529 let mut iv = [0u8; 16];
532 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
533 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
534 iv[8..].copy_from_slice(&salt_bytes);
535
536 let mut buffer = plaintext.to_vec();
537 super::crypto::provider().encrypt(self.protocol, key, &iv, &mut buffer)?;
538
539 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt_bytes)))
540 }
541
542 fn decrypt_aes(
544 &self,
545 ciphertext: &[u8],
546 engine_boots: u32,
547 engine_time: u32,
548 priv_params: &[u8],
549 ) -> PrivacyResult<Bytes> {
550 let key_len = match self.protocol {
551 PrivProtocol::Aes128 => 16,
552 PrivProtocol::Aes192 => 24,
553 PrivProtocol::Aes256 => 32,
554 _ => unreachable!(),
555 };
556
557 let key = &self.key[..key_len];
559
560 let mut iv = [0u8; 16];
562 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
563 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
564 iv[8..].copy_from_slice(priv_params);
565
566 let mut buffer = ciphertext.to_vec();
567 super::crypto::provider().decrypt(self.protocol, key, &iv, &mut buffer)?;
568
569 Ok(Bytes::from(buffer))
570 }
571}
572
573impl std::fmt::Debug for PrivKey {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 f.debug_struct("PrivKey")
576 .field("protocol", &self.protocol)
577 .field("key", &"[REDACTED]")
578 .finish()
579 }
580}
581
582impl Clone for PrivKey {
583 fn clone(&self) -> Self {
584 Self {
585 key: self.key.clone(),
586 protocol: self.protocol,
587 salt_counter: Self::init_salt().expect("OS random source unavailable"),
589 }
590 }
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::format::hex::decode as decode_hex;
597
598 #[cfg(feature = "crypto-rustcrypto")]
599 #[test]
600 fn test_des_encrypt_decrypt_roundtrip() {
601 let key = vec![
603 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, ];
606 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
607
608 let plaintext = b"Hello, SNMPv3 World!";
609 let engine_boots = 100u32;
610 let engine_time = 12345u32;
611
612 let (ciphertext, priv_params) = priv_key
613 .encrypt(plaintext, engine_boots, engine_time, None)
614 .expect("encryption failed");
615
616 assert_ne!(ciphertext.as_ref(), plaintext);
618 assert_eq!(priv_params.len(), 8);
620
621 let decrypted = priv_key
623 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
624 .expect("decryption failed");
625
626 assert!(decrypted.len() >= plaintext.len());
628 assert_eq!(&decrypted[..plaintext.len()], plaintext);
629 }
630
631 #[cfg(feature = "crypto-rustcrypto")]
632 #[test]
633 fn test_des3_encrypt_decrypt_roundtrip() {
634 let key = vec![
636 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, ];
641 let priv_key = PrivKey::from_bytes(PrivProtocol::Des3, key).unwrap();
642
643 let plaintext = b"Hello, SNMPv3 World with 3DES!";
644 let engine_boots = 100u32;
645 let engine_time = 12345u32;
646
647 let (ciphertext, priv_params) = priv_key
648 .encrypt(plaintext, engine_boots, engine_time, None)
649 .expect("encryption failed");
650
651 assert_ne!(ciphertext.as_ref(), plaintext);
653 assert_eq!(priv_params.len(), 8);
655
656 let decrypted = priv_key
658 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
659 .expect("decryption failed");
660
661 assert!(decrypted.len() >= plaintext.len());
663 assert_eq!(&decrypted[..plaintext.len()], plaintext);
664 }
665
666 #[test]
667 fn test_aes128_encrypt_decrypt_roundtrip() {
668 let key = vec![
670 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
671 0x0f, 0x10,
672 ];
673 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
674
675 let plaintext = b"Hello, SNMPv3 AES World!";
676 let engine_boots = 200u32;
677 let engine_time = 54321u32;
678
679 let (ciphertext, priv_params) = priv_key
680 .encrypt(plaintext, engine_boots, engine_time, None)
681 .expect("encryption failed");
682
683 assert_ne!(ciphertext.as_ref(), plaintext);
685 assert_eq!(priv_params.len(), 8);
687
688 let decrypted = priv_key
690 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
691 .expect("decryption failed");
692
693 assert_eq!(decrypted.len(), plaintext.len());
695 assert_eq!(decrypted.as_ref(), plaintext);
696 }
697
698 #[cfg(feature = "crypto-rustcrypto")]
699 #[test]
700 fn test_des_invalid_ciphertext_length() {
701 let key = vec![0u8; 16];
702 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
703
704 let ciphertext = [0u8; 13];
706 let priv_params = [0u8; 8];
707
708 let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
709 assert!(result.is_err());
710 }
711
712 #[test]
713 fn test_invalid_priv_params_length() {
714 let key = vec![0u8; 16];
715 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
716
717 let ciphertext = [0u8; 16];
719 let priv_params = [0u8; 4]; let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
722 assert!(result.is_err());
723 }
724
725 #[test]
726 fn test_salt_counter() {
727 let counter = SaltCounter::new();
728 let s1 = counter.next();
729 let s2 = counter.next();
730 let s3 = counter.next();
731
732 assert_eq!(s2, s1.wrapping_add(1));
734 assert_eq!(s3, s2.wrapping_add(1));
735 }
736
737 #[test]
742 fn test_salt_counter_skips_zero() {
743 let counter = SaltCounter::from_value(u64::MAX - 1);
749
750 let s1 = counter.next();
751 assert_eq!(s1, u64::MAX);
752
753 let s2 = counter.next();
755 assert_ne!(s2, 0, "SaltCounter should never return zero");
756 assert_eq!(s2, 1, "SaltCounter should skip 0 and return 1");
757
758 let s3 = counter.next();
760 assert_eq!(s3, 2);
761 }
762
763 #[test]
768 fn test_priv_key_internal_salt_skips_zero() {
769 let key = vec![0u8; 16];
770 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
771
772 priv_key.salt_counter.store(u64::MAX, Ordering::Relaxed);
774
775 let plaintext = b"test";
776
777 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
779 assert_eq!(
780 u64::from_be_bytes(salt1.as_ref().try_into().unwrap()),
781 u64::MAX
782 );
783
784 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
786 let salt2_value = u64::from_be_bytes(salt2.as_ref().try_into().unwrap());
787 assert_ne!(salt2_value, 0, "Salt should never be zero");
788 assert_eq!(salt2_value, 1, "Salt should skip 0 and be 1");
789
790 let (_, salt3) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
792 let salt3_value = u64::from_be_bytes(salt3.as_ref().try_into().unwrap());
793 assert_eq!(salt3_value, 2);
794 }
795
796 #[test]
797 fn test_multiple_encryptions_different_salt() {
798 let key = vec![0u8; 16];
799 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
800
801 let plaintext = b"test data";
802
803 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
804 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
805
806 assert_ne!(salt1, salt2);
808 }
809
810 #[test]
811 fn test_from_password() {
812 let password = b"maplesyrup";
814 let engine_id = decode_hex("000000000000000000000002").unwrap();
815
816 let priv_key = PrivKey::from_password(
817 AuthProtocol::Sha1,
818 PrivProtocol::Aes128,
819 password,
820 &engine_id,
821 )
822 .unwrap();
823
824 let plaintext = b"test message";
826 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
827 let decrypted = priv_key
828 .decrypt(&ciphertext, 100, 200, &priv_params)
829 .unwrap();
830
831 assert_eq!(decrypted.as_ref(), plaintext);
832 }
833
834 #[test]
835 fn test_aes192_encrypt_decrypt_roundtrip() {
836 let key = vec![
838 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
839 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
840 ];
841 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, key).unwrap();
842
843 let plaintext = b"Hello, SNMPv3 AES-192 World!";
844 let engine_boots = 300u32;
845 let engine_time = 67890u32;
846
847 let (ciphertext, priv_params) = priv_key
848 .encrypt(plaintext, engine_boots, engine_time, None)
849 .expect("AES-192 encryption failed");
850
851 assert_ne!(ciphertext.as_ref(), plaintext);
853 assert_eq!(priv_params.len(), 8);
855
856 let decrypted = priv_key
858 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
859 .expect("AES-192 decryption failed");
860
861 assert_eq!(decrypted.len(), plaintext.len());
863 assert_eq!(decrypted.as_ref(), plaintext);
864 }
865
866 #[test]
867 fn test_aes256_encrypt_decrypt_roundtrip() {
868 let key = vec![
870 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
871 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
872 0x1d, 0x1e, 0x1f, 0x20,
873 ];
874 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, key).unwrap();
875
876 let plaintext = b"Hello, SNMPv3 AES-256 World!";
877 let engine_boots = 400u32;
878 let engine_time = 11111u32;
879
880 let (ciphertext, priv_params) = priv_key
881 .encrypt(plaintext, engine_boots, engine_time, None)
882 .expect("AES-256 encryption failed");
883
884 assert_ne!(ciphertext.as_ref(), plaintext);
886 assert_eq!(priv_params.len(), 8);
888
889 let decrypted = priv_key
891 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
892 .expect("AES-256 decryption failed");
893
894 assert_eq!(decrypted.len(), plaintext.len());
896 assert_eq!(decrypted.as_ref(), plaintext);
897 }
898
899 #[test]
900 fn test_aes192_from_password() {
901 let password = b"longpassword123";
903 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
904
905 let priv_key = PrivKey::from_password(
906 AuthProtocol::Sha256, PrivProtocol::Aes192,
908 password,
909 &engine_id,
910 )
911 .unwrap();
912
913 let plaintext = b"test message for AES-192";
914 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
915 let decrypted = priv_key
916 .decrypt(&ciphertext, 100, 200, &priv_params)
917 .unwrap();
918
919 assert_eq!(decrypted.as_ref(), plaintext);
920 }
921
922 #[test]
923 fn test_aes256_from_password() {
924 let password = b"anotherlongpassword456";
926 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
927
928 let priv_key = PrivKey::from_password(
929 AuthProtocol::Sha256, PrivProtocol::Aes256,
931 password,
932 &engine_id,
933 )
934 .unwrap();
935
936 let plaintext = b"test message for AES-256";
937 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
938 let decrypted = priv_key
939 .decrypt(&ciphertext, 100, 200, &priv_params)
940 .unwrap();
941
942 assert_eq!(decrypted.as_ref(), plaintext);
943 }
944
945 #[cfg(feature = "crypto-rustcrypto")]
955 #[test]
956 fn test_des_wrong_key_produces_garbage() {
957 let correct_key = vec![
959 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
960 0x17, 0x18,
961 ];
962 let wrong_key = vec![
964 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2,
965 0xE1, 0xE0,
966 ];
967
968 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Des, correct_key).unwrap();
969 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Des, wrong_key).unwrap();
970
971 let plaintext = b"Secret SNMPv3 message data!";
972 let engine_boots = 100u32;
973 let engine_time = 12345u32;
974
975 let (ciphertext, priv_params) = correct_priv_key
977 .encrypt(plaintext, engine_boots, engine_time, None)
978 .expect("encryption failed");
979
980 let wrong_decrypted = wrong_priv_key
982 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
983 .expect("decryption should succeed cryptographically");
984
985 assert_ne!(
987 &wrong_decrypted[..plaintext.len()],
988 plaintext,
989 "wrong key should NOT produce the original plaintext"
990 );
991
992 let correct_decrypted = correct_priv_key
994 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
995 .expect("correct key decryption failed");
996 assert_eq!(
997 &correct_decrypted[..plaintext.len()],
998 plaintext,
999 "correct key should produce the original plaintext"
1000 );
1001 }
1002
1003 #[test]
1004 fn test_aes128_wrong_key_produces_garbage() {
1005 let correct_key = vec![
1006 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1007 0x0f, 0x10,
1008 ];
1009 let wrong_key = vec![
1010 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1011 0xF1, 0xF0,
1012 ];
1013
1014 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, correct_key).unwrap();
1015 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, wrong_key).unwrap();
1016
1017 let plaintext = b"Secret AES-128 message data!";
1018 let engine_boots = 200u32;
1019 let engine_time = 54321u32;
1020
1021 let (ciphertext, priv_params) = correct_priv_key
1023 .encrypt(plaintext, engine_boots, engine_time, None)
1024 .expect("encryption failed");
1025
1026 let wrong_decrypted = wrong_priv_key
1028 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1029 .expect("decryption should succeed cryptographically");
1030
1031 assert_ne!(
1033 wrong_decrypted.as_ref(),
1034 plaintext,
1035 "wrong key should NOT produce the original plaintext"
1036 );
1037
1038 let correct_decrypted = correct_priv_key
1040 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1041 .expect("correct key decryption failed");
1042 assert_eq!(correct_decrypted.as_ref(), plaintext);
1043 }
1044
1045 #[test]
1046 fn test_aes192_wrong_key_produces_garbage() {
1047 let correct_key = vec![
1048 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1049 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1050 ];
1051 let wrong_key = vec![
1052 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1053 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8,
1054 ];
1055
1056 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, correct_key).unwrap();
1057 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, wrong_key).unwrap();
1058
1059 let plaintext = b"Secret AES-192 message data!";
1060 let engine_boots = 300u32;
1061 let engine_time = 67890u32;
1062
1063 let (ciphertext, priv_params) = correct_priv_key
1064 .encrypt(plaintext, engine_boots, engine_time, None)
1065 .expect("encryption failed");
1066
1067 let wrong_decrypted = wrong_priv_key
1068 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1069 .expect("decryption should succeed cryptographically");
1070
1071 assert_ne!(
1072 wrong_decrypted.as_ref(),
1073 plaintext,
1074 "wrong key should NOT produce the original plaintext"
1075 );
1076 }
1077
1078 #[test]
1079 fn test_aes256_wrong_key_produces_garbage() {
1080 let correct_key = vec![
1081 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1082 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1083 0x1d, 0x1e, 0x1f, 0x20,
1084 ];
1085 let wrong_key = vec![
1086 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1087 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4,
1088 0xE3, 0xE2, 0xE1, 0xE0,
1089 ];
1090
1091 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, correct_key).unwrap();
1092 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, wrong_key).unwrap();
1093
1094 let plaintext = b"Secret AES-256 message data!";
1095 let engine_boots = 400u32;
1096 let engine_time = 11111u32;
1097
1098 let (ciphertext, priv_params) = correct_priv_key
1099 .encrypt(plaintext, engine_boots, engine_time, None)
1100 .expect("encryption failed");
1101
1102 let wrong_decrypted = wrong_priv_key
1103 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1104 .expect("decryption should succeed cryptographically");
1105
1106 assert_ne!(
1107 wrong_decrypted.as_ref(),
1108 plaintext,
1109 "wrong key should NOT produce the original plaintext"
1110 );
1111 }
1112
1113 #[cfg(feature = "crypto-rustcrypto")]
1114 #[test]
1115 fn test_des_wrong_priv_params_produces_garbage() {
1116 let key = vec![
1119 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1120 0x17, 0x18,
1121 ];
1122
1123 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1124
1125 let plaintext = b"DES test message";
1126 let engine_boots = 100u32;
1127 let engine_time = 12345u32;
1128
1129 let (ciphertext, correct_priv_params) = priv_key
1130 .encrypt(plaintext, engine_boots, engine_time, None)
1131 .expect("encryption failed");
1132
1133 let wrong_priv_params = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
1135
1136 let wrong_decrypted = priv_key
1137 .decrypt(&ciphertext, engine_boots, engine_time, &wrong_priv_params)
1138 .expect("decryption should succeed cryptographically");
1139
1140 assert_ne!(
1142 &wrong_decrypted[..plaintext.len()],
1143 plaintext,
1144 "wrong priv_params should NOT produce the original plaintext"
1145 );
1146
1147 let correct_decrypted = priv_key
1149 .decrypt(&ciphertext, engine_boots, engine_time, &correct_priv_params)
1150 .expect("correct decryption failed");
1151 assert_eq!(&correct_decrypted[..plaintext.len()], plaintext);
1152 }
1153
1154 #[test]
1159 fn test_salt_counter_no_duplicates_concurrent() {
1160 use std::collections::HashSet;
1161 use std::sync::{Arc, Mutex};
1162 use std::thread;
1163
1164 let counter = Arc::new(SaltCounter::new());
1165 let results = Arc::new(Mutex::new(HashSet::new()));
1166 let iterations = 10_000usize;
1167 let threads = 8usize;
1168
1169 let handles: Vec<_> = (0..threads)
1170 .map(|_| {
1171 let counter = Arc::clone(&counter);
1172 let results = Arc::clone(&results);
1173 thread::spawn(move || {
1174 for _ in 0..iterations {
1175 let salt = counter.next();
1176 assert_ne!(salt, 0, "SaltCounter must never return zero");
1177 let mut set = results.lock().unwrap();
1178 assert!(set.insert(salt), "SaltCounter emitted duplicate: {salt}");
1179 }
1180 })
1181 })
1182 .collect();
1183
1184 for h in handles {
1185 h.join().expect("thread panicked");
1186 }
1187 }
1188
1189 #[test]
1194 fn test_priv_key_clone_independent_salts() {
1195 let key = vec![0u8; 16];
1196 let original = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1197 let cloned = original.clone();
1198
1199 let plaintext = b"test";
1200
1201 let (_, salt_orig) = original.encrypt(plaintext, 0, 0, None).unwrap();
1203 let (_, salt_clone) = cloned.encrypt(plaintext, 0, 0, None).unwrap();
1204
1205 assert_ne!(
1206 salt_orig, salt_clone,
1207 "cloned PrivKey must start with an independent salt counter"
1208 );
1209 }
1210
1211 #[test]
1212 fn test_aes_wrong_engine_time_produces_garbage() {
1213 let key = vec![
1216 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1217 0x0f, 0x10,
1218 ];
1219
1220 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1221
1222 let plaintext = b"AES test message";
1223 let engine_boots = 200u32;
1224 let engine_time = 54321u32;
1225
1226 let (ciphertext, priv_params) = priv_key
1227 .encrypt(plaintext, engine_boots, engine_time, None)
1228 .expect("encryption failed");
1229
1230 let wrong_decrypted = priv_key
1232 .decrypt(&ciphertext, engine_boots, engine_time + 1, &priv_params)
1233 .expect("decryption should succeed cryptographically");
1234
1235 assert_ne!(
1236 wrong_decrypted.as_ref(),
1237 plaintext,
1238 "wrong engine_time should NOT produce the original plaintext"
1239 );
1240
1241 let wrong_decrypted2 = priv_key
1243 .decrypt(&ciphertext, engine_boots + 1, engine_time, &priv_params)
1244 .expect("decryption should succeed cryptographically");
1245
1246 assert_ne!(
1247 wrong_decrypted2.as_ref(),
1248 plaintext,
1249 "wrong engine_boots should NOT produce the original plaintext"
1250 );
1251 }
1252}