1use std::{fmt, time::Instant};
2
3#[cfg(feature = "crypto-openssl")]
4use openssl::{
5 hash::{Hasher, MessageDigest},
6 pkey::PKey,
7 sign::Signer,
8};
9
10#[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
11use {
12 aes::{Aes128, Aes192, Aes256},
13 cbc::{Decryptor as CbcDecryptor, Encryptor as CbcEncryptor},
14 cfb_mode::{Decryptor as CfbDecryptor, Encryptor as CfbEncryptor},
15 cipher::{AsyncStreamCipher, BlockDecryptMut, BlockEncryptMut, KeyIvInit},
16 des::Des,
17 digest::{Digest, DynDigest},
18 hmac::{Hmac, Mac},
19};
20
21use crate::{
22 AsnReader, BUFFER_SIZE, Error, MessageType, Oid, Pdu, Result, Value, Varbinds, Version, asn1,
23 pdu::{self, Buf},
24 snmp::{self, V3_MSG_FLAGS_AUTH, V3_MSG_FLAGS_PRIVACY, V3_MSG_FLAGS_REPORTABLE},
25};
26
27const ENGINE_TIME_WINDOW: i64 = 150;
28
29#[cfg(feature = "v3")]
30#[derive(Debug, PartialEq, Eq, Clone)]
31pub enum AuthErrorKind {
32 UnsupportedUSM,
33 EngineBootsMismatch,
34 EngineBootsNotProvided,
35 EngineTimeMismatch,
36 NotAuthenticated,
37 UsernameMismatch,
38 EngineIdMismatch,
39 SignatureMismatch,
40 MessageIdMismatch,
41 PrivLengthMismatch,
42 KeyLengthMismatch,
43 PayloadLengthMismatch,
44 ReplyNotEncrypted,
45 SecurityNotProvided,
46 SecurityNotReady,
47 KeyExtensionRequired,
48}
49
50impl fmt::Display for AuthErrorKind {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 AuthErrorKind::UnsupportedUSM => write!(f, "Unsupported USM"),
54 AuthErrorKind::EngineBootsMismatch => write!(f, "Engine boots counter mismatch"),
55 AuthErrorKind::EngineTimeMismatch => write!(f, "Engine time counter mismatch"),
56 AuthErrorKind::NotAuthenticated => write!(f, "Not authenticated"),
57 AuthErrorKind::EngineBootsNotProvided => write!(f, "Engine boots counter not provided"),
58 AuthErrorKind::EngineIdMismatch => write!(f, "Engine ID mismatch"),
59 AuthErrorKind::UsernameMismatch => write!(f, "Username mismatch"),
60 AuthErrorKind::SignatureMismatch => write!(f, "HMAC signature mismatch"),
61 AuthErrorKind::MessageIdMismatch => write!(f, "Message ID mismatch"),
62 AuthErrorKind::PrivLengthMismatch => write!(f, "Privacy parameters length mismatch"),
63 AuthErrorKind::KeyLengthMismatch => write!(f, "Key length mismatch"),
64 AuthErrorKind::PayloadLengthMismatch => write!(f, "Payload length mismatch"),
65 AuthErrorKind::ReplyNotEncrypted => write!(f, "Not an encrypted reply"),
66 AuthErrorKind::SecurityNotProvided => write!(f, "Security parameters not provided"),
67 AuthErrorKind::SecurityNotReady => write!(f, "Security parameters not ready"),
68 AuthErrorKind::KeyExtensionRequired => {
69 write!(f, "Auth/Priv pair needs a key extension method")
70 }
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
76pub(crate) struct AuthoritativeState {
77 auth_key: Vec<u8>,
78 priv_key: Vec<u8>,
79 pub(crate) engine_id: Vec<u8>,
80 engine_boots: i64,
81 engine_time: i64,
82 engine_time_current: i64,
83 start_time: Instant,
84}
85
86impl Default for AuthoritativeState {
87 fn default() -> Self {
88 Self {
89 auth_key: Vec::new(),
90 priv_key: Vec::new(),
91 engine_id: Vec::new(),
92 engine_boots: 0,
93 engine_time: 0,
94 engine_time_current: 0,
95 start_time: Instant::now(),
96 }
97 }
98}
99
100impl AuthoritativeState {
101 fn update_authoritative(&mut self, engine_boots: i64, engine_time: i64) {
102 self.engine_boots = engine_boots;
103 self.engine_time = engine_time;
104 self.start_time = Instant::now();
105 }
106
107 fn update_authoritative_engine_time(&mut self, engine_time: i64) {
108 self.engine_time = engine_time;
109 self.start_time = Instant::now();
110 }
111
112 fn correct_engine_time(&mut self) {
113 if self.engine_boots == 0 {
114 self.engine_time_current = 0;
115 return;
116 }
117 let max = i32::MAX.into();
118 self.engine_time_current =
119 i64::try_from(self.start_time.elapsed().as_secs()).unwrap() + self.engine_time;
120 if self.engine_time_current >= max {
121 self.engine_time_current -= max;
122 self.engine_boots += 1;
123 }
124 }
125
126 fn generate_key(&self, password: &[u8], auth_protocol: AuthProtocol) -> Result<Vec<u8>> {
127 let mut hasher = auth_protocol.create_hasher()?;
128 let mut password_index = 0;
129 let mut password_buf = vec![0; 64];
130 for _ in 0..16384 {
131 for x in &mut password_buf {
132 *x = password[password_index];
133 password_index += 1;
134 if password_index == password.len() {
135 password_index = 0;
136 }
137 }
138 hasher.update(&password_buf)?;
139 }
140 let key = hasher.finish()?;
141 password_buf.clear();
142 password_buf.extend_from_slice(&key);
143 password_buf.extend_from_slice(&self.engine_id);
144 password_buf.extend_from_slice(&key);
145 hasher.update(&password_buf)?;
146 #[allow(clippy::implicit_clone)]
147 Ok(hasher.finish()?.to_vec())
148 }
149
150 fn update_auth_key(
151 &mut self,
152 authentication_password: &[u8],
153 auth_protocol: AuthProtocol,
154 ) -> Result<()> {
155 if self.engine_id.is_empty() {
156 self.auth_key.clear();
157 return Err(Error::AuthFailure(AuthErrorKind::NotAuthenticated));
158 }
159 self.auth_key = self.generate_key(authentication_password, auth_protocol)?;
160 Ok(())
161 }
162
163 fn update_priv_key(
164 &mut self,
165 privacy_password: &[u8],
166 auth_protocol: AuthProtocol,
167 cipher: Cipher,
168 extension_method: Option<&KeyExtension>,
169 ) -> Result<()> {
170 if self.engine_id.is_empty() {
171 self.priv_key.clear();
172 return Err(Error::AuthFailure(AuthErrorKind::NotAuthenticated));
173 }
174 self.priv_key = self.generate_key(privacy_password, auth_protocol)?;
175 if !cipher.priv_key_needs_extension(&auth_protocol) {
176 return Ok(());
177 }
178 match extension_method.as_ref() {
179 Some(KeyExtension::Blumenthal) => {
180 self.extend_priv_key_with_blumenthal_method(cipher.priv_key_len(), auth_protocol)?;
181 }
182 Some(KeyExtension::Reeder) => {
183 self.extend_priv_key_with_reeder_method(cipher.priv_key_len(), auth_protocol)?;
184 }
185 None => return Err(Error::AuthFailure(AuthErrorKind::KeyExtensionRequired)),
186 }
187 Ok(())
188 }
189
190 fn extend_priv_key_with_blumenthal_method(
193 &mut self,
194 need_key_len: usize,
195 auth_protocol: AuthProtocol,
196 ) -> Result<()> {
197 if need_key_len <= self.priv_key.len() {
198 return Ok(());
199 }
200
201 let mut remaining = need_key_len - self.priv_key.len();
202
203 while remaining > 0 {
204 let mut hasher = auth_protocol.create_hasher()?;
206 hasher.update(&self.priv_key)?;
207 let new_hash = hasher.finish()?; let copy_len = remaining.min(new_hash.len());
211 self.priv_key.extend_from_slice(&new_hash[..copy_len]);
212 remaining -= copy_len;
213 }
214
215 Ok(())
216 }
217 fn extend_priv_key_with_reeder_method(
220 &mut self,
221 need_key_len: usize,
222 auth_protocol: AuthProtocol,
223 ) -> Result<()> {
224 if need_key_len < self.priv_key.len() {
225 return Ok(());
226 }
227 let mut remaining = need_key_len - self.priv_key.len();
228 while remaining > 0 {
229 let new_kul = self.generate_key(&self.priv_key, auth_protocol)?;
232
233 let copy_len = remaining.min(new_kul.len());
235 self.priv_key.extend_from_slice(&new_kul[..copy_len]);
236 remaining -= copy_len;
237 }
238 Ok(())
239 }
240}
241
242#[derive(Debug, Copy, Clone, Eq, PartialEq)]
243pub enum KeyExtension {
244 Blumenthal,
245 Reeder,
246}
247
248impl KeyExtension {
249 pub fn other(&self) -> Self {
250 match self {
251 KeyExtension::Blumenthal => KeyExtension::Reeder,
252 KeyExtension::Reeder => KeyExtension::Blumenthal,
253 }
254 }
255}
256
257#[derive(Debug, Clone)]
258pub struct Security {
259 pub(crate) username: Vec<u8>,
260 pub(crate) authentication_password: Vec<u8>,
261 pub(crate) auth: Auth,
262 pub(crate) auth_protocol: AuthProtocol,
263 pub(crate) key_extension_method: Option<KeyExtension>,
264 pub(crate) authoritative_state: AuthoritativeState,
265 pub(crate) plain_buf: Vec<u8>,
266}
267
268impl Security {
269 pub fn new(username: &[u8], authentication_password: &[u8]) -> Self {
270 Self {
271 username: username.to_vec(),
272 authentication_password: authentication_password.to_vec(),
273 auth: Auth::AuthNoPriv,
274 auth_protocol: AuthProtocol::Md5,
275 key_extension_method: None,
276 authoritative_state: AuthoritativeState::default(),
277 plain_buf: Vec::new(),
278 }
279 }
280
281 pub fn with_auth(mut self, auth: Auth) -> Self {
282 self.auth = auth;
283 self
284 }
285
286 pub fn with_auth_protocol(mut self, auth_protocol: AuthProtocol) -> Self {
287 self.auth_protocol = auth_protocol;
288 self
289 }
290
291 pub fn with_key_extension_method(mut self, key_extension_method: KeyExtension) -> Self {
292 self.key_extension_method = Some(key_extension_method);
293 self
294 }
295
296 pub(crate) fn another_key_extension_method(&mut self) -> Option<KeyExtension> {
297 if let Auth::AuthPriv { ref cipher, .. } = self.auth
298 && cipher.priv_key_needs_extension(&self.auth_protocol)
299 && let Some(used_method) = self.key_extension_method
300 {
301 self.key_extension_method = Some(used_method.other());
302 return self.key_extension_method;
303 }
304 None
305 }
306
307 pub fn with_engine_id(mut self, engine_id: &[u8]) -> Result<Self> {
311 self.authoritative_state.engine_id = engine_id.to_vec();
312 self.update_key()?;
313 Ok(self)
314 }
315
316 pub fn with_engine_boots_and_time(mut self, engine_boots: i64, engine_time: i64) -> Self {
317 self.authoritative_state.engine_boots = engine_boots;
318 self.authoritative_state
319 .update_authoritative_engine_time(engine_time);
320 self
321 }
322
323 pub fn reset_engine_id(&mut self) {
324 self.authoritative_state.engine_id.clear();
325 self.authoritative_state.auth_key.clear();
326 self.authoritative_state.priv_key.clear();
327 }
328
329 pub fn reset_engine_counters(&mut self) {
330 self.authoritative_state.engine_boots = 0;
331 self.authoritative_state.update_authoritative_engine_time(0);
332 }
333
334 fn calculate_hmac(&self, data: &[u8]) -> Result<Vec<u8>> {
335 if self.engine_id().is_empty() {
336 return Err(Error::AuthFailure(AuthErrorKind::SecurityNotReady));
337 }
338 #[cfg(feature = "crypto-openssl")]
339 {
340 let pkey = PKey::hmac(&self.authoritative_state.auth_key)?;
341 let mut signer = Signer::new(self.auth_protocol.digest(), &pkey)?;
342 signer.update(data)?;
343 signer.sign_to_vec().map_err(Error::from)
344 }
345 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
346 {
347 let key = &self.authoritative_state.auth_key;
348 match self.auth_protocol {
349 AuthProtocol::Md5 => {
350 let mut mac = Hmac::<md5::Md5>::new_from_slice(key)
351 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
352 mac.update(data);
353 Ok(mac.finalize().into_bytes().to_vec())
354 }
355 AuthProtocol::Sha1 => {
356 let mut mac = Hmac::<sha1::Sha1>::new_from_slice(key)
357 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
358 mac.update(data);
359 Ok(mac.finalize().into_bytes().to_vec())
360 }
361 AuthProtocol::Sha224 => {
362 let mut mac = Hmac::<sha2::Sha224>::new_from_slice(key)
363 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
364 mac.update(data);
365 Ok(mac.finalize().into_bytes().to_vec())
366 }
367 AuthProtocol::Sha256 => {
368 let mut mac = Hmac::<sha2::Sha256>::new_from_slice(key)
369 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
370 mac.update(data);
371 Ok(mac.finalize().into_bytes().to_vec())
372 }
373 AuthProtocol::Sha384 => {
374 let mut mac = Hmac::<sha2::Sha384>::new_from_slice(key)
375 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
376 mac.update(data);
377 Ok(mac.finalize().into_bytes().to_vec())
378 }
379 AuthProtocol::Sha512 => {
380 let mut mac = Hmac::<sha2::Sha512>::new_from_slice(key)
381 .map_err(|_| Error::Crypto("Invalid key length".into()))?;
382 mac.update(data);
383 Ok(mac.finalize().into_bytes().to_vec())
384 }
385 }
386 }
387 }
388
389 pub(crate) fn update_key(&mut self) -> Result<()> {
390 if !self.need_auth() {
391 return Ok(());
392 }
393
394 self.authoritative_state
395 .update_auth_key(&self.authentication_password, self.auth_protocol)?;
396 if let Auth::AuthPriv {
397 cipher,
398 privacy_password,
399 } = &self.auth
400 {
401 self.authoritative_state.update_priv_key(
402 privacy_password,
403 self.auth_protocol,
404 *cipher,
405 self.key_extension_method.as_ref(),
406 )?;
407 }
408 Ok(())
409 }
410
411 pub fn engine_id(&self) -> &[u8] {
412 &self.authoritative_state.engine_id
413 }
414
415 pub fn engine_boots(&self) -> i64 {
416 self.authoritative_state.engine_boots
417 }
418
419 pub fn engine_time(&self) -> i64 {
420 self.authoritative_state.engine_time
421 }
422
423 pub fn username(&self) -> &[u8] {
424 &self.username
425 }
426
427 pub(crate) fn correct_authoritative_engine_time(&mut self) {
429 self.authoritative_state.correct_engine_time();
430 }
431
432 pub(crate) fn need_auth(&self) -> bool {
433 self.auth != Auth::NoAuthNoPriv
434 }
435
436 pub(crate) fn need_encrypt(&self) -> bool {
437 !self.authoritative_state.priv_key.is_empty()
438 }
439
440 pub(crate) fn need_init(&self) -> bool {
441 self.engine_id().is_empty()
442 }
443
444 fn encrypt_des(&self, data: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
445 let mut salt = [0; 8];
446 salt[..4].copy_from_slice(&u32::try_from(self.engine_boots())?.to_be_bytes());
447 #[cfg(feature = "crypto-openssl")]
448 openssl::rand::rand_bytes(&mut salt[4..])?;
449 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
450 {
451 use rand::Rng;
452 rand::rng().fill(&mut salt[4..]);
453 }
454
455 if data.is_empty() {
456 return Ok((vec![], salt.to_vec()));
457 }
458
459 if self.authoritative_state.priv_key.len() < 16 {
460 return Err(Error::AuthFailure(AuthErrorKind::KeyLengthMismatch));
461 }
462
463 let des_key = &self.authoritative_state.priv_key[..8];
464 let pre_iv = &self.authoritative_state.priv_key[8..16];
465
466 let mut iv = [0; 8];
467 for (i, (a, b)) in pre_iv.iter().zip(salt.iter()).enumerate() {
468 iv[i] = a ^ b;
469 }
470
471 #[cfg(feature = "crypto-openssl")]
472 {
473 let cipher = openssl::symm::Cipher::des_cbc();
474 let mut encrypted = vec![0; data.len() + cipher.block_size()];
475 let mut crypter = openssl::symm::Crypter::new(
476 cipher,
477 openssl::symm::Mode::Encrypt,
478 des_key,
479 Some(&iv),
480 )?;
481 let mut count = crypter.update(data, &mut encrypted)?;
482
483 if count < encrypted.len() {
484 count += crypter.finalize(&mut encrypted[count..])?;
485 }
486
487 encrypted.truncate(count);
488 Ok((encrypted, salt.to_vec()))
489 }
490 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
491 {
492 let mut encryptor = CbcEncryptor::<Des>::new_from_slices(des_key, &iv)
493 .map_err(|e| Error::Crypto(e.to_string()))?;
494
495 let len = data.len();
497 let pad_len = 8 - (len % 8);
498 let mut padded = Vec::with_capacity(len + pad_len);
499 padded.extend_from_slice(data);
500 padded.extend(std::iter::repeat_n(u8::try_from(pad_len)?, pad_len));
501
502 for chunk in padded.chunks_mut(8) {
503 let block = cipher::generic_array::GenericArray::from_mut_slice(chunk);
504 encryptor.encrypt_block_mut(block);
505 }
506
507 Ok((padded, salt.to_vec()))
508 }
509 }
510
511 fn encrypt_aes(&self, data: &[u8], key_len: usize) -> Result<(Vec<u8>, Vec<u8>)> {
512 let iv_len = 16;
513 let mut iv = Vec::with_capacity(iv_len);
514 iv.extend_from_slice(&u32::try_from(self.engine_boots())?.to_be_bytes());
515 iv.extend_from_slice(&u32::try_from(self.engine_time())?.to_be_bytes());
516 let salt_pos = iv.len();
517 iv.resize(iv_len, 0);
518
519 #[cfg(feature = "crypto-openssl")]
520 openssl::rand::rand_bytes(&mut iv[salt_pos..])?;
521 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
522 {
523 use rand::Rng;
524 rand::rng().fill(&mut iv[salt_pos..]);
525 }
526
527 if self.authoritative_state.priv_key.len() < key_len {
528 return Err(Error::AuthFailure(AuthErrorKind::KeyLengthMismatch));
529 }
530
531 #[cfg(feature = "crypto-openssl")]
532 {
533 let cipher = match key_len {
534 16 => openssl::symm::Cipher::aes_128_cfb128(),
535 24 => openssl::symm::Cipher::aes_192_cfb128(),
536 32 => openssl::symm::Cipher::aes_256_cfb128(),
537 _ => return Err(Error::Crypto("Invalid key len".into())),
538 };
539
540 let mut crypter = openssl::symm::Crypter::new(
541 cipher,
542 openssl::symm::Mode::Encrypt,
543 &self.authoritative_state.priv_key[..key_len],
544 Some(&iv),
545 )?;
546
547 let mut encrypted = vec![0; data.len() + 16];
548 let mut count = crypter.update(data, &mut encrypted)?;
549
550 if count < encrypted.len() {
551 count += crypter.finalize(&mut encrypted[count..])?;
552 }
553
554 encrypted.truncate(count);
555 Ok((encrypted, iv[salt_pos..].to_vec()))
556 }
557 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
558 {
559 let key = &self.authoritative_state.priv_key[..key_len];
560 let mut encrypted = data.to_vec();
561
562 match key_len {
563 16 => {
564 let encryptor = CfbEncryptor::<Aes128>::new_from_slices(key, &iv)
565 .map_err(|e| Error::Crypto(e.to_string()))?;
566 encryptor.encrypt(&mut encrypted);
567 }
568 24 => {
569 let encryptor = CfbEncryptor::<Aes192>::new_from_slices(key, &iv)
570 .map_err(|e| Error::Crypto(e.to_string()))?;
571 encryptor.encrypt(&mut encrypted);
572 }
573 32 => {
574 let encryptor = CfbEncryptor::<Aes256>::new_from_slices(key, &iv)
575 .map_err(|e| Error::Crypto(e.to_string()))?;
576 encryptor.encrypt(&mut encrypted);
577 }
578 _ => return Err(Error::Crypto("Invalid key len".into())),
579 }
580
581 Ok((encrypted, iv[salt_pos..].to_vec()))
582 }
583 }
584
585 pub(crate) fn encrypt(&self, data: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
587 let Auth::AuthPriv {
588 cipher: cipher_kind,
589 ..
590 } = &self.auth
591 else {
592 return Err(Error::AuthFailure(AuthErrorKind::SecurityNotProvided));
593 };
594
595 if self.engine_id().is_empty() {
596 return Err(Error::AuthFailure(AuthErrorKind::SecurityNotReady));
597 }
598
599 match cipher_kind {
600 Cipher::Des => self.encrypt_des(data),
601 Cipher::Aes128 => self.encrypt_aes(data, 16),
602 Cipher::Aes192 => self.encrypt_aes(data, 24),
603 Cipher::Aes256 => self.encrypt_aes(data, 32),
604 }
605 }
606
607 #[cfg(feature = "crypto-openssl")]
608 fn decrypt_data_to_plain_buf(
609 &mut self,
610 mut crypter: openssl::symm::Crypter,
611 block_size: usize,
612 encrypted: &[u8],
613 ) -> Result<()> {
614 self.plain_buf.resize(encrypted.len() + block_size, 0);
615 let mut count = crypter.update(encrypted, &mut self.plain_buf)?;
616
617 if count < self.plain_buf.len() {
618 count += crypter.finalize(&mut self.plain_buf[count..])?;
619 }
620
621 self.plain_buf.truncate(count);
622 Ok(())
623 }
624
625 fn decrypt_des(&mut self, encrypted: &[u8], priv_params: &[u8]) -> Result<()> {
626 if priv_params.len() != 8 {
627 return Err(Error::AuthFailure(AuthErrorKind::PrivLengthMismatch));
628 }
629
630 if self.authoritative_state.priv_key.len() < 16 {
631 return Err(Error::AuthFailure(AuthErrorKind::KeyLengthMismatch));
632 }
633
634 let des_key = &self.authoritative_state.priv_key[..8];
635 let pre_iv = &self.authoritative_state.priv_key[8..16];
636 let mut iv = [0; 8];
637
638 for (i, (a, b)) in pre_iv.iter().zip(priv_params.iter()).enumerate() {
639 iv[i] = a ^ b;
640 }
641
642 #[cfg(feature = "crypto-openssl")]
643 {
644 let cipher = openssl::symm::Cipher::des_cbc();
645 let block_size = 8;
646
647 if !encrypted.len().is_multiple_of(block_size) {
648 return Err(Error::AuthFailure(AuthErrorKind::PayloadLengthMismatch));
649 }
650
651 let crypter = openssl::symm::Crypter::new(
652 cipher,
653 openssl::symm::Mode::Decrypt,
654 des_key,
655 Some(&iv),
656 )?;
657 self.decrypt_data_to_plain_buf(crypter, block_size, encrypted)
658 }
659 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
660 {
661 let block_size = 8;
662 if !encrypted.len().is_multiple_of(block_size) {
663 return Err(Error::AuthFailure(AuthErrorKind::PayloadLengthMismatch));
664 }
665
666 let mut decryptor = CbcDecryptor::<Des>::new_from_slices(des_key, &iv)
667 .map_err(|e| Error::Crypto(e.to_string()))?;
668
669 self.plain_buf.clear();
670 self.plain_buf.extend_from_slice(encrypted);
671
672 for chunk in self.plain_buf.chunks_mut(8) {
673 let block = cipher::generic_array::GenericArray::from_mut_slice(chunk);
674 decryptor.decrypt_block_mut(block);
675 }
676
677 if let Some(&pad_len) = self.plain_buf.last() {
678 let pad_len = pad_len as usize;
679 if pad_len > 0 && pad_len <= block_size && pad_len <= self.plain_buf.len() {
680 let new_len = self.plain_buf.len() - pad_len;
681 self.plain_buf.truncate(new_len);
682 Ok(())
683 } else {
684 Err(Error::Crypto("Invalid padding".into()))
685 }
686 } else {
687 Ok(())
688 }
689 }
690 }
691
692 fn decrypt_aes(&mut self, encrypted: &[u8], priv_params: &[u8], key_len: usize) -> Result<()> {
693 let iv_len = 16;
694 let mut iv = Vec::with_capacity(iv_len);
695 iv.extend_from_slice(&u32::try_from(self.engine_boots())?.to_be_bytes());
696 iv.extend_from_slice(&u32::try_from(self.engine_time())?.to_be_bytes());
697 iv.extend_from_slice(priv_params);
698
699 if iv.len() != iv_len {
700 return Err(Error::AuthFailure(AuthErrorKind::PrivLengthMismatch));
701 }
702
703 if self.authoritative_state.priv_key.len() < key_len {
704 return Err(Error::AuthFailure(AuthErrorKind::KeyLengthMismatch));
705 }
706
707 #[cfg(feature = "crypto-openssl")]
708 {
709 let cipher = match key_len {
710 16 => openssl::symm::Cipher::aes_128_cfb128(),
711 24 => openssl::symm::Cipher::aes_192_cfb128(),
712 32 => openssl::symm::Cipher::aes_256_cfb128(),
713 _ => return Err(Error::Crypto("Invalid key len".into())),
714 };
715
716 let crypter = openssl::symm::Crypter::new(
717 cipher,
718 openssl::symm::Mode::Decrypt,
719 &self.authoritative_state.priv_key[..key_len],
720 Some(&iv),
721 )?;
722
723 self.decrypt_data_to_plain_buf(crypter, 16, encrypted)
724 }
725 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
726 {
727 let key = &self.authoritative_state.priv_key[..key_len];
728 self.plain_buf.clear();
729 self.plain_buf.extend_from_slice(encrypted);
730
731 match key_len {
732 16 => {
733 let decryptor = CfbDecryptor::<Aes128>::new_from_slices(key, &iv)
734 .map_err(|e| Error::Crypto(e.to_string()))?;
735 decryptor.decrypt(&mut self.plain_buf);
736 }
737 24 => {
738 let decryptor = CfbDecryptor::<Aes192>::new_from_slices(key, &iv)
739 .map_err(|e| Error::Crypto(e.to_string()))?;
740 decryptor.decrypt(&mut self.plain_buf);
741 }
742 32 => {
743 let decryptor = CfbDecryptor::<Aes256>::new_from_slices(key, &iv)
744 .map_err(|e| Error::Crypto(e.to_string()))?;
745 decryptor.decrypt(&mut self.plain_buf);
746 }
747 _ => return Err(Error::Crypto("Invalid key len".into())),
748 }
749 Ok(())
750 }
751 }
752
753 fn decrypt(&mut self, encrypted: &[u8], priv_params: &[u8]) -> Result<()> {
755 let Auth::AuthPriv {
756 cipher: cipher_kind,
757 ..
758 } = &self.auth
759 else {
760 return Err(Error::AuthFailure(AuthErrorKind::SecurityNotProvided));
761 };
762
763 match cipher_kind {
764 Cipher::Des => self.decrypt_des(encrypted, priv_params),
765 Cipher::Aes128 => self.decrypt_aes(encrypted, priv_params, 16),
766 Cipher::Aes192 => self.decrypt_aes(encrypted, priv_params, 24),
767 Cipher::Aes256 => self.decrypt_aes(encrypted, priv_params, 32),
768 }
769 }
770}
771
772#[derive(Debug, Clone, Eq, PartialEq)]
773pub enum Auth {
774 NoAuthNoPriv,
775 AuthNoPriv,
777 AuthPriv {
779 cipher: Cipher,
780 privacy_password: Vec<u8>,
781 },
782}
783
784#[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
785pub struct Hasher(Box<dyn DynDigest + Send + Sync>);
786
787#[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
788impl Hasher {
789 pub fn new(d: Box<dyn DynDigest + Send + Sync>) -> Result<Self> {
790 Ok(Self(d))
791 }
792
793 pub fn update(&mut self, data: &[u8]) -> Result<()> {
794 self.0.update(data);
795 Ok(())
796 }
797
798 pub fn finish(&mut self) -> Result<Vec<u8>> {
799 Ok(self.0.finalize_reset().to_vec())
800 }
801}
802
803#[derive(Debug, Copy, Clone, Eq, PartialEq)]
804pub enum AuthProtocol {
805 Md5,
806 Sha1,
807 Sha224,
808 Sha256,
809 Sha384,
810 Sha512,
811}
812
813impl AuthProtocol {
814 fn create_hasher(self) -> Result<Hasher> {
815 #[cfg(feature = "crypto-openssl")]
816 {
817 Hasher::new(self.digest()).map_err(Into::into)
818 }
819 #[cfg(all(feature = "crypto-rust", not(feature = "crypto-openssl")))]
820 {
821 let d: Box<dyn DynDigest + Send + Sync> = match self {
822 AuthProtocol::Md5 => Box::new(md5::Md5::new()),
823 AuthProtocol::Sha1 => Box::new(sha1::Sha1::new()),
824 AuthProtocol::Sha224 => Box::new(sha2::Sha224::new()),
825 AuthProtocol::Sha256 => Box::new(sha2::Sha256::new()),
826 AuthProtocol::Sha384 => Box::new(sha2::Sha384::new()),
827 AuthProtocol::Sha512 => Box::new(sha2::Sha512::new()),
828 };
829 Hasher::new(d)
830 }
831 }
832 #[cfg(feature = "crypto-openssl")]
833 fn digest(self) -> MessageDigest {
834 match self {
835 AuthProtocol::Md5 => MessageDigest::md5(),
836 AuthProtocol::Sha1 => MessageDigest::sha1(),
837 AuthProtocol::Sha224 => MessageDigest::sha224(),
838 AuthProtocol::Sha256 => MessageDigest::sha256(),
839 AuthProtocol::Sha384 => MessageDigest::sha384(),
840 AuthProtocol::Sha512 => MessageDigest::sha512(),
841 }
842 }
843
844 fn truncation_length(self) -> usize {
845 match self {
846 AuthProtocol::Md5 | AuthProtocol::Sha1 => 12,
847 AuthProtocol::Sha224 => 16,
848 AuthProtocol::Sha256 => 24,
849 AuthProtocol::Sha384 => 32,
850 AuthProtocol::Sha512 => 48,
851 }
852 }
853}
854
855#[derive(Debug, Copy, Clone, Eq, PartialEq)]
856pub enum Cipher {
857 Des,
858 Aes128,
859 Aes192,
860 Aes256,
861}
862
863impl Cipher {
864 pub fn priv_key_len(&self) -> usize {
865 match self {
866 Cipher::Des | Cipher::Aes128 => 16,
867 Cipher::Aes192 => 24,
868 Cipher::Aes256 => 32,
869 }
870 }
871
872 pub fn priv_key_needs_extension(&self, auth_protocol: &AuthProtocol) -> bool {
885 #[allow(clippy::match_like_matches_macro, clippy::unnested_or_patterns)]
886 match (auth_protocol, self) {
887 (AuthProtocol::Md5, Cipher::Aes192)
888 | (AuthProtocol::Md5, Cipher::Aes256)
889 | (AuthProtocol::Sha1, Cipher::Aes192)
890 | (AuthProtocol::Sha1, Cipher::Aes256)
891 | (AuthProtocol::Sha224, Cipher::Aes256) => true,
892 _ => false,
893 }
894 }
895}
896
897impl<'a> Pdu<'a> {
898 #[allow(clippy::too_many_lines)]
899 pub(crate) fn parse_v3(
900 bytes: &'a [u8],
901 mut rdr: AsnReader<'a>,
902 security: &'a mut Security,
903 ) -> Result<Pdu<'a>> {
904 let truncation_len = security.auth_protocol.truncation_length();
905 let global_data_seq = rdr.read_raw(asn1::TYPE_SEQUENCE)?;
906 let mut global_data_rdr = AsnReader::from_bytes(global_data_seq);
907 let msg_id = global_data_rdr.read_asn_integer()?;
908 let max_size = global_data_rdr.read_asn_integer()?;
909
910 if max_size < 0 || max_size > i64::try_from(BUFFER_SIZE).unwrap() {
911 return Err(Error::BufferOverflow);
912 }
913
914 let flags = global_data_rdr
915 .read_asn_octetstring()?
916 .first()
917 .copied()
918 .unwrap_or_default();
919
920 let security_model = global_data_rdr.read_asn_integer()?;
921 if security_model != 3 {
922 return Err(Error::AuthFailure(AuthErrorKind::UnsupportedUSM));
923 }
924
925 let security_params = rdr.read_asn_octetstring()?;
926 let security_seq = AsnReader::from_bytes(security_params).read_raw(asn1::TYPE_SEQUENCE)?;
927 let mut security_rdr = AsnReader::from_bytes(security_seq);
928 let engine_id = security_rdr.read_asn_octetstring()?;
929 let engine_boots = security_rdr.read_asn_integer()?;
930 let engine_time = security_rdr.read_asn_integer()?;
931
932 let username = security_rdr.read_asn_octetstring()?;
933 let auth_params = security_rdr.read_asn_octetstring().map(<[u8]>::to_vec)?;
934 let auth_params_pos =
935 bytes.len() - rdr.bytes_left() - auth_params.len() - security_rdr.bytes_left();
936 let priv_params = security_rdr.read_asn_octetstring()?;
937
938 let mut is_discovery = false;
948
949 let mut prev_engine_time = security.engine_time();
950
951 if flags & V3_MSG_FLAGS_AUTH == 0 {
952 if security.authoritative_state.engine_id.is_empty() {
955 security.authoritative_state.engine_id = engine_id.to_vec();
956 security.update_key()?;
957 is_discovery = true;
958 } else if engine_id != security.authoritative_state.engine_id && !engine_id.is_empty() {
959 return Err(Error::AuthFailure(AuthErrorKind::EngineIdMismatch));
961 }
962
963 if security.authoritative_state.engine_boots < engine_boots {
966 is_discovery = true;
967 prev_engine_time = engine_time;
968 security
969 .authoritative_state
970 .update_authoritative(engine_boots, engine_time);
971 }
972
973 if is_discovery {
975 return Err(Error::AuthUpdated);
976 }
977
978 if security.need_auth() {
980 return Err(Error::AuthFailure(AuthErrorKind::NotAuthenticated));
981 }
982 } else {
983 if security.authoritative_state.engine_boots == 0 && engine_boots == 0 {
985 return Err(Error::AuthFailure(AuthErrorKind::EngineBootsNotProvided));
986 }
987
988 if security.authoritative_state.engine_boots < engine_boots {
989 is_discovery = true;
990 prev_engine_time = engine_time;
991 security
992 .authoritative_state
993 .update_authoritative(engine_boots, engine_time);
994 } else {
995 security
996 .authoritative_state
997 .update_authoritative_engine_time(engine_time);
998 }
999
1000 if username != security.username {
1001 return Err(Error::AuthFailure(AuthErrorKind::UsernameMismatch));
1002 }
1003
1004 if engine_id.is_empty() {
1005 return Err(Error::AuthFailure(AuthErrorKind::NotAuthenticated));
1006 }
1007
1008 if security.authoritative_state.engine_id.is_empty() {
1009 security.authoritative_state.engine_id = engine_id.to_vec();
1010 security.update_key()?;
1011 } else if engine_id != security.authoritative_state.engine_id {
1012 return Err(Error::AuthFailure(AuthErrorKind::EngineIdMismatch));
1013 }
1014
1015 if auth_params.len() != truncation_len
1016 || auth_params_pos + auth_params.len() > bytes.len()
1017 {
1018 return Err(Error::ValueOutOfRange);
1019 }
1020
1021 unsafe {
1022 let auth_params_ptr = bytes.as_ptr().add(auth_params_pos).cast_mut();
1023 std::hint::black_box(|| {
1025 std::ptr::write_bytes(auth_params_ptr, 0, auth_params.len());
1026 })();
1027 }
1028
1029 if security.need_auth() {
1030 let hmac = security.calculate_hmac(bytes)?;
1031
1032 if hmac.len() < truncation_len || hmac[..truncation_len] != auth_params {
1033 return Err(Error::AuthFailure(AuthErrorKind::SignatureMismatch));
1034 }
1035 }
1036 }
1037
1038 let scoped_pdu_seq = if flags & V3_MSG_FLAGS_PRIVACY == 0 {
1039 if security.need_encrypt() && !is_discovery {
1040 return Err(Error::AuthFailure(AuthErrorKind::ReplyNotEncrypted));
1041 }
1042
1043 rdr.read_raw(asn1::TYPE_SEQUENCE)?
1044 } else {
1045 let encrypted_pdu = rdr.read_asn_octetstring()?;
1046 security.decrypt(encrypted_pdu, priv_params)?;
1047 let mut rdr = AsnReader::from_bytes(&security.plain_buf);
1048 rdr.read_raw(asn1::TYPE_SEQUENCE)?
1049 };
1050
1051 let mut scoped_pdu_rdr = AsnReader::from_bytes(scoped_pdu_seq);
1052
1053 let _context_engine_id = scoped_pdu_rdr.read_asn_octetstring()?;
1054
1055 let _context_name = scoped_pdu_rdr.read_asn_octetstring()?;
1056
1057 let ident = scoped_pdu_rdr.peek_byte()?;
1058
1059 let message_type = MessageType::from_ident(ident)?;
1060
1061 if message_type == MessageType::Trap {
1062 is_discovery = false;
1063 } else {
1064 if security.engine_boots() > engine_boots {
1065 return Err(Error::AuthFailure(AuthErrorKind::EngineBootsMismatch));
1066 }
1067 if security.engine_boots() == engine_boots
1068 && (engine_time - prev_engine_time).abs() > ENGINE_TIME_WINDOW
1069 {
1070 return Err(Error::AuthFailure(AuthErrorKind::EngineTimeMismatch));
1071 }
1072 }
1073
1074 let mut response_pdu = AsnReader::from_bytes(scoped_pdu_rdr.read_raw(ident)?);
1075
1076 let req_id: i32 = i32::try_from(response_pdu.read_asn_integer()?)?;
1077
1078 let error_status: u32 =
1079 u32::try_from(response_pdu.read_asn_integer()?).map_err(|_| Error::ValueOutOfRange)?;
1080
1081 let error_index: u32 = u32::try_from(response_pdu.read_asn_integer()?)?;
1082
1083 let varbind_bytes = response_pdu.read_raw(asn1::TYPE_SEQUENCE)?;
1084 let varbinds = Varbinds::from_bytes(varbind_bytes);
1085
1086 if is_discovery {
1087 return Err(Error::AuthUpdated);
1088 }
1089
1090 Ok(Pdu {
1091 version: Version::V3 as i64,
1092 community: username,
1093 message_type,
1094 req_id,
1095 error_status,
1096 error_index,
1097 varbinds,
1098 v1_trap_info: None,
1099 v3_msg_id: i32::try_from(msg_id).map_err(|_| Error::ValueOutOfRange)?,
1100 })
1101 }
1102}
1103
1104pub(crate) fn build_init(req_id: i32, buf: &mut Buf) {
1105 buf.reset();
1106 let mut sec_buf = Buf::default();
1107 sec_buf.push_sequence(|sec| {
1108 sec.push_octet_string(&[]); sec.push_octet_string(&[]); sec.push_octet_string(&[]); sec.push_integer(0); sec.push_integer(0); sec.push_octet_string(&[]); });
1115 buf.push_sequence(|message| {
1116 message.push_sequence(|pdu| {
1117 pdu.push_constructed(snmp::MSG_GET, |req| {
1118 req.push_sequence(|_varbinds| {});
1119 req.push_integer(0); req.push_integer(0); req.push_integer(req_id.into());
1122 });
1123 pdu.push_octet_string(&[]);
1124 pdu.push_octet_string(&[]);
1125 });
1126 message.push_octet_string(&sec_buf);
1127 message.push_sequence(|global| {
1128 global.push_integer(3); global.push_octet_string(&[V3_MSG_FLAGS_REPORTABLE]); global.push_integer(BUFFER_SIZE.try_into().unwrap()); global.push_integer(req_id.into()); });
1133 message.push_integer(Version::V3 as i64);
1134 });
1135}
1136
1137pub(crate) fn build(
1138 ident: u8,
1139 req_id: i32,
1140 values: &[(&Oid, Value)],
1141 non_repeaters: u32,
1142 max_repetitions: u32,
1143 buf: &mut Buf,
1144 security: Option<&Security>,
1145) -> Result<()> {
1146 let security = security.ok_or(Error::AuthFailure(AuthErrorKind::SecurityNotProvided))?;
1147 let truncation_len = security.auth_protocol.truncation_length();
1148 buf.reset();
1149 let mut sec_buf_seq = Buf::default();
1150 sec_buf_seq.reset();
1151 let mut auth_pos = 0;
1152 let mut sec_buf_len = 0;
1153 let mut priv_params: Vec<u8> = Vec::new();
1154 let mut inner_len = 0;
1155 let mut flags = V3_MSG_FLAGS_REPORTABLE;
1156
1157 if security.need_auth() {
1158 flags |= V3_MSG_FLAGS_AUTH;
1159 }
1160
1161 let encrypted = if security.need_encrypt() {
1162 flags |= V3_MSG_FLAGS_PRIVACY;
1163 let mut pdu_buf = Buf::default();
1164 pdu_buf.push_sequence(|buf| {
1165 pdu::build_inner(req_id, ident, values, max_repetitions, non_repeaters, buf);
1166 buf.push_octet_string(&[]);
1167 buf.push_octet_string(security.engine_id());
1168 });
1169 let (encrypted, salt) = security.encrypt(&pdu_buf)?;
1170 priv_params.extend_from_slice(&salt);
1171 Some(encrypted)
1172 } else {
1173 None
1174 };
1175
1176 buf.push_sequence(|buf| {
1177 if let Some(ref encrypted) = encrypted {
1178 buf.push_octet_string(encrypted);
1179 } else {
1180 buf.push_sequence(|buf| {
1181 pdu::build_inner(req_id, ident, values, max_repetitions, non_repeaters, buf);
1182 buf.push_octet_string(&[]);
1183 buf.push_octet_string(security.engine_id());
1184 });
1185 }
1186 let l0 = buf.len();
1187 sec_buf_seq.push_sequence(|buf| {
1188 buf.push_octet_string(&priv_params); let l0 = buf.len() - priv_params.len();
1190 buf.push_octet_string(&vec![0u8; truncation_len]); let l1 = buf.len() - l0;
1192 buf.push_octet_string(security.username()); buf.push_integer(security.engine_time()); buf.push_integer(security.engine_boots()); buf.push_octet_string(security.engine_id()); auth_pos = buf.len() - l1;
1197 sec_buf_len = buf.len();
1198 });
1199 buf.push_octet_string(&sec_buf_seq);
1200 buf.push_sequence(|buf| {
1201 buf.push_integer(3); buf.push_octet_string(&[flags]); buf.push_integer(BUFFER_SIZE.try_into().unwrap()); buf.push_integer(req_id.into()); });
1206 buf.push_integer(3); auth_pos = buf.len() - l0 - (sec_buf_len - auth_pos);
1208 inner_len = buf.len();
1209 });
1210
1211 auth_pos += buf.len() - inner_len;
1212 if (auth_pos + truncation_len) > buf.len() {
1213 return Err(Error::ValueOutOfRange);
1214 }
1215
1216 if security.need_auth() {
1217 let hmac = security.calculate_hmac(buf)?;
1218 buf[auth_pos..auth_pos + truncation_len].copy_from_slice(&hmac[..truncation_len]);
1219 }
1220
1221 Ok(())
1222}