autosar_data_abstraction/communication/pdu/
secured_ipdu.rs

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