Skip to main content

tock_tbf/
parse.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Tock Binary Format parsing code.
6
7use core::{mem, str};
8
9use crate::types;
10
11/// Parse the TBF header length and the entire length of the TBF binary.
12///
13/// ## Return
14///
15/// If all parsing is successful:
16/// - Ok((Version, TBF header length, entire TBF length))
17///
18/// If we cannot parse the header because we have run out of flash, or the
19/// values are entirely wrong we return `UnableToParse`. This means we have hit
20/// the end of apps in flash.
21/// - Err(InitialTbfParseError::UnableToParse)
22///
23/// Any other error we return an error and the length of the entire app so that
24/// we can skip over it and check for the next app.
25/// - Err(InitialTbfParseError::InvalidHeader(app_length))
26pub fn parse_tbf_header_lengths(
27    app: &[u8; 8],
28) -> Result<(u16, u16, u32), types::InitialTbfParseError> {
29    // Version is the first 16 bits of the app TBF contents. We need this to
30    // correctly parse the other lengths.
31    //
32    // # Safety
33    // We trust that the version number has been checked prior to running this
34    // parsing code. That is, whatever loaded this application has verified that
35    // the version is valid and therefore we can trust it.
36    let version = u16::from_le_bytes([app[0], app[1]]);
37
38    match version {
39        2 => {
40            // In version 2, the next 16 bits after the version represent
41            // the size of the TBF header in bytes.
42            let tbf_header_size = u16::from_le_bytes([app[2], app[3]]);
43
44            // The next 4 bytes are the size of the entire app's TBF space
45            // including the header. This also must be checked before parsing
46            // this header and we trust the value in flash.
47            let tbf_size = u32::from_le_bytes([app[4], app[5], app[6], app[7]]);
48
49            // Check that the header length isn't greater than the entire app,
50            // and is at least as large as the v2 required header. If that at
51            // least looks good then return the sizes.
52            if u32::from(tbf_header_size) > tbf_size
53                || usize::from(tbf_header_size) < types::TBF_HEADER_V2_BASE_SIZE
54            {
55                Err(types::InitialTbfParseError::InvalidHeader(tbf_size))
56            } else {
57                Ok((version, tbf_header_size, tbf_size))
58            }
59        }
60
61        // Since we have to trust the total size, and by extension the version
62        // number, if we don't know how to handle the version this must not be
63        // an actual app. Likely this is just the end of the app linked list.
64        _ => Err(types::InitialTbfParseError::UnableToParse),
65    }
66}
67
68/// Parse a TBF header stored in flash.
69///
70/// The `header` must be a slice that only contains the TBF header. The caller
71/// should use the `parse_tbf_header_lengths()` function to determine this
72/// length to create the correct sized slice.
73pub fn parse_tbf_header(
74    header: &[u8],
75    version: u16,
76) -> Result<types::TbfHeader<'_>, types::TbfParseError> {
77    match version {
78        2 => {
79            // Get the required base. This will succeed because we parsed the
80            // first bit of the header already in `parse_tbf_header_lengths()`.
81            let tbf_header_base: types::TbfHeaderV2Base = header.try_into()?;
82
83            // Calculate checksum. The checksum is the XOR of each 4 byte word
84            // in the header.
85            let mut checksum: u32 = 0;
86
87            // Get an iterator across 4 byte fields in the header.
88            let (header_chunks, _remainder) = header.as_chunks::<4>();
89
90            // Iterate all chunks and XOR the chunks to compute the checksum.
91            for (i, chunk) in header_chunks.iter().enumerate() {
92                let word = u32::from_le_bytes(*chunk);
93                if i == 3 {
94                    // Skip the checksum field.
95                } else {
96                    checksum ^= word;
97                }
98            }
99
100            // Verify the header matches.
101            if checksum != tbf_header_base.checksum {
102                return Err(types::TbfParseError::ChecksumMismatch(
103                    tbf_header_base.checksum,
104                    checksum,
105                ));
106            }
107
108            // Get the rest of the header. The `remaining` variable will
109            // continue to hold the remainder of the header we have not
110            // processed.
111            let mut remaining = header
112                .get(types::TBF_HEADER_V2_BASE_SIZE..)
113                .ok_or(types::TbfParseError::NotEnoughFlash)?;
114
115            // If there is nothing left in the header then this is just a
116            // padding "app" between two other apps.
117            if remaining.len() == 0 {
118                // Just padding.
119                Ok(types::TbfHeader::Padding(tbf_header_base))
120            } else {
121                // This is an actual app.
122
123                // Places to save fields that we parse out of the header
124                // options.
125                let mut main_pointer: Option<types::TbfHeaderV2Main> = None;
126                let mut program_pointer: Option<types::TbfHeaderV2Program> = None;
127                let mut wfr_pointer: Option<&[u8]> = None;
128                let mut app_name_str = "";
129                let mut fixed_address_pointer: Option<&[u8]> = None;
130                let mut permissions_pointer: Option<&[u8]> = None;
131                let mut storage_permissions_pointer: Option<&[u8]> = None;
132                let mut kernel_version: Option<types::TbfHeaderV2KernelVersion> = None;
133                let mut short_id: Option<types::TbfHeaderV2ShortId> = None;
134
135                // Iterate the remainder of the header looking for TLV entries.
136                while remaining.len() > 0 {
137                    // Get the T and L portions of the next header (if it is
138                    // there).
139                    let tlv_header: types::TbfTlv = remaining
140                        .get(0..4)
141                        .ok_or(types::TbfParseError::NotEnoughFlash)?
142                        .try_into()?;
143                    remaining = remaining
144                        .get(4..)
145                        .ok_or(types::TbfParseError::NotEnoughFlash)?;
146
147                    match tlv_header.tipe {
148                        types::TbfHeaderTypes::TbfHeaderMain => {
149                            let entry_len = mem::size_of::<types::TbfHeaderV2Main>();
150                            // If there is already a header do nothing: if this is a second Main
151                            // keep the first one, if it's a Program we ignore the Main
152                            if main_pointer.is_none() {
153                                if tlv_header.length as usize == entry_len {
154                                    main_pointer = Some(
155                                        remaining
156                                            .get(0..entry_len)
157                                            .ok_or(types::TbfParseError::NotEnoughFlash)?
158                                            .try_into()?,
159                                    );
160                                } else {
161                                    return Err(types::TbfParseError::BadTlvEntry(
162                                        tlv_header.tipe as usize,
163                                    ));
164                                }
165                            }
166                        }
167                        types::TbfHeaderTypes::TbfHeaderProgram => {
168                            let entry_len = mem::size_of::<types::TbfHeaderV2Program>();
169                            if program_pointer.is_none() {
170                                if tlv_header.length as usize == entry_len {
171                                    program_pointer = Some(
172                                        remaining
173                                            .get(0..entry_len)
174                                            .ok_or(types::TbfParseError::NotEnoughFlash)?
175                                            .try_into()?,
176                                    );
177                                } else {
178                                    return Err(types::TbfParseError::BadTlvEntry(
179                                        tlv_header.tipe as usize,
180                                    ));
181                                }
182                            }
183                        }
184                        types::TbfHeaderTypes::TbfHeaderWriteableFlashRegions => {
185                            // Length must be a multiple of the size of a region definition.
186                            if (tlv_header.length as usize)
187                                .is_multiple_of(mem::size_of::<
188                                    types::TbfHeaderV2WriteableFlashRegion,
189                                >())
190                            {
191                                // Capture a slice with just the wfr information.
192                                let wfr_slice = remaining
193                                    .get(0..tlv_header.length as usize)
194                                    .ok_or(types::TbfParseError::NotEnoughFlash)?;
195
196                                wfr_pointer = Some(wfr_slice);
197                            } else {
198                                return Err(types::TbfParseError::BadTlvEntry(
199                                    tlv_header.tipe as usize,
200                                ));
201                            }
202                        }
203
204                        types::TbfHeaderTypes::TbfHeaderPackageName => {
205                            let name_buf = remaining
206                                .get(0..tlv_header.length as usize)
207                                .ok_or(types::TbfParseError::NotEnoughFlash)?;
208
209                            str::from_utf8(name_buf)
210                                .map(|name_str| {
211                                    app_name_str = name_str;
212                                })
213                                .or(Err(types::TbfParseError::BadProcessName))?;
214                        }
215
216                        types::TbfHeaderTypes::TbfHeaderFixedAddresses => {
217                            let entry_len = mem::size_of::<types::TbfHeaderV2FixedAddresses>();
218                            if tlv_header.length as usize == entry_len {
219                                fixed_address_pointer = Some(
220                                    remaining
221                                        .get(0..entry_len)
222                                        .ok_or(types::TbfParseError::NotEnoughFlash)?,
223                                );
224                            } else {
225                                return Err(types::TbfParseError::BadTlvEntry(
226                                    tlv_header.tipe as usize,
227                                ));
228                            }
229                        }
230
231                        types::TbfHeaderTypes::TbfHeaderPermissions => {
232                            permissions_pointer = Some(
233                                remaining
234                                    .get(0..tlv_header.length as usize)
235                                    .ok_or(types::TbfParseError::NotEnoughFlash)?,
236                            );
237                        }
238
239                        types::TbfHeaderTypes::TbfHeaderStoragePermissions => {
240                            storage_permissions_pointer = Some(
241                                remaining
242                                    .get(0..tlv_header.length as usize)
243                                    .ok_or(types::TbfParseError::NotEnoughFlash)?,
244                            );
245                        }
246
247                        types::TbfHeaderTypes::TbfHeaderKernelVersion => {
248                            let entry_len = mem::size_of::<types::TbfHeaderV2KernelVersion>();
249                            if tlv_header.length as usize == entry_len {
250                                kernel_version = Some(
251                                    remaining
252                                        .get(0..entry_len)
253                                        .ok_or(types::TbfParseError::NotEnoughFlash)?
254                                        .try_into()?,
255                                );
256                            } else {
257                                return Err(types::TbfParseError::BadTlvEntry(
258                                    tlv_header.tipe as usize,
259                                ));
260                            }
261                        }
262
263                        types::TbfHeaderTypes::TbfHeaderShortId => {
264                            let entry_len = mem::size_of::<types::TbfHeaderV2ShortId>();
265                            if tlv_header.length as usize == entry_len {
266                                short_id = Some(
267                                    remaining
268                                        .get(0..entry_len)
269                                        .ok_or(types::TbfParseError::NotEnoughFlash)?
270                                        .try_into()?,
271                                );
272                            } else {
273                                return Err(types::TbfParseError::BadTlvEntry(
274                                    tlv_header.tipe as usize,
275                                ));
276                            }
277                        }
278
279                        _ => {}
280                    }
281
282                    // All TLV blocks are padded to 4 bytes, so we need to skip
283                    // more if the length is not a multiple of 4.
284                    let skip_len: usize = (tlv_header.length as usize)
285                        .checked_next_multiple_of(4)
286                        .ok_or(types::TbfParseError::InternalError)?;
287                    remaining = remaining
288                        .get(skip_len..)
289                        .ok_or(types::TbfParseError::NotEnoughFlash)?;
290                }
291
292                let tbf_header = types::TbfHeaderV2 {
293                    base: tbf_header_base,
294                    main: main_pointer,
295                    program: program_pointer,
296                    package_name: Some(app_name_str),
297                    writeable_regions: wfr_pointer,
298                    fixed_addresses: fixed_address_pointer,
299                    permissions: permissions_pointer,
300                    storage_permissions: storage_permissions_pointer,
301                    kernel_version,
302                    short_id,
303                };
304
305                Ok(types::TbfHeader::TbfHeaderV2(tbf_header))
306            }
307        }
308        _ => Err(types::TbfParseError::UnsupportedVersion(version)),
309    }
310}
311
312pub fn parse_tbf_footer(
313    footers: &'static [u8],
314) -> Result<(types::TbfFooterV2Credentials, u32), types::TbfParseError> {
315    let mut remaining = footers;
316    let tlv_header: types::TbfTlv = remaining
317        .get(0..4)
318        .ok_or(types::TbfParseError::NotEnoughFlash)?
319        .try_into()?;
320    remaining = remaining
321        .get(4..)
322        .ok_or(types::TbfParseError::NotEnoughFlash)?;
323    match tlv_header.tipe {
324        types::TbfHeaderTypes::TbfFooterCredentials => {
325            let credential: types::TbfFooterV2Credentials = remaining
326                .get(0..tlv_header.length as usize)
327                .ok_or(types::TbfParseError::NotEnoughFlash)?
328                .try_into()?;
329            // Check length here
330            let length = tlv_header.length;
331            Ok((credential, length as u32))
332        }
333        _ => Err(types::TbfParseError::BadTlvEntry(tlv_header.tipe as usize)),
334    }
335}