Skip to content

Commit 36ad129

Browse files
committed
feat: brought updates from tock-tbf into tbf-parser
1 parent d3c5ff7 commit 36ad129

5 files changed

Lines changed: 181 additions & 5 deletions

File tree

.github/.lastcommsha

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4586a000ce72d970be7cfaef0ae40371c1e80177
1+
bc8ae6ddac5c165f9d0d14f1278be0068d225f5e

.lastcommsha

86 Bytes
Binary file not shown.

tbf-parser/src/parse.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ pub fn parse_tbf_header(
143143
types::TbfHeaderV2StoragePermissions<8>,
144144
> = None;
145145
let mut kernel_version: Option<types::TbfHeaderV2KernelVersion> = None;
146+
let mut short_id: Option<types::TbfHeaderV2ShortId> = None;
146147

147148
// Iterate the remainder of the header looking for TLV entries.
148149
while !remaining.is_empty() {
@@ -282,6 +283,22 @@ pub fn parse_tbf_header(
282283
}
283284
}
284285

286+
types::TbfHeaderTypes::TbfHeaderShortId => {
287+
let entry_len = mem::size_of::<types::TbfHeaderV2ShortId>();
288+
if tlv_header.length as usize == entry_len {
289+
short_id = Some(
290+
remaining
291+
.get(0..entry_len)
292+
.ok_or(types::TbfParseError::NotEnoughFlash)?
293+
.try_into()?,
294+
);
295+
} else {
296+
return Err(types::TbfParseError::BadTlvEntry(
297+
tlv_header.tipe as usize,
298+
));
299+
}
300+
}
301+
285302
_ => {}
286303
}
287304

@@ -303,6 +320,7 @@ pub fn parse_tbf_header(
303320
permissions: permissions_pointer,
304321
storage_permissions: storage_permissions_pointer,
305322
kernel_version,
323+
short_id,
306324
};
307325

308326
Ok(types::TbfHeader::TbfHeaderV2(tbf_header))

