Skip to main content

rusty_copp/messages/
user_data.rs

1use der_parser::{
2    Oid,
3    asn1_rs::{Any, FromBer},
4    ber::{BerObject, parse_ber_any},
5    der::{Class, Header, Tag},
6    error::BerError,
7};
8
9use crate::messages::parsers::process_constructed_data;
10
11#[derive(PartialEq, Eq, Clone, Debug)]
12pub enum UserData {
13    FullyEncoded(Vec<PresentationDataValueList>),
14    // Not yet supported and not required for MMS/ICCP
15    // SimplyEncoded(Vec<u8>),
16}
17
18// Technically SingleAsn1Type is only allowed if there is one PDV. But We do not restrict this here.
19#[derive(PartialEq, Eq, Clone, Debug)]
20pub struct PresentationDataValueList {
21    pub transfer_syntax_name: Option<Oid<'static>>,
22    pub presentation_context_identifier: Vec<u8>,
23    pub presentation_data_values: PresentationDataValues,
24}
25
26#[derive(PartialEq, Eq, Clone, Debug)]
27pub enum PresentationDataValues {
28    SingleAsn1Type(Vec<u8>),
29    // TODO IMPL Not required for MMS/ICCP
30    // OctetAligned(Vec<u8>),
31    // Arbitrary(Vec<u8>),
32}
33
34impl UserData {
35    pub fn to_ber(&self) -> BerObject<'_> {
36        match &self {
37            UserData::FullyEncoded(presentation_data_value_lists) => {
38                let mut pdv_lists = vec![];
39                for presentation_data_value_list in presentation_data_value_lists {
40                    pdv_lists.push(presentation_data_value_list.to_ber());
41                }
42                der_parser::ber::BerObject::from_header_and_content(
43                    Header::new(Class::Application, true, Tag::from(1), der_parser::ber::Length::Definite(0)),
44                    der_parser::ber::BerObjectContent::Sequence(pdv_lists),
45                )
46            }
47        }
48    }
49
50    pub fn parse(data: Any<'_>) -> Result<UserData, BerError> {
51        match data.header.raw_tag() {
52            Some(&[97]) => {
53                let mut presentation_list = vec![];
54                for pdv_list in process_constructed_data(data.data)? {
55                    pdv_list.header.assert_class(Class::Universal)?;
56                    pdv_list.header.assert_tag(Tag::Sequence)?;
57
58                    let mut transfer_syntax_name: Option<Oid<'static>> = None;
59                    let mut presentation_contaxt_id = None;
60                    let mut presentation_data_values = None;
61                    for pdv_list_part in process_constructed_data(pdv_list.data)? {
62                        match pdv_list_part.header.raw_tag() {
63                            Some(&[6]) => transfer_syntax_name = Some(Oid::from_ber(pdv_list.data)?.1.to_owned()),
64                            Some(&[2]) => presentation_contaxt_id = Some(pdv_list_part.data.to_vec()),
65                            Some(&[160]) => presentation_data_values = Some(PresentationDataValues::SingleAsn1Type(pdv_list_part.data.to_vec())),
66                            // TODO Other formats
67                            x => tracing::warn!("Unknown data in copp user data: {:?}", x),
68                        }
69                    }
70                    presentation_list.push(PresentationDataValueList {
71                        transfer_syntax_name,
72                        presentation_context_identifier: presentation_contaxt_id.ok_or_else(|| BerError::BerValueError)?,
73                        presentation_data_values: presentation_data_values.ok_or_else(|| BerError::BerValueError)?,
74                    });
75                }
76                Ok(UserData::FullyEncoded(presentation_list))
77            }
78            _ => todo!(),
79        }
80    }
81
82    pub fn parse_raw(data: &[u8]) -> Result<UserData, BerError> {
83        let (_, packet) = parse_ber_any(data)?;
84        Ok(UserData::parse(packet)?)
85    }
86}
87
88impl PresentationDataValueList {
89    pub fn to_ber(&self) -> BerObject<'_> {
90        let mut object_content = vec![];
91        if let Some(transfer_syntax_name) = &self.transfer_syntax_name {
92            object_content.push(der_parser::ber::BerObject::from_obj(der_parser::ber::BerObjectContent::OID(transfer_syntax_name.clone())));
93        }
94        object_content.push(der_parser::ber::BerObject::from_obj(der_parser::ber::BerObjectContent::Integer(&self.presentation_context_identifier)));
95        object_content.push(self.presentation_data_values.to_ber());
96
97        der_parser::ber::BerObject::from_seq(object_content)
98    }
99}
100
101impl PresentationDataValues {
102    pub fn to_ber(&self) -> BerObject<'_> {
103        match &self {
104            PresentationDataValues::SingleAsn1Type(data) => der_parser::ber::BerObject::from_header_and_content(
105                Header::new(Class::ContextSpecific, true, Tag::from(0), der_parser::ber::Length::Definite(0)),
106                // Shoehorn the BER data into the payload but make it still look like BER data.
107                der_parser::ber::BerObjectContent::OctetString(data),
108            ),
109            // TODO IMPL Not required for MMS/ICCP
110            // PresentationDataValues::OctetAligned(data) => der_parser::ber::BerObject::from_header_and_content(
111            //     Header::new(Class::ContextSpecific, true, Tag::from(1), der_parser::ber::Length::Definite(0)),
112            //     der_parser::ber::BerObjectContent::OctetString(data),
113            // ),
114            // PresentationDataValues::Arbitrary(data) => der_parser::ber::BerObject::from_header_and_content(
115            //     Header::new(Class::ContextSpecific, true, Tag::from(2), der_parser::ber::Length::Definite(0)),
116            //     der_parser::ber::BerObjectContent::OctetString(data),
117            // ),
118        }
119    }
120}