1use crate::ber::{Decoder, EncodeBuf};
23use crate::error::{DecodeErrorKind, Error, Result};
24use crate::pdu::Pdu;
25use bytes::Bytes;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[repr(i32)]
30pub enum SecurityModel {
31 Usm = 3,
33}
34
35impl SecurityModel {
36 pub fn from_i32(value: i32) -> Option<Self> {
38 match value {
39 3 => Some(Self::Usm),
40 _ => None,
41 }
42 }
43
44 pub fn as_i32(self) -> i32 {
46 self as i32
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
55pub enum SecurityLevel {
56 NoAuthNoPriv,
58 AuthNoPriv,
60 AuthPriv,
62}
63
64impl SecurityLevel {
65 pub fn from_flags(flags: u8) -> Option<Self> {
67 let auth = flags & 0x01 != 0;
68 let priv_ = flags & 0x02 != 0;
69
70 match (auth, priv_) {
71 (false, false) => Some(Self::NoAuthNoPriv),
72 (true, false) => Some(Self::AuthNoPriv),
73 (true, true) => Some(Self::AuthPriv),
74 (false, true) => None, }
76 }
77
78 pub fn to_flags(self) -> u8 {
80 match self {
81 Self::NoAuthNoPriv => 0x00,
82 Self::AuthNoPriv => 0x01,
83 Self::AuthPriv => 0x03,
84 }
85 }
86
87 pub fn requires_auth(self) -> bool {
89 matches!(self, Self::AuthNoPriv | Self::AuthPriv)
90 }
91
92 pub fn requires_priv(self) -> bool {
94 matches!(self, Self::AuthPriv)
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct MsgFlags {
101 pub security_level: SecurityLevel,
103 pub reportable: bool,
105}
106
107impl MsgFlags {
108 pub fn new(security_level: SecurityLevel, reportable: bool) -> Self {
110 Self {
111 security_level,
112 reportable,
113 }
114 }
115
116 pub fn from_byte(byte: u8) -> Result<Self> {
118 let security_level = SecurityLevel::from_flags(byte)
119 .ok_or_else(|| Error::decode(0, DecodeErrorKind::InvalidMsgFlags))?;
120 let reportable = byte & 0x04 != 0;
121 Ok(Self {
122 security_level,
123 reportable,
124 })
125 }
126
127 pub fn to_byte(self) -> u8 {
129 let mut flags = self.security_level.to_flags();
130 if self.reportable {
131 flags |= 0x04;
132 }
133 flags
134 }
135}
136
137#[derive(Debug, Clone)]
139pub struct MsgGlobalData {
140 pub msg_id: i32,
142 pub msg_max_size: i32,
144 pub msg_flags: MsgFlags,
146 pub msg_security_model: SecurityModel,
148}
149
150impl MsgGlobalData {
151 pub fn new(msg_id: i32, msg_max_size: i32, msg_flags: MsgFlags) -> Self {
153 Self {
154 msg_id,
155 msg_max_size,
156 msg_flags,
157 msg_security_model: SecurityModel::Usm,
158 }
159 }
160
161 pub fn encode(&self, buf: &mut EncodeBuf) {
163 buf.push_sequence(|buf| {
164 buf.push_integer(self.msg_security_model.as_i32());
165 buf.push_octet_string(&[self.msg_flags.to_byte()]);
167 buf.push_integer(self.msg_max_size);
168 buf.push_integer(self.msg_id);
169 });
170 }
171
172 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
178 const MSG_MAX_SIZE_MINIMUM: i32 = 484;
180
181 let mut seq = decoder.read_sequence()?;
182
183 let msg_id = seq.read_integer()?;
184 let msg_max_size = seq.read_integer()?;
185
186 if msg_max_size < MSG_MAX_SIZE_MINIMUM {
188 return Err(Error::decode(
189 seq.offset(),
190 DecodeErrorKind::MsgMaxSizeTooSmall {
191 value: msg_max_size,
192 minimum: MSG_MAX_SIZE_MINIMUM,
193 },
194 ));
195 }
196
197 let flags_bytes = seq.read_octet_string()?;
198 if flags_bytes.len() != 1 {
199 return Err(Error::decode(
200 seq.offset(),
201 DecodeErrorKind::UnexpectedTag {
202 expected: 1,
203 actual: flags_bytes.len() as u8,
204 },
205 ));
206 }
207 let msg_flags = MsgFlags::from_byte(flags_bytes[0])?;
208
209 let msg_security_model_raw = seq.read_integer()?;
210 let msg_security_model =
212 SecurityModel::from_i32(msg_security_model_raw).ok_or_else(|| {
213 Error::decode(
214 seq.offset(),
215 DecodeErrorKind::UnknownSecurityModel(msg_security_model_raw),
216 )
217 })?;
218
219 Ok(Self {
220 msg_id,
221 msg_max_size,
222 msg_flags,
223 msg_security_model,
224 })
225 }
226}
227
228#[derive(Debug, Clone)]
230pub struct ScopedPdu {
231 pub context_engine_id: Bytes,
233 pub context_name: Bytes,
235 pub pdu: Pdu,
237}
238
239impl ScopedPdu {
240 pub fn new(
242 context_engine_id: impl Into<Bytes>,
243 context_name: impl Into<Bytes>,
244 pdu: Pdu,
245 ) -> Self {
246 Self {
247 context_engine_id: context_engine_id.into(),
248 context_name: context_name.into(),
249 pdu,
250 }
251 }
252
253 pub fn with_empty_context(pdu: Pdu) -> Self {
255 Self {
256 context_engine_id: Bytes::new(),
257 context_name: Bytes::new(),
258 pdu,
259 }
260 }
261
262 pub fn encode(&self, buf: &mut EncodeBuf) {
264 buf.push_sequence(|buf| {
265 self.pdu.encode(buf);
266 buf.push_octet_string(&self.context_name);
267 buf.push_octet_string(&self.context_engine_id);
268 });
269 }
270
271 pub fn encode_to_bytes(&self) -> Bytes {
273 let mut buf = EncodeBuf::new();
274 self.encode(&mut buf);
275 buf.finish()
276 }
277
278 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
280 let mut seq = decoder.read_sequence()?;
281
282 let context_engine_id = seq.read_octet_string()?;
283 let context_name = seq.read_octet_string()?;
284 let pdu = Pdu::decode(&mut seq)?;
285
286 Ok(Self {
287 context_engine_id,
288 context_name,
289 pdu,
290 })
291 }
292}
293
294#[derive(Debug, Clone)]
296pub struct V3Message {
297 pub global_data: MsgGlobalData,
299 pub security_params: Bytes,
301 pub data: V3MessageData,
303}
304
305#[derive(Debug, Clone)]
307pub enum V3MessageData {
308 Plaintext(ScopedPdu),
310 Encrypted(Bytes),
312}
313
314impl V3Message {
315 pub fn new(global_data: MsgGlobalData, security_params: Bytes, scoped_pdu: ScopedPdu) -> Self {
317 Self {
318 global_data,
319 security_params,
320 data: V3MessageData::Plaintext(scoped_pdu),
321 }
322 }
323
324 pub fn new_encrypted(
326 global_data: MsgGlobalData,
327 security_params: Bytes,
328 encrypted: Bytes,
329 ) -> Self {
330 Self {
331 global_data,
332 security_params,
333 data: V3MessageData::Encrypted(encrypted),
334 }
335 }
336
337 pub fn scoped_pdu(&self) -> Option<&ScopedPdu> {
339 match &self.data {
340 V3MessageData::Plaintext(pdu) => Some(pdu),
341 V3MessageData::Encrypted(_) => None,
342 }
343 }
344
345 pub fn into_scoped_pdu(self) -> Option<ScopedPdu> {
347 match self.data {
348 V3MessageData::Plaintext(pdu) => Some(pdu),
349 V3MessageData::Encrypted(_) => None,
350 }
351 }
352
353 pub fn pdu(&self) -> Option<&Pdu> {
355 self.scoped_pdu().map(|s| &s.pdu)
356 }
357
358 pub fn into_pdu(self) -> Option<Pdu> {
360 self.into_scoped_pdu().map(|s| s.pdu)
361 }
362
363 pub fn msg_id(&self) -> i32 {
365 self.global_data.msg_id
366 }
367
368 pub fn security_level(&self) -> SecurityLevel {
370 self.global_data.msg_flags.security_level
371 }
372
373 pub fn encode(&self) -> Bytes {
380 let mut buf = EncodeBuf::new();
381
382 buf.push_sequence(|buf| {
383 match &self.data {
385 V3MessageData::Plaintext(scoped_pdu) => {
386 scoped_pdu.encode(buf);
387 }
388 V3MessageData::Encrypted(ciphertext) => {
389 buf.push_octet_string(ciphertext);
390 }
391 }
392
393 buf.push_octet_string(&self.security_params);
395
396 self.global_data.encode(buf);
398
399 buf.push_integer(3);
401 });
402
403 buf.finish()
404 }
405
406 pub fn decode(data: Bytes) -> Result<Self> {
411 let mut decoder = Decoder::new(data);
412 let mut seq = decoder.read_sequence()?;
413
414 let version = seq.read_integer()?;
416 if version != 3 {
417 return Err(Error::decode(
418 seq.offset(),
419 DecodeErrorKind::UnknownVersion(version),
420 ));
421 }
422
423 let global_data = MsgGlobalData::decode(&mut seq)?;
425
426 let security_params = seq.read_octet_string()?;
428
429 let data = if global_data.msg_flags.security_level.requires_priv() {
431 let encrypted = seq.read_octet_string()?;
433 V3MessageData::Encrypted(encrypted)
434 } else {
435 let scoped_pdu = ScopedPdu::decode(&mut seq)?;
437 V3MessageData::Plaintext(scoped_pdu)
438 };
439
440 Ok(Self {
441 global_data,
442 security_params,
443 data,
444 })
445 }
446
447 pub fn discovery_request(msg_id: i32) -> Self {
452 let global_data = MsgGlobalData::new(
453 msg_id,
454 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
456 );
457
458 let security_params = crate::v3::UsmSecurityParams::empty().encode();
460
461 let pdu = Pdu::get_request(0, &[]);
463 let scoped_pdu = ScopedPdu::with_empty_context(pdu);
464
465 Self::new(global_data, security_params, scoped_pdu)
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472 use crate::oid;
473
474 #[test]
475 fn test_security_level_flags() {
476 assert_eq!(SecurityLevel::NoAuthNoPriv.to_flags(), 0x00);
477 assert_eq!(SecurityLevel::AuthNoPriv.to_flags(), 0x01);
478 assert_eq!(SecurityLevel::AuthPriv.to_flags(), 0x03);
479
480 assert_eq!(
481 SecurityLevel::from_flags(0x00),
482 Some(SecurityLevel::NoAuthNoPriv)
483 );
484 assert_eq!(
485 SecurityLevel::from_flags(0x01),
486 Some(SecurityLevel::AuthNoPriv)
487 );
488 assert_eq!(
489 SecurityLevel::from_flags(0x03),
490 Some(SecurityLevel::AuthPriv)
491 );
492 assert_eq!(SecurityLevel::from_flags(0x02), None); }
494
495 #[test]
496 fn test_msg_flags_roundtrip() {
497 let flags = MsgFlags::new(SecurityLevel::AuthPriv, true);
498 let byte = flags.to_byte();
499 assert_eq!(byte, 0x07); let decoded = MsgFlags::from_byte(byte).unwrap();
502 assert_eq!(decoded.security_level, SecurityLevel::AuthPriv);
503 assert!(decoded.reportable);
504 }
505
506 #[test]
507 fn test_msg_global_data_roundtrip() {
508 let global =
509 MsgGlobalData::new(12345, 1472, MsgFlags::new(SecurityLevel::AuthNoPriv, true));
510
511 let mut buf = EncodeBuf::new();
512 global.encode(&mut buf);
513 let encoded = buf.finish();
514
515 let mut decoder = Decoder::new(encoded);
516 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
517
518 assert_eq!(decoded.msg_id, 12345);
519 assert_eq!(decoded.msg_max_size, 1472);
520 assert_eq!(decoded.msg_flags.security_level, SecurityLevel::AuthNoPriv);
521 assert!(decoded.msg_flags.reportable);
522 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
523 }
524
525 #[test]
526 fn test_scoped_pdu_roundtrip() {
527 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
528 let scoped = ScopedPdu::new(b"engine".as_slice(), b"ctx".as_slice(), pdu);
529
530 let mut buf = EncodeBuf::new();
531 scoped.encode(&mut buf);
532 let encoded = buf.finish();
533
534 let mut decoder = Decoder::new(encoded);
535 let decoded = ScopedPdu::decode(&mut decoder).unwrap();
536
537 assert_eq!(decoded.context_engine_id.as_ref(), b"engine");
538 assert_eq!(decoded.context_name.as_ref(), b"ctx");
539 assert_eq!(decoded.pdu.request_id, 42);
540 }
541
542 #[test]
543 fn test_v3_message_plaintext_roundtrip() {
544 let global =
545 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
546 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
547 let scoped = ScopedPdu::with_empty_context(pdu);
548 let msg = V3Message::new(global, Bytes::from_static(b"usm-params"), scoped);
549
550 let encoded = msg.encode();
551 let decoded = V3Message::decode(encoded).unwrap();
552
553 assert_eq!(decoded.global_data.msg_id, 100);
554 assert_eq!(decoded.security_level(), SecurityLevel::NoAuthNoPriv);
555 assert_eq!(decoded.security_params.as_ref(), b"usm-params");
556
557 let scoped_pdu = decoded.scoped_pdu().unwrap();
558 assert_eq!(scoped_pdu.pdu.request_id, 42);
559 }
560
561 #[test]
562 fn test_v3_message_encrypted_roundtrip() {
563 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
564 let msg = V3Message::new_encrypted(
565 global,
566 Bytes::from_static(b"usm-params"),
567 Bytes::from_static(b"encrypted-data"),
568 );
569
570 let encoded = msg.encode();
571 let decoded = V3Message::decode(encoded).unwrap();
572
573 assert_eq!(decoded.global_data.msg_id, 200);
574 assert_eq!(decoded.security_level(), SecurityLevel::AuthPriv);
575
576 match &decoded.data {
577 V3MessageData::Encrypted(data) => {
578 assert_eq!(data.as_ref(), b"encrypted-data");
579 }
580 V3MessageData::Plaintext(_) => panic!("expected encrypted data"),
581 }
582 }
583
584 #[test]
585 fn test_msg_global_data_rejects_msg_max_size_below_minimum() {
586 let global = MsgGlobalData {
588 msg_id: 100,
589 msg_max_size: 400, msg_flags: MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
591 msg_security_model: SecurityModel::Usm,
592 };
593
594 let mut buf = EncodeBuf::new();
595 global.encode(&mut buf);
596 let encoded = buf.finish();
597
598 let mut decoder = Decoder::new(encoded);
599 let result = MsgGlobalData::decode(&mut decoder);
600
601 assert!(result.is_err());
602 match result.unwrap_err() {
603 Error::Decode {
604 kind: DecodeErrorKind::MsgMaxSizeTooSmall { value, minimum },
605 ..
606 } => {
607 assert_eq!(value, 400);
608 assert_eq!(minimum, 484);
609 }
610 e => panic!("expected MsgMaxSizeTooSmall error, got {:?}", e),
611 }
612 }
613
614 #[test]
615 fn test_msg_global_data_accepts_msg_max_size_at_minimum() {
616 let global = MsgGlobalData::new(100, 484, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
618
619 let mut buf = EncodeBuf::new();
620 global.encode(&mut buf);
621 let encoded = buf.finish();
622
623 let mut decoder = Decoder::new(encoded);
624 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
625
626 assert_eq!(decoded.msg_max_size, 484);
627 }
628
629 #[test]
630 fn test_msg_global_data_rejects_unknown_security_model() {
631 let mut buf = EncodeBuf::new();
634 buf.push_sequence(|buf| {
635 buf.push_integer(99); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
640 let encoded = buf.finish();
641
642 let mut decoder = Decoder::new(encoded);
643 let result = MsgGlobalData::decode(&mut decoder);
644
645 assert!(result.is_err());
646 match result.unwrap_err() {
647 Error::Decode {
648 kind: DecodeErrorKind::UnknownSecurityModel(model),
649 ..
650 } => {
651 assert_eq!(model, 99);
652 }
653 e => panic!("expected UnknownSecurityModel error, got {:?}", e),
654 }
655 }
656
657 #[test]
658 fn test_msg_global_data_accepts_usm_security_model() {
659 let global =
661 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
662
663 let mut buf = EncodeBuf::new();
664 global.encode(&mut buf);
665 let encoded = buf.finish();
666
667 let mut decoder = Decoder::new(encoded);
668 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
669
670 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
671 }
672}