1use std::fmt::{Debug, Formatter};
23use std::sync::Arc;
24use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
25
26use bytes::Bytes;
27use zeroize::{Zeroize, ZeroizeOnDrop};
28
29use super::crypto::{CryptoBackend, CryptoError};
30use super::{AuthProtocol, PrivProtocol};
31
32#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum PrivacyError {
37 InvalidPrivParamsLength { expected: usize, actual: usize },
39 InvalidCiphertextLength { length: usize, block_size: usize },
41 Crypto(CryptoError),
43 SenderStateMismatch,
45 DesSaltExhausted { engine_boots: u32 },
47 DesEngineBootsMismatch {
49 state_engine_boots: u32,
50 generating_engine_boots: u32,
51 },
52}
53
54impl From<CryptoError> for PrivacyError {
55 fn from(e: CryptoError) -> Self {
56 Self::Crypto(e)
57 }
58}
59
60impl std::fmt::Display for PrivacyError {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 Self::InvalidPrivParamsLength { expected, actual } => {
64 write!(
65 f,
66 "invalid privParameters length: expected {expected}, got {actual}"
67 )
68 }
69 Self::InvalidCiphertextLength { length, block_size } => {
70 write!(
71 f,
72 "ciphertext length {length} not multiple of block size {block_size}"
73 )
74 }
75 Self::Crypto(e) => write!(f, "{e}"),
76 Self::SenderStateMismatch => {
77 f.write_str("privacy sender state does not match protocol")
78 }
79 Self::DesSaltExhausted { engine_boots } => write!(
80 f,
81 "DES privacy salt exhausted for generating-engine boots {engine_boots}"
82 ),
83 Self::DesEngineBootsMismatch {
84 state_engine_boots,
85 generating_engine_boots,
86 } => write!(
87 f,
88 "DES sender state boots {state_engine_boots} do not match generating-engine boots {generating_engine_boots}"
89 ),
90 }
91 }
92}
93
94type DesPersistenceSource = Box<dyn std::error::Error + Send + Sync + 'static>;
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98#[non_exhaustive]
99pub enum DesSaltPersistenceOperation {
100 Install,
102 Restart,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct PersistedDesSaltState {
109 engine_boots: u32,
110}
111
112impl PersistedDesSaltState {
113 pub fn new(engine_boots: u32) -> std::result::Result<Self, DesSaltStateError> {
115 if !(1..=super::MAX_ENGINE_TIME).contains(&engine_boots) {
116 return Err(DesSaltStateError::InvalidEpoch { engine_boots });
117 }
118 Ok(Self { engine_boots })
119 }
120
121 #[must_use]
123 pub fn engine_boots(self) -> u32 {
124 self.engine_boots
125 }
126}
127
128#[derive(Debug)]
130pub struct DesSaltPersistenceError {
131 operation: DesSaltPersistenceOperation,
132 previous_engine_boots: Option<u32>,
133 attempted_engine_boots: u32,
134 source: DesPersistenceSource,
135}
136
137impl DesSaltPersistenceError {
138 #[must_use]
140 pub fn operation(&self) -> DesSaltPersistenceOperation {
141 self.operation
142 }
143
144 #[must_use]
146 pub fn previous_engine_boots(&self) -> Option<u32> {
147 self.previous_engine_boots
148 }
149
150 #[must_use]
152 pub fn attempted_engine_boots(&self) -> u32 {
153 self.attempted_engine_boots
154 }
155
156 #[must_use]
158 pub fn persistence_source(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
159 self.source.as_ref()
160 }
161
162 #[must_use]
164 pub fn downcast_source_ref<E: std::error::Error + 'static>(&self) -> Option<&E> {
165 self.source.downcast_ref()
166 }
167}
168
169impl std::fmt::Display for DesSaltPersistenceError {
170 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171 write!(
172 f,
173 "DES sender-state persistence failed during {:?} at boots {}: {}",
174 self.operation, self.attempted_engine_boots, self.source
175 )
176 }
177}
178
179impl std::error::Error for DesSaltPersistenceError {
180 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181 Some(self.source.as_ref())
182 }
183}
184
185#[derive(Debug)]
187#[non_exhaustive]
188pub enum DesSaltStateError {
189 InvalidEpoch { engine_boots: u32 },
191 EpochSaturated { engine_boots: u32 },
193 Persistence(DesSaltPersistenceError),
195}
196
197impl std::fmt::Display for DesSaltStateError {
198 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199 match self {
200 Self::InvalidEpoch { engine_boots } => {
201 write!(
202 f,
203 "invalid DES generating-engine boots epoch {engine_boots}"
204 )
205 }
206 Self::EpochSaturated { engine_boots } => {
207 write!(
208 f,
209 "DES generating-engine boots epoch {engine_boots} is saturated"
210 )
211 }
212 Self::Persistence(error) => std::fmt::Display::fmt(error, f),
213 }
214 }
215}
216
217impl std::error::Error for DesSaltStateError {
218 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
219 match self {
220 Self::Persistence(error) => Some(error),
221 _ => None,
222 }
223 }
224}
225
226#[derive(Clone)]
235pub struct DesSaltState {
236 inner: Arc<DesSaltStateInner>,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub struct DesSaltReservation {
242 engine_boots: u32,
243 salt: u32,
244}
245
246impl DesSaltReservation {
247 #[must_use]
249 pub fn engine_boots(self) -> u32 {
250 self.engine_boots
251 }
252
253 #[must_use]
255 pub fn salt(self) -> u32 {
256 self.salt
257 }
258}
259
260struct DesSaltStateInner {
261 engine_boots: u32,
262 last_salt: AtomicU32,
263}
264
265impl Debug for DesSaltState {
266 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
267 f.debug_struct("DesSaltState")
268 .field("engine_boots", &self.engine_boots())
269 .finish_non_exhaustive()
270 }
271}
272
273impl DesSaltState {
274 pub fn install<E, F>(mut persist: F) -> std::result::Result<Self, DesSaltStateError>
280 where
281 E: Into<DesPersistenceSource>,
282 F: FnMut(&PersistedDesSaltState) -> std::result::Result<(), E>,
283 {
284 Self::start(None, 1, DesSaltPersistenceOperation::Install, &mut persist)
285 }
286
287 pub fn restart<E, F>(
292 previous: PersistedDesSaltState,
293 mut persist: F,
294 ) -> std::result::Result<Self, DesSaltStateError>
295 where
296 E: Into<DesPersistenceSource>,
297 F: FnMut(&PersistedDesSaltState) -> std::result::Result<(), E>,
298 {
299 let next = previous
300 .engine_boots
301 .checked_add(1)
302 .filter(|boots| *boots <= super::MAX_ENGINE_TIME)
303 .ok_or(DesSaltStateError::EpochSaturated {
304 engine_boots: previous.engine_boots,
305 })?;
306 Self::start(
307 Some(previous.engine_boots),
308 next,
309 DesSaltPersistenceOperation::Restart,
310 &mut persist,
311 )
312 }
313
314 fn start<E, F>(
315 previous_engine_boots: Option<u32>,
316 engine_boots: u32,
317 operation: DesSaltPersistenceOperation,
318 persist: &mut F,
319 ) -> std::result::Result<Self, DesSaltStateError>
320 where
321 E: Into<DesPersistenceSource>,
322 F: FnMut(&PersistedDesSaltState) -> std::result::Result<(), E>,
323 {
324 let persisted = PersistedDesSaltState::new(engine_boots)?;
325 persist(&persisted).map_err(|source| {
326 DesSaltStateError::Persistence(DesSaltPersistenceError {
327 operation,
328 previous_engine_boots,
329 attempted_engine_boots: engine_boots,
330 source: source.into(),
331 })
332 })?;
333 Ok(Self {
334 inner: Arc::new(DesSaltStateInner {
335 engine_boots,
336 last_salt: AtomicU32::new(0),
337 }),
338 })
339 }
340
341 #[must_use]
343 pub fn engine_boots(&self) -> u32 {
344 self.inner.engine_boots
345 }
346
347 #[must_use]
349 pub fn persisted_state(&self) -> PersistedDesSaltState {
350 PersistedDesSaltState {
351 engine_boots: self.inner.engine_boots,
352 }
353 }
354
355 pub fn reserve(&self) -> PrivacyResult<DesSaltReservation> {
360 let salt = self
361 .inner
362 .last_salt
363 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
364 current.checked_add(1)
365 })
366 .map(|previous| previous + 1)
367 .map_err(|_| PrivacyError::DesSaltExhausted {
368 engine_boots: self.inner.engine_boots,
369 })?;
370 Ok(DesSaltReservation {
371 engine_boots: self.inner.engine_boots,
372 salt,
373 })
374 }
375
376 pub(crate) fn validate_generating_engine_boots(
377 &self,
378 generating_engine_boots: u32,
379 ) -> PrivacyResult<()> {
380 if self.engine_boots() != generating_engine_boots {
381 return Err(PrivacyError::DesEngineBootsMismatch {
382 state_engine_boots: self.engine_boots(),
383 generating_engine_boots,
384 });
385 }
386 Ok(())
387 }
388
389 #[cfg(test)]
390 fn with_last_salt_for_test(engine_boots: u32, last_salt: u32) -> Self {
391 Self {
392 inner: Arc::new(DesSaltStateInner {
393 engine_boots,
394 last_salt: AtomicU32::new(last_salt),
395 }),
396 }
397 }
398}
399
400pub(crate) enum PrivacyEncryptContext<'a> {
402 Des(DesSaltReservation),
404 Aes {
406 engine_boots: u32,
407 engine_time: u32,
408 salt_counter: &'a SaltCounter,
409 },
410}
411
412impl std::error::Error for PrivacyError {
413 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
414 match self {
415 Self::Crypto(e) => Some(e),
416 _ => None,
417 }
418 }
419}
420
421pub type PrivacyResult<T> = std::result::Result<T, PrivacyError>;
423
424fn random_nonzero_u64() -> crate::error::Result<u64> {
428 random_nonzero_u64_with(getrandom::fill)
429}
430
431fn random_nonzero_u64_with(
432 mut fill: impl FnMut(&mut [u8]) -> std::result::Result<(), getrandom::Error>,
433) -> crate::error::Result<u64> {
434 let mut buf = [0u8; 8];
435 loop {
436 fill(&mut buf).map_err(|source| crate::Error::RandomSource { source }.boxed())?;
437 let val = u64::from_ne_bytes(buf);
438 if val != 0 {
439 return Ok(val);
440 }
441 }
443}
444
445#[derive(Clone, Zeroize, ZeroizeOnDrop)]
457pub struct PrivKey {
458 key: Vec<u8>,
460 #[zeroize(skip)]
462 protocol: PrivProtocol,
463 #[zeroize(skip)]
464 backend: CryptoBackend,
465}
466
467pub struct SaltCounter(AtomicU64);
474
475impl SaltCounter {
476 pub fn new() -> crate::error::Result<Self> {
483 Ok(Self(AtomicU64::new(random_nonzero_u64()?)))
484 }
485
486 #[cfg(test)]
488 #[must_use]
489 pub(crate) fn from_value(value: u64) -> Self {
490 Self(AtomicU64::new(value))
491 }
492
493 pub fn next(&self) -> u64 {
501 loop {
502 let old = self.0.fetch_add(1, Ordering::SeqCst);
503 let val = old.wrapping_add(1);
504 if val as u32 != 0 {
505 return val;
506 }
507 }
508 }
509}
510
511impl PrivKey {
512 pub fn from_password(
554 auth_protocol: AuthProtocol,
555 priv_protocol: PrivProtocol,
556 password: &[u8],
557 engine_id: &[u8],
558 ) -> super::crypto::CryptoResult<Self> {
559 if password.len() < super::auth::MIN_PASSWORD_LENGTH {
560 return Err(CryptoError::PasswordTooShort);
561 }
562 Self::from_password_with_backend(
563 auth_protocol,
564 priv_protocol,
565 password,
566 engine_id,
567 CryptoBackend::require_default()?,
568 )
569 }
570
571 pub fn from_password_with_backend(
573 auth_protocol: AuthProtocol,
574 priv_protocol: PrivProtocol,
575 password: &[u8],
576 engine_id: &[u8],
577 backend: CryptoBackend,
578 ) -> super::crypto::CryptoResult<Self> {
579 use super::MasterKey;
580
581 if password.len() < super::auth::MIN_PASSWORD_LENGTH {
582 return Err(CryptoError::PasswordTooShort);
583 }
584 backend.validate_auth_protocol(auth_protocol)?;
585 backend.validate_priv_protocol(priv_protocol)?;
586 let master = MasterKey::from_password_with_backend(auth_protocol, password, backend)?;
587 Self::from_master_key(&master, priv_protocol, engine_id)
588 }
589
590 pub fn from_master_key(
620 master: &super::MasterKey,
621 priv_protocol: PrivProtocol,
622 engine_id: &[u8],
623 ) -> super::crypto::CryptoResult<Self> {
624 use super::{
625 KeyExtension,
626 auth::{extend_key_reeder_with_backend, extend_key_with_backend},
627 };
628
629 let auth_protocol = master.protocol();
630 master
631 .crypto_backend()
632 .validate_auth_protocol(auth_protocol)?;
633 master
634 .crypto_backend()
635 .validate_priv_protocol(priv_protocol)?;
636 let key_extension = priv_protocol.key_extension_for(auth_protocol);
637
638 let localized = master.localize(engine_id)?;
640 let key_bytes = localized.as_bytes();
641
642 let key = match key_extension {
643 KeyExtension::None => key_bytes.to_vec(),
644 KeyExtension::Blumenthal => extend_key_with_backend(
645 master.crypto_backend(),
646 auth_protocol,
647 key_bytes,
648 priv_protocol.key_len(),
649 )?,
650 KeyExtension::Reeder => extend_key_reeder_with_backend(
651 master.crypto_backend(),
652 auth_protocol,
653 key_bytes,
654 engine_id,
655 priv_protocol.key_len(),
656 )?,
657 };
658
659 Ok(Self {
660 key,
661 protocol: priv_protocol,
662 backend: master.crypto_backend(),
663 })
664 }
665
666 pub fn from_bytes(
680 protocol: PrivProtocol,
681 key: impl Into<Vec<u8>>,
682 ) -> super::crypto::CryptoResult<Self> {
683 let key = key.into();
684 if key.len() < protocol.key_len() {
685 return Err(CryptoError::InvalidKeyLength);
686 }
687 Self::from_bytes_with_backend(protocol, key, CryptoBackend::require_default()?)
688 }
689
690 pub fn from_bytes_with_backend(
692 protocol: PrivProtocol,
693 key: impl Into<Vec<u8>>,
694 backend: CryptoBackend,
695 ) -> super::crypto::CryptoResult<Self> {
696 let key = key.into();
697 if key.len() < protocol.key_len() {
698 return Err(CryptoError::InvalidKeyLength);
699 }
700 backend.validate_priv_protocol(protocol)?;
701 Ok(Self {
702 key,
703 protocol,
704 backend,
705 })
706 }
707
708 pub fn protocol(&self) -> PrivProtocol {
710 self.protocol
711 }
712
713 pub fn crypto_backend(&self) -> CryptoBackend {
715 self.backend
716 }
717
718 pub fn encryption_key(&self) -> &[u8] {
720 match self.protocol {
721 PrivProtocol::Des => &self.key[..8],
722 PrivProtocol::Des3 => &self.key[..24],
723 PrivProtocol::Aes128 => &self.key[..16],
724 PrivProtocol::Aes192Blumenthal | PrivProtocol::Aes192Reeder => &self.key[..24],
725 PrivProtocol::Aes256Blumenthal | PrivProtocol::Aes256Reeder => &self.key[..32],
726 }
727 }
728
729 pub(crate) fn encrypt_with_context(
731 &self,
732 plaintext: &[u8],
733 context: PrivacyEncryptContext<'_>,
734 ) -> PrivacyResult<(Bytes, Bytes)> {
735 match (self.protocol, context) {
736 (PrivProtocol::Des, PrivacyEncryptContext::Des(reservation)) => self.encrypt_des_cbc(
737 plaintext,
738 reservation.engine_boots,
739 u64::from(reservation.salt),
740 ),
741 (PrivProtocol::Des3, PrivacyEncryptContext::Des(reservation)) => self.encrypt_des3_cbc(
742 plaintext,
743 reservation.engine_boots,
744 u64::from(reservation.salt),
745 ),
746 (
747 PrivProtocol::Aes128,
748 PrivacyEncryptContext::Aes {
749 engine_boots,
750 engine_time,
751 salt_counter,
752 },
753 ) => self.encrypt_aes_cfb(
754 plaintext,
755 engine_boots,
756 engine_time,
757 salt_counter.next(),
758 16,
759 ),
760 (
761 PrivProtocol::Aes192Blumenthal | PrivProtocol::Aes192Reeder,
762 PrivacyEncryptContext::Aes {
763 engine_boots,
764 engine_time,
765 salt_counter,
766 },
767 ) => self.encrypt_aes_cfb(
768 plaintext,
769 engine_boots,
770 engine_time,
771 salt_counter.next(),
772 24,
773 ),
774 (
775 PrivProtocol::Aes256Blumenthal | PrivProtocol::Aes256Reeder,
776 PrivacyEncryptContext::Aes {
777 engine_boots,
778 engine_time,
779 salt_counter,
780 },
781 ) => self.encrypt_aes_cfb(
782 plaintext,
783 engine_boots,
784 engine_time,
785 salt_counter.next(),
786 32,
787 ),
788 _ => Err(PrivacyError::SenderStateMismatch),
789 }
790 }
791
792 pub fn encrypt_des_family(
804 &self,
805 plaintext: &[u8],
806 state: &DesSaltState,
807 ) -> PrivacyResult<(Bytes, Bytes)> {
808 let reservation = state.reserve()?;
809 self.encrypt_with_context(plaintext, PrivacyEncryptContext::Des(reservation))
810 }
811
812 pub fn encrypt_aes(
819 &self,
820 plaintext: &[u8],
821 engine_boots: u32,
822 engine_time: u32,
823 salt_counter: &SaltCounter,
824 ) -> PrivacyResult<(Bytes, Bytes)> {
825 self.encrypt_with_context(
826 plaintext,
827 PrivacyEncryptContext::Aes {
828 engine_boots,
829 engine_time,
830 salt_counter,
831 },
832 )
833 }
834
835 pub fn decrypt(
847 &self,
848 ciphertext: &[u8],
849 engine_boots: u32,
850 engine_time: u32,
851 priv_params: &[u8],
852 ) -> PrivacyResult<Bytes> {
853 if priv_params.len() != 8 {
854 tracing::debug!(target: "async_snmp::crypto", { expected = 8, actual = priv_params.len() }, "invalid privParameters length");
855 return Err(PrivacyError::InvalidPrivParamsLength {
856 expected: 8,
857 actual: priv_params.len(),
858 });
859 }
860
861 match self.protocol {
862 PrivProtocol::Des => self.decrypt_des(ciphertext, priv_params),
863 PrivProtocol::Des3 => self.decrypt_des3(ciphertext, priv_params),
864 PrivProtocol::Aes128
865 | PrivProtocol::Aes192Blumenthal
866 | PrivProtocol::Aes192Reeder
867 | PrivProtocol::Aes256Blumenthal
868 | PrivProtocol::Aes256Reeder => {
869 self.decrypt_aes(ciphertext, engine_boots, engine_time, priv_params)
870 }
871 }
872 }
873
874 fn encrypt_des_cbc(
876 &self,
877 plaintext: &[u8],
878 engine_boots: u32,
879 salt_int: u64,
880 ) -> PrivacyResult<(Bytes, Bytes)> {
881 let key = &self.key[..8];
883 let pre_iv = &self.key[8..16];
885
886 let mut salt = [0u8; 8];
889 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
890 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
891
892 let mut iv = [0u8; 8];
894 for i in 0..8 {
895 iv[i] = pre_iv[i] ^ salt[i];
896 }
897
898 let mut buffer = plaintext.to_vec();
899 self.backend
900 .encrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
901
902 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
903 }
904
905 fn decrypt_des(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
907 if !ciphertext.len().is_multiple_of(8) {
908 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "DES decryption failed: invalid ciphertext length");
909 return Err(PrivacyError::InvalidCiphertextLength {
910 length: ciphertext.len(),
911 block_size: 8,
912 });
913 }
914
915 let key = &self.key[..8];
917 let pre_iv = &self.key[8..16];
919
920 let salt = priv_params;
922
923 let mut iv = [0u8; 8];
925 for i in 0..8 {
926 iv[i] = pre_iv[i] ^ salt[i];
927 }
928
929 let mut buffer = ciphertext.to_vec();
930 self.backend
931 .decrypt(PrivProtocol::Des, key, &iv, &mut buffer)?;
932
933 Ok(Bytes::from(buffer))
934 }
935
936 fn encrypt_des3_cbc(
938 &self,
939 plaintext: &[u8],
940 engine_boots: u32,
941 salt_int: u64,
942 ) -> PrivacyResult<(Bytes, Bytes)> {
943 let key = &self.key[..24];
945 let pre_iv = &self.key[24..32];
947
948 let mut salt = [0u8; 8];
950 salt[..4].copy_from_slice(&engine_boots.to_be_bytes());
951 salt[4..].copy_from_slice(&(salt_int as u32).to_be_bytes());
952
953 let mut iv = [0u8; 8];
955 for i in 0..8 {
956 iv[i] = pre_iv[i] ^ salt[i];
957 }
958
959 let mut buffer = plaintext.to_vec();
960 self.backend
961 .encrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
962
963 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt)))
964 }
965
966 fn decrypt_des3(&self, ciphertext: &[u8], priv_params: &[u8]) -> PrivacyResult<Bytes> {
968 if !ciphertext.len().is_multiple_of(8) {
969 tracing::debug!(target: "async_snmp::crypto", { length = ciphertext.len(), block_size = 8 }, "3DES decryption failed: invalid ciphertext length");
970 return Err(PrivacyError::InvalidCiphertextLength {
971 length: ciphertext.len(),
972 block_size: 8,
973 });
974 }
975
976 let key = &self.key[..24];
978 let pre_iv = &self.key[24..32];
980
981 let salt = priv_params;
983
984 let mut iv = [0u8; 8];
986 for i in 0..8 {
987 iv[i] = pre_iv[i] ^ salt[i];
988 }
989
990 let mut buffer = ciphertext.to_vec();
991 self.backend
992 .decrypt(PrivProtocol::Des3, key, &iv, &mut buffer)?;
993
994 Ok(Bytes::from(buffer))
995 }
996
997 fn encrypt_aes_cfb(
999 &self,
1000 plaintext: &[u8],
1001 engine_boots: u32,
1002 engine_time: u32,
1003 salt: u64,
1004 key_len: usize,
1005 ) -> PrivacyResult<(Bytes, Bytes)> {
1006 let key = &self.key[..key_len];
1008
1009 let salt_bytes = salt.to_be_bytes();
1011
1012 let mut iv = [0u8; 16];
1015 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
1016 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
1017 iv[8..].copy_from_slice(&salt_bytes);
1018
1019 let mut buffer = plaintext.to_vec();
1020 self.backend.encrypt(self.protocol, key, &iv, &mut buffer)?;
1021
1022 Ok((Bytes::from(buffer), Bytes::copy_from_slice(&salt_bytes)))
1023 }
1024
1025 fn decrypt_aes(
1027 &self,
1028 ciphertext: &[u8],
1029 engine_boots: u32,
1030 engine_time: u32,
1031 priv_params: &[u8],
1032 ) -> PrivacyResult<Bytes> {
1033 let key_len = match self.protocol {
1034 PrivProtocol::Aes128 => 16,
1035 PrivProtocol::Aes192Blumenthal | PrivProtocol::Aes192Reeder => 24,
1036 PrivProtocol::Aes256Blumenthal | PrivProtocol::Aes256Reeder => 32,
1037 _ => unreachable!(),
1038 };
1039
1040 let key = &self.key[..key_len];
1042
1043 let mut iv = [0u8; 16];
1045 iv[..4].copy_from_slice(&engine_boots.to_be_bytes());
1046 iv[4..8].copy_from_slice(&engine_time.to_be_bytes());
1047 iv[8..].copy_from_slice(priv_params);
1048
1049 let mut buffer = ciphertext.to_vec();
1050 self.backend.decrypt(self.protocol, key, &iv, &mut buffer)?;
1051
1052 Ok(Bytes::from(buffer))
1053 }
1054}
1055
1056impl std::fmt::Debug for PrivKey {
1057 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1058 f.debug_struct("PrivKey")
1059 .field("protocol", &self.protocol)
1060 .field("key", &"[REDACTED]")
1061 .finish()
1062 }
1063}
1064
1065#[cfg(all(test, not(any(feature = "crypto-rustcrypto", feature = "crypto-fips"))))]
1066mod no_backend_key_tests {
1067 use super::*;
1068
1069 #[test]
1070 fn active_privacy_key_constructors_reject_unavailable_backend() {
1071 for protocol in [
1072 PrivProtocol::Des,
1073 PrivProtocol::Des3,
1074 PrivProtocol::Aes128,
1075 PrivProtocol::Aes192Blumenthal,
1076 PrivProtocol::Aes192Reeder,
1077 PrivProtocol::Aes256Blumenthal,
1078 PrivProtocol::Aes256Reeder,
1079 ] {
1080 let len = protocol.key_len();
1081 assert_eq!(
1082 PrivKey::from_bytes(protocol, vec![0_u8; len - 1]).unwrap_err(),
1083 CryptoError::InvalidKeyLength
1084 );
1085 assert_eq!(
1086 PrivKey::from_bytes(protocol, vec![0_u8; len]).unwrap_err(),
1087 CryptoError::BackendUnavailable
1088 );
1089 }
1090 assert_eq!(
1091 PrivKey::from_password(
1092 AuthProtocol::Sha256,
1093 PrivProtocol::Aes128,
1094 b"short",
1095 b"engine-id",
1096 )
1097 .unwrap_err(),
1098 CryptoError::PasswordTooShort
1099 );
1100 assert_eq!(
1101 PrivKey::from_password(
1102 AuthProtocol::Sha256,
1103 PrivProtocol::Aes128,
1104 b"long-enough",
1105 b"engine-id",
1106 )
1107 .unwrap_err(),
1108 CryptoError::BackendUnavailable
1109 );
1110 }
1111}
1112
1113#[cfg(test)]
1114mod entropy_tests {
1115 use super::*;
1116
1117 #[test]
1118 fn salt_counter_propagates_random_source_failure() {
1119 let error = random_nonzero_u64_with(|_| Err(getrandom::Error::UNEXPECTED)).unwrap_err();
1120 assert_eq!(error.kind(), crate::ErrorKind::RandomSource);
1121 }
1122
1123 #[test]
1124 fn salt_counter_retries_a_zero_seed() {
1125 let mut attempts = 0;
1126 let value = random_nonzero_u64_with(|bytes| {
1127 attempts += 1;
1128 if attempts == 2 {
1129 bytes.copy_from_slice(&1_u64.to_ne_bytes());
1130 }
1131 Ok(())
1132 })
1133 .unwrap();
1134 assert_eq!(value, 1);
1135 assert_eq!(attempts, 2);
1136 }
1137}
1138
1139#[cfg(test)]
1140mod des_state_tests {
1141 use super::*;
1142 use std::sync::{Arc, Mutex};
1143
1144 #[derive(Debug)]
1145 struct PersistFailure(&'static str);
1146
1147 impl std::fmt::Display for PersistFailure {
1148 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1149 f.write_str(self.0)
1150 }
1151 }
1152
1153 impl std::error::Error for PersistFailure {}
1154
1155 #[test]
1156 fn install_and_restart_persist_before_use() {
1157 let writes = Arc::new(Mutex::new(Vec::new()));
1158 let install_writes = Arc::clone(&writes);
1159 let installed = DesSaltState::install(move |state| {
1160 install_writes.lock().unwrap().push(state.engine_boots());
1161 Ok::<(), std::convert::Infallible>(())
1162 })
1163 .unwrap();
1164 assert_eq!(installed.engine_boots(), 1);
1165
1166 let restart_writes = Arc::clone(&writes);
1167 let restarted = DesSaltState::restart(installed.persisted_state(), move |state| {
1168 restart_writes.lock().unwrap().push(state.engine_boots());
1169 Ok::<(), std::convert::Infallible>(())
1170 })
1171 .unwrap();
1172 assert_eq!(restarted.engine_boots(), 2);
1173 assert_eq!(*writes.lock().unwrap(), [1, 2]);
1174 }
1175
1176 #[test]
1177 fn persistence_failure_and_epoch_saturation_are_typed() {
1178 let error = DesSaltState::install(|_| Err(PersistFailure("disk unavailable"))).unwrap_err();
1179 let DesSaltStateError::Persistence(error) = error else {
1180 panic!("expected persistence error");
1181 };
1182 assert_eq!(error.operation(), DesSaltPersistenceOperation::Install);
1183 assert_eq!(error.attempted_engine_boots(), 1);
1184 assert_eq!(
1185 error.downcast_source_ref::<PersistFailure>().unwrap().0,
1186 "disk unavailable"
1187 );
1188
1189 assert!(matches!(
1190 DesSaltState::restart(
1191 PersistedDesSaltState::new(super::super::MAX_ENGINE_TIME).unwrap(),
1192 |_| Ok::<(), std::convert::Infallible>(())
1193 ),
1194 Err(DesSaltStateError::EpochSaturated { .. })
1195 ));
1196 }
1197
1198 #[test]
1199 fn atomic_restart_contract_rejects_a_second_live_owner() {
1200 let durable = Arc::new(Mutex::new(1_u32));
1201 let previous = PersistedDesSaltState::new(1).unwrap();
1202 let lease = |durable: Arc<Mutex<u32>>| {
1203 move |attempted: &PersistedDesSaltState| {
1204 let mut current = durable.lock().unwrap();
1205 if *current != 1 {
1206 return Err(PersistFailure("stale compare-and-set"));
1207 }
1208 *current = attempted.engine_boots();
1209 Ok(())
1210 }
1211 };
1212
1213 assert!(DesSaltState::restart(previous, lease(Arc::clone(&durable))).is_ok());
1214 let error = DesSaltState::restart(previous, lease(durable)).unwrap_err();
1215 assert!(matches!(error, DesSaltStateError::Persistence(_)));
1216 }
1217
1218 #[test]
1219 fn atomic_install_contract_rejects_a_second_live_owner() {
1220 let durable = Arc::new(Mutex::new(None::<u32>));
1221 let lease = |durable: Arc<Mutex<Option<u32>>>| {
1222 move |attempted: &PersistedDesSaltState| {
1223 let mut current = durable.lock().unwrap();
1224 if current.is_some() {
1225 return Err(PersistFailure("domain already installed"));
1226 }
1227 *current = Some(attempted.engine_boots());
1228 Ok(())
1229 }
1230 };
1231
1232 assert!(DesSaltState::install(lease(Arc::clone(&durable))).is_ok());
1233 let error = DesSaltState::install(lease(durable)).unwrap_err();
1234 assert!(matches!(error, DesSaltStateError::Persistence(_)));
1235 }
1236
1237 #[test]
1238 fn clones_share_one_nonwrapping_allocator() {
1239 let state = DesSaltState::install(|_| Ok::<(), std::convert::Infallible>(())).unwrap();
1240 let clone = state.clone();
1241 assert_eq!(state.reserve().unwrap().salt(), 1);
1242 assert_eq!(clone.reserve().unwrap().salt(), 2);
1243
1244 let exhausted = DesSaltState::with_last_salt_for_test(9, u32::MAX);
1245 assert!(matches!(
1246 exhausted.reserve(),
1247 Err(PrivacyError::DesSaltExhausted { engine_boots: 9 })
1248 ));
1249 }
1250}
1251
1252#[cfg(all(test, any(feature = "crypto-rustcrypto", feature = "crypto-fips")))]
1253mod tests {
1254 use super::*;
1255 use crate::format::hex::decode as decode_hex;
1256
1257 #[cfg(feature = "crypto-rustcrypto")]
1258 fn des_state(engine_boots: u32) -> DesSaltState {
1259 if engine_boots == 1 {
1260 DesSaltState::install(|_| Ok::<(), std::convert::Infallible>(())).unwrap()
1261 } else {
1262 DesSaltState::restart(
1263 PersistedDesSaltState::new(engine_boots - 1).unwrap(),
1264 |_| Ok::<(), std::convert::Infallible>(()),
1265 )
1266 .unwrap()
1267 }
1268 }
1269
1270 #[cfg(feature = "crypto-rustcrypto")]
1271 #[test]
1272 fn test_des_encrypt_decrypt_roundtrip() {
1273 let key = vec![
1275 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, ];
1278 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1279
1280 let plaintext = b"Hello, SNMPv3 World!";
1281 let engine_boots = 100u32;
1282 let engine_time = 12345u32;
1283
1284 let (ciphertext, priv_params) = priv_key
1285 .encrypt_des_family(plaintext, &des_state(engine_boots))
1286 .expect("encryption failed");
1287
1288 assert_ne!(ciphertext.as_ref(), plaintext);
1290 assert_eq!(priv_params.len(), 8);
1292
1293 let decrypted = priv_key
1295 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1296 .expect("decryption failed");
1297
1298 assert!(decrypted.len() >= plaintext.len());
1300 assert_eq!(&decrypted[..plaintext.len()], plaintext);
1301 }
1302
1303 #[cfg(feature = "crypto-rustcrypto")]
1304 #[test]
1305 fn test_des3_encrypt_decrypt_roundtrip() {
1306 let key = vec![
1308 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, ];
1313 let priv_key = PrivKey::from_bytes(PrivProtocol::Des3, key).unwrap();
1314
1315 let plaintext = b"Hello, SNMPv3 World with 3DES!";
1316 let engine_boots = 100u32;
1317 let engine_time = 12345u32;
1318
1319 let (ciphertext, priv_params) = priv_key
1320 .encrypt_des_family(plaintext, &des_state(engine_boots))
1321 .expect("encryption failed");
1322
1323 assert_ne!(ciphertext.as_ref(), plaintext);
1325 assert_eq!(priv_params.len(), 8);
1327
1328 let decrypted = priv_key
1330 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1331 .expect("decryption failed");
1332
1333 assert!(decrypted.len() >= plaintext.len());
1335 assert_eq!(&decrypted[..plaintext.len()], plaintext);
1336 }
1337
1338 #[test]
1339 fn test_aes128_encrypt_decrypt_roundtrip() {
1340 let key = vec![
1342 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1343 0x0f, 0x10,
1344 ];
1345 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1346
1347 let plaintext = b"Hello, SNMPv3 AES World!";
1348 let engine_boots = 200u32;
1349 let engine_time = 54321u32;
1350
1351 let (ciphertext, priv_params) = priv_key
1352 .encrypt_aes(
1353 plaintext,
1354 engine_boots,
1355 engine_time,
1356 &SaltCounter::new().unwrap(),
1357 )
1358 .expect("encryption failed");
1359
1360 assert_ne!(ciphertext.as_ref(), plaintext);
1362 assert_eq!(priv_params.len(), 8);
1364
1365 let decrypted = priv_key
1367 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1368 .expect("decryption failed");
1369
1370 assert_eq!(decrypted.len(), plaintext.len());
1372 assert_eq!(decrypted.as_ref(), plaintext);
1373 }
1374
1375 #[cfg(feature = "crypto-rustcrypto")]
1376 #[test]
1377 fn test_des_invalid_ciphertext_length() {
1378 let key = vec![0u8; 16];
1379 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1380
1381 let ciphertext = [0u8; 13];
1383 let priv_params = [0u8; 8];
1384
1385 let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
1386 assert!(result.is_err());
1387 }
1388
1389 #[test]
1390 fn test_invalid_priv_params_length() {
1391 let key = vec![0u8; 16];
1392 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1393
1394 let ciphertext = [0u8; 16];
1396 let priv_params = [0u8; 4]; let result = priv_key.decrypt(&ciphertext, 0, 0, &priv_params);
1399 assert!(result.is_err());
1400 }
1401
1402 #[test]
1403 fn test_from_bytes_rejects_undersized_key() {
1404 let result = PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; 4]);
1407 assert!(matches!(result, Err(CryptoError::InvalidKeyLength)));
1408 }
1409
1410 #[test]
1411 fn test_from_bytes_accepts_exact_length_key() {
1412 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, vec![0u8; 16]);
1413 if CryptoBackend::default_backend()
1414 .unwrap()
1415 .validate_priv_protocol(PrivProtocol::Des)
1416 .is_ok()
1417 {
1418 assert!(priv_key.is_ok());
1419 } else {
1420 assert_eq!(
1421 priv_key.unwrap_err(),
1422 CryptoError::UnsupportedAlgorithm("DES")
1423 );
1424 }
1425
1426 assert!(PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; 16]).is_ok());
1427 }
1428
1429 #[test]
1430 fn test_from_bytes_accepts_oversized_key() {
1431 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; 20]).unwrap();
1435 let _ = priv_key.encrypt_aes(b"data", 0, 0, &SaltCounter::new().unwrap());
1437 }
1438
1439 #[test]
1440 fn test_from_bytes_key_len_boundary() {
1441 let aes128_len = PrivProtocol::Aes128.key_len();
1442 assert!(PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; aes128_len - 1]).is_err());
1443 assert!(PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; aes128_len]).is_ok());
1444
1445 let aes256_len = PrivProtocol::Aes256Blumenthal.key_len();
1446 assert!(
1447 PrivKey::from_bytes(PrivProtocol::Aes256Blumenthal, vec![0u8; aes256_len - 1]).is_err()
1448 );
1449 assert!(PrivKey::from_bytes(PrivProtocol::Aes256Blumenthal, vec![0u8; aes256_len]).is_ok());
1450 }
1451
1452 #[test]
1453 fn raw_privacy_key_capabilities_for_each_protocol_and_backend() {
1454 let protocols = [
1455 PrivProtocol::Des,
1456 PrivProtocol::Des3,
1457 PrivProtocol::Aes128,
1458 PrivProtocol::Aes192Blumenthal,
1459 PrivProtocol::Aes192Reeder,
1460 PrivProtocol::Aes256Blumenthal,
1461 PrivProtocol::Aes256Reeder,
1462 ];
1463 let backends = [
1464 #[cfg(feature = "crypto-rustcrypto")]
1465 CryptoBackend::RustCrypto,
1466 #[cfg(feature = "crypto-fips")]
1467 CryptoBackend::AwsLcFips,
1468 ];
1469
1470 for backend in backends {
1471 for protocol in protocols {
1472 let len = protocol.key_len();
1473 assert_eq!(
1474 PrivKey::from_bytes_with_backend(protocol, vec![0_u8; len - 1], backend)
1475 .unwrap_err(),
1476 CryptoError::InvalidKeyLength
1477 );
1478
1479 let result = PrivKey::from_bytes_with_backend(protocol, vec![0_u8; len], backend);
1480 let is_supported = match backend {
1481 CryptoBackend::RustCrypto => true,
1482 CryptoBackend::AwsLcFips => {
1483 !matches!(protocol, PrivProtocol::Des | PrivProtocol::Des3)
1484 }
1485 };
1486 if is_supported {
1487 assert_eq!(result.unwrap().key.len(), protocol.key_len());
1488 } else {
1489 let algorithm = match protocol {
1490 PrivProtocol::Des => "DES",
1491 PrivProtocol::Des3 => "3DES",
1492 PrivProtocol::Aes128
1493 | PrivProtocol::Aes192Blumenthal
1494 | PrivProtocol::Aes192Reeder
1495 | PrivProtocol::Aes256Blumenthal
1496 | PrivProtocol::Aes256Reeder => {
1497 unreachable!()
1498 }
1499 };
1500 assert_eq!(
1501 result.unwrap_err(),
1502 CryptoError::UnsupportedAlgorithm(algorithm)
1503 );
1504 }
1505 }
1506 }
1507 }
1508
1509 #[test]
1510 fn test_salt_counter() {
1511 let counter = SaltCounter::from_value(100);
1512 let s1 = counter.next();
1513 let s2 = counter.next();
1514 let s3 = counter.next();
1515
1516 assert_eq!(s2, s1.wrapping_add(1));
1518 assert_eq!(s3, s2.wrapping_add(1));
1519 }
1520
1521 #[test]
1526 fn test_salt_counter_skips_zero() {
1527 let counter = SaltCounter::from_value(u64::MAX - 1);
1533
1534 let s1 = counter.next();
1535 assert_eq!(s1, u64::MAX);
1536
1537 let s2 = counter.next();
1539 assert_ne!(s2, 0, "SaltCounter should never return zero");
1540 assert_eq!(s2, 1, "SaltCounter should skip 0 and return 1");
1541
1542 let s3 = counter.next();
1544 assert_eq!(s3, 2);
1545 }
1546
1547 #[test]
1554 fn test_salt_counter_wrap_no_duplicate() {
1555 use std::collections::HashSet;
1556
1557 let counter = SaltCounter::from_value(u64::MAX - 2);
1558 let mut seen = HashSet::new();
1559 for _ in 0..6 {
1561 let v = counter.next();
1562 assert_ne!(v, 0, "SaltCounter must never return zero");
1563 assert!(seen.insert(v), "SaltCounter emitted duplicate: {v}");
1564 }
1565 }
1566
1567 #[test]
1568 fn test_multiple_encryptions_use_shared_counter() {
1569 let key = vec![0u8; 16];
1570 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
1571 let counter = SaltCounter::from_value(100);
1572
1573 let (_, salt1) = priv_key.encrypt_aes(b"test data", 0, 0, &counter).unwrap();
1574 let (_, salt2) = priv_key.encrypt_aes(b"test data", 0, 0, &counter).unwrap();
1575
1576 assert_eq!(u64::from_be_bytes(salt1[..].try_into().unwrap()), 101);
1577 assert_eq!(u64::from_be_bytes(salt2[..].try_into().unwrap()), 102);
1578 }
1579
1580 #[test]
1581 fn test_from_password() {
1582 let password = b"maplesyrup";
1584 let engine_id = decode_hex("000000000000000000000002").unwrap();
1585
1586 let priv_key = PrivKey::from_password(
1587 AuthProtocol::Sha1,
1588 PrivProtocol::Aes128,
1589 password,
1590 &engine_id,
1591 )
1592 .unwrap();
1593
1594 let plaintext = b"test message";
1596 let (ciphertext, priv_params) = priv_key
1597 .encrypt_aes(plaintext, 100, 200, &SaltCounter::new().unwrap())
1598 .unwrap();
1599 let decrypted = priv_key
1600 .decrypt(&ciphertext, 100, 200, &priv_params)
1601 .unwrap();
1602
1603 assert_eq!(decrypted.as_ref(), plaintext);
1604 }
1605
1606 #[test]
1607 fn test_aes192_encrypt_decrypt_roundtrip() {
1608 let key = vec![
1610 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1611 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1612 ];
1613 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes192Blumenthal, key).unwrap();
1614
1615 let plaintext = b"Hello, SNMPv3 AES-192 World!";
1616 let engine_boots = 300u32;
1617 let engine_time = 67890u32;
1618
1619 let (ciphertext, priv_params) = priv_key
1620 .encrypt_aes(
1621 plaintext,
1622 engine_boots,
1623 engine_time,
1624 &SaltCounter::new().unwrap(),
1625 )
1626 .expect("AES-192 encryption failed");
1627
1628 assert_ne!(ciphertext.as_ref(), plaintext);
1630 assert_eq!(priv_params.len(), 8);
1632
1633 let decrypted = priv_key
1635 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1636 .expect("AES-192 decryption failed");
1637
1638 assert_eq!(decrypted.len(), plaintext.len());
1640 assert_eq!(decrypted.as_ref(), plaintext);
1641 }
1642
1643 #[test]
1644 fn test_aes256_encrypt_decrypt_roundtrip() {
1645 let key = vec![
1647 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1648 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1649 0x1d, 0x1e, 0x1f, 0x20,
1650 ];
1651 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes256Blumenthal, key).unwrap();
1652
1653 let plaintext = b"Hello, SNMPv3 AES-256 World!";
1654 let engine_boots = 400u32;
1655 let engine_time = 11111u32;
1656
1657 let (ciphertext, priv_params) = priv_key
1658 .encrypt_aes(
1659 plaintext,
1660 engine_boots,
1661 engine_time,
1662 &SaltCounter::new().unwrap(),
1663 )
1664 .expect("AES-256 encryption failed");
1665
1666 assert_ne!(ciphertext.as_ref(), plaintext);
1668 assert_eq!(priv_params.len(), 8);
1670
1671 let decrypted = priv_key
1673 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1674 .expect("AES-256 decryption failed");
1675
1676 assert_eq!(decrypted.len(), plaintext.len());
1678 assert_eq!(decrypted.as_ref(), plaintext);
1679 }
1680
1681 #[test]
1682 fn test_aes192_from_password() {
1683 let password = b"longpassword123";
1685 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
1686
1687 let priv_key = PrivKey::from_password(
1688 AuthProtocol::Sha256, PrivProtocol::Aes192Blumenthal,
1690 password,
1691 &engine_id,
1692 )
1693 .unwrap();
1694
1695 let plaintext = b"test message for AES-192";
1696 let (ciphertext, priv_params) = priv_key
1697 .encrypt_aes(plaintext, 100, 200, &SaltCounter::new().unwrap())
1698 .unwrap();
1699 let decrypted = priv_key
1700 .decrypt(&ciphertext, 100, 200, &priv_params)
1701 .unwrap();
1702
1703 assert_eq!(decrypted.as_ref(), plaintext);
1704 }
1705
1706 #[test]
1707 fn test_aes256_from_password() {
1708 let password = b"anotherlongpassword456";
1710 let engine_id = decode_hex("80001f8880e9b104617361000000").unwrap();
1711
1712 let priv_key = PrivKey::from_password(
1713 AuthProtocol::Sha256, PrivProtocol::Aes256Blumenthal,
1715 password,
1716 &engine_id,
1717 )
1718 .unwrap();
1719
1720 let plaintext = b"test message for AES-256";
1721 let (ciphertext, priv_params) = priv_key
1722 .encrypt_aes(plaintext, 100, 200, &SaltCounter::new().unwrap())
1723 .unwrap();
1724 let decrypted = priv_key
1725 .decrypt(&ciphertext, 100, 200, &priv_params)
1726 .unwrap();
1727
1728 assert_eq!(decrypted.as_ref(), plaintext);
1729 }
1730
1731 #[cfg(feature = "crypto-rustcrypto")]
1741 #[test]
1742 fn test_des_wrong_key_produces_garbage() {
1743 let correct_key = vec![
1745 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1746 0x17, 0x18,
1747 ];
1748 let wrong_key = vec![
1750 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2,
1751 0xE1, 0xE0,
1752 ];
1753
1754 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Des, correct_key).unwrap();
1755 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Des, wrong_key).unwrap();
1756
1757 let plaintext = b"Secret SNMPv3 message data!";
1758 let engine_boots = 100u32;
1759 let engine_time = 12345u32;
1760
1761 let (ciphertext, priv_params) = correct_priv_key
1763 .encrypt_des_family(plaintext, &des_state(engine_boots))
1764 .expect("encryption failed");
1765
1766 let wrong_decrypted = wrong_priv_key
1768 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1769 .expect("decryption should succeed cryptographically");
1770
1771 assert_ne!(
1773 &wrong_decrypted[..plaintext.len()],
1774 plaintext,
1775 "wrong key should NOT produce the original plaintext"
1776 );
1777
1778 let correct_decrypted = correct_priv_key
1780 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1781 .expect("correct key decryption failed");
1782 assert_eq!(
1783 &correct_decrypted[..plaintext.len()],
1784 plaintext,
1785 "correct key should produce the original plaintext"
1786 );
1787 }
1788
1789 #[test]
1790 fn test_aes128_wrong_key_produces_garbage() {
1791 let correct_key = vec![
1792 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1793 0x0f, 0x10,
1794 ];
1795 let wrong_key = vec![
1796 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1797 0xF1, 0xF0,
1798 ];
1799
1800 let correct_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, correct_key).unwrap();
1801 let wrong_priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, wrong_key).unwrap();
1802
1803 let plaintext = b"Secret AES-128 message data!";
1804 let engine_boots = 200u32;
1805 let engine_time = 54321u32;
1806
1807 let (ciphertext, priv_params) = correct_priv_key
1809 .encrypt_aes(
1810 plaintext,
1811 engine_boots,
1812 engine_time,
1813 &SaltCounter::new().unwrap(),
1814 )
1815 .expect("encryption failed");
1816
1817 let wrong_decrypted = wrong_priv_key
1819 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1820 .expect("decryption should succeed cryptographically");
1821
1822 assert_ne!(
1824 wrong_decrypted.as_ref(),
1825 plaintext,
1826 "wrong key should NOT produce the original plaintext"
1827 );
1828
1829 let correct_decrypted = correct_priv_key
1831 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1832 .expect("correct key decryption failed");
1833 assert_eq!(correct_decrypted.as_ref(), plaintext);
1834 }
1835
1836 #[test]
1837 fn test_aes192_wrong_key_produces_garbage() {
1838 let correct_key = vec![
1839 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1840 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1841 ];
1842 let wrong_key = vec![
1843 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1844 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8,
1845 ];
1846
1847 let correct_priv_key =
1848 PrivKey::from_bytes(PrivProtocol::Aes192Blumenthal, correct_key).unwrap();
1849 let wrong_priv_key =
1850 PrivKey::from_bytes(PrivProtocol::Aes192Blumenthal, wrong_key).unwrap();
1851
1852 let plaintext = b"Secret AES-192 message data!";
1853 let engine_boots = 300u32;
1854 let engine_time = 67890u32;
1855
1856 let (ciphertext, priv_params) = correct_priv_key
1857 .encrypt_aes(
1858 plaintext,
1859 engine_boots,
1860 engine_time,
1861 &SaltCounter::new().unwrap(),
1862 )
1863 .expect("encryption failed");
1864
1865 let wrong_decrypted = wrong_priv_key
1866 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1867 .expect("decryption should succeed cryptographically");
1868
1869 assert_ne!(
1870 wrong_decrypted.as_ref(),
1871 plaintext,
1872 "wrong key should NOT produce the original plaintext"
1873 );
1874 }
1875
1876 #[test]
1877 fn test_aes256_wrong_key_produces_garbage() {
1878 let correct_key = vec![
1879 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1880 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1881 0x1d, 0x1e, 0x1f, 0x20,
1882 ];
1883 let wrong_key = vec![
1884 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2,
1885 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4,
1886 0xE3, 0xE2, 0xE1, 0xE0,
1887 ];
1888
1889 let correct_priv_key =
1890 PrivKey::from_bytes(PrivProtocol::Aes256Blumenthal, correct_key).unwrap();
1891 let wrong_priv_key =
1892 PrivKey::from_bytes(PrivProtocol::Aes256Blumenthal, wrong_key).unwrap();
1893
1894 let plaintext = b"Secret AES-256 message data!";
1895 let engine_boots = 400u32;
1896 let engine_time = 11111u32;
1897
1898 let (ciphertext, priv_params) = correct_priv_key
1899 .encrypt_aes(
1900 plaintext,
1901 engine_boots,
1902 engine_time,
1903 &SaltCounter::new().unwrap(),
1904 )
1905 .expect("encryption failed");
1906
1907 let wrong_decrypted = wrong_priv_key
1908 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
1909 .expect("decryption should succeed cryptographically");
1910
1911 assert_ne!(
1912 wrong_decrypted.as_ref(),
1913 plaintext,
1914 "wrong key should NOT produce the original plaintext"
1915 );
1916 }
1917
1918 #[cfg(feature = "crypto-rustcrypto")]
1919 #[test]
1920 fn test_des_wrong_priv_params_produces_garbage() {
1921 let key = vec![
1924 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1925 0x17, 0x18,
1926 ];
1927
1928 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1929
1930 let plaintext = b"DES test message";
1931 let engine_boots = 100u32;
1932 let engine_time = 12345u32;
1933
1934 let (ciphertext, correct_priv_params) = priv_key
1935 .encrypt_des_family(plaintext, &des_state(engine_boots))
1936 .expect("encryption failed");
1937
1938 let wrong_priv_params = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
1940
1941 let wrong_decrypted = priv_key
1942 .decrypt(&ciphertext, engine_boots, engine_time, &wrong_priv_params)
1943 .expect("decryption should succeed cryptographically");
1944
1945 assert_ne!(
1947 &wrong_decrypted[..plaintext.len()],
1948 plaintext,
1949 "wrong priv_params should NOT produce the original plaintext"
1950 );
1951
1952 let correct_decrypted = priv_key
1954 .decrypt(&ciphertext, engine_boots, engine_time, &correct_priv_params)
1955 .expect("correct decryption failed");
1956 assert_eq!(&correct_decrypted[..plaintext.len()], plaintext);
1957 }
1958
1959 #[cfg(feature = "crypto-rustcrypto")]
1966 #[test]
1967 fn test_des_salt_and_iv_composition() {
1968 let key = vec![
1970 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, ];
1973 let pre_iv = [0xAAu8, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22];
1974 let priv_key = PrivKey::from_bytes(PrivProtocol::Des, key).unwrap();
1975
1976 let expected_salt_value: u32 = 0x1001;
1977
1978 let engine_boots: u32 = 0x1234_5678;
1979 let des_state = DesSaltState::with_last_salt_for_test(engine_boots, 0x1000);
1980 let engine_time: u32 = 999;
1981 let plaintext = b"RFC 3414 8.1.1.1 salt/IV composition test";
1982
1983 let (ciphertext, priv_params) = priv_key
1984 .encrypt_des_family(plaintext, &des_state)
1985 .expect("encryption failed");
1986
1987 assert_eq!(priv_params.len(), 8);
1989 assert_eq!(&priv_params[..4], &engine_boots.to_be_bytes());
1990 assert_eq!(&priv_params[4..8], &expected_salt_value.to_be_bytes());
1991
1992 let mut expected_iv = [0u8; 8];
1995 for i in 0..8 {
1996 expected_iv[i] = pre_iv[i] ^ priv_params[i];
1997 }
1998 assert_ne!(
1999 expected_iv, pre_iv,
2000 "salt XOR must change the IV relative to the raw pre-IV"
2001 );
2002
2003 let decrypted = priv_key
2006 .decrypt(&ciphertext, engine_boots, engine_time, &priv_params)
2007 .expect("decryption failed");
2008 assert_eq!(&decrypted[..plaintext.len()], plaintext);
2009 }
2010
2011 #[test]
2016 fn test_salt_counter_no_duplicates_concurrent() {
2017 use std::collections::HashSet;
2018 use std::sync::{Arc, Mutex};
2019 use std::thread;
2020
2021 let counter = Arc::new(SaltCounter::new().unwrap());
2022 let results = Arc::new(Mutex::new(HashSet::new()));
2023 let iterations = 10_000usize;
2024 let threads = 8usize;
2025
2026 let handles: Vec<_> = (0..threads)
2027 .map(|_| {
2028 let counter = Arc::clone(&counter);
2029 let results = Arc::clone(&results);
2030 thread::spawn(move || {
2031 for _ in 0..iterations {
2032 let salt = counter.next();
2033 assert_ne!(salt, 0, "SaltCounter must never return zero");
2034 let mut set = results.lock().unwrap();
2035 assert!(set.insert(salt), "SaltCounter emitted duplicate: {salt}");
2036 }
2037 })
2038 })
2039 .collect();
2040
2041 for h in handles {
2042 h.join().expect("thread panicked");
2043 }
2044 }
2045
2046 #[cfg(feature = "crypto-rustcrypto")]
2047 #[test]
2048 fn test_des_family_shared_state_concurrency_and_exhaustion() {
2049 use std::collections::HashSet;
2050 use std::sync::{Arc, Mutex};
2051 use std::thread;
2052
2053 for (protocol, key_len) in [(PrivProtocol::Des, 16), (PrivProtocol::Des3, 32)] {
2054 let key = Arc::new(PrivKey::from_bytes(protocol, vec![0x5A; key_len]).unwrap());
2055 let state = Arc::new(DesSaltState::with_last_salt_for_test(7, 0));
2056 let portions = Arc::new(Mutex::new(HashSet::new()));
2057
2058 let handles: Vec<_> = (0..4)
2059 .map(|_| {
2060 let key = Arc::clone(&key);
2061 let state = Arc::clone(&state);
2062 let portions = Arc::clone(&portions);
2063 thread::spawn(move || {
2064 let (_, params) = key.encrypt_des_family(b"shared", &state).unwrap();
2065 assert_eq!(¶ms[..4], &7_u32.to_be_bytes());
2066 let low = u32::from_be_bytes(params[4..].try_into().unwrap());
2067 assert_ne!(low, 0);
2068 assert!(portions.lock().unwrap().insert(low));
2069 })
2070 })
2071 .collect();
2072
2073 for handle in handles {
2074 handle.join().unwrap();
2075 }
2076 assert_eq!(*portions.lock().unwrap(), HashSet::from([1, 2, 3, 4]));
2077
2078 let exhausted = DesSaltState::with_last_salt_for_test(7, u32::MAX);
2079 assert!(matches!(
2080 key.encrypt_des_family(b"shared", &exhausted),
2081 Err(PrivacyError::DesSaltExhausted { engine_boots: 7 })
2082 ));
2083 }
2084 }
2085
2086 #[test]
2087 fn test_cloned_priv_keys_require_same_explicit_counter() {
2088 let original = PrivKey::from_bytes(PrivProtocol::Aes128, vec![0u8; 16]).unwrap();
2089 let cloned = original.clone();
2090 let counter = SaltCounter::from_value(200);
2091
2092 let (_, salt_orig) = original.encrypt_aes(b"test", 0, 0, &counter).unwrap();
2093 let (_, salt_clone) = cloned.encrypt_aes(b"test", 0, 0, &counter).unwrap();
2094
2095 assert_eq!(u64::from_be_bytes(salt_orig[..].try_into().unwrap()), 201);
2096 assert_eq!(u64::from_be_bytes(salt_clone[..].try_into().unwrap()), 202);
2097 }
2098
2099 #[cfg(all(feature = "crypto-rustcrypto", feature = "crypto-fips"))]
2100 #[test]
2101 fn test_aes128_backends_match_with_deterministic_salt() {
2102 let key = vec![0x5A; PrivProtocol::Aes128.key_len()];
2103 let rust = PrivKey::from_bytes_with_backend(
2104 PrivProtocol::Aes128,
2105 key.clone(),
2106 CryptoBackend::RustCrypto,
2107 )
2108 .unwrap();
2109 let fips =
2110 PrivKey::from_bytes_with_backend(PrivProtocol::Aes128, key, CryptoBackend::AwsLcFips)
2111 .unwrap();
2112 let rust_counter = SaltCounter::from_value(41);
2113 let fips_counter = SaltCounter::from_value(41);
2114
2115 let rust_encrypted = rust
2116 .encrypt_aes(b"shared AES-128 provider KAT", 7, 11, &rust_counter)
2117 .unwrap();
2118 let fips_encrypted = fips
2119 .encrypt_aes(b"shared AES-128 provider KAT", 7, 11, &fips_counter)
2120 .unwrap();
2121
2122 assert_eq!(rust_encrypted, fips_encrypted);
2123 }
2124
2125 #[test]
2126 fn test_aes_wrong_engine_time_produces_garbage() {
2127 let key = vec![
2130 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
2131 0x0f, 0x10,
2132 ];
2133
2134 let priv_key = PrivKey::from_bytes(PrivProtocol::Aes128, key).unwrap();
2135
2136 let plaintext = b"AES test message";
2137 let engine_boots = 200u32;
2138 let engine_time = 54321u32;
2139
2140 let (ciphertext, priv_params) = priv_key
2141 .encrypt_aes(
2142 plaintext,
2143 engine_boots,
2144 engine_time,
2145 &SaltCounter::new().unwrap(),
2146 )
2147 .expect("encryption failed");
2148
2149 let wrong_decrypted = priv_key
2151 .decrypt(&ciphertext, engine_boots, engine_time + 1, &priv_params)
2152 .expect("decryption should succeed cryptographically");
2153
2154 assert_ne!(
2155 wrong_decrypted.as_ref(),
2156 plaintext,
2157 "wrong engine_time should NOT produce the original plaintext"
2158 );
2159
2160 let wrong_decrypted2 = priv_key
2162 .decrypt(&ciphertext, engine_boots + 1, engine_time, &priv_params)
2163 .expect("decryption should succeed cryptographically");
2164
2165 assert_ne!(
2166 wrong_decrypted2.as_ref(),
2167 plaintext,
2168 "wrong engine_boots should NOT produce the original plaintext"
2169 );
2170 }
2171}