opcua_core/comms/
message_chunk.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! A message chunk is a message or a portion of a message, optionally encrypted & signed, which
6//! has been split for transmission.
7
8use std::io::{Cursor, Read, Write};
9
10use opcua_types::{status_code::StatusCode, *};
11
12use crate::comms::{
13    message_chunk_info::ChunkInfo,
14    secure_channel::SecureChannel,
15    security_header::{
16        AsymmetricSecurityHeader, SecurityHeader, SequenceHeader, SymmetricSecurityHeader,
17    },
18    tcp_types::{
19        CHUNK_FINAL, CHUNK_FINAL_ERROR, CHUNK_INTERMEDIATE, CHUNK_MESSAGE,
20        CLOSE_SECURE_CHANNEL_MESSAGE, MIN_CHUNK_SIZE, OPEN_SECURE_CHANNEL_MESSAGE,
21    },
22};
23
24/// The size of a chunk header, used by several places
25pub const MESSAGE_CHUNK_HEADER_SIZE: usize = 12;
26
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum MessageChunkType {
29    Message,
30    OpenSecureChannel,
31    CloseSecureChannel,
32}
33
34impl MessageChunkType {
35    pub fn is_open_secure_channel(&self) -> bool {
36        *self == MessageChunkType::OpenSecureChannel
37    }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
41pub enum MessageIsFinalType {
42    /// Intermediate
43    Intermediate,
44    /// Final chunk
45    Final,
46    /// Abort
47    FinalError,
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct MessageChunkHeader {
52    /// The kind of chunk - message, open or close
53    pub message_type: MessageChunkType,
54    /// The chunk type - C == intermediate, F = the final chunk, A = the final chunk when aborting
55    pub is_final: MessageIsFinalType,
56    /// The size of the chunk (message) including the header
57    pub message_size: u32,
58    /// Secure channel id
59    pub secure_channel_id: u32,
60}
61
62impl BinaryEncoder<MessageChunkHeader> for MessageChunkHeader {
63    fn byte_len(&self) -> usize {
64        MESSAGE_CHUNK_HEADER_SIZE
65    }
66
67    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
68        let message_type = match self.message_type {
69            MessageChunkType::Message => CHUNK_MESSAGE,
70            MessageChunkType::OpenSecureChannel => OPEN_SECURE_CHANNEL_MESSAGE,
71            MessageChunkType::CloseSecureChannel => CLOSE_SECURE_CHANNEL_MESSAGE,
72        };
73
74        let is_final = match self.is_final {
75            MessageIsFinalType::Intermediate => CHUNK_INTERMEDIATE,
76            MessageIsFinalType::Final => CHUNK_FINAL,
77            MessageIsFinalType::FinalError => CHUNK_FINAL_ERROR,
78        };
79
80        let mut size = 0;
81        size += process_encode_io_result(stream.write(message_type))?;
82        size += write_u8(stream, is_final)?;
83        size += write_u32(stream, self.message_size)?;
84        size += write_u32(stream, self.secure_channel_id)?;
85        assert_eq!(size, self.byte_len());
86        Ok(size)
87    }
88
89    fn decode<S: Read>(stream: &mut S, _: &DecodingOptions) -> EncodingResult<Self> {
90        let mut message_type_code = [0u8; 3];
91        process_decode_io_result(stream.read_exact(&mut message_type_code))?;
92        let message_type = if message_type_code == CHUNK_MESSAGE {
93            MessageChunkType::Message
94        } else if message_type_code == OPEN_SECURE_CHANNEL_MESSAGE {
95            MessageChunkType::OpenSecureChannel
96        } else if message_type_code == CLOSE_SECURE_CHANNEL_MESSAGE {
97            MessageChunkType::CloseSecureChannel
98        } else {
99            error!("Invalid message code");
100            return Err(StatusCode::BadDecodingError);
101        };
102
103        let chunk_type_code = read_u8(stream)?;
104        let is_final = match chunk_type_code {
105            CHUNK_FINAL => MessageIsFinalType::Final,
106            CHUNK_INTERMEDIATE => MessageIsFinalType::Intermediate,
107            CHUNK_FINAL_ERROR => MessageIsFinalType::FinalError,
108            _ => {
109                error!("Invalid chunk type");
110                return Err(StatusCode::BadDecodingError);
111            }
112        };
113
114        let message_size = read_u32(stream)?;
115        let secure_channel_id = read_u32(stream)?;
116
117        Ok(MessageChunkHeader {
118            message_type,
119            is_final,
120            message_size,
121            secure_channel_id,
122        })
123    }
124}
125
126impl MessageChunkHeader {}
127
128/// A chunk holds a message or a portion of a message, if the message has been split into multiple chunks.
129/// The chunk's data may be signed and encrypted. To extract the message requires all the chunks
130/// to be available in sequence so they can be formed back into the message.
131#[derive(Debug)]
132pub struct MessageChunk {
133    /// All of the chunk's data including headers, payload, padding, signature
134    pub data: Vec<u8>,
135}
136
137impl BinaryEncoder<MessageChunk> for MessageChunk {
138    fn byte_len(&self) -> usize {
139        self.data.len()
140    }
141
142    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
143        stream.write(&self.data).map_err(|_| {
144            error!("Encoding error while writing to stream");
145            StatusCode::BadEncodingError
146        })
147    }
148
149    fn decode<S: Read>(
150        in_stream: &mut S,
151        decoding_options: &DecodingOptions,
152    ) -> EncodingResult<Self> {
153        // Read the header out first
154        let chunk_header =
155            MessageChunkHeader::decode(in_stream, decoding_options).map_err(|err| {
156                error!("Cannot decode chunk header {:?}", err);
157                StatusCode::BadCommunicationError
158            })?;
159
160        let message_size = chunk_header.message_size as usize;
161        if decoding_options.max_chunk_count > 0 && message_size > decoding_options.max_chunk_count {
162            // Message_size should be sanity checked and rejected if too large.
163            Err(StatusCode::BadTcpMessageTooLarge)
164        } else {
165            // Now make a buffer to write the header and message into
166            let data = vec![0u8; message_size];
167            let mut stream = Cursor::new(data);
168
169            // Write header to a buffer
170            let chunk_header_size = chunk_header.encode(&mut stream)?;
171            assert_eq!(chunk_header_size, MESSAGE_CHUNK_HEADER_SIZE);
172
173            // Get the data (with header written to it)
174            let mut data = stream.into_inner();
175
176            // Read remainder of stream into slice after the header
177            let _ = in_stream.read_exact(&mut data[chunk_header_size..]);
178
179            Ok(MessageChunk { data })
180        }
181    }
182}
183
184impl MessageChunk {
185    pub fn new(
186        sequence_number: u32,
187        request_id: u32,
188        message_type: MessageChunkType,
189        is_final: MessageIsFinalType,
190        secure_channel: &SecureChannel,
191        data: &[u8],
192    ) -> Result<MessageChunk, StatusCode> {
193        // security header depends on message type
194        let security_header = secure_channel.make_security_header(message_type);
195        let sequence_header = SequenceHeader {
196            sequence_number,
197            request_id,
198        };
199
200        // Calculate the chunk body size
201        let mut message_size = MESSAGE_CHUNK_HEADER_SIZE;
202        message_size += security_header.byte_len();
203        message_size += sequence_header.byte_len();
204        message_size += data.len();
205
206        trace!(
207            "Creating a chunk with a size of {}, data excluding padding & signature",
208            message_size
209        );
210        let secure_channel_id = secure_channel.secure_channel_id();
211        let chunk_header = MessageChunkHeader {
212            message_type,
213            is_final,
214            message_size: message_size as u32,
215            secure_channel_id,
216        };
217
218        let mut stream = Cursor::new(vec![0u8; message_size]);
219        // write chunk header
220        let _ = chunk_header.encode(&mut stream);
221        // write security header
222        let _ = security_header.encode(&mut stream);
223        // write sequence header
224        let _ = sequence_header.encode(&mut stream);
225        // write message
226        let _ = stream.write(data);
227
228        Ok(MessageChunk {
229            data: stream.into_inner(),
230        })
231    }
232
233    /// Calculates the body size that fit inside of a message chunk of a particular size.
234    /// This requires calculating the size of the header, the signature, padding etc. and deducting it
235    /// to reveal the message size
236    pub fn body_size_from_message_size(
237        message_type: MessageChunkType,
238        secure_channel: &SecureChannel,
239        message_size: usize,
240    ) -> Result<usize, ()> {
241        if message_size < MIN_CHUNK_SIZE {
242            error!(
243                "message size {} is less than minimum allowed by the spec",
244                message_size
245            );
246            Err(())
247        } else {
248            let security_header = secure_channel.make_security_header(message_type);
249
250            let mut data_size = MESSAGE_CHUNK_HEADER_SIZE;
251            data_size += security_header.byte_len();
252            data_size += (SequenceHeader {
253                sequence_number: 0,
254                request_id: 0,
255            })
256            .byte_len();
257
258            // 1 byte == most padding
259            let signature_size = secure_channel.signature_size(&security_header);
260            data_size += secure_channel
261                .padding_size(&security_header, 1, signature_size)
262                .0;
263
264            // signature length
265            data_size += signature_size;
266
267            // Message size is what's left
268            Ok(message_size - data_size)
269        }
270    }
271
272    pub fn message_header(
273        &self,
274        decoding_options: &DecodingOptions,
275    ) -> Result<MessageChunkHeader, StatusCode> {
276        // Message header is first so just read it
277        let mut stream = Cursor::new(&self.data);
278        MessageChunkHeader::decode(&mut stream, decoding_options)
279    }
280
281    pub fn security_header(
282        &self,
283        decoding_options: &DecodingOptions,
284    ) -> Result<SecurityHeader, StatusCode> {
285        // Message header is first so just read it
286        let mut stream = Cursor::new(&self.data);
287        let message_header = MessageChunkHeader::decode(&mut stream, decoding_options)?;
288        let security_header = if message_header.message_type == MessageChunkType::OpenSecureChannel
289        {
290            SecurityHeader::Asymmetric(AsymmetricSecurityHeader::decode(
291                &mut stream,
292                decoding_options,
293            )?)
294        } else {
295            SecurityHeader::Symmetric(SymmetricSecurityHeader::decode(
296                &mut stream,
297                decoding_options,
298            )?)
299        };
300        Ok(security_header)
301    }
302
303    pub fn is_open_secure_channel(&self, decoding_options: &DecodingOptions) -> bool {
304        if let Ok(message_header) = self.message_header(decoding_options) {
305            message_header.message_type.is_open_secure_channel()
306        } else {
307            false
308        }
309    }
310
311    pub fn chunk_info(
312        &self,
313        secure_channel: &SecureChannel,
314    ) -> std::result::Result<ChunkInfo, StatusCode> {
315        ChunkInfo::new(self, secure_channel)
316    }
317}