Skip to main content

opcua_core/comms/
security_header.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! [SecurityHeader] and related utilities.
6//!
7//! The security header is part of an OPC-UA message containing information about
8//! the security token and encryption used.
9
10use std::io::{Read, Write};
11
12use opcua_types::{
13    ByteString, DecodingOptions, EncodingResult, Error, SimpleBinaryDecodable,
14    SimpleBinaryEncodable, UAString,
15};
16
17use opcua_types::{constants, status_code::StatusCode};
18
19use opcua_crypto::{SecurityPolicy, Thumbprint, X509};
20
21/// Holds the security header associated with the chunk. Secure channel requests use an asymmetric
22/// security header, regular messages use a symmetric security header.
23#[derive(Debug, Clone, PartialEq)]
24pub enum SecurityHeader {
25    /// Security header for asymmetric encryption.
26    Asymmetric(AsymmetricSecurityHeader),
27    /// Security header for symmetric encryption.
28    Symmetric(SymmetricSecurityHeader),
29}
30
31impl SimpleBinaryEncodable for SecurityHeader {
32    fn byte_len(&self) -> usize {
33        match self {
34            SecurityHeader::Asymmetric(value) => value.byte_len(),
35            SecurityHeader::Symmetric(value) => value.byte_len(),
36        }
37    }
38
39    fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
40        match self {
41            SecurityHeader::Asymmetric(value) => value.encode(stream),
42            SecurityHeader::Symmetric(value) => value.encode(stream),
43        }
44    }
45}
46
47impl SecurityHeader {
48    /// Decode the security header from a stream. The type of header is
49    /// given by the message header, so this type doesn't implement BinaryDecodable.
50    pub fn decode_from_stream<S: Read + ?Sized>(
51        stream: &mut S,
52        is_open_secure_channel: bool,
53        decoding_options: &DecodingOptions,
54    ) -> EncodingResult<Self> {
55        if is_open_secure_channel {
56            let security_header = AsymmetricSecurityHeader::decode(stream, decoding_options)?;
57
58            let security_policy = if security_header.security_policy_uri.is_empty() {
59                SecurityPolicy::None
60            } else {
61                SecurityPolicy::from_uri(security_header.security_policy_uri.as_ref())
62            };
63
64            if security_policy == SecurityPolicy::Unknown {
65                return Err(Error::new(
66                    StatusCode::BadSecurityPolicyRejected,
67                    format!(
68                        "Security policy of chunk is unknown, policy = {:?}",
69                        security_header.security_policy_uri
70                    ),
71                ));
72            }
73
74            Ok(SecurityHeader::Asymmetric(security_header))
75        } else {
76            let security_header = SymmetricSecurityHeader::decode(stream, decoding_options)?;
77            Ok(SecurityHeader::Symmetric(security_header))
78        }
79    }
80}
81
82#[derive(Debug, Clone, PartialEq)]
83/// Security header for symmetric encryption.
84pub struct SymmetricSecurityHeader {
85    /// Security token ID.
86    pub token_id: u32,
87}
88
89impl SimpleBinaryEncodable for SymmetricSecurityHeader {
90    fn byte_len(&self) -> usize {
91        4
92    }
93
94    fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
95        self.token_id.encode(stream)
96    }
97}
98
99impl SimpleBinaryDecodable for SymmetricSecurityHeader {
100    fn decode<S: Read + ?Sized>(
101        stream: &mut S,
102        decoding_options: &DecodingOptions,
103    ) -> EncodingResult<Self> {
104        let token_id = u32::decode(stream, decoding_options)?;
105        Ok(SymmetricSecurityHeader { token_id })
106    }
107}
108
109#[derive(Debug, Clone, PartialEq)]
110/// Security header for asymmetric encryption.
111pub struct AsymmetricSecurityHeader {
112    /// Security policy URI.
113    pub security_policy_uri: UAString,
114    /// Sender certificate as a byte string.
115    pub sender_certificate: ByteString,
116    /// Thumbprint of the receiver certificate as a byte string.
117    pub receiver_certificate_thumbprint: ByteString,
118}
119
120impl SimpleBinaryEncodable for AsymmetricSecurityHeader {
121    fn byte_len(&self) -> usize {
122        let mut size = 0;
123        size += self.security_policy_uri.byte_len();
124        size += self.sender_certificate.byte_len();
125        size += self.receiver_certificate_thumbprint.byte_len();
126        size
127    }
128
129    fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
130        self.security_policy_uri.encode(stream)?;
131        self.sender_certificate.encode(stream)?;
132        self.receiver_certificate_thumbprint.encode(stream)?;
133        Ok(())
134    }
135}
136
137impl SimpleBinaryDecodable for AsymmetricSecurityHeader {
138    fn decode<S: Read + ?Sized>(
139        stream: &mut S,
140        decoding_options: &DecodingOptions,
141    ) -> EncodingResult<Self> {
142        let security_policy_uri = UAString::decode(stream, decoding_options)?;
143        let sender_certificate = ByteString::decode(stream, decoding_options)?;
144        let receiver_certificate_thumbprint = ByteString::decode(stream, decoding_options)?;
145
146        // validate sender_certificate_length < MaxCertificateSize
147        if sender_certificate
148            .value
149            .as_ref()
150            .is_some_and(|v| v.len() >= constants::MAX_CERTIFICATE_LENGTH)
151        {
152            Err(Error::new(
153                StatusCode::BadEncodingLimitsExceeded,
154                format!(
155                    "Sender certificate has length {}, which exceeds max certificate size {}",
156                    sender_certificate
157                        .value
158                        .as_ref()
159                        .map(|v| v.len())
160                        .unwrap_or_default(),
161                    constants::MAX_CERTIFICATE_LENGTH
162                ),
163            ))
164        } else {
165            // validate receiver_certificate_thumbprint_length == 20
166            let thumbprint_len = if let Some(value) = &receiver_certificate_thumbprint.value {
167                value.len()
168            } else {
169                0
170            };
171            if thumbprint_len > 0 && thumbprint_len != Thumbprint::THUMBPRINT_SIZE {
172                Err(Error::decoding(format!(
173                    "Receiver certificate thumbprint is not 20 bytes long, {} bytes",
174                    receiver_certificate_thumbprint
175                        .value
176                        .as_ref()
177                        .unwrap()
178                        .len(),
179                )))
180            } else {
181                Ok(AsymmetricSecurityHeader {
182                    security_policy_uri,
183                    sender_certificate,
184                    receiver_certificate_thumbprint,
185                })
186            }
187        }
188    }
189}
190
191impl AsymmetricSecurityHeader {
192    /// Create a new asymmetric security header with no security policy.
193    pub fn none() -> AsymmetricSecurityHeader {
194        AsymmetricSecurityHeader {
195            security_policy_uri: UAString::from(SecurityPolicy::None.to_uri()),
196            sender_certificate: ByteString::null(),
197            receiver_certificate_thumbprint: ByteString::null(),
198        }
199    }
200
201    /// Create a new asymmetric security header.
202    pub fn new(
203        security_policy: SecurityPolicy,
204        sender_certificate: &X509,
205        receiver_certificate_thumbprint: ByteString,
206    ) -> AsymmetricSecurityHeader {
207        AsymmetricSecurityHeader {
208            security_policy_uri: UAString::from(security_policy.to_uri()),
209            sender_certificate: sender_certificate.as_byte_string(),
210            receiver_certificate_thumbprint,
211        }
212    }
213}
214
215#[derive(Debug, Clone, PartialEq)]
216/// Part of message headers containing the sequence number of the chunk
217/// and the request ID it is part of.
218pub struct SequenceHeader {
219    /// Sequence number of the chunk.
220    pub sequence_number: u32,
221    /// ID of the request this chunk is part of.
222    pub request_id: u32,
223}
224
225impl SimpleBinaryEncodable for SequenceHeader {
226    fn byte_len(&self) -> usize {
227        8
228    }
229
230    fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
231        self.sequence_number.encode(stream)?;
232        self.request_id.encode(stream)?;
233        Ok(())
234    }
235}
236
237impl SimpleBinaryDecodable for SequenceHeader {
238    fn decode<S: Read + ?Sized>(
239        stream: &mut S,
240        decoding_options: &DecodingOptions,
241    ) -> EncodingResult<Self> {
242        let sequence_number = u32::decode(stream, decoding_options)?;
243        let request_id = u32::decode(stream, decoding_options)?;
244        Ok(SequenceHeader {
245            sequence_number,
246            request_id,
247        })
248    }
249}