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}