1use std::sync::atomic::{AtomicU64, Ordering};
21
22use bytes::Bytes;
23use zeroize::{Zeroize, ZeroizeOnDrop};
24
25use super::{AuthProtocol, PrivProtocol};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum PrivacyError {
33 InvalidPrivParamsLength { expected: usize, actual: usize },
35 InvalidCiphertextLength { length: usize, block_size: usize },
37 InvalidKeyLength,
39 CipherError,
41 UnsupportedProtocol,
43}
44
45impl std::fmt::Display for PrivacyError {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::InvalidPrivParamsLength { expected, actual } => {
49 write!(
50 f,
51 "invalid privParameters length: expected {}, got {}",
52 expected, actual
53 )
54 }
55 Self::InvalidCiphertextLength { length, block_size } => {
56 write!(
57 f,
58 "ciphertext length {} not multiple of block size {}",
59 length, block_size
60 )
61 }
62 Self::InvalidKeyLength => write!(f, "invalid key length"),
63 Self::CipherError => write!(f, "cipher operation failed"),
64 Self::UnsupportedProtocol => write!(f, "unsupported privacy protocol"),
65 }
66 }
67}
68
69impl std::error::Error for PrivacyError {}
70
71pub type PrivacyResult<T> = std::result::Result<T, PrivacyError>;
73
74fn random_nonzero_u64() -> u64 {
78 let mut buf = [0u8; 8];
79 loop {
80 getrandom::fill(&mut buf).expect("getrandom failed");
81 let val = u64::from_ne_bytes(buf);
82 if val != 0 {
83 return val;
84 }
85 }
87}
88
89#[derive(Clone, Zeroize, ZeroizeOnDrop)]
101pub struct PrivKey {
102 key: Vec<u8>,
104 #[zeroize(skip)]
106 protocol: PrivProtocol,
107 #[zeroize(skip)]
110 salt_counter: u64,
111}
112
113pub struct SaltCounter(AtomicU64);
115
116impl SaltCounter {
117 pub fn new() -> Self {
119 Self(AtomicU64::new(random_nonzero_u64()))
120 }
121
122 pub fn from_value(value: u64) -> Self {
126 Self(AtomicU64::new(value))
127 }
128
129 pub fn next(&self) -> u64 {
134 let val = self.0.fetch_add(1, Ordering::SeqCst);
135 if val == 0 {
137 self.0.fetch_add(1, Ordering::SeqCst)
138 } else {
139 val
140 }
141 }
142}
143
144impl Default for SaltCounter {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150impl PrivKey {
151 pub fn from_password(
188 auth_protocol: AuthProtocol,
189 priv_protocol: PrivProtocol,
190 password: &[u8],
191 engine_id: &[u8],
192 ) -> Self {
193 use super::MasterKey;
194
195 let master = MasterKey::from_password(auth_protocol, password);
196 Self::from_master_key(&master, priv_protocol, engine_id)
197 }
198
199 pub fn from_master_key(
221 master: &super::MasterKey,
222 priv_protocol: PrivProtocol,
223 engine_id: &[u8],
224 ) -> Self {
225 use super::{
226 KeyExtension,
227 auth::{extend_key, extend_key_reeder},
228 };
229
230 let auth_protocol = master.protocol();
231 let key_extension = priv_protocol.key_extension_for(auth_protocol);
232
233 let localized = master.localize(engine_id);
235 let key_bytes = localized.as_bytes();
236
237 let key = match key_extension {
238 KeyExtension::None => key_bytes.to_vec(),
239 KeyExtension::Blumenthal => {
240 extend_key(auth_protocol, key_bytes, priv_protocol.key_len())
241 }
242 KeyExtension::Reeder => {
243 extend_key_reeder(auth_protocol, key_bytes, engine_id, priv_protocol.key_len())
244 }
245 };
246
247 Self {
248 key,
249 protocol: priv_protocol,
250 salt_counter: Self::init_salt(),
251 }
252 }
253
254 pub fn from_bytes(protocol: PrivProtocol, key: impl Into<Vec<u8>>) -> Self {
256 Self {
257 key: key.into(),
258 protocol,
259 salt_counter: Self::init_salt(),
260 }
261 }
262
263 fn init_salt() -> u64 {
267 random_nonzero_u64()
268 }
269
270 pub fn protocol(&self) -> PrivProtocol {
272 self.protocol
273 }
274
275 pub fn encryption_key(&self) -> &[u8] {
277 match self.protocol {
278 PrivProtocol::Des => &self.key[..8],
279 PrivProtocol::Des3 => &self.key[..24],
280 PrivProtocol::Aes128 => &self.key[..16],
281 PrivProtocol::Aes192 => &self.key[..24],
282 PrivProtocol::Aes256 => &self.key[..32],
283 }
284 }
285
286 pub fn encrypt(
298 &mut self,
299 plaintext: &[u8],
300 engine_boots: u32,
301 engine_time: u32,
302 salt_counter: Option<&SaltCounter>,
303 ) -> PrivacyResult<(Bytes, Bytes)> {
304 let salt = salt_counter.map(|c| c.next()).unwrap_or_else(|| {
305 let mut s = self.salt_counter;
306 self.salt_counter = self.salt_counter.wrapping_add(1);
307 if s == 0 {
309 s = self.salt_counter;
310 self.salt_counter = self.salt_counter.wrapping_add(1);
311 }
312 s
313 });
314
315 match self.protocol {
316 PrivProtocol::Des => self.encrypt_des(plaintext, engine_boots, salt),
317 PrivProtocol::Des3 => self.encrypt_des3(plaintext, engine_boots, salt),
318 PrivProtocol::Aes128 => {
319 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 16)
320 }
321 PrivProtocol::Aes192 => {
322 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 24)
323 }
324 PrivProtocol::Aes256 => {
325 self.encrypt_aes(plaintext, engine_boots, engine_time, salt, 32)
326 }
327 }
328 }
329
330 pub fn decrypt(
342 &self,
343 ciphertext: &[u8],
344 engine_boots: u32,
345 engine_time: u32,
346 priv_params: &[u8],
347 ) -> PrivacyResult<Bytes> {
348 if priv_params.len() != 8 {
349 tracing::debug!(target: "async_snmp::crypto", { expected = 8, actual = priv_params.len() }, "invalid privParameters length");
350 return Err(PrivacyError::InvalidPrivParamsLength {
351 expected: 8,
352 actual: priv_params.len(),
353 });
354 }
355
356 match self.protocol {
357 PrivProtocol::Des => self.decrypt_des(ciphertext, priv_params),
358 PrivProtocol::Des3 => self.decrypt_des3(ciphertext, priv_params),
359 PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
360 self.decrypt_aes(ciphertext, engine_boots, engine_time, priv_params)
361 }
362 }
363 }
364
365 fn encrypt_des(
367 &self,
368 plaintext: &[u8],
369 engine_boots: u32,
370 salt_int: u64,
371 ) -> PrivacyResult<(Bytes, Bytes)> {
372 use cbc::cipher::{BlockEncryptMut, KeyIvInit};
373 type DesCbc = cbc::Encryptor<des::Des>;
374
375 let key = &self.key[..8];
377 let pre_iv = &self.key[8..16];
379
380 let mut salt = [0u8; 8];
383 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
384 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
385
386 let mut iv = [0u8; 8];
388 for i in 0..8 {
389 iv[i] = pre_iv[i] ^ salt[i];
390 }
391
392 let pad_len = (8 - (plaintext.len() % 8)) % 8;
394 let padded_len = plaintext.len() + if pad_len == 0 { 0 } else { pad_len };
395 let padded_len = if padded_len == plaintext.len() && !plaintext.len().is_multiple_of(8) {
397 plaintext.len() + (8 - plaintext.len() % 8)
398 } else {
399 padded_len
400 };
401
402 let mut buffer = vec![
403 0u8;
404 if padded_len > plaintext.len() {
405 padded_len
406 } else {
407 plaintext.len() + 8 - (plaintext.len() % 8)
408 }
409 ];
410 let padded_len = buffer.len();
411 buffer[..plaintext.len()].copy_from_slice(plaintext);
412
413 let cipher = DesCbc::new_from_slices(key, &iv).map_err(|_| {
415 tracing::debug!(target: "async_snmp::crypto", "DES encryption failed: invalid key length");
416 PrivacyError::InvalidKeyLength
417 })?;
418
419 let ciphertext = cipher
420 .encrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer, padded_len)
421 .map_err(|_| {
422 tracing::debug!(target: "async_snmp::crypto", "DES encryption failed: cipher error");
423 PrivacyError::CipherError
424 })?;
425
426 Ok((
427 Bytes::copy_from_slice(ciphertext),
428 Bytes::copy_from_slice(&salt),
429 ))
430 }
431
432 fn decrypt_des(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
434 use cbc::cipher::{BlockDecryptMut, KeyIvInit};
435 type DesCbc = cbc::Decryptor<des::Des>;
436
437 if !ciphertext.len().is_multiple_of(8) {
438 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "DES decryption failed: invalid ciphertext length");
439 return Err(PrivacyError::InvalidCiphertextLength {
440 length: ciphertext.len(),
441 block_size: 8,
442 });
443 }
444
445 let key = &self.key[..8];
447 let pre_iv = &self.key[8..16];
449
450 let salt = priv_params;
452
453 let mut iv = [0u8; 8];
455 for i in 0..8 {
456 iv[i] = pre_iv[i] ^ salt[i];
457 }
458
459 let cipher = DesCbc::new_from_slices(key, &iv).map_err(|_| {
461 tracing::debug!(target: "async_snmp::crypto", "DES decryption failed: invalid key length");
462 PrivacyError::InvalidKeyLength
463 })?;
464
465 let mut buffer = ciphertext.to_vec();
466 let plaintext = cipher
467 .decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer)
468 .map_err(|_| {
469 tracing::debug!(target: "async_snmp::crypto", "DES decryption failed: cipher error");
470 PrivacyError::CipherError
471 })?;
472
473 Ok(Bytes::copy_from_slice(plaintext))
474 }
475
476 fn encrypt_des3(
478 &self,
479 plaintext: &[u8],
480 engine_boots: u32,
481 salt_int: u64,
482 ) -> PrivacyResult<(Bytes, Bytes)> {
483 use cbc::cipher::{BlockEncryptMut, KeyIvInit};
484 type Des3Cbc = cbc::Encryptor<des::TdesEde3>;
485
486 let key = &self.key[..24];
488 let pre_iv = &self.key[24..32];
490
491 let mut salt = [0u8; 8];
493 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
494 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
495
496 let mut iv = [0u8; 8];
498 for i in 0..8 {
499 iv[i] = pre_iv[i] ^ salt[i];
500 }
501
502 let padded_len = plaintext.len().next_multiple_of(8);
504 let mut buffer = vec![0u8; padded_len];
505 buffer[..plaintext.len()].copy_from_slice(plaintext);
506
507 let cipher = Des3Cbc::new_from_slices(key, &iv).map_err(|_| {
509 tracing::debug!(target: "async_snmp::crypto", "3DES encryption failed: invalid key length");
510 PrivacyError::InvalidKeyLength
511 })?;
512
513 let ciphertext = cipher
514 .encrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer, padded_len)
515 .map_err(|_| {
516 tracing::debug!(target: "async_snmp::crypto", "3DES encryption failed: cipher error");
517 PrivacyError::CipherError
518 })?;
519
520 Ok((
521 Bytes::copy_from_slice(ciphertext),
522 Bytes::copy_from_slice(&salt),
523 ))
524 }
525
526 fn decrypt_des3(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
528 use cbc::cipher::{BlockDecryptMut, KeyIvInit};
529 type Des3Cbc = cbc::Decryptor<des::TdesEde3>;
530
531 if !ciphertext.len().is_multiple_of(8) {
532 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "3DES decryption failed: invalid ciphertext length");
533 return Err(PrivacyError::InvalidCiphertextLength {
534 length: ciphertext.len(),
535 block_size: 8,
536 });
537 }
538
539 let key = &self.key[..24];
541 let pre_iv = &self.key[24..32];
543
544 let salt = priv_params;
546
547 let mut iv = [0u8; 8];
549 for i in 0..8 {
550 iv[i] = pre_iv[i] ^ salt[i];
551 }
552
553 let cipher = Des3Cbc::new_from_slices(key, &iv).map_err(|_| {
555 tracing::debug!(target: "async_snmp::crypto", "3DES decryption failed: invalid key length");
556 PrivacyError::InvalidKeyLength
557 })?;
558
559 let mut buffer = ciphertext.to_vec();
560 let plaintext = cipher
561 .decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer)
562 .map_err(|_| {
563 tracing::debug!(target: "async_snmp::crypto", "3DES decryption failed: cipher error");
564 PrivacyError::CipherError
565 })?;
566
567 Ok(Bytes::copy_from_slice(plaintext))
568 }
569
570 fn encrypt_aes(
572 &self,
573 plaintext: &[u8],
574 engine_boots: u32,
575 engine_time: u32,
576 salt: u64,
577 key_len: usize,
578 ) -> PrivacyResult<(Bytes, Bytes)> {
579 use aes::{Aes128, Aes192, Aes256};
580 use cfb_mode::cipher::{AsyncStreamCipher, KeyIvInit};
581
582 let key = &self.key[..key_len];
584
585 let salt_bytes = salt.to_be_bytes();
587
588 let mut iv = [0u8; 16];
591 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
592 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
593 iv[8..].copy_from_slice(&salt_bytes);
594
595 let mut buffer = plaintext.to_vec();
596
597 match key_len {
598 16 => {
599 type Aes128Cfb = cfb_mode::Encryptor<Aes128>;
600 let cipher = Aes128Cfb::new_from_slices(key, &iv).map_err(|_| {
601 tracing::debug!(target: "async_snmp::crypto", "AES-128 encryption failed: invalid key length");
602 PrivacyError::InvalidKeyLength
603 })?;
604 cipher.encrypt(&mut buffer);
605 }
606 24 => {
607 type Aes192Cfb = cfb_mode::Encryptor<Aes192>;
608 let cipher = Aes192Cfb::new_from_slices(key, &iv).map_err(|_| {
609 tracing::debug!(target: "async_snmp::crypto", "AES-192 encryption failed: invalid key length");
610 PrivacyError::InvalidKeyLength
611 })?;
612 cipher.encrypt(&mut buffer);
613 }
614 32 => {
615 type Aes256Cfb = cfb_mode::Encryptor<Aes256>;
616 let cipher = Aes256Cfb::new_from_slices(key, &iv).map_err(|_| {
617 tracing::debug!(target: "async_snmp::crypto", "AES-256 encryption failed: invalid key length");
618 PrivacyError::InvalidKeyLength
619 })?;
620 cipher.encrypt(&mut buffer);
621 }
622 _ => {
623 tracing::debug!(target: "async_snmp::crypto", { key_len }, "AES encryption failed: unsupported key length");
624 return Err(PrivacyError::UnsupportedProtocol);
625 }
626 }
627
628 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt_bytes)))
629 }
630
631 fn decrypt_aes(
633 &self,
634 ciphertext: &[u8],
635 engine_boots: u32,
636 engine_time: u32,
637 priv_params: &[u8],
638 ) -> PrivacyResult<Bytes> {
639 use aes::{Aes128, Aes192, Aes256};
640 use cfb_mode::cipher::{AsyncStreamCipher, KeyIvInit};
641
642 let key_len = match self.protocol {
643 PrivProtocol::Aes128 => 16,
644 PrivProtocol::Aes192 => 24,
645 PrivProtocol::Aes256 => 32,
646 _ => unreachable!(),
647 };
648
649 let key = &self.key[..key_len];
651
652 let mut iv = [0u8; 16];
654 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
655 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
656 iv[8..].copy_from_slice(priv_params);
657
658 let mut buffer = ciphertext.to_vec();
659
660 match key_len {
661 16 => {
662 type Aes128Cfb = cfb_mode::Decryptor<Aes128>;
663 let cipher = Aes128Cfb::new_from_slices(key, &iv).map_err(|_| {
664 tracing::debug!(target: "async_snmp::crypto", "AES-128 decryption failed: invalid key length");
665 PrivacyError::InvalidKeyLength
666 })?;
667 cipher.decrypt(&mut buffer);
668 }
669 24 => {
670 type Aes192Cfb = cfb_mode::Decryptor<Aes192>;
671 let cipher = Aes192Cfb::new_from_slices(key, &iv).map_err(|_| {
672 tracing::debug!(target: "async_snmp::crypto", "AES-192 decryption failed: invalid key length");
673 PrivacyError::InvalidKeyLength
674 })?;
675 cipher.decrypt(&mut buffer);
676 }
677 32 => {
678 type Aes256Cfb = cfb_mode::Decryptor<Aes256>;
679 let cipher = Aes256Cfb::new_from_slices(key, &iv).map_err(|_| {
680 tracing::debug!(target: "async_snmp::crypto", "AES-256 decryption failed: invalid key length");
681 PrivacyError::InvalidKeyLength
682 })?;
683 cipher.decrypt(&mut buffer);
684 }
685 _ => {
686 tracing::debug!(target: "async_snmp::crypto", { key_len }, "AES decryption failed: unsupported key length");
687 return Err(PrivacyError::UnsupportedProtocol);
688 }
689 }
690
691 Ok(Bytes::from(buffer))
692 }
693}
694
695impl std::fmt::Debug for PrivKey {
696 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
697 f.debug_struct("PrivKey")
698 .field("protocol", &self.protocol)
699 .field("key", &"[REDACTED]")
700 .finish()
701 }
702}
703
704#[cfg(test)]
705mod tests {
706 use super::*;
707 use crate::format::hex::decode as decode_hex;
708
709 #[test]
710 fn test_des_encrypt_decrypt_roundtrip() {
711 let key = vec![
713 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, ];
716 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Des, key);
717
718 let plaintext = b"Hello, SNMPv3 World!";
719 let engine_boots = 100u32;
720 let engine_time = 12345u32;
721
722 let (ciphertext, priv_params) = priv_key
723 .encrypt(plaintext, engine_boots, engine_time, None)
724 .expect("encryption failed");
725
726 assert_ne!(ciphertext.as_ref(), plaintext);
728 assert_eq!(priv_params.len(), 8);
730
731 let decrypted = priv_key
733 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
734 .expect("decryption failed");
735
736 assert!(decrypted.len() >= plaintext.len());
738 assert_eq!(&decrypted[..plaintext.len()], plaintext);
739 }
740
741 #[test]
742 fn test_des3_encrypt_decrypt_roundtrip() {
743 let key = vec![
745 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, ];
750 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Des3, key);
751
752 let plaintext = b"Hello, SNMPv3 World with 3DES!";
753 let engine_boots = 100u32;
754 let engine_time = 12345u32;
755
756 let (ciphertext, priv_params) = priv_key
757 .encrypt(plaintext, engine_boots, engine_time, None)
758 .expect("encryption failed");
759
760 assert_ne!(ciphertext.as_ref(), plaintext);
762 assert_eq!(priv_params.len(), 8);
764
765 let decrypted = priv_key
767 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
768 .expect("decryption failed");
769
770 assert!(decrypted.len() >= plaintext.len());
772 assert_eq!(&decrypted[..plaintext.len()], plaintext);
773 }
774
775 #[test]
776 fn test_aes128_encrypt_decrypt_roundtrip() {
777 let key = vec![
779 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
780 0x0f, 0x10,
781 ];
782 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key);
783
784 let plaintext = b"Hello, SNMPv3 AES World!";
785 let engine_boots = 200u32;
786 let engine_time = 54321u32;
787
788 let (ciphertext, priv_params) = priv_key
789 .encrypt(plaintext, engine_boots, engine_time, None)
790 .expect("encryption failed");
791
792 assert_ne!(ciphertext.as_ref(), plaintext);
794 assert_eq!(priv_params.len(), 8);
796
797 let decrypted = priv_key
799 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
800 .expect("decryption failed");
801
802 assert_eq!(decrypted.len(), plaintext.len());
804 assert_eq!(decrypted.as_ref(), plaintext);
805 }
806
807 #[test]
808 fn test_des_invalid_ciphertext_length() {
809 let key = vec![0u8; 16];
810 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key);
811
812 let ciphertext = [0u8; 13];
814 let priv_params = [0u8; 8];
815
816 let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
817 assert!(result.is_err());
818 }
819
820 #[test]
821 fn test_invalid_priv_params_length() {
822 let key = vec![0u8; 16];
823 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key);
824
825 let ciphertext = [0u8; 16];
827 let priv_params = [0u8; 4]; let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
830 assert!(result.is_err());
831 }
832
833 #[test]
834 fn test_salt_counter() {
835 let counter = SaltCounter::new();
836 let s1 = counter.next();
837 let s2 = counter.next();
838 let s3 = counter.next();
839
840 assert_eq!(s2, s1.wrapping_add(1));
842 assert_eq!(s3, s2.wrapping_add(1));
843 }
844
845 #[test]
850 fn test_salt_counter_skips_zero() {
851 let counter = SaltCounter::from_value(u64::MAX);
853
854 let s1 = counter.next();
856 assert_eq!(s1, u64::MAX);
857
858 let s2 = counter.next();
860 assert_ne!(s2, 0, "SaltCounter should never return zero");
861 assert_eq!(s2, 1, "SaltCounter should skip 0 and return 1");
862
863 let s3 = counter.next();
865 assert_eq!(s3, 2);
866 }
867
868 #[test]
873 fn test_priv_key_internal_salt_skips_zero() {
874 let key = vec![0u8; 16];
875 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key);
876
877 priv_key.salt_counter = u64::MAX;
879
880 let plaintext = b"test";
881
882 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
884 assert_eq!(
885 u64::from_be_bytes(salt1.as_ref().try_into().unwrap()),
886 u64::MAX
887 );
888
889 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
891 let salt2_value = u64::from_be_bytes(salt2.as_ref().try_into().unwrap());
892 assert_ne!(salt2_value, 0, "Salt should never be zero");
893 assert_eq!(salt2_value, 1, "Salt should skip 0 and be 1");
894
895 let (_, salt3) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
897 let salt3_value = u64::from_be_bytes(salt3.as_ref().try_into().unwrap());
898 assert_eq!(salt3_value, 2);
899 }
900
901 #[test]
902 fn test_multiple_encryptions_different_salt() {
903 let key = vec![0u8; 16];
904 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key);
905
906 let plaintext = b"test data";
907
908 let (_, salt1) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
909 let (_, salt2) = priv_key.encrypt(plaintext, 0, 0, None).unwrap();
910
911 assert_ne!(salt1, salt2);
913 }
914
915 #[test]
916 fn test_from_password() {
917 let password = b"maplesyrup";
919 let engine_id = decode_hex("000000000000000000000002").unwrap();
920
921 let mut priv_key = PrivKey::from_password(
922 AuthProtocol::Sha1,
923 PrivProtocol::Aes128,
924 password,
925 &engine_id,
926 );
927
928 let plaintext = b"test message";
930 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
931 let decrypted = priv_key
932 .decrypt(&ciphertext, 100, 200, &priv_params)
933 .unwrap();
934
935 assert_eq!(decrypted.as_ref(), plaintext);
936 }
937
938 #[test]
939 fn test_aes192_encrypt_decrypt_roundtrip() {
940 let key = vec![
942 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
943 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
944 ];
945 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, key);
946
947 let plaintext = b"Hello, SNMPv3 AES-192 World!";
948 let engine_boots = 300u32;
949 let engine_time = 67890u32;
950
951 let (ciphertext, priv_params) = priv_key
952 .encrypt(plaintext, engine_boots, engine_time, None)
953 .expect("AES-192 encryption failed");
954
955 assert_ne!(ciphertext.as_ref(), plaintext);
957 assert_eq!(priv_params.len(), 8);
959
960 let decrypted = priv_key
962 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
963 .expect("AES-192 decryption failed");
964
965 assert_eq!(decrypted.len(), plaintext.len());
967 assert_eq!(decrypted.as_ref(), plaintext);
968 }
969
970 #[test]
971 fn test_aes256_encrypt_decrypt_roundtrip() {
972 let key = vec![
974 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
975 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
976 0x1d, 0x1e, 0x1f, 0x20,
977 ];
978 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, key);
979
980 let plaintext = b"Hello, SNMPv3 AES-256 World!";
981 let engine_boots = 400u32;
982 let engine_time = 11111u32;
983
984 let (ciphertext, priv_params) = priv_key
985 .encrypt(plaintext, engine_boots, engine_time, None)
986 .expect("AES-256 encryption failed");
987
988 assert_ne!(ciphertext.as_ref(), plaintext);
990 assert_eq!(priv_params.len(), 8);
992
993 let decrypted = priv_key
995 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
996 .expect("AES-256 decryption failed");
997
998 assert_eq!(decrypted.len(), plaintext.len());
1000 assert_eq!(decrypted.as_ref(), plaintext);
1001 }
1002
1003 #[test]
1004 fn test_aes192_from_password() {
1005 let password = b"longpassword123";
1007 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
1008
1009 let mut priv_key = PrivKey::from_password(
1010 AuthProtocol::Sha256, PrivProtocol::Aes192,
1012 password,
1013 &engine_id,
1014 );
1015
1016 let plaintext = b"test message for AES-192";
1017 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
1018 let decrypted = priv_key
1019 .decrypt(&ciphertext, 100, 200, &priv_params)
1020 .unwrap();
1021
1022 assert_eq!(decrypted.as_ref(), plaintext);
1023 }
1024
1025 #[test]
1026 fn test_aes256_from_password() {
1027 let password = b"anotherlongpassword456";
1029 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
1030
1031 let mut priv_key = PrivKey::from_password(
1032 AuthProtocol::Sha256, PrivProtocol::Aes256,
1034 password,
1035 &engine_id,
1036 );
1037
1038 let plaintext = b"test message for AES-256";
1039 let (ciphertext, priv_params) = priv_key.encrypt(plaintext, 100, 200, None).unwrap();
1040 let decrypted = priv_key
1041 .decrypt(&ciphertext, 100, 200, &priv_params)
1042 .unwrap();
1043
1044 assert_eq!(decrypted.as_ref(), plaintext);
1045 }
1046
1047 #[test]
1057 fn test_des_wrong_key_produces_garbage() {
1058 let correct_key = vec![
1060 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1061 0x17, 0x18,
1062 ];
1063 let wrong_key = vec![
1065 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2,
1066 0xE1, 0xE0,
1067 ];
1068
1069 let mut correct_priv_key = PrivKey::from_bytes(PrivProtocol::Des, correct_key);
1070 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Des, wrong_key);
1071
1072 let plaintext = b"Secret SNMPv3 message data!";
1073 let engine_boots = 100u32;
1074 let engine_time = 12345u32;
1075
1076 let (ciphertext, priv_params) = correct_priv_key
1078 .encrypt(plaintext, engine_boots, engine_time, None)
1079 .expect("encryption failed");
1080
1081 let wrong_decrypted = wrong_priv_key
1083 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1084 .expect("decryption should succeed cryptographically");
1085
1086 assert_ne!(
1088 &wrong_decrypted[..plaintext.len()],
1089 plaintext,
1090 "wrong key should NOT produce the original plaintext"
1091 );
1092
1093 let correct_decrypted = correct_priv_key
1095 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1096 .expect("correct key decryption failed");
1097 assert_eq!(
1098 &correct_decrypted[..plaintext.len()],
1099 plaintext,
1100 "correct key should produce the original plaintext"
1101 );
1102 }
1103
1104 #[test]
1105 fn test_aes128_wrong_key_produces_garbage() {
1106 let correct_key = vec![
1107 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1108 0x0f, 0x10,
1109 ];
1110 let wrong_key = vec![
1111 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1112 0xF1, 0xF0,
1113 ];
1114
1115 let mut correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, correct_key);
1116 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, wrong_key);
1117
1118 let plaintext = b"Secret AES-128 message data!";
1119 let engine_boots = 200u32;
1120 let engine_time = 54321u32;
1121
1122 let (ciphertext, priv_params) = correct_priv_key
1124 .encrypt(plaintext, engine_boots, engine_time, None)
1125 .expect("encryption failed");
1126
1127 let wrong_decrypted = wrong_priv_key
1129 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1130 .expect("decryption should succeed cryptographically");
1131
1132 assert_ne!(
1134 wrong_decrypted.as_ref(),
1135 plaintext,
1136 "wrong key should NOT produce the original plaintext"
1137 );
1138
1139 let correct_decrypted = correct_priv_key
1141 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1142 .expect("correct key decryption failed");
1143 assert_eq!(correct_decrypted.as_ref(), plaintext);
1144 }
1145
1146 #[test]
1147 fn test_aes192_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,
1151 ];
1152 let wrong_key = vec![
1153 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1154 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8,
1155 ];
1156
1157 let mut correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, correct_key);
1158 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes192, wrong_key);
1159
1160 let plaintext = b"Secret AES-192 message data!";
1161 let engine_boots = 300u32;
1162 let engine_time = 67890u32;
1163
1164 let (ciphertext, priv_params) = correct_priv_key
1165 .encrypt(plaintext, engine_boots, engine_time, None)
1166 .expect("encryption failed");
1167
1168 let wrong_decrypted = wrong_priv_key
1169 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1170 .expect("decryption should succeed cryptographically");
1171
1172 assert_ne!(
1173 wrong_decrypted.as_ref(),
1174 plaintext,
1175 "wrong key should NOT produce the original plaintext"
1176 );
1177 }
1178
1179 #[test]
1180 fn test_aes256_wrong_key_produces_garbage() {
1181 let correct_key = vec![
1182 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1183 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1184 0x1d, 0x1e, 0x1f, 0x20,
1185 ];
1186 let wrong_key = vec![
1187 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1188 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4,
1189 0xE3, 0xE2, 0xE1, 0xE0,
1190 ];
1191
1192 let mut correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, correct_key);
1193 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes256, wrong_key);
1194
1195 let plaintext = b"Secret AES-256 message data!";
1196 let engine_boots = 400u32;
1197 let engine_time = 11111u32;
1198
1199 let (ciphertext, priv_params) = correct_priv_key
1200 .encrypt(plaintext, engine_boots, engine_time, None)
1201 .expect("encryption failed");
1202
1203 let wrong_decrypted = wrong_priv_key
1204 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1205 .expect("decryption should succeed cryptographically");
1206
1207 assert_ne!(
1208 wrong_decrypted.as_ref(),
1209 plaintext,
1210 "wrong key should NOT produce the original plaintext"
1211 );
1212 }
1213
1214 #[test]
1215 fn test_des_wrong_priv_params_produces_garbage() {
1216 let key = vec![
1219 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1220 0x17, 0x18,
1221 ];
1222
1223 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Des, key);
1224
1225 let plaintext = b"DES test message";
1226 let engine_boots = 100u32;
1227 let engine_time = 12345u32;
1228
1229 let (ciphertext, correct_priv_params) = priv_key
1230 .encrypt(plaintext, engine_boots, engine_time, None)
1231 .expect("encryption failed");
1232
1233 let wrong_priv_params = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
1235
1236 let wrong_decrypted = priv_key
1237 .decrypt(&ciphertext, engine_boots, engine_time, &wrong_priv_params)
1238 .expect("decryption should succeed cryptographically");
1239
1240 assert_ne!(
1242 &wrong_decrypted[..plaintext.len()],
1243 plaintext,
1244 "wrong priv_params should NOT produce the original plaintext"
1245 );
1246
1247 let correct_decrypted = priv_key
1249 .decrypt(&ciphertext, engine_boots, engine_time, &correct_priv_params)
1250 .expect("correct decryption failed");
1251 assert_eq!(&correct_decrypted[..plaintext.len()], plaintext);
1252 }
1253
1254 #[test]
1255 fn test_aes_wrong_engine_time_produces_garbage() {
1256 let key = vec![
1259 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1260 0x0f, 0x10,
1261 ];
1262
1263 let mut priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key);
1264
1265 let plaintext = b"AES test message";
1266 let engine_boots = 200u32;
1267 let engine_time = 54321u32;
1268
1269 let (ciphertext, priv_params) = priv_key
1270 .encrypt(plaintext, engine_boots, engine_time, None)
1271 .expect("encryption failed");
1272
1273 let wrong_decrypted = priv_key
1275 .decrypt(&ciphertext, engine_boots, engine_time + 1, &priv_params)
1276 .expect("decryption should succeed cryptographically");
1277
1278 assert_ne!(
1279 wrong_decrypted.as_ref(),
1280 plaintext,
1281 "wrong engine_time should NOT produce the original plaintext"
1282 );
1283
1284 let wrong_decrypted2 = priv_key
1286 .decrypt(&ciphertext, engine_boots + 1, engine_time, &priv_params)
1287 .expect("decryption should succeed cryptographically");
1288
1289 assert_ne!(
1290 wrong_decrypted2.as_ref(),
1291 plaintext,
1292 "wrong engine_boots should NOT produce the original plaintext"
1293 );
1294 }
1295}