tbf-parser/src/types.rs

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl fmt::Debug for TbfParseError {
106106
TbfParseError::TooManyEntries(tipe) => {
107107
write!(
108108
f,
109-
"There are too many variable entries of {tipe} for Tock to parse"
109+
"There are too many variable entries of {tipe} for Tock to parse",
110110
)
111111
}
112112
TbfParseError::PackageNameTooLong => write!(f, "The package name is too long."),
@@ -137,6 +137,7 @@ pub enum TbfHeaderTypes {
137137
TbfHeaderStoragePermissions = 7,
138138
TbfHeaderKernelVersion = 8,
139139
TbfHeaderProgram = 9,
140+
TbfHeaderShortId = 10,
140141
TbfFooterCredentials = 128,
141142

142143
/// Some field in the header that we do not understand. Since the TLV format
@@ -254,6 +255,14 @@ pub struct TbfHeaderV2KernelVersion {
254255
minor: u16,
255256
}
256257

258+
/// The v2 ShortId for apps.
259+
///
260+
/// Header to specify a fixed ShortID for an app.
261+
#[derive(Clone, Copy, Debug)]
262+
pub struct TbfHeaderV2ShortId {
263+
short_id: Option<core::num::NonZeroU32>,
264+
}
265+
257266
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
258267
pub enum TbfFooterV2CredentialsType {
259268
Reserved = 0,
@@ -262,6 +271,7 @@ pub enum TbfFooterV2CredentialsType {
262271
SHA256 = 3,
263272
SHA384 = 4,
264273
SHA512 = 5,
274+
EcdsaNistP256 = 6,
265275
}
266276

267277
/// Reference: https://github.com/tock/tock/blob/master/doc/reference/trd-appid.md#52-credentials-footer
@@ -274,6 +284,7 @@ pub enum TbfFooterV2Credentials {
274284
SHA256(TbfFooterV2SHA<32>),
275285
SHA384(TbfFooterV2SHA<48>),
276286
SHA512(TbfFooterV2SHA<64>),
287+
EcdsaNistP256(TbfFooterV2Ecdsa<32>),
277288
}
278289

279290
#[derive(Clone, Copy, Debug)]
@@ -287,6 +298,12 @@ pub struct TbfFooterV2RSA<const L: usize> {
287298
signature: [u8; L],
288299
}
289300

301+
#[derive(Clone, Copy, Debug)]
302+
pub struct TbfFooterV2Ecdsa<const L: usize> {
303+
signature_r: [u8; L],
304+
signature_s: [u8; L],
305+
}
306+
290307
impl<const L: usize> TbfFooterV2SHA<L> {
291308
pub fn get_format(&self) -> Result<TbfFooterV2CredentialsType, TbfParseError> {
292309
match L {
@@ -320,6 +337,20 @@ impl<const L: usize> TbfFooterV2RSA<L> {
320337
}
321338
}
322339

340+
impl<const L: usize> TbfFooterV2Ecdsa<L> {
341+
pub fn get_format(&self) -> Result<TbfFooterV2CredentialsType, TbfParseError> {
342+
Ok(TbfFooterV2CredentialsType::EcdsaNistP256)
343+
}
344+
345+
pub fn get_signature_r(&self) -> &[u8; L] {
346+
&self.signature_r
347+
}
348+
349+
pub fn get_signature_s(&self) -> &[u8; L] {
350+
&self.signature_s
351+
}
352+
}
353+
323354
// Conversion functions from slices to the various TBF fields.
324355

325356
impl core::convert::TryFrom<&[u8]> for TbfHeaderV2Base {
@@ -372,6 +403,7 @@ impl core::convert::TryFrom<u16> for TbfHeaderTypes {
372403
7 => Ok(TbfHeaderTypes::TbfHeaderStoragePermissions),
373404
8 => Ok(TbfHeaderTypes::TbfHeaderKernelVersion),
374405
9 => Ok(TbfHeaderTypes::TbfHeaderProgram),
406+
10 => Ok(TbfHeaderTypes::TbfHeaderShortId),
375407
128 => Ok(TbfHeaderTypes::TbfFooterCredentials),
376408
_ => Ok(TbfHeaderTypes::Unknown),
377409
}
@@ -675,6 +707,20 @@ impl core::convert::TryFrom<&[u8]> for TbfHeaderV2KernelVersion {
675707
}
676708
}
677709

710+
impl core::convert::TryFrom<&[u8]> for TbfHeaderV2ShortId {
711+
type Error = TbfParseError;
712+
713+
fn try_from(b: &[u8]) -> Result<TbfHeaderV2ShortId, Self::Error> {
714+
Ok(TbfHeaderV2ShortId {
715+
short_id: core::num::NonZeroU32::new(u32::from_le_bytes(
716+
b.get(0..4)
717+
.ok_or(TbfParseError::InternalError)?
718+
.try_into()?,
719+
)),
720+
})
721+
}
722+
}
723+
678724
impl core::convert::TryFrom<&[u8]> for TbfFooterV2Credentials {
679725
type Error = TbfParseError;
680726

@@ -691,6 +737,7 @@ impl core::convert::TryFrom<&[u8]> for TbfFooterV2Credentials {
691737
3 => TbfFooterV2CredentialsType::SHA256,
692738
4 => TbfFooterV2CredentialsType::SHA384,
693739
5 => TbfFooterV2CredentialsType::SHA512,
740+
6 => TbfFooterV2CredentialsType::EcdsaNistP256,
694741
_ => {
695742
return Err(TbfParseError::InternalError);
696743
}
@@ -702,6 +749,7 @@ impl core::convert::TryFrom<&[u8]> for TbfFooterV2Credentials {
702749
TbfFooterV2CredentialsType::SHA256 => 32,
703750
TbfFooterV2CredentialsType::SHA384 => 48,
704751
TbfFooterV2CredentialsType::SHA512 => 64,
752+
TbfFooterV2CredentialsType::EcdsaNistP256 => 64,
705753
};
706754

707755
let data = b
@@ -747,6 +795,16 @@ impl core::convert::TryFrom<&[u8]> for TbfFooterV2Credentials {
747795
.map_err(|_| TbfParseError::InternalError)?,
748796
}))
749797
}
798+
TbfFooterV2CredentialsType::EcdsaNistP256 => {
799+
Ok(TbfFooterV2Credentials::EcdsaNistP256(TbfFooterV2Ecdsa {
800+
signature_r: data[0..32]
801+
.try_into()
802+
.map_err(|_| TbfParseError::InternalError)?,
803+
signature_s: data[32..64]
804+
.try_into()
805+
.map_err(|_| TbfParseError::InternalError)?,
806+
}))
807+
}
750808
}
751809
}
752810
}
@@ -760,6 +818,7 @@ impl TbfFooterV2Credentials {
760818
TbfFooterV2Credentials::SHA256(_) => "SHA256",
761819
TbfFooterV2Credentials::SHA384(_) => "SHA384",
762820
TbfFooterV2Credentials::SHA512(_) => "SHA512",
821+
TbfFooterV2Credentials::EcdsaNistP256(_) => "EcdsaNistP256",
763822
}
764823
}
765824
}
@@ -794,6 +853,7 @@ pub struct TbfHeaderV2 {
794853
pub(crate) permissions: Option<TbfHeaderV2Permissions<8>>,
795854
pub(crate) storage_permissions: Option<TbfHeaderV2StoragePermissions<NUM_STORAGE_PERMISSIONS>>,
796855
pub(crate) kernel_version: Option<TbfHeaderV2KernelVersion>,
856+
pub(crate) short_id: Option<TbfHeaderV2ShortId>,
797857
}
798858

799859
/// Type that represents the fields of the Tock Binary Format header.
@@ -967,13 +1027,13 @@ impl TbfHeader {
9671027
}
9681028

