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