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}