9691029
/// Get the offset and size of a given flash region.
970-
pub fn get_writeable_flash_region(&self, index: usize) -> (u32, u32) {
1030+
pub fn get_writeable_flash_region(&self, index: usize) -> (usize, usize) {
9711031
match *self {
9721032
TbfHeader::TbfHeaderV2(hd) => hd.writeable_regions.map_or((0, 0), |wrs| {
9731033
wrs.get(index).unwrap_or(&None).map_or((0, 0), |wr| {
9741034
(
975-
wr.writeable_flash_region_offset,
976-
wr.writeable_flash_region_size,
1035+
wr.writeable_flash_region_offset as usize,
1036+
wr.writeable_flash_region_size as usize,
9771037
)
9781038
})
9791039
}),
@@ -1114,4 +1174,13 @@ impl TbfHeader {
11141174
_ => 0,
11151175
}
11161176
}
1177+
1178+
/// Return the fixed ShortId of the application if it was specified in the
1179+
/// TBF header.
1180+
pub fn get_fixed_short_id(&self) -> Option<core::num::NonZeroU32> {
1181+
match self {
1182+
TbfHeader::TbfHeaderV2(hd) => hd.short_id.and_then(|si| si.short_id),
1183+
_ => None,
1184+
}
1185+
}
11171186
}

tbf-parser/tests/parse.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,92 @@ fn footer_rsa4096() {
120120
panic!("Footer is not of type 'Reserved'!");
121121
}
122122
}
123+
124+
#[test]
125+
fn shortid_valid() {
126+
let buffer: Vec<u8> = vec![
127+
0x02, 0x00, 0x2c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x70, 0x41,
128+
0x73, 0x03, 0x00, 0x08, 0x00, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00,
129+
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0xd2, 0x04, 0x00, 0x00,
130+
];
131+
132+
let (_ver, header_len, _) = parse_tbf_header_lengths(&buffer[0..8].try_into().unwrap())
133+
.ok()
134+
.unwrap();
135+
136+
let header = parse_tbf_header(&buffer[0..header_len as usize], 2).unwrap();
137+
dbg!(&header);
138+
139+
// Corrected: The expected ShortID is 1234 (0x4d2 in hex), not 0x12345678.
140+
let expected_short_id = core::num::NonZeroU32::new(1234);
141+
assert_eq!(header.get_fixed_short_id(), expected_short_id);
142+
}
143+
144+
#[test]
145+
fn shortid_invalid() {
146+
// The buffer generated by the previous step
147+
let invalid_short_buffer: Vec<u8> = vec![
148+
0x02, 0x00, 0x32, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xfc, 0xae,
149+
0x47, 0x03, 0x00, 0x08, 0x00, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00,
150+
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0xd2, 0x04, 0x00, 0x00,
151+
];
152+
153+
let result = parse_tbf_header(&invalid_short_buffer, 2);
154+
assert!(result.is_err());
155+
let error = result.unwrap_err();
156+
let debug_string = format!("{:?}", error);
157+
158+
assert!(debug_string.contains("Checksum verification failed"));
159+
}
160+
161+
#[test]
162+
fn shortid_nonexistent() {
163+
// We can reuse the `simple.dat` artifact, as it should not have a ShortID TLV.
164+
let buffer = include_bytes!("./flashes/simple.dat").to_vec();
165+
166+
let (_ver, header_len, _) = parse_tbf_header_lengths(&buffer[0..8].try_into().unwrap())
167+
.ok()
168+
.unwrap();
169+
170+
let header = parse_tbf_header(&buffer[0..header_len as usize], 2).unwrap();
171+
dbg!(&header);
172+
173+
// The core assertion: check that the ShortID is not present.
174+
assert_eq!(header.get_fixed_short_id(), None);
175+
}
176+
177+
#[test]
178+
fn ecdsa_nist_p256_smoke_test() {
179+
let tlv_type_credentials: u16 = 128; // 2 bytes
180+
let credential_format: u32 = TbfFooterV2CredentialsType::EcdsaNistP256 as u32; // 6
181+
let signature_r = [0xAA; 32];
182+
let signature_s = [0xBB; 32];
183+
184+
// Length of payload: 4 (format) + 32 (r) + 32 (s) = 68
185+
let tlv_length: u16 = 68;
186+
187+
let mut buffer = Vec::new();
188+
189+
buffer.extend_from_slice(&tlv_type_credentials.to_le_bytes()); // 2 bytes
190+
buffer.extend_from_slice(&tlv_length.to_le_bytes()); // 2 bytes
191+
buffer.extend_from_slice(&credential_format.to_le_bytes()); // 4 bytes
192+
buffer.extend_from_slice(&signature_r); // 32 bytes
193+
buffer.extend_from_slice(&signature_s); // 32 bytes
194+
195+
assert_eq!(buffer.len(), 4 + tlv_length as usize);
196+
197+
match parse_tbf_footer(&buffer) {
198+
Ok((footer, returned_footer_size)) => {
199+
assert_eq!(returned_footer_size, tlv_length as u32);
200+
if let TbfFooterV2Credentials::EcdsaNistP256(ecdsa) = footer {
201+
assert_eq!(ecdsa.get_signature_r(), &signature_r);
202+
assert_eq!(ecdsa.get_signature_s(), &signature_s);
203+
} else {
204+
panic!("Footer is not of type EcdsaNistP256!");
205+
}
206+
}
207+
Err(e) => {
208+
panic!("Failed to parse footer: {:?}", e);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)