1use bytes::Bytes;
23
24use crate::ber::{Decoder, EncodeBuf};
25use crate::error::internal::DecodeErrorKind;
26use crate::error::{Error, Result, UNKNOWN_TARGET};
27use crate::pdu::Pdu;
28
29const MSG_MAX_SIZE_MINIMUM: i32 = 484;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(i32)]
35pub enum SecurityModel {
36 Usm = 3,
38}
39
40impl SecurityModel {
41 #[must_use]
43 pub fn from_i32(value: i32) -> Option<Self> {
44 match value {
45 3 => Some(Self::Usm),
46 _ => None,
47 }
48 }
49
50 #[must_use]
52 pub fn as_i32(self) -> i32 {
53 self as i32
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
62pub enum SecurityLevel {
63 NoAuthNoPriv,
65 AuthNoPriv,
67 AuthPriv,
69}
70
71impl SecurityLevel {
72 #[must_use]
74 pub fn from_flags(flags: u8) -> Option<Self> {
75 let auth = flags & 0x01 != 0;
76 let priv_ = flags & 0x02 != 0;
77
78 match (auth, priv_) {
79 (false, false) => Some(Self::NoAuthNoPriv),
80 (true, false) => Some(Self::AuthNoPriv),
81 (true, true) => Some(Self::AuthPriv),
82 (false, true) => None, }
84 }
85
86 #[must_use]
88 pub fn to_flags(self) -> u8 {
89 match self {
90 Self::NoAuthNoPriv => 0x00,
91 Self::AuthNoPriv => 0x01,
92 Self::AuthPriv => 0x03,
93 }
94 }
95
96 #[must_use]
98 pub fn requires_auth(self) -> bool {
99 matches!(self, Self::AuthNoPriv | Self::AuthPriv)
100 }
101
102 #[must_use]
104 pub fn requires_priv(self) -> bool {
105 matches!(self, Self::AuthPriv)
106 }
107}
108
109impl TryFrom<u8> for SecurityLevel {
110 type Error = u8;
111
112 fn try_from(flags: u8) -> std::result::Result<Self, u8> {
113 Self::from_flags(flags).ok_or(flags)
114 }
115}
116
117impl From<SecurityLevel> for u8 {
118 fn from(level: SecurityLevel) -> u8 {
119 level.to_flags()
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub struct MsgFlags {
126 pub security_level: SecurityLevel,
128 pub reportable: bool,
130}
131
132impl MsgFlags {
133 #[must_use]
135 pub fn new(security_level: SecurityLevel, reportable: bool) -> Self {
136 Self {
137 security_level,
138 reportable,
139 }
140 }
141
142 pub fn from_byte(byte: u8) -> Result<Self> {
144 let security_level = SecurityLevel::from_flags(byte).ok_or_else(|| {
145 tracing::debug!(target: "async_snmp::v3", { byte, kind = %DecodeErrorKind::InvalidMsgFlags }, "decode error");
146 Error::MalformedResponse {
147 target: UNKNOWN_TARGET,
148 }
149 .boxed()
150 })?;
151 let reportable = byte & 0x04 != 0;
152 Ok(Self {
153 security_level,
154 reportable,
155 })
156 }
157
158 #[must_use]
160 pub fn to_byte(self) -> u8 {
161 let mut flags = self.security_level.to_flags();
162 if self.reportable {
163 flags |= 0x04;
164 }
165 flags
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct MsgGlobalData {
172 pub msg_id: i32,
174 pub msg_max_size: i32,
176 pub msg_flags: MsgFlags,
178 pub msg_security_model: SecurityModel,
180}
181
182impl MsgGlobalData {
183 #[must_use]
185 pub fn new(msg_id: i32, msg_max_size: i32, msg_flags: MsgFlags) -> Self {
186 Self {
187 msg_id,
188 msg_max_size,
189 msg_flags,
190 msg_security_model: SecurityModel::Usm,
191 }
192 }
193
194 pub fn encode(&self, buf: &mut EncodeBuf) {
196 buf.push_sequence(|buf| {
197 buf.push_integer(self.msg_security_model.as_i32());
198 buf.push_octet_string(&[self.msg_flags.to_byte()]);
200 buf.push_integer(self.msg_max_size);
201 buf.push_integer(self.msg_id);
202 });
203 }
204
205 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
212 let mut seq = decoder.read_sequence()?;
213
214 let msg_id = seq.read_bounded_integer(0, i32::MAX)?;
217 let msg_max_size = seq.read_bounded_integer(MSG_MAX_SIZE_MINIMUM, i32::MAX)?;
218
219 let flags_bytes = seq.read_octet_string()?;
220 if flags_bytes.len() != 1 {
221 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), expected = 1, actual = flags_bytes.len() }, "invalid msgFlags length");
222 return Err(Error::MalformedResponse {
223 target: UNKNOWN_TARGET,
224 }
225 .boxed());
226 }
227 let msg_flags = MsgFlags::from_byte(flags_bytes[0])?;
228
229 let msg_security_model_raw = seq.read_bounded_integer(1, i32::MAX)?;
230 let msg_security_model =
232 SecurityModel::from_i32(msg_security_model_raw).ok_or_else(|| {
233 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), model = msg_security_model_raw, kind = %DecodeErrorKind::UnknownSecurityModel(msg_security_model_raw) }, "decode error");
234 Error::MalformedResponse {
235 target: UNKNOWN_TARGET,
236 }
237 .boxed()
238 })?;
239
240 if !seq.is_empty() {
241 return Err(Error::MalformedResponse {
242 target: UNKNOWN_TARGET,
243 }
244 .boxed());
245 }
246
247 Ok(Self {
248 msg_id,
249 msg_max_size,
250 msg_flags,
251 msg_security_model,
252 })
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct ScopedPdu {
259 pub context_engine_id: Bytes,
261 pub context_name: Bytes,
263 pub pdu: Pdu,
265}
266
267impl ScopedPdu {
268 pub fn new(
270 context_engine_id: impl Into<Bytes>,
271 context_name: impl Into<Bytes>,
272 pdu: Pdu,
273 ) -> Self {
274 Self {
275 context_engine_id: context_engine_id.into(),
276 context_name: context_name.into(),
277 pdu,
278 }
279 }
280
281 #[must_use]
283 pub fn with_empty_context(pdu: Pdu) -> Self {
284 Self {
285 context_engine_id: Bytes::new(),
286 context_name: Bytes::new(),
287 pdu,
288 }
289 }
290
291 pub fn encode(&self, buf: &mut EncodeBuf) {
293 buf.push_sequence(|buf| {
294 self.pdu.encode(buf);
295 buf.push_octet_string(&self.context_name);
296 buf.push_octet_string(&self.context_engine_id);
297 });
298 }
299
300 pub fn encode_to_bytes(&self) -> Bytes {
302 let mut buf = EncodeBuf::new();
303 self.encode(&mut buf);
304 buf.finish()
305 }
306
307 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
309 let mut seq = decoder.read_sequence()?;
310
311 let context_engine_id = seq.read_octet_string()?;
312 let context_name = seq.read_octet_string()?;
313 let pdu = Pdu::decode(&mut seq)?;
314
315 if !seq.is_empty() {
316 return Err(Error::MalformedResponse {
317 target: UNKNOWN_TARGET,
318 }
319 .boxed());
320 }
321
322 Ok(Self {
323 context_engine_id,
324 context_name,
325 pdu,
326 })
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct V3Message {
333 pub global_data: MsgGlobalData,
335 pub security_params: Bytes,
337 pub data: V3MessageData,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq)]
343pub enum V3MessageData {
344 Plaintext(ScopedPdu),
346 Encrypted(Bytes),
348}
349
350impl V3Message {
351 pub fn new(global_data: MsgGlobalData, security_params: Bytes, scoped_pdu: ScopedPdu) -> Self {
353 Self {
354 global_data,
355 security_params,
356 data: V3MessageData::Plaintext(scoped_pdu),
357 }
358 }
359
360 pub fn new_encrypted(
362 global_data: MsgGlobalData,
363 security_params: Bytes,
364 encrypted: Bytes,
365 ) -> Self {
366 Self {
367 global_data,
368 security_params,
369 data: V3MessageData::Encrypted(encrypted),
370 }
371 }
372
373 pub fn scoped_pdu(&self) -> Option<&ScopedPdu> {
375 match &self.data {
376 V3MessageData::Plaintext(pdu) => Some(pdu),
377 V3MessageData::Encrypted(_) => None,
378 }
379 }
380
381 pub fn into_scoped_pdu(self) -> Option<ScopedPdu> {
383 match self.data {
384 V3MessageData::Plaintext(pdu) => Some(pdu),
385 V3MessageData::Encrypted(_) => None,
386 }
387 }
388
389 pub fn pdu(&self) -> Option<&Pdu> {
391 self.scoped_pdu().map(|s| &s.pdu)
392 }
393
394 pub fn into_pdu(self) -> Option<Pdu> {
396 self.into_scoped_pdu().map(|s| s.pdu)
397 }
398
399 pub fn msg_id(&self) -> i32 {
401 self.global_data.msg_id
402 }
403
404 pub fn security_level(&self) -> SecurityLevel {
406 self.global_data.msg_flags.security_level
407 }
408
409 pub fn encode(&self) -> Bytes {
416 let mut buf = EncodeBuf::new();
417
418 buf.push_sequence(|buf| {
419 match &self.data {
421 V3MessageData::Plaintext(scoped_pdu) => {
422 scoped_pdu.encode(buf);
423 }
424 V3MessageData::Encrypted(ciphertext) => {
425 buf.push_octet_string(ciphertext);
426 }
427 }
428
429 buf.push_octet_string(&self.security_params);
431
432 self.global_data.encode(buf);
434
435 buf.push_integer(3);
437 });
438
439 buf.finish()
440 }
441
442 pub fn decode(data: Bytes) -> Result<Self> {
450 let mut decoder = Decoder::new(data);
451 let mut seq = decoder.read_sequence()?;
452
453 let version = seq.read_bounded_integer(0, i32::MAX)?;
455 if version != 3 {
456 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), version, kind = %DecodeErrorKind::UnknownVersion(version) }, "decode error");
457 return Err(Error::MalformedResponse {
458 target: UNKNOWN_TARGET,
459 }
460 .boxed());
461 }
462
463 Self::decode_from_sequence(&mut seq)
464 }
465
466 pub(crate) fn decode_from_sequence(seq: &mut Decoder) -> Result<Self> {
468 let global_data = MsgGlobalData::decode(seq)?;
470
471 let security_params = seq.read_octet_string()?;
473
474 let data = if global_data.msg_flags.security_level.requires_priv() {
476 let encrypted = seq.read_octet_string()?;
478 V3MessageData::Encrypted(encrypted)
479 } else {
480 let scoped_pdu = ScopedPdu::decode(seq)?;
482 V3MessageData::Plaintext(scoped_pdu)
483 };
484
485 Ok(Self {
486 global_data,
487 security_params,
488 data,
489 })
490 }
491
492 #[must_use]
499 pub fn discovery_request(msg_id: i32) -> Self {
500 let global_data = MsgGlobalData::new(
501 msg_id,
502 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
504 );
505
506 let security_params = crate::v3::UsmSecurityParams::empty().encode();
508
509 let pdu = Pdu::get_request(0, &[]);
511 let scoped_pdu = ScopedPdu::with_empty_context(pdu);
512
513 Self::new(global_data, security_params, scoped_pdu)
514 }
515}
516
517#[derive(Debug, Clone, PartialEq, Eq)]
525pub struct RawV3Message {
526 pub(crate) global_data: MsgGlobalData,
528 pub(crate) security_params: Bytes,
530 pub(crate) msg_data: RawMsgData,
532}
533
534#[derive(Debug, Clone, PartialEq, Eq)]
536pub enum RawMsgData {
537 Plaintext(Bytes),
539 Encrypted(Bytes),
541}
542
543impl RawV3Message {
544 pub fn decode(data: Bytes) -> Result<Self> {
550 let mut decoder = Decoder::new(data);
551 let mut seq = decoder.read_sequence()?;
552
553 let version = seq.read_bounded_integer(0, i32::MAX)?;
554 if version != 3 {
555 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), version, kind = %DecodeErrorKind::UnknownVersion(version) }, "decode error");
556 return Err(Error::MalformedResponse {
557 target: UNKNOWN_TARGET,
558 }
559 .boxed());
560 }
561
562 let global_data = MsgGlobalData::decode(&mut seq)?;
563 let security_params = seq.read_octet_string()?;
564
565 let msg_data = if global_data.msg_flags.security_level.requires_priv() {
566 RawMsgData::Encrypted(seq.read_octet_string()?)
567 } else {
568 let start = seq.offset();
570 seq.skip_tlv()?;
571 RawMsgData::Plaintext(seq.as_bytes().slice(start..seq.offset()))
572 };
573
574 if !seq.is_empty() || !decoder.is_empty() {
575 return Err(Error::MalformedResponse {
576 target: UNKNOWN_TARGET,
577 }
578 .boxed());
579 }
580
581 Ok(Self {
582 global_data,
583 security_params,
584 msg_data,
585 })
586 }
587
588 pub fn global_data(&self) -> &MsgGlobalData {
590 &self.global_data
591 }
592
593 pub fn security_params(&self) -> &Bytes {
595 &self.security_params
596 }
597
598 pub fn msg_data(&self) -> &RawMsgData {
600 &self.msg_data
601 }
602
603 pub fn msg_id(&self) -> i32 {
605 self.global_data.msg_id
606 }
607
608 pub fn security_level(&self) -> SecurityLevel {
610 self.global_data.msg_flags.security_level
611 }
612}
613
614#[derive(Debug, Clone, Copy, PartialEq, Eq)]
617pub(crate) enum MpdFailure {
618 InvalidMsgFlags,
620 UnknownSecurityModel,
622}
623
624pub(crate) fn classify_mpd_failure(data: Bytes) -> Option<MpdFailure> {
630 let mut decoder = Decoder::new(data);
631 let mut seq = decoder.read_sequence().ok()?;
632 if seq.read_bounded_integer(0, i32::MAX).ok()? != 3 {
633 return None;
634 }
635 let mut global = seq.read_sequence().ok()?;
636 global.read_bounded_integer(0, i32::MAX).ok()?;
643 global
644 .read_bounded_integer(MSG_MAX_SIZE_MINIMUM, i32::MAX)
645 .ok()?;
646 let flags_bytes = global.read_octet_string().ok()?;
647 if flags_bytes.len() != 1 {
648 return None;
649 }
650 if MsgFlags::from_byte(flags_bytes[0]).is_err() {
651 return Some(MpdFailure::InvalidMsgFlags);
652 }
653 let model = global.read_bounded_integer(1, i32::MAX).ok()?;
654 if SecurityModel::from_i32(model).is_none() {
655 return Some(MpdFailure::UnknownSecurityModel);
656 }
657 None
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663 use crate::oid;
664
665 fn push_integer_content(buf: &mut EncodeBuf, content: &[u8]) {
666 buf.push_bytes(content);
667 buf.push_length(content.len());
668 buf.push_tag(crate::ber::tag::universal::INTEGER);
669 }
670
671 fn global_data_with_integer_contents(
672 msg_id: &[u8],
673 msg_max_size: &[u8],
674 security_model: &[u8],
675 ) -> Bytes {
676 let mut buf = EncodeBuf::new();
677 buf.push_sequence(|buf| {
678 push_integer_content(buf, security_model);
679 buf.push_octet_string(&[0x04]);
680 push_integer_content(buf, msg_max_size);
681 push_integer_content(buf, msg_id);
682 });
683 buf.finish()
684 }
685
686 #[test]
687 fn test_security_level_flags() {
688 assert_eq!(SecurityLevel::NoAuthNoPriv.to_flags(), 0x00);
689 assert_eq!(SecurityLevel::AuthNoPriv.to_flags(), 0x01);
690 assert_eq!(SecurityLevel::AuthPriv.to_flags(), 0x03);
691
692 assert_eq!(
693 SecurityLevel::from_flags(0x00),
694 Some(SecurityLevel::NoAuthNoPriv)
695 );
696 assert_eq!(
697 SecurityLevel::from_flags(0x01),
698 Some(SecurityLevel::AuthNoPriv)
699 );
700 assert_eq!(
701 SecurityLevel::from_flags(0x03),
702 Some(SecurityLevel::AuthPriv)
703 );
704 assert_eq!(SecurityLevel::from_flags(0x02), None); }
706
707 #[test]
708 fn security_level_try_from_u8() {
709 assert_eq!(
710 SecurityLevel::try_from(0x00),
711 Ok(SecurityLevel::NoAuthNoPriv)
712 );
713 assert_eq!(SecurityLevel::try_from(0x01), Ok(SecurityLevel::AuthNoPriv));
714 assert_eq!(SecurityLevel::try_from(0x03), Ok(SecurityLevel::AuthPriv));
715 assert_eq!(SecurityLevel::try_from(0x02), Err(0x02));
716 }
717
718 #[test]
719 fn security_level_into_u8() {
720 assert_eq!(u8::from(SecurityLevel::NoAuthNoPriv), 0x00);
721 assert_eq!(u8::from(SecurityLevel::AuthNoPriv), 0x01);
722 assert_eq!(u8::from(SecurityLevel::AuthPriv), 0x03);
723 }
724
725 #[test]
726 fn test_msg_flags_roundtrip() {
727 let flags = MsgFlags::new(SecurityLevel::AuthPriv, true);
728 let byte = flags.to_byte();
729 assert_eq!(byte, 0x07); let decoded = MsgFlags::from_byte(byte).unwrap();
732 assert_eq!(decoded.security_level, SecurityLevel::AuthPriv);
733 assert!(decoded.reportable);
734 }
735
736 #[test]
741 fn classify_mpd_failure_mirrors_decode_fail_fast() {
742 use crate::pdu::Pdu;
743 use crate::v3::UsmSecurityParams;
744
745 let global = MsgGlobalData::new(1, 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
748 let usm =
749 UsmSecurityParams::new(Bytes::from_static(b"eid"), 0, 0, Bytes::from_static(b"u"));
750 let scoped = ScopedPdu::new(
751 Bytes::from_static(b"eid"),
752 Bytes::new(),
753 Pdu::get_request(42, &[]),
754 );
755 let base = V3Message::new(global, usm.encode(), scoped).encode();
756
757 let patch = |data: &Bytes, pattern: &[u8], off: usize, val: u8| -> Bytes {
758 let mut b = data.to_vec();
759 let pos = b
760 .windows(pattern.len())
761 .position(|w| w == pattern)
762 .expect("pattern not found");
763 b[pos + off] = val;
764 Bytes::from(b)
765 };
766
767 let model_pattern = [0x04, 0x01, 0x04, 0x02, 0x01, 0x03];
769 let unknown_model = patch(&base, &model_pattern, 5, 99);
770 assert_eq!(
771 classify_mpd_failure(unknown_model),
772 Some(MpdFailure::UnknownSecurityModel)
773 );
774
775 let neg_id = patch(&base, &[0x02, 0x01, 0x01, 0x02, 0x03], 2, 0x81);
778 let neg_id_unknown_model = patch(&neg_id, &model_pattern, 5, 99);
779 assert_eq!(classify_mpd_failure(neg_id_unknown_model), None);
780 }
781
782 #[test]
787 fn raw_decode_does_not_parse_plaintext_scoped_pdu() {
788 let mut buf = EncodeBuf::new();
789 buf.push_sequence(|buf| {
790 buf.push_sequence(|buf| {
792 buf.push_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]);
793 });
794 buf.push_octet_string(b"usm-params");
795 MsgGlobalData::new(7, 65507, MsgFlags::new(SecurityLevel::AuthNoPriv, false))
796 .encode(buf);
797 buf.push_integer(3);
798 });
799 let encoded = buf.finish();
800
801 assert!(
802 V3Message::decode(encoded.clone()).is_err(),
803 "eager decode must reject the malformed scoped PDU"
804 );
805
806 let raw = RawV3Message::decode(encoded).unwrap();
807 assert_eq!(raw.msg_id(), 7);
808 assert_eq!(raw.security_level(), SecurityLevel::AuthNoPriv);
809 assert_eq!(raw.security_params.as_ref(), b"usm-params");
810 let RawMsgData::Plaintext(scoped) = raw.msg_data else {
811 panic!("expected plaintext msgData");
812 };
813 assert_eq!(scoped.as_ref(), &[0x30, 0x04, 0xDE, 0xAD, 0xBE, 0xEF]);
814 }
815
816 #[test]
817 fn v3_decoders_reject_over_width_version_alias() {
818 let global = MsgGlobalData::new(7, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
819 let scoped = ScopedPdu::with_empty_context(Pdu::get_request(42, &[]));
820 let security_params = crate::v3::UsmSecurityParams::empty().encode();
821
822 let mut buf = EncodeBuf::new();
823 buf.push_sequence(|buf| {
824 scoped.encode(buf);
825 buf.push_octet_string(&security_params);
826 global.encode(buf);
827 push_integer_content(buf, &[0x01, 0x00, 0x00, 0x00, 0x03]);
829 });
830 let encoded = buf.finish();
831
832 assert!(V3Message::decode(encoded.clone()).is_err());
833 assert!(RawV3Message::decode(encoded.clone()).is_err());
834 assert!(crate::message::Message::decode(encoded).is_err());
835 }
836
837 #[test]
840 fn raw_plaintext_bytes_reparse_as_scoped_pdu() {
841 let global = MsgGlobalData::new(9, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
842 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
843 let scoped = ScopedPdu::new(b"eng".as_slice(), b"ctx".as_slice(), pdu);
844 let msg = V3Message::new(global, Bytes::from_static(b"usm"), scoped);
845
846 let raw = RawV3Message::decode(msg.encode()).unwrap();
847 let RawMsgData::Plaintext(bytes) = raw.msg_data else {
848 panic!("expected plaintext msgData");
849 };
850 let mut decoder = Decoder::new(bytes);
851 let reparsed = ScopedPdu::decode(&mut decoder).unwrap();
852 assert_eq!(reparsed.context_engine_id.as_ref(), b"eng");
853 assert_eq!(reparsed.context_name.as_ref(), b"ctx");
854 assert_eq!(reparsed.pdu.request_id, 42);
855 }
856
857 #[test]
859 fn raw_decode_keeps_ciphertext() {
860 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
861 let msg = V3Message::new_encrypted(
862 global,
863 Bytes::from_static(b"usm-params"),
864 Bytes::from_static(b"encrypted-data"),
865 );
866
867 let raw = RawV3Message::decode(msg.encode()).unwrap();
868 assert_eq!(raw.security_level(), SecurityLevel::AuthPriv);
869 let RawMsgData::Encrypted(ciphertext) = raw.msg_data else {
870 panic!("expected encrypted msgData");
871 };
872 assert_eq!(ciphertext.as_ref(), b"encrypted-data");
873 }
874
875 #[test]
878 fn raw_decode_rejects_priv_without_auth_flags() {
879 let global = MsgGlobalData::new(1, 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
880 let pdu = Pdu::get_request(1, &[]);
881 let msg = V3Message::new(
882 global,
883 Bytes::from_static(b"usm"),
884 ScopedPdu::with_empty_context(pdu),
885 );
886 let mut bytes = msg.encode().to_vec();
887 let pos = bytes
890 .windows(3)
891 .position(|w| w == [0x04, 0x01, 0x04])
892 .expect("msgFlags not found");
893 bytes[pos + 2] = 0x02;
894
895 let result = RawV3Message::decode(Bytes::from(bytes));
896 assert!(result.is_err());
897 assert!(matches!(
898 *result.unwrap_err(),
899 Error::MalformedResponse { .. }
900 ));
901 }
902
903 #[test]
907 fn raw_decode_ignores_reserved_bits_for_level() {
908 let global = MsgGlobalData::new(1, 65507, MsgFlags::new(SecurityLevel::AuthNoPriv, false));
909 let pdu = Pdu::get_request(1, &[]);
910 let msg = V3Message::new(
911 global,
912 Bytes::from_static(b"usm"),
913 ScopedPdu::with_empty_context(pdu),
914 );
915 let mut bytes = msg.encode().to_vec();
916 let pos = bytes
917 .windows(3)
918 .position(|w| w == [0x04, 0x01, 0x01])
919 .expect("msgFlags not found");
920 bytes[pos + 2] = 0x01 | 0x04 | 0x08;
922
923 let raw = RawV3Message::decode(Bytes::from(bytes)).unwrap();
924 assert_eq!(raw.security_level(), SecurityLevel::AuthNoPriv);
925 assert!(raw.global_data.msg_flags.reportable);
926 }
927
928 #[test]
929 fn raw_decode_rejects_trailing_envelope_fields() {
930 let global =
931 MsgGlobalData::new(17, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, false));
932 let scoped = ScopedPdu::with_empty_context(Pdu::get_request(23, &[]));
933
934 let mut with_outer_field = EncodeBuf::new();
936 with_outer_field.push_sequence(|buf| {
937 buf.push_integer(99);
938 scoped.encode(buf);
939 buf.push_octet_string(b"usm");
940 global.encode(buf);
941 buf.push_integer(3);
942 });
943 assert!(RawV3Message::decode(with_outer_field.finish()).is_err());
944
945 let mut with_global_field = EncodeBuf::new();
947 with_global_field.push_sequence(|buf| {
948 scoped.encode(buf);
949 buf.push_octet_string(b"usm");
950 buf.push_sequence(|buf| {
951 buf.push_integer(99);
952 buf.push_integer(SecurityModel::Usm.as_i32());
953 buf.push_octet_string(&[0]);
954 buf.push_integer(1472);
955 buf.push_integer(17);
956 });
957 buf.push_integer(3);
958 });
959 assert!(RawV3Message::decode(with_global_field.finish()).is_err());
960
961 let message = V3Message::new(global, Bytes::from_static(b"usm"), scoped);
963 let mut with_root_trailing = message.encode().to_vec();
964 with_root_trailing.extend_from_slice(&[0x05, 0]);
965 assert!(RawV3Message::decode(Bytes::from(with_root_trailing)).is_err());
966 }
967
968 #[test]
969 fn test_msg_global_data_roundtrip() {
970 let global =
971 MsgGlobalData::new(12345, 1472, MsgFlags::new(SecurityLevel::AuthNoPriv, true));
972
973 let mut buf = EncodeBuf::new();
974 global.encode(&mut buf);
975 let encoded = buf.finish();
976
977 let mut decoder = Decoder::new(encoded);
978 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
979
980 assert_eq!(decoded.msg_id, 12345);
981 assert_eq!(decoded.msg_max_size, 1472);
982 assert_eq!(decoded.msg_flags.security_level, SecurityLevel::AuthNoPriv);
983 assert!(decoded.msg_flags.reportable);
984 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
985 }
986
987 #[test]
988 fn test_scoped_pdu_roundtrip() {
989 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
990 let scoped = ScopedPdu::new(b"engine".as_slice(), b"ctx".as_slice(), pdu);
991
992 let mut buf = EncodeBuf::new();
993 scoped.encode(&mut buf);
994 let encoded = buf.finish();
995
996 let mut decoder = Decoder::new(encoded);
997 let decoded = ScopedPdu::decode(&mut decoder).unwrap();
998
999 assert_eq!(decoded.context_engine_id.as_ref(), b"engine");
1000 assert_eq!(decoded.context_name.as_ref(), b"ctx");
1001 assert_eq!(decoded.pdu.request_id, 42);
1002 }
1003
1004 #[test]
1005 fn scoped_pdu_rejects_trailing_sequence_fields() {
1006 let pdu = Pdu::get_request(42, &[]);
1007 let mut buf = EncodeBuf::new();
1008 buf.push_sequence(|buf| {
1009 buf.push_integer(99);
1010 pdu.encode(buf);
1011 buf.push_octet_string(b"ctx");
1012 buf.push_octet_string(b"engine");
1013 });
1014
1015 let mut decoder = Decoder::new(buf.finish());
1016 assert!(ScopedPdu::decode(&mut decoder).is_err());
1017 }
1018
1019 #[test]
1020 fn test_v3_message_plaintext_roundtrip() {
1021 let global =
1022 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
1023 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
1024 let scoped = ScopedPdu::with_empty_context(pdu);
1025 let msg = V3Message::new(global, Bytes::from_static(b"usm-params"), scoped);
1026
1027 let encoded = msg.encode();
1028 let decoded = V3Message::decode(encoded).unwrap();
1029
1030 assert_eq!(decoded.global_data.msg_id, 100);
1031 assert_eq!(decoded.security_level(), SecurityLevel::NoAuthNoPriv);
1032 assert_eq!(decoded.security_params.as_ref(), b"usm-params");
1033
1034 let scoped_pdu = decoded.scoped_pdu().unwrap();
1035 assert_eq!(scoped_pdu.pdu.request_id, 42);
1036 }
1037
1038 #[test]
1039 fn test_v3_message_encrypted_roundtrip() {
1040 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
1041 let msg = V3Message::new_encrypted(
1042 global,
1043 Bytes::from_static(b"usm-params"),
1044 Bytes::from_static(b"encrypted-data"),
1045 );
1046
1047 let encoded = msg.encode();
1048 let decoded = V3Message::decode(encoded).unwrap();
1049
1050 assert_eq!(decoded.global_data.msg_id, 200);
1051 assert_eq!(decoded.security_level(), SecurityLevel::AuthPriv);
1052
1053 match &decoded.data {
1054 V3MessageData::Encrypted(data) => {
1055 assert_eq!(data.as_ref(), b"encrypted-data");
1056 }
1057 V3MessageData::Plaintext(_) => panic!("expected encrypted data"),
1058 }
1059 }
1060
1061 #[test]
1062 fn test_msg_global_data_rejects_msg_max_size_below_minimum() {
1063 let global = MsgGlobalData {
1065 msg_id: 100,
1066 msg_max_size: 400, msg_flags: MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
1068 msg_security_model: SecurityModel::Usm,
1069 };
1070
1071 let mut buf = EncodeBuf::new();
1072 global.encode(&mut buf);
1073 let encoded = buf.finish();
1074
1075 let mut decoder = Decoder::new(encoded);
1076 let result = MsgGlobalData::decode(&mut decoder);
1077
1078 assert!(result.is_err());
1079 assert!(matches!(
1080 *result.unwrap_err(),
1081 Error::MalformedResponse { .. }
1082 ));
1083 }
1084
1085 #[test]
1086 fn test_msg_global_data_accepts_msg_max_size_at_minimum() {
1087 let global = MsgGlobalData::new(100, 484, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
1089
1090 let mut buf = EncodeBuf::new();
1091 global.encode(&mut buf);
1092 let encoded = buf.finish();
1093
1094 let mut decoder = Decoder::new(encoded);
1095 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1096
1097 assert_eq!(decoded.msg_max_size, 484);
1098 }
1099
1100 #[test]
1101 fn test_msg_global_data_rejects_unknown_security_model() {
1102 let mut buf = EncodeBuf::new();
1105 buf.push_sequence(|buf| {
1106 buf.push_integer(99); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
1111 let encoded = buf.finish();
1112
1113 let mut decoder = Decoder::new(encoded);
1114 let result = MsgGlobalData::decode(&mut decoder);
1115
1116 assert!(result.is_err());
1117 assert!(matches!(
1118 *result.unwrap_err(),
1119 Error::MalformedResponse { .. }
1120 ));
1121 }
1122
1123 #[test]
1124 fn msg_global_data_rejects_over_width_integer_aliases() {
1125 const ZERO: &[u8] = &[0x00];
1126 const MSG_MAX_SIZE: &[u8] = &[0x05, 0xC0];
1127 const USM: &[u8] = &[0x03];
1128
1129 let aliased_msg_id =
1131 global_data_with_integer_contents(&[0x01, 0x00, 0x00, 0x00, 0x00], MSG_MAX_SIZE, USM);
1132 let aliased_msg_max_size =
1133 global_data_with_integer_contents(ZERO, &[0x01, 0x00, 0x00, 0x05, 0xC0], USM);
1134 let aliased_security_model =
1135 global_data_with_integer_contents(ZERO, MSG_MAX_SIZE, &[0x01, 0x00, 0x00, 0x00, 0x03]);
1136
1137 for encoded in [aliased_msg_id, aliased_msg_max_size, aliased_security_model] {
1138 let mut decoder = Decoder::new(encoded);
1139 assert!(MsgGlobalData::decode(&mut decoder).is_err());
1140 }
1141 }
1142
1143 #[test]
1144 fn test_msg_global_data_rejects_zero_length_msg_flags() {
1145 let mut buf = EncodeBuf::new();
1148 buf.push_sequence(|buf| {
1149 buf.push_integer(3); buf.push_octet_string(&[]); buf.push_integer(1472); buf.push_integer(100); });
1154 let encoded = buf.finish();
1155
1156 let mut decoder = Decoder::new(encoded);
1157 let result = MsgGlobalData::decode(&mut decoder);
1158
1159 assert!(result.is_err());
1160 assert!(matches!(
1161 *result.unwrap_err(),
1162 Error::MalformedResponse { .. }
1163 ));
1164 }
1165
1166 #[test]
1167 fn test_msg_global_data_rejects_two_byte_msg_flags() {
1168 let mut buf = EncodeBuf::new();
1171 buf.push_sequence(|buf| {
1172 buf.push_integer(3); buf.push_octet_string(&[0x04, 0x00]); buf.push_integer(1472); buf.push_integer(100); });
1177 let encoded = buf.finish();
1178
1179 let mut decoder = Decoder::new(encoded);
1180 let result = MsgGlobalData::decode(&mut decoder);
1181
1182 assert!(result.is_err());
1183 assert!(matches!(
1184 *result.unwrap_err(),
1185 Error::MalformedResponse { .. }
1186 ));
1187 }
1188
1189 #[test]
1190 fn test_msg_global_data_accepts_one_byte_msg_flags() {
1191 let mut buf = EncodeBuf::new();
1194 buf.push_sequence(|buf| {
1195 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
1200 let encoded = buf.finish();
1201
1202 let mut decoder = Decoder::new(encoded);
1203 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1204
1205 assert_eq!(decoded.msg_flags, MsgFlags::from_byte(0x04).unwrap());
1206 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
1207 }
1208
1209 #[test]
1210 fn test_msg_global_data_accepts_usm_security_model() {
1211 let global =
1213 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
1214
1215 let mut buf = EncodeBuf::new();
1216 global.encode(&mut buf);
1217 let encoded = buf.finish();
1218
1219 let mut decoder = Decoder::new(encoded);
1220 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1221
1222 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
1223 }
1224
1225 #[test]
1234 fn test_msg_global_data_rejects_negative_msg_id() {
1235 let mut buf = EncodeBuf::new();
1238 buf.push_sequence(|buf| {
1239 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(-1); });
1244 let encoded = buf.finish();
1245
1246 let mut decoder = Decoder::new(encoded);
1247 let result = MsgGlobalData::decode(&mut decoder);
1248
1249 assert!(result.is_err());
1250 assert!(matches!(
1251 *result.unwrap_err(),
1252 Error::MalformedResponse { .. }
1253 ));
1254 }
1255
1256 #[test]
1257 fn test_msg_global_data_rejects_negative_msg_max_size() {
1258 let mut buf = EncodeBuf::new();
1261 buf.push_sequence(|buf| {
1262 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(-1); buf.push_integer(100); });
1267 let encoded = buf.finish();
1268
1269 let mut decoder = Decoder::new(encoded);
1270 let result = MsgGlobalData::decode(&mut decoder);
1271
1272 assert!(result.is_err());
1273 assert!(matches!(
1274 *result.unwrap_err(),
1275 Error::MalformedResponse { .. }
1276 ));
1277 }
1278
1279 #[test]
1280 fn test_msg_global_data_accepts_msg_id_at_zero() {
1281 let mut buf = EncodeBuf::new();
1283 buf.push_sequence(|buf| {
1284 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(0); });
1289 let encoded = buf.finish();
1290
1291 let mut decoder = Decoder::new(encoded);
1292 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1293
1294 assert_eq!(decoded.msg_id, 0);
1295 }
1296
1297 #[test]
1298 fn test_msg_global_data_accepts_msg_id_at_maximum() {
1299 let mut buf = EncodeBuf::new();
1301 buf.push_sequence(|buf| {
1302 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(i32::MAX); });
1307 let encoded = buf.finish();
1308
1309 let mut decoder = Decoder::new(encoded);
1310 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1311
1312 assert_eq!(decoded.msg_id, i32::MAX);
1313 }
1314
1315 #[test]
1316 fn test_msg_global_data_accepts_msg_max_size_at_maximum() {
1317 let mut buf = EncodeBuf::new();
1319 buf.push_sequence(|buf| {
1320 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(i32::MAX); buf.push_integer(100); });
1325 let encoded = buf.finish();
1326
1327 let mut decoder = Decoder::new(encoded);
1328 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
1329
1330 assert_eq!(decoded.msg_max_size, i32::MAX);
1331 }
1332}