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: &[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<&[u8]> = None;
126 let mut app_name_str = "";
127 let mut fixed_address_pointer: Option<&[u8]> = None;
128 let mut permissions_pointer: Option<&[u8]> = None;
129 let mut storage_permissions_pointer: Option<&[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 .is_multiple_of(mem::size_of::<
186 types::TbfHeaderV2WriteableFlashRegion,
187 >())
188 {
189 // Capture a slice with just the wfr information.
190 let wfr_slice = remaining
191 .get(0..tlv_header.length as usize)
192 .ok_or(types::TbfParseError::NotEnoughFlash)?;
193
194 wfr_pointer = Some(wfr_slice);
195 } else {
196 return Err(types::TbfParseError::BadTlvEntry(
197 tlv_header.tipe as usize,
198 ));
199 }
200 }
201
202 types::TbfHeaderTypes::TbfHeaderPackageName => {
203 let name_buf = remaining
204 .get(0..tlv_header.length as usize)
205 .ok_or(types::TbfParseError::NotEnoughFlash)?;
206
207 str::from_utf8(name_buf)
208 .map(|name_str| {
209 app_name_str = name_str;
210 })
211 .or(Err(types::TbfParseError::BadProcessName))?;
212 }
213
214 types::TbfHeaderTypes::TbfHeaderFixedAddresses => {
215 let entry_len = mem::size_of::<types::TbfHeaderV2FixedAddresses>();
216 if tlv_header.length as usize == entry_len {
217 fixed_address_pointer = Some(
218 remaining
219 .get(0..entry_len)
220 .ok_or(types::TbfParseError::NotEnoughFlash)?,
221 );
222 } else {
223 return Err(types::TbfParseError::BadTlvEntry(
224 tlv_header.tipe as usize,
225 ));
226 }
227 }
228
229 types::TbfHeaderTypes::TbfHeaderPermissions => {
230 permissions_pointer = Some(
231 remaining
232 .get(0..tlv_header.length as usize)
233 .ok_or(types::TbfParseError::NotEnoughFlash)?,
234 );
235 }
236
237 types::TbfHeaderTypes::TbfHeaderStoragePermissions => {
238 storage_permissions_pointer = Some(
239 remaining
240 .get(0..tlv_header.length as usize)
241 .ok_or(types::TbfParseError::NotEnoughFlash)?,
242 );
243 }
244
245 types::TbfHeaderTypes::TbfHeaderKernelVersion => {
246 let entry_len = mem::size_of::<types::TbfHeaderV2KernelVersion>();
247 if tlv_header.length as usize == entry_len {
248 kernel_version = Some(
249 remaining
250 .get(0..entry_len)
251 .ok_or(types::TbfParseError::NotEnoughFlash)?
252 .try_into()?,
253 );
254 } else {
255 return Err(types::TbfParseError::BadTlvEntry(
256 tlv_header.tipe as usize,
257 ));
258 }
259 }
260
261 types::TbfHeaderTypes::TbfHeaderShortId => {
262 let entry_len = mem::size_of::<types::TbfHeaderV2ShortId>();
263 if tlv_header.length as usize == entry_len {
264 short_id = Some(
265 remaining
266 .get(0..entry_len)
267 .ok_or(types::TbfParseError::NotEnoughFlash)?
268 .try_into()?,
269 );
270 } else {
271 return Err(types::TbfParseError::BadTlvEntry(
272 tlv_header.tipe as usize,
273 ));
274 }
275 }
276
277 _ => {}
278 }
279
280 // All TLV blocks are padded to 4 bytes, so we need to skip
281 // more if the length is not a multiple of 4.
282 let skip_len: usize = (tlv_header.length as usize)
283 .checked_next_multiple_of(4)
284 .ok_or(types::TbfParseError::InternalError)?;
285 remaining = remaining
286 .get(skip_len..)
287 .ok_or(types::TbfParseError::NotEnoughFlash)?;
288 }
289
290 let tbf_header = types::TbfHeaderV2 {
291 base: tbf_header_base,
292 main: main_pointer,
293 program: program_pointer,
294 package_name: Some(app_name_str),
295 writeable_regions: wfr_pointer,
296 fixed_addresses: fixed_address_pointer,
297 permissions: permissions_pointer,
298 storage_permissions: storage_permissions_pointer,
299 kernel_version,
300 short_id,
301 };
302
303 Ok(types::TbfHeader::TbfHeaderV2(tbf_header))
304 }
305 }
306 _ => Err(types::TbfParseError::UnsupportedVersion(version)),
307 }
308}
309
310pub fn parse_tbf_footer(
311 footers: &'static [u8],
312) -> Result<(types::TbfFooterV2Credentials, u32), types::TbfParseError> {
313 let mut remaining = footers;
314 let tlv_header: types::TbfTlv = remaining
315 .get(0..4)
316 .ok_or(types::TbfParseError::NotEnoughFlash)?
317 .try_into()?;
318 remaining = remaining
319 .get(4..)
320 .ok_or(types::TbfParseError::NotEnoughFlash)?;
321 match tlv_header.tipe {
322 types::TbfHeaderTypes::TbfFooterCredentials => {
323 let credential: types::TbfFooterV2Credentials = remaining
324 .get(0..tlv_header.length as usize)
325 .ok_or(types::TbfParseError::NotEnoughFlash)?
326 .try_into()?;
327 // Check length here
328 let length = tlv_header.length;
329 Ok((credential, length as u32))
330 }
331 _ => Err(types::TbfParseError::BadTlvEntry(tlv_header.tipe as usize)),
332 }
333}