autosar_data_abstraction/communication/pdu/
secured_ipdu.rs

1use crate::communication::{
2    AbstractIpdu, AbstractPdu, AbstractPhysicalChannel, IPdu, Pdu, PduToFrameMapping, PduTriggering,
3};
4use crate::{
5    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
6    get_reference_parents,
7};
8use autosar_data::{Element, ElementName};
9
10//##################################################################
11
12/// Wraps an `IPdu` to protect it from unauthorized manipulation
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct SecuredIPdu(Element);
15abstraction_element!(SecuredIPdu, SecuredIPdu);
16impl IdentifiableAbstractionElement for SecuredIPdu {}
17
18impl SecuredIPdu {
19    pub(crate) fn new(
20        name: &str,
21        package: &ArPackage,
22        length: u32,
23        secure_props: &SecureCommunicationProps,
24    ) -> Result<Self, AutosarAbstractionError> {
25        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
26        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::SecuredIPdu, name)?;
27        let secured_ipdu = Self(elem_pdu);
28        secured_ipdu.set_length(length)?;
29        secured_ipdu.set_secure_communication_props(secure_props)?;
30
31        Ok(secured_ipdu)
32    }
33
34    /// remove this `SecuredIPdu` from the model
35    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
36        let opt_pdu_triggering = self.payload_pdu_triggering();
37
38        // remove all triggerings of this PDU
39        for pdu_triggering in self.pdu_triggerings() {
40            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
41            let _ = pdu_triggering.remove(deep);
42        }
43
44        let ref_parents = get_reference_parents(self.element())?;
45
46        AbstractionElement::remove(self, deep)?;
47
48        for (named_parent, _parent) in ref_parents {
49            if named_parent.element_name() == ElementName::PduToFrameMapping
50                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
51            {
52                pdu_to_frame_mapping.remove(deep)?;
53            }
54        }
55
56        // if there is a payload pdu triggering, remove it too
57        if let Some(pdu_triggering) = opt_pdu_triggering {
58            pdu_triggering.remove(deep)?;
59        }
60
61        Ok(())
62    }
63
64    /// set the properties of the secured communication
65    pub fn set_secure_communication_props(
66        &self,
67        props: &SecureCommunicationProps,
68    ) -> Result<(), AutosarAbstractionError> {
69        SecureCommunicationProps::set_props(self.element(), props)
70    }
71
72    /// get the properties of the secured communication
73    #[must_use]
74    pub fn secure_communication_props(&self) -> Option<SecureCommunicationProps> {
75        SecureCommunicationProps::get_props(self.element())
76    }
77
78    /// set or remove the `useAsCryptographicIPdu` flag
79    pub fn set_use_as_cryptographic_ipdu(&self, value: Option<bool>) -> Result<(), AutosarAbstractionError> {
80        if let Some(value) = value {
81            self.element()
82                .get_or_create_sub_element(ElementName::UseAsCryptographicIPdu)?
83                .set_character_data(value.to_string())?;
84        } else {
85            let _ = self
86                .element()
87                .remove_sub_element_kind(ElementName::UseAsCryptographicIPdu);
88        }
89        Ok(())
90    }
91
92    /// get the `useAsCryptographicIPdu` flag
93    #[must_use]
94    pub fn use_as_cryptographic_ipdu(&self) -> Option<bool> {
95        self.element()
96            .get_sub_element(ElementName::UseAsCryptographicIPdu)?
97            .character_data()?
98            .parse_bool()
99    }
100
101    /// set the payload `PduTriggering` based on an `IPdu`
102    ///
103    /// This function should be used when `useAsCryptographicIPdu` is false or not set.
104    /// A `PduTriggering` is created for the `Pdu`
105    pub fn set_payload_ipdu<T: AbstractIpdu + AbstractPdu, U: AbstractPhysicalChannel>(
106        &self,
107        ipdu: &T,
108        physical_channel: &U,
109    ) -> Result<PduTriggering, AutosarAbstractionError> {
110        if let Some(ppt) = self.payload_pdu_triggering() {
111            ppt.remove(false)?;
112        }
113        let pdu_triggering = PduTriggering::new(&ipdu.clone().into(), &physical_channel.clone().into())?;
114        self.0
115            .get_or_create_sub_element(ElementName::PayloadRef)?
116            .set_reference_target(pdu_triggering.element())?;
117
118        Ok(pdu_triggering)
119    }
120
121    /// set the payload `PduTriggering` with an existing `PduTriggering`
122    ///
123    /// This function should be used when useAsCryptographicIPdu is true.
124    /// In this case the payload is transmitted separately from the
125    /// cryptographic data, so the `PduTriggering` already exists.
126    pub fn set_payload_pdu_triggering(&self, pdu_triggering: &PduTriggering) -> Result<(), AutosarAbstractionError> {
127        if let Some(ppt) = self.payload_pdu_triggering()
128            && ppt != *pdu_triggering
129        {
130            ppt.remove(false)?;
131        }
132        self.0
133            .get_or_create_sub_element(ElementName::PayloadRef)?
134            .set_reference_target(pdu_triggering.element())?;
135        Ok(())
136    }
137
138    /// get the payload `PduTriggering`
139    #[must_use]
140    pub fn payload_pdu_triggering(&self) -> Option<PduTriggering> {
141        let elem = self.0.get_sub_element(ElementName::PayloadRef)?;
142        PduTriggering::try_from(elem.get_reference_target().ok()?).ok()
143    }
144}
145
146impl AbstractPdu for SecuredIPdu {}
147
148impl AbstractIpdu for SecuredIPdu {}
149
150impl From<SecuredIPdu> for Pdu {
151    fn from(value: SecuredIPdu) -> Self {
152        Pdu::SecuredIPdu(value)
153    }
154}
155
156impl From<SecuredIPdu> for IPdu {
157    fn from(value: SecuredIPdu) -> Self {
158        IPdu::SecuredIPdu(value)
159    }
160}
161
162//##################################################################
163
164/// The properties of a `SecuredIPdu`
165#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
166pub struct SecureCommunicationProps {
167    /// length in bits of the authentic PDU data
168    pub auth_data_freshness_length: Option<u32>,
169    /// start position in bits of the authentic PDU data
170    pub auth_data_freshness_start_position: Option<u32>,
171    /// number of authentication build attempts
172    pub authentication_build_attempts: Option<u32>,
173    /// number of additional authentication attempts. If this value is zero, the authentication is not repeated
174    pub authentication_retries: Option<u32>,
175    /// numerical identifier of the secured `IPdu`
176    pub data_id: Option<u32>,
177    /// id of the freshness value
178    pub freshness_value_id: Option<u32>,
179    /// message link length in bits
180    pub message_link_length: Option<u32>,
181    /// message link start position in bits
182    pub message_link_position: Option<u32>,
183    /// seconday freshness value id
184    pub secondary_freshness_value_id: Option<u32>,
185    /// length in bytes of the secure area inside the payload pdu
186    pub secured_area_length: Option<u32>,
187    /// start position in bytes of the secure area inside the payload pdu
188    pub secured_area_offset: Option<u32>,
189}
190
191impl SecureCommunicationProps {
192    pub(crate) fn set_props(
193        element: &Element,
194        props: &SecureCommunicationProps,
195    ) -> Result<(), AutosarAbstractionError> {
196        let sub_elem = element.get_or_create_sub_element(ElementName::SecureCommunicationProps)?;
197        if let Some(value) = props.auth_data_freshness_length {
198            sub_elem
199                .create_sub_element(ElementName::AuthDataFreshnessLength)?
200                .set_character_data(value as u64)?;
201        }
202        if let Some(value) = props.auth_data_freshness_start_position {
203            sub_elem
204                .create_sub_element(ElementName::AuthDataFreshnessStartPosition)?
205                .set_character_data(value as u64)?;
206        }
207        if let Some(value) = props.authentication_build_attempts {
208            sub_elem
209                .create_sub_element(ElementName::AuthenticationBuildAttempts)?
210                .set_character_data(value as u64)?;
211        }
212        if let Some(value) = props.authentication_retries {
213            sub_elem
214                .create_sub_element(ElementName::AuthenticationRetries)?
215                .set_character_data(value as u64)?;
216        }
217        if let Some(value) = props.data_id {
218            sub_elem
219                .create_sub_element(ElementName::DataId)?
220                .set_character_data(value as u64)?;
221        }
222        if let Some(value) = props.freshness_value_id {
223            sub_elem
224                .create_sub_element(ElementName::FreshnessValueId)?
225                .set_character_data(value as u64)?;
226        }
227        if let Some(value) = props.message_link_length {
228            sub_elem
229                .create_sub_element(ElementName::MessageLinkLength)?
230                .set_character_data(value as u64)?;
231        }
232        if let Some(value) = props.message_link_position {
233            sub_elem
234                .create_sub_element(ElementName::MessageLinkPosition)?
235                .set_character_data(value as u64)?;
236        }
237        if let Some(value) = props.secondary_freshness_value_id {
238            sub_elem
239                .create_sub_element(ElementName::SecondaryFreshnessValueId)?
240                .set_character_data(value as u64)?;
241        }
242        if let Some(value) = props.secured_area_length {
243            sub_elem
244                .create_sub_element(ElementName::SecuredAreaLength)?
245                .set_character_data(value as u64)?;
246        }
247        if let Some(value) = props.secured_area_offset {
248            sub_elem
249                .create_sub_element(ElementName::SecuredAreaOffset)?
250                .set_character_data(value as u64)?;
251        }
252        Ok(())
253    }
254
255    pub(crate) fn get_props(element: &Element) -> Option<SecureCommunicationProps> {
256        let sub_elem = element.get_sub_element(ElementName::SecureCommunicationProps)?;
257        Some(SecureCommunicationProps {
258            auth_data_freshness_length: sub_elem
259                .get_sub_element(ElementName::AuthDataFreshnessLength)
260                .and_then(|elem| elem.character_data()?.parse_integer()),
261            auth_data_freshness_start_position: sub_elem
262                .get_sub_element(ElementName::AuthDataFreshnessStartPosition)
263                .and_then(|elem| elem.character_data()?.parse_integer()),
264            authentication_build_attempts: sub_elem
265                .get_sub_element(ElementName::AuthenticationBuildAttempts)
266                .and_then(|elem| elem.character_data()?.parse_integer()),
267            authentication_retries: sub_elem
268                .get_sub_element(ElementName::AuthenticationRetries)
269                .and_then(|elem| elem.character_data()?.parse_integer()),
270            data_id: sub_elem
271                .get_sub_element(ElementName::DataId)
272                .and_then(|elem| elem.character_data()?.parse_integer()),
273            freshness_value_id: sub_elem
274                .get_sub_element(ElementName::FreshnessValueId)
275                .and_then(|elem| elem.character_data()?.parse_integer()),
276            message_link_length: sub_elem
277                .get_sub_element(ElementName::MessageLinkLength)
278                .and_then(|elem| elem.character_data()?.parse_integer()),
279            message_link_position: sub_elem
280                .get_sub_element(ElementName::MessageLinkPosition)
281                .and_then(|elem| elem.character_data()?.parse_integer()),
282            secondary_freshness_value_id: sub_elem
283                .get_sub_element(ElementName::SecondaryFreshnessValueId)
284                .and_then(|elem| elem.character_data()?.parse_integer()),
285            secured_area_length: sub_elem
286                .get_sub_element(ElementName::SecuredAreaLength)
287                .and_then(|elem| elem.character_data()?.parse_integer()),
288            secured_area_offset: sub_elem
289                .get_sub_element(ElementName::SecuredAreaOffset)
290                .and_then(|elem| elem.character_data()?.parse_integer()),
291        })
292    }
293}
294
295//##################################################################
296
297#[cfg(test)]
298mod test {
299    use super::*;
300    use crate::{
301        AutosarModelAbstraction, ByteOrder, SystemCategory,
302        communication::{AbstractFrame, CanAddressingMode, CanFrameType},
303    };
304    use autosar_data::AutosarVersion;
305
306    #[test]
307    fn test_secured_ipdu() -> Result<(), AutosarAbstractionError> {
308        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
309        let package = model.get_or_create_package("/pkg1")?;
310        let system = package.create_system("System", SystemCategory::SystemExtract)?;
311        let can_cluster = system.create_can_cluster("Cluster", &package, None)?;
312        let can_channel = can_cluster.create_physical_channel("Channel")?;
313
314        let secure_communication_props = SecureCommunicationProps {
315            auth_data_freshness_length: Some(1),
316            auth_data_freshness_start_position: Some(2),
317            authentication_build_attempts: Some(3),
318            authentication_retries: Some(4),
319            data_id: Some(5),
320            freshness_value_id: Some(6),
321            message_link_length: Some(7),
322            message_link_position: Some(8),
323            secondary_freshness_value_id: Some(9),
324            secured_area_length: Some(10),
325            secured_area_offset: Some(11),
326        };
327        let secured_ipdu = system.create_secured_ipdu("SecuredIPdu", &package, 64, &secure_communication_props)?;
328        assert_eq!(
329            secured_ipdu.secure_communication_props(),
330            Some(secure_communication_props)
331        );
332        assert_eq!(secured_ipdu.use_as_cryptographic_ipdu(), None);
333        secured_ipdu.set_use_as_cryptographic_ipdu(Some(true))?;
334        assert_eq!(secured_ipdu.use_as_cryptographic_ipdu(), Some(true));
335        secured_ipdu.set_use_as_cryptographic_ipdu(Some(false))?;
336        assert_eq!(secured_ipdu.use_as_cryptographic_ipdu(), Some(false));
337        secured_ipdu.set_use_as_cryptographic_ipdu(None)?;
338        assert_eq!(secured_ipdu.use_as_cryptographic_ipdu(), None);
339
340        let payload_ipdu = system.create_isignal_ipdu("PayloadIPdu", &package, 64)?;
341        let pdu_triggering = secured_ipdu.set_payload_ipdu(&payload_ipdu, &can_channel)?;
342        assert_eq!(secured_ipdu.payload_pdu_triggering(), Some(pdu_triggering));
343
344        let external_ipdu = system.create_isignal_ipdu("ExternalIPdu", &package, 64)?;
345        let can_frame = system.create_can_frame("CanFrame", &package, 64)?;
346        can_channel
347            .trigger_frame(&can_frame, 0x101, CanAddressingMode::Standard, CanFrameType::CanFd)
348            .unwrap();
349        can_frame
350            .map_pdu(&external_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
351            .unwrap();
352        let external_pdu_triggering = &external_ipdu.pdu_triggerings()[0];
353        secured_ipdu.set_payload_pdu_triggering(external_pdu_triggering)?;
354        assert_eq!(
355            secured_ipdu.payload_pdu_triggering(),
356            Some(external_pdu_triggering.clone())
357        );
358
359        Ok(())
360    }
361}