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 {
153 loop {
154 let old = self.0.fetch_add(1, Ordering::SeqCst);
155 let val = old.wrapping_add(1);
156 if val != 0 {
157 return val;
158 }
159 }
164 }
165}
166
167impl Default for SaltCounter {
168 fn default() -> Self {
169 Self::new()
170 }
171}
172
173impl PrivKey {
174 pub fn from_password(
211 auth_protocol: AuthProtocol,
212 priv_protocol: PrivProtocol,
213 password: &[u8],
214 engine_id: &[u8],
215 ) -> super::crypto::CryptoResult<Self> {
216 use super::MasterKey;
217
218 let master = MasterKey::from_password(auth_protocol, password)?;
219 Self::from_master_key(&master, priv_protocol, engine_id)
220 }
221
222 pub fn from_master_key(
244 master: &super::MasterKey,
245 priv_protocol: PrivProtocol,
246 engine_id: &[u8],
247 ) -> super::crypto::CryptoResult<Self> {
248 use super::{
249 KeyExtension,
250 auth::{extend_key, extend_key_reeder},
251 };
252
253 let auth_protocol = master.protocol();
254 let key_extension = priv_protocol.key_extension_for(auth_protocol);
255
256 let localized = master.localize(engine_id)?;
258 let key_bytes = localized.as_bytes();
259
260 let key = match key_extension {
261 KeyExtension::None => key_bytes.to_vec(),
262 KeyExtension::Blumenthal => {
263 extend_key(auth_protocol, key_bytes, priv_protocol.key_len())?
264 }
265 KeyExtension::Reeder => {
266 extend_key_reeder(auth_protocol, key_bytes, engine_id, priv_protocol.key_len())?
267 }
268 };
269
270 Ok(Self {
271 key,
272 protocol: priv_protocol,
273 salt_counter: Self::init_salt()?,
274 })
275 }
276
277 pub fn from_bytes(
286 protocol: PrivProtocol,
287 key: impl Into<Vec<u8>>,
288 ) -> super::crypto::CryptoResult<Self> {
289 let key = key.into();
290 if key.len() < protocol.key_len() {
291 return Err(CryptoError::InvalidKeyLength);
292 }
293 Ok(Self {
294 key,
295 protocol,
296 salt_counter: Self::init_salt()?,
297 })
298 }
299
300 fn init_salt() -> super::crypto::CryptoResult<AtomicU64> {
304 Ok(AtomicU64::new(random_nonzero_u64()?))
305 }
306
307 pub fn protocol(&self) -> PrivProtocol {
309 self.protocol
310 }
311
312 pub fn encryption_key(&self) -> &[u8] {
314 match self.protocol {
315 PrivProtocol::Des => &self.key[..8],
316 PrivProtocol::Des3 => &self.key[..24],
317 PrivProtocol::Aes128 => &self.key[..16],
318 PrivProtocol::Aes192 => &self.key[..24],
319 PrivProtocol::Aes256 => &self.key[..32],
320 }
321 }
322
323 pub fn encrypt(
335 &self,
336 plaintext: &[u8],
337 engine_boots: u32,
338 engine_time: u32,
339 salt_counter: Option<&SaltCounter>,
340 ) -> PrivacyResult<(Bytes, Bytes)> {
341 let salt = salt_counter.map_or_else(
342 || {
343 let val = self.salt_counter.fetch_add(1, Ordering::Relaxed);
345 if val != 0 {
346 return val;
347 }
348 self.salt_counter.fetch_add(1, Ordering::Relaxed)
350 },
351 SaltCounter::next,
352 );
353
354 match self.protocol {
355 PrivProtocol::Des => self.encrypt_des(plaintext, engine_boots, salt),
356 PrivProtocol::Des3 => self.encrypt_des3(plaintext, engine_boots, salt),
357 PrivProtocol::Aes128 => {
358 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 16)
359 }
360 PrivProtocol::Aes192 => {
361 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 24)
362 }
363 PrivProtocol::Aes256 => {
364 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 32)
365 }
366 }
367 }
368
369 pub fn decrypt(
381 &self,
382 ciphertext: &[u8],
383 engine_boots: u32,
384 engine_time: u32,
385 priv_params: &[u8],
386 ) -> PrivacyResult<Bytes> {
387 if priv_params.len() != 8 {
388 tracing::debug!(target: "async_snmp::crypto", { expected = 8, actual = priv_params.len() }, "invalid privParameters length");
389 return Err(PrivacyError::InvalidPrivParamsLength {
390 expected: 8,
391 actual: priv_params.len(),
392 });
393 }
394
395 match self.protocol {
396 PrivProtocol::Des => self.decrypt_des(ciphertext, priv_params),
397 PrivProtocol::Des3 => self.decrypt_des3(ciphertext, priv_params),
398 PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
399 self.decrypt_aes(ciphertext, engine_boots, engine_time, priv_params)
400 }
401 }
402 }
403
404 fn encrypt_des(
406 &self,
407 plaintext: &[u8],
408 engine_boots: u32,
409 salt_int: u64,
410 ) -> PrivacyResult<(Bytes, Bytes)> {
411 let key = &self.key[..8];
413 let pre_iv = &self.key[8..16];
415
416 let mut salt = [0u8; 8];
419 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
420 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
421
422 let mut iv = [0u8; 8];
424 for i in 0..8 {
425 iv[i] = pre_iv[i] ^ salt[i];
426 }
427
428 let mut buffer = plaintext.to_vec();
429 super::crypto::provider().encrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
430
431 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
432 }
433
434 fn decrypt_des(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
436 if !ciphertext.len().is_multiple_of(8) {
437 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "DES decryption failed: invalid ciphertext length");
438 return Err(PrivacyError::InvalidCiphertextLength {
439 length: ciphertext.len(),
440 block_size: 8,
441 });
442 }
443
444 let key = &self.key[..8];
446 let pre_iv = &self.key[8..16];
448
449 let salt = priv_params;
451
452 let mut iv = [0u8; 8];
454 for i in 0..8 {
455 iv[i] = pre_iv[i] ^ salt[i];
456 }
457
458 let mut buffer = ciphertext.to_vec();
459 super::crypto::provider().decrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
460
461 Ok(Bytes::from(buffer))
462 }
463
464 fn encrypt_des3(
466 &self,
467 plaintext: &[u8],
468 engine_boots: u32,
469 salt_int: u64,
470 ) -> PrivacyResult<(Bytes, Bytes)> {
471 let key = &self.key[..24];
473 let pre_iv = &self.key[24..32];
475
476 let mut salt = [0u8; 8];
478 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
479 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
480
481 let mut iv = [0u8; 8];
483 for i in 0..8 {
484 iv[i] = pre_iv[i] ^ salt[i];
485 }
486
487 let mut buffer = plaintext.to_vec();
488 super::crypto::provider().encrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
489
490 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
491 }
492
493 fn decrypt_des3(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
495 if !ciphertext.len().is_multiple_of(8) {
496 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "3DES decryption failed: invalid ciphertext length");
497 return Err(PrivacyError::InvalidCiphertextLength {
498 length: ciphertext.len(),
499 block_size: 8,
500 });
501 }
502
503 let key = &self.key[..24];
505 let pre_iv = &self.key[24..32];
507
508 let salt = priv_params;
510
511 let mut iv = [0u8; 8];
513 for i in 0..8 {
514 iv[i] = pre_iv[i] ^ salt[i];
515 }
516
517 let mut buffer = ciphertext.to_vec();
518 super::crypto::provider().decrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
519
520 Ok(Bytes::from(buffer))
521 }
522
523 fn encrypt_aes(
525 &self,
526 plaintext: &[u8],
527 engine_boots: u32,
528 engine_time: u32,
529 salt: u64,
530 key_len: usize,
531 ) -> PrivacyResult<(Bytes, Bytes)> {
532 let key = &self.key[..key_len];
534
535 let salt_bytes = salt.to_be_bytes();
537
538 let mut iv = [0u8; 16];
541 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
542 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
543 iv[8..].copy_from_slice(&salt_bytes);
544
545 let mut buffer = plaintext.to_vec();
546 super::crypto::provider().encrypt(self.protocol, key, &iv, &mut buffer)?;
547
548 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt_bytes)))
549 }
550
551 fn decrypt_aes(
553 &self,
554 ciphertext: &[u8],
555 engine_boots: u32,
556 engine_time: u32,
557 priv_params: &[u8],
558 ) -> PrivacyResult<Bytes> {
559 let key_len = match self.protocol {
560 PrivProtocol::Aes128 => 16,
561 PrivProtocol::Aes192 => 24,
562 PrivProtocol::Aes256 => 32,
563 _ => unreachable!(),
564 };
565
566 let key = &self.key[..key_len];
568
569 let mut iv = [0u8; 16];
571 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
572 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
573 iv[8..].copy_from_slice(priv_params);
574
575 let mut buffer = ciphertext.to_vec();
576 super::crypto::provider().decrypt(self.protocol, key, &iv, &mut buffer)?;
577
578 Ok(Bytes::from(buffer))
579 }
580}
581
582impl std::fmt::Debug for PrivKey {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 f.debug_struct("PrivKey")
585 .field("protocol", &self.protocol)
586 .field("key", &"[REDACTED]")
587 .field("salt_counter", &"[REDACTED]")
588 .finish()
589 }
590}
591
592impl Clone for PrivKey {
593 fn clone(&self) -> Self {
594 Self {
595 key: self.key.clone(),
596 protocol: self.protocol,
597 salt_counter: Self::init_salt().expect("OS random source unavailable"),
599 }
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606 use crate::format::hex::decode as decode_hex;
607
608 #[cfg(feature = "crypto-rustcrypto")]
609 #[test]
610 fn test_des_encrypt_decrypt_roundtrip() {
611 let key = vec![
613 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, ];
616 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
617
618 let plaintext = b"Hello, SNMPv3 World!";
619 let engine_boots = 100u32;
620 let engine_time = 12345u32;
621
622 let (ciphertext, priv_params) = priv_key
623 .encrypt(plaintext, engine_boots, engine_time, None)
624 .expect("encryption failed");
625
626 assert_ne!(ciphertext.as_ref(), plaintext);
628 assert_eq!(priv_params.len(), 8);
630
631 let decrypted = priv_key
633 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
634 .expect("decryption failed");
635
636 assert!(decrypted.len() >= plaintext.len());
638 assert_eq!(&decrypted[..plaintext.len()], plaintext);
639 }
640
641 #[cfg(feature = "crypto-rustcrypto")]
642 #[test]
643 fn test_des3_encrypt_decrypt_roundtrip() {
644 let key = vec![
646 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, ];
651 let priv_key = PrivKey::from_bytes(PrivProtocol::Des3, key).unwrap();
652
653 let plaintext = b"Hello, SNMPv3 World with 3DES!";
654 let engine_boots = 100u32;
655 let engine_time = 12345u32;
656
657 let (ciphertext, priv_params) = priv_key
658 .encrypt(plaintext, engine_boots, engine_time, None)
659 .expect("encryption failed");
660
661 assert_ne!(ciphertext.as_ref(), plaintext);
663 assert_eq!(priv_params.len(), 8);
665
666 let decrypted = priv_key
668 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
669 .expect("decryption failed");
670
671 assert!(decrypted.len() >= plaintext.len());
673 assert_eq!(&decrypted[..plaintext.len()], plaintext);
674 }
675
676 #[test]
677 fn test_aes128_encrypt_decrypt_roundtrip() {
678 let key = vec![
680 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
681 0x0f, 0x10,
682 ];
683 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
684
685 let plaintext = b"Hello, SNMPv3 AES World!";
686 let engine_boots = 200u32;
687 let engine_time = 54321u32;
688
689 let (ciphertext, priv_params) = priv_key
690 .encrypt(plaintext, engine_boots, engine_time, None)
691 .expect("encryption failed");
692
693 assert_ne!(ciphertext.as_ref(), plaintext);
695 assert_eq!(priv_params.len(), 8);
697
698 let decrypted = priv_key
700 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
701 .expect("decryption failed");
702
703 assert_eq!(decrypted.len(), plaintext.len());
705 assert_eq!(decrypted.as_ref(), plaintext);
706 }
707
708 #[cfg(feature = "crypto-rustcrypto")]
709 #[test]
710 fn test_des_invalid_ciphertext_length() {
711 let key = vec![0u8; 16];
712 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
713
714 let ciphertext = [0u8; 13];
716 let priv_params = [0u8; 8];
717
718 let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
719 assert!(result.is_err());
720 }
721
722 #[test]
723 fn test_invalid_priv_params_length() {
724 let key = vec![0u8; 16];
725 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
726
727 let ciphertext = [0u8; 16];
729 let priv_params = [0u8; 4]; let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
732 assert!(result.is_err());
733 }
734
735 #[test]
736 fn test_from_bytes_rejects_undersized_key() {
737 let result = PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; 4]);
740 assert!(matches!(result, Err(CryptoError::InvalidKeyLength)));
741 }
742
743 #[test]
744 fn test_from_bytes_accepts_exact_length_key() {
745 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; 16]);
746 assert!(priv_key.is_ok());
747
748 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; 16]);
749 assert!(priv_key.is_ok());
750 }
751
752 #[test]
753 fn test_from_bytes_accepts_oversized_key() {
754 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; 20]).unwrap();
758 let _ = priv_key.encrypt(b"data", 0, 0, None);
760 }
761
762 #[test]
763 fn test_from_bytes_key_len_boundary() {
764 let des_len = PrivProtocol::Des.key_len();
765 assert!(PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; des_len - 1]).is_err());
766 assert!(PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; des_len]).is_ok());
767
768 let aes256_len = PrivProtocol::Aes256.key_len();
769 assert!(PrivKey::from_bytes(PrivProtocol::Aes256, vec![0u8; aes256_len - 1]).is_err());
770 assert!(PrivKey::from_bytes(PrivProtocol::Aes256, vec![0u8; aes256_len]).is_ok());
771 }
772
773 #[test]
774 fn test_salt_counter() {
775 let counter = SaltCounter::new();
776 let s1 = counter.next();
777 let s2 = counter.next();
778 let s3 = counter.next();
779
780 assert_eq!(s2, s1.wrapping_add(1));
782 assert_eq!(s3, s2.wrapping_add(1));
783 }
784
785 #[test]
790 fn test_salt_counter_skips_zero() {
791 let counter = SaltCounter::from_value(u64::MAX - 1);
797
798 let s1 = counter.next();
799 assert_eq!(s1, u64::MAX);
800
801 let s2 = counter.next();
803 assert_ne!(s2, 0, "SaltCounter should never return zero");
804 assert_eq!(s2, 1, "SaltCounter should skip 0 and return 1");
805
806 let s3 = counter.next();
808 assert_eq!(s3, 2);
809 }
810
811 #[test]
818 fn test_salt_counter_wrap_no_duplicate() {
819 use std::collections::HashSet;
820
821 let counter = SaltCounter::from_value(u64::MAX - 2);
822 let mut seen = HashSet::new();
823 for _ in 0..6 {
825 let v = counter.next();
826 assert_ne!(v, 0, "SaltCounter must never return zero");
827 assert!(seen.insert(v), "SaltCounter emitted duplicate: {v}");
828 }
829 }
830
831 #[test]
836 fn test_priv_key_internal_salt_skips_zero() {
837 let key = vec![0u8; 16];
838 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
839
840 priv_key.salt_counter.store(u64::MAX, Ordering::Relaxed);
842
843 let plaintext = b"test";
844
845 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
847 assert_eq!(
848 u64::from_be_bytes(salt1.as_ref().try_into().unwrap()),
849 u64::MAX
850 );
851
852 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
854 let salt2_value = u64::from_be_bytes(salt2.as_ref().try_into().unwrap());
855 assert_ne!(salt2_value, 0, "Salt should never be zero");
856 assert_eq!(salt2_value, 1, "Salt should skip 0 and be 1");
857
858 let (_, salt3) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
860 let salt3_value = u64::from_be_bytes(salt3.as_ref().try_into().unwrap());
861 assert_eq!(salt3_value, 2);
862 }
863
864 #[test]
865 fn test_multiple_encryptions_different_salt() {
866 let key = vec![0u8; 16];
867 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
868
869 let plaintext = b"test data";
870
871 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
872 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
873
874 assert_ne!(salt1, salt2);
876 }
877
878 #[test]
879 fn test_from_password() {
880 let password = b"maplesyrup";
882 let engine_id = decode_hex("000000000000000000000002").unwrap();
883
884 let priv_key = PrivKey::from_password(
885 AuthProtocol::Sha1,
886 PrivProtocol::Aes128,
887 password,
888 &engine_id,
889 )
890 .unwrap();
891
892 let plaintext = b"test message";
894 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
895 let decrypted = priv_key
896 .decrypt(&ciphertext, 100, 200, &priv_params)
897 .unwrap();
898
899 assert_eq!(decrypted.as_ref(), plaintext);
900 }
901
902 #[test]
903 fn test_aes192_encrypt_decrypt_roundtrip() {
904 let key = vec![
906 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
907 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
908 ];
909 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, key).unwrap();
910
911 let plaintext = b"Hello, SNMPv3 AES-192 World!";
912 let engine_boots = 300u32;
913 let engine_time = 67890u32;
914
915 let (ciphertext, priv_params) = priv_key
916 .encrypt(plaintext, engine_boots, engine_time, None)
917 .expect("AES-192 encryption failed");
918
919 assert_ne!(ciphertext.as_ref(), plaintext);
921 assert_eq!(priv_params.len(), 8);
923
924 let decrypted = priv_key
926 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
927 .expect("AES-192 decryption failed");
928
929 assert_eq!(decrypted.len(), plaintext.len());
931 assert_eq!(decrypted.as_ref(), plaintext);
932 }
933
934 #[test]
935 fn test_aes256_encrypt_decrypt_roundtrip() {
936 let key = vec![
938 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
939 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
940 0x1d, 0x1e, 0x1f, 0x20,
941 ];
942 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, key).unwrap();
943
944 let plaintext = b"Hello, SNMPv3 AES-256 World!";
945 let engine_boots = 400u32;
946 let engine_time = 11111u32;
947
948 let (ciphertext, priv_params) = priv_key
949 .encrypt(plaintext, engine_boots, engine_time, None)
950 .expect("AES-256 encryption failed");
951
952 assert_ne!(ciphertext.as_ref(), plaintext);
954 assert_eq!(priv_params.len(), 8);
956
957 let decrypted = priv_key
959 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
960 .expect("AES-256 decryption failed");
961
962 assert_eq!(decrypted.len(), plaintext.len());
964 assert_eq!(decrypted.as_ref(), plaintext);
965 }
966
967 #[test]
968 fn test_aes192_from_password() {
969 let password = b"longpassword123";
971 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
972
973 let priv_key = PrivKey::from_password(
974 AuthProtocol::Sha256, PrivProtocol::Aes192,
976 password,
977 &engine_id,
978 )
979 .unwrap();
980
981 let plaintext = b"test message for AES-192";
982 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
983 let decrypted = priv_key
984 .decrypt(&ciphertext, 100, 200, &priv_params)
985 .unwrap();
986
987 assert_eq!(decrypted.as_ref(), plaintext);
988 }
989
990 #[test]
991 fn test_aes256_from_password() {
992 let password = b"anotherlongpassword456";
994 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
995
996 let priv_key = PrivKey::from_password(
997 AuthProtocol::Sha256, PrivProtocol::Aes256,
999 password,
1000 &engine_id,
1001 )
1002 .unwrap();
1003
1004 let plaintext = b"test message for AES-256";
1005 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
1006 let decrypted = priv_key
1007 .decrypt(&ciphertext, 100, 200, &priv_params)
1008 .unwrap();
1009
1010 assert_eq!(decrypted.as_ref(), plaintext);
1011 }
1012
1013 #[cfg(feature = "crypto-rustcrypto")]
1023 #[test]
1024 fn test_des_wrong_key_produces_garbage() {
1025 let correct_key = vec![
1027 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1028 0x17, 0x18,
1029 ];
1030 let wrong_key = vec![
1032 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2,
1033 0xE1, 0xE0,
1034 ];
1035
1036 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Des, correct_key).unwrap();
1037 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Des, wrong_key).unwrap();
1038
1039 let plaintext = b"Secret SNMPv3 message data!";
1040 let engine_boots = 100u32;
1041 let engine_time = 12345u32;
1042
1043 let (ciphertext, priv_params) = correct_priv_key
1045 .encrypt(plaintext, engine_boots, engine_time, None)
1046 .expect("encryption failed");
1047
1048 let wrong_decrypted = wrong_priv_key
1050 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1051 .expect("decryption should succeed cryptographically");
1052
1053 assert_ne!(
1055 &wrong_decrypted[..plaintext.len()],
1056 plaintext,
1057 "wrong key should NOT produce the original plaintext"
1058 );
1059
1060 let correct_decrypted = correct_priv_key
1062 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1063 .expect("correct key decryption failed");
1064 assert_eq!(
1065 &correct_decrypted[..plaintext.len()],
1066 plaintext,
1067 "correct key should produce the original plaintext"
1068 );
1069 }
1070
1071 #[test]
1072 fn test_aes128_wrong_key_produces_garbage() {
1073 let correct_key = vec![
1074 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1075 0x0f, 0x10,
1076 ];
1077 let wrong_key = vec![
1078 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1079 0xF1, 0xF0,
1080 ];
1081
1082 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, correct_key).unwrap();
1083 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, wrong_key).unwrap();
1084
1085 let plaintext = b"Secret AES-128 message data!";
1086 let engine_boots = 200u32;
1087 let engine_time = 54321u32;
1088
1089 let (ciphertext, priv_params) = correct_priv_key
1091 .encrypt(plaintext, engine_boots, engine_time, None)
1092 .expect("encryption failed");
1093
1094 let wrong_decrypted = wrong_priv_key
1096 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1097 .expect("decryption should succeed cryptographically");
1098
1099 assert_ne!(
1101 wrong_decrypted.as_ref(),
1102 plaintext,
1103 "wrong key should NOT produce the original plaintext"
1104 );
1105
1106 let correct_decrypted = correct_priv_key
1108 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1109 .expect("correct key decryption failed");
1110 assert_eq!(correct_decrypted.as_ref(), plaintext);
1111 }
1112
1113 #[test]
1114 fn test_aes192_wrong_key_produces_garbage() {
1115 let correct_key = vec![
1116 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1117 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1118 ];
1119 let wrong_key = vec![
1120 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1121 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8,
1122 ];
1123
1124 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, correct_key).unwrap();
1125 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, wrong_key).unwrap();
1126
1127 let plaintext = b"Secret AES-192 message data!";
1128 let engine_boots = 300u32;
1129 let engine_time = 67890u32;
1130
1131 let (ciphertext, priv_params) = correct_priv_key
1132 .encrypt(plaintext, engine_boots, engine_time, None)
1133 .expect("encryption failed");
1134
1135 let wrong_decrypted = wrong_priv_key
1136 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1137 .expect("decryption should succeed cryptographically");
1138
1139 assert_ne!(
1140 wrong_decrypted.as_ref(),
1141 plaintext,
1142 "wrong key should NOT produce the original plaintext"
1143 );
1144 }
1145
1146 #[test]
1147 fn test_aes256_wrong_key_produces_garbage() {
1148 let correct_key = vec![
1149 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1150 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1151 0x1d, 0x1e, 0x1f, 0x20,
1152 ];
1153 let wrong_key = vec![
1154 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1155 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4,
1156 0xE3, 0xE2, 0xE1, 0xE0,
1157 ];
1158
1159 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, correct_key).unwrap();
1160 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, wrong_key).unwrap();
1161
1162 let plaintext = b"Secret AES-256 message data!";
1163 let engine_boots = 400u32;
1164 let engine_time = 11111u32;
1165
1166 let (ciphertext, priv_params) = correct_priv_key
1167 .encrypt(plaintext, engine_boots, engine_time, None)
1168 .expect("encryption failed");
1169
1170 let wrong_decrypted = wrong_priv_key
1171 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1172 .expect("decryption should succeed cryptographically");
1173
1174 assert_ne!(
1175 wrong_decrypted.as_ref(),
1176 plaintext,
1177 "wrong key should NOT produce the original plaintext"
1178 );
1179 }
1180
1181 #[cfg(feature = "crypto-rustcrypto")]
1182 #[test]
1183 fn test_des_wrong_priv_params_produces_garbage() {
1184 let key = vec![
1187 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1188 0x17, 0x18,
1189 ];
1190
1191 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1192
1193 let plaintext = b"DES test message";
1194 let engine_boots = 100u32;
1195 let engine_time = 12345u32;
1196
1197 let (ciphertext, correct_priv_params) = priv_key
1198 .encrypt(plaintext, engine_boots, engine_time, None)
1199 .expect("encryption failed");
1200
1201 let wrong_priv_params = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
1203
1204 let wrong_decrypted = priv_key
1205 .decrypt(&ciphertext, engine_boots, engine_time, &wrong_priv_params)
1206 .expect("decryption should succeed cryptographically");
1207
1208 assert_ne!(
1210 &wrong_decrypted[..plaintext.len()],
1211 plaintext,
1212 "wrong priv_params should NOT produce the original plaintext"
1213 );
1214
1215 let correct_decrypted = priv_key
1217 .decrypt(&ciphertext, engine_boots, engine_time, &correct_priv_params)
1218 .expect("correct decryption failed");
1219 assert_eq!(&correct_decrypted[..plaintext.len()], plaintext);
1220 }
1221
1222 #[cfg(feature = "crypto-rustcrypto")]
1229 #[test]
1230 fn test_des_salt_and_iv_composition() {
1231 let key = vec![
1233 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, ];
1236 let pre_iv = [0xAAu8, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22];
1237 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1238
1239 let salt_counter = SaltCounter::from_value(0x1000);
1242 let expected_salt_value: u32 = 0x1001;
1243
1244 let engine_boots: u32 = 0x1234_5678;
1245 let engine_time: u32 = 999;
1246 let plaintext = b"RFC 3414 8.1.1.1 salt/IV composition test";
1247
1248 let (ciphertext, priv_params) = priv_key
1249 .encrypt(plaintext, engine_boots, engine_time, Some(&salt_counter))
1250 .expect("encryption failed");
1251
1252 assert_eq!(priv_params.len(), 8);
1254 assert_eq!(&priv_params[..4], &engine_boots.to_be_bytes());
1255 assert_eq!(&priv_params[4..8], &expected_salt_value.to_be_bytes());
1256
1257 let mut expected_iv = [0u8; 8];
1260 for i in 0..8 {
1261 expected_iv[i] = pre_iv[i] ^ priv_params[i];
1262 }
1263 assert_ne!(
1264 expected_iv, pre_iv,
1265 "salt XOR must change the IV relative to the raw pre-IV"
1266 );
1267
1268 let decrypted = priv_key
1271 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1272 .expect("decryption failed");
1273 assert_eq!(&decrypted[..plaintext.len()], plaintext);
1274 }
1275
1276 #[test]
1281 fn test_salt_counter_no_duplicates_concurrent() {
1282 use std::collections::HashSet;
1283 use std::sync::{Arc, Mutex};
1284 use std::thread;
1285
1286 let counter = Arc::new(SaltCounter::new());
1287 let results = Arc::new(Mutex::new(HashSet::new()));
1288 let iterations = 10_000usize;
1289 let threads = 8usize;
1290
1291 let handles: Vec<_> = (0..threads)
1292 .map(|_| {
1293 let counter = Arc::clone(&counter);
1294 let results = Arc::clone(&results);
1295 thread::spawn(move || {
1296 for _ in 0..iterations {
1297 let salt = counter.next();
1298 assert_ne!(salt, 0, "SaltCounter must never return zero");
1299 let mut set = results.lock().unwrap();
1300 assert!(set.insert(salt), "SaltCounter emitted duplicate: {salt}");
1301 }
1302 })
1303 })
1304 .collect();
1305
1306 for h in handles {
1307 h.join().expect("thread panicked");
1308 }
1309 }
1310
1311 #[test]
1316 fn test_priv_key_clone_independent_salts() {
1317 let key = vec![0u8; 16];
1318 let original = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1319 let cloned = original.clone();
1320
1321 let plaintext = b"test";
1322
1323 let (_, salt_orig) = original.encrypt(plaintext, 0, 0, None).unwrap();
1325 let (_, salt_clone) = cloned.encrypt(plaintext, 0, 0, None).unwrap();
1326
1327 assert_ne!(
1328 salt_orig, salt_clone,
1329 "cloned PrivKey must start with an independent salt counter"
1330 );
1331 }
1332
1333 #[test]
1334 fn test_aes_wrong_engine_time_produces_garbage() {
1335 let key = vec![
1338 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1339 0x0f, 0x10,
1340 ];
1341
1342 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1343
1344 let plaintext = b"AES test message";
1345 let engine_boots = 200u32;
1346 let engine_time = 54321u32;
1347
1348 let (ciphertext, priv_params) = priv_key
1349 .encrypt(plaintext, engine_boots, engine_time, None)
1350 .expect("encryption failed");
1351
1352 let wrong_decrypted = priv_key
1354 .decrypt(&ciphertext, engine_boots, engine_time + 1, &priv_params)
1355 .expect("decryption should succeed cryptographically");
1356
1357 assert_ne!(
1358 wrong_decrypted.as_ref(),
1359 plaintext,
1360 "wrong engine_time should NOT produce the original plaintext"
1361 );
1362
1363 let wrong_decrypted2 = priv_key
1365 .decrypt(&ciphertext, engine_boots + 1, engine_time, &priv_params)
1366 .expect("decryption should succeed cryptographically");
1367
1368 assert_ne!(
1369 wrong_decrypted2.as_ref(),
1370 plaintext,
1371 "wrong engine_boots should NOT produce the original plaintext"
1372 );
1373 }
1374}