1mod error;
4mod image_header;
5mod image_tlv;
6mod mcuboot_constants;
7
8use std::{fs::File, io::Cursor, path::Path};
9
10use log::trace;
11
12use crate::{
13 image_header::ImageHeader,
14 image_tlv::{
15 ImageTlvAreaHeader, ImageTlvAreaType, ImageTlvEntry, ImageTlvEntryType, TakeTlvEntry,
16 },
17};
18
19pub use crate::error::Error;
20
21#[derive(Debug)]
22pub struct ImageMetadata {
24 pub header: ImageHeader,
25 pub sha256_hash: Vec<u8>,
27 pub signature_key_hash: Vec<u8>,
28 pub signature: Vec<u8>,
29}
30
31pub fn parse_image(path: impl AsRef<Path>) -> Result<ImageMetadata, Error> {
44 use std::io::Read;
45
46 let mut file = File::open(path)?;
47 let mut data = Vec::new();
48 file.read_to_end(&mut data)?;
49 let mut cursor = Cursor::new(data.as_slice());
50
51 let header: ImageHeader = (&mut cursor).try_into()?;
52 trace!("Image header: {:#?}", header);
53
54 cursor.set_position(header.header_size as u64 + header.image_size as u64);
56
57 let tlv_header = ImageTlvAreaHeader::try_from(&mut cursor)?;
58
59 let tlv_header = match tlv_header.area_type() {
60 ImageTlvAreaType::Protected => {
61 trace!(
62 "Found TLV protected area of size: {}bytes",
63 tlv_header.area_size()
64 );
65
66 cursor.set_position(cursor.position() + tlv_header.area_payload_size()? as u64);
68 ImageTlvAreaHeader::try_from(&mut cursor)?
69 }
70 ImageTlvAreaType::Unprotected => tlv_header,
71 ImageTlvAreaType::Invalid(magic) => return Err(Error::InvalidTlvMagic(magic)),
72 };
73
74 let area_type = tlv_header.area_type();
76 if let ImageTlvAreaType::Invalid(_) | ImageTlvAreaType::Protected = area_type {
77 return Err(Error::NonProtectedTlvAreaNotFound(area_type));
78 }
79
80 trace!(
81 "Reading TLV entries from TLV area ({}bytes)",
82 tlv_header.area_size()
83 );
84
85 let mut unprotected_tlvs = read_tlv_entries(&mut cursor, &tlv_header)?;
86
87 let signature = unprotected_tlvs.take(ImageTlvEntryType::EcdsaSig)?.value;
93 let signature_key_hash = unprotected_tlvs.take(ImageTlvEntryType::KeyHash)?.value;
94 let sha256_hash = unprotected_tlvs.take(ImageTlvEntryType::Sha256)?.value;
95
96 Ok(ImageMetadata {
97 header,
98 sha256_hash,
99 signature_key_hash,
100 signature,
101 })
102}
103
104fn read_tlv_entries(
105 cursor: &mut Cursor<&[u8]>,
106 tlv_header: &ImageTlvAreaHeader,
107) -> Result<Vec<ImageTlvEntry>, Error> {
108 let mut tlvs = Vec::new();
109
110 let end_of_area = cursor.position() + tlv_header.area_payload_size()? as u64;
111 while cursor.position() < end_of_area {
112 let tlv_entry = ImageTlvEntry::try_from(&mut *cursor)?;
113 trace!(
114 "Read TLV entry: {}, {:?}, value: {:x?}",
115 tlv_entry.len, tlv_entry.type_, tlv_entry.value
116 );
117 tlvs.push(tlv_entry);
118 }
119 Ok(tlvs)
120}
121
122fn read_u8(c: &mut Cursor<&[u8]>) -> Result<u8, Error> {
123 use std::io::Read;
124
125 let mut buf = [0u8; 1];
126 c.read_exact(&mut buf).map_err(Error::ImageParsing)?;
127 Ok(buf[0])
128}
129
130fn read_u16(c: &mut Cursor<&[u8]>) -> Result<u16, Error> {
131 use std::io::Read;
132
133 let mut buf = [0u8; 2];
134 c.read_exact(&mut buf).map_err(Error::ImageParsing)?;
135 Ok(u16::from_le_bytes(buf))
136}
137
138fn read_u32(c: &mut Cursor<&[u8]>) -> Result<u32, Error> {
139 use std::io::Read;
140
141 let mut buf = [0u8; 4];
142 c.read_exact(&mut buf).map_err(Error::ImageParsing)?;
143 Ok(u32::from_le_bytes(buf))
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn parsing_valid_image_works() {
152 let path = Path::new("test/test_image.signed.bin");
153 let image = parse_image(&path).expect("Failed to parse valid image");
154
155 println!("Image:\n{:#x?}", image);
156
157 assert_eq!(image.header.protect_tlv_size, 0);
158 assert_eq!(image.header.image_size, 852540);
159 assert_eq!(
160 image.header.flags,
161 mcuboot_constants::ImageHeaderFlags::empty()
162 );
163 assert_eq!(image.header.version.major, 1);
164 assert_eq!(image.header.version.minor, 4);
165 assert_eq!(image.header.version.revision, 2);
166 assert_eq!(image.header.version.build_num, 0);
167 assert_eq!(
168 image.sha256_hash,
169 vec![
170 0x80, 0xf3, 0xc5, 0xfb, 0x50, 0xa0, 0x16, 0xc1, 0xf6, 0xe4, 0x57, 0x49, 0x96, 0x47,
171 0x2e, 0xb3, 0xf7, 0xb6, 0x14, 0xee, 0xc2, 0xd6, 0xa5, 0xd0, 0x96, 0xbc, 0x7, 0xb6,
172 0x9a, 0x2d, 0x81, 0x21
173 ]
174 );
175 assert_eq!(
176 image.signature_key_hash,
177 vec![
178 0xe3, 0x4, 0x66, 0xf6, 0xb8, 0x47, 0xc, 0x1f, 0x29, 0x7, 0xb, 0x17, 0xf1, 0xe2,
179 0xd3, 0xe9, 0x4d, 0x44, 0x5e, 0x3f, 0x60, 0x80, 0x87, 0xfd, 0xc7, 0x11, 0xe4, 0x38,
180 0x2b, 0xb5, 0x38, 0xb6
181 ]
182 );
183 assert_eq!(
184 image.signature,
185 vec![
186 0x30, 0x44, 0x2, 0x20, 0x23, 0x14, 0xd5, 0xd3, 0x86, 0xeb, 0x61, 0x1d, 0xd6, 0xf5,
187 0xa9, 0xa8, 0x2, 0xcf, 0x7e, 0x26, 0xcc, 0x95, 0x57, 0x99, 0x43, 0xf5, 0xd6, 0xa5,
188 0xd0, 0x30, 0xe6, 0x22, 0x73, 0x26, 0x56, 0x92, 0x2, 0x20, 0xa, 0x30, 0xf7, 0x54,
189 0xb2, 0x1c, 0x22, 0x23, 0xe1, 0x75, 0xfa, 0x43, 0x49, 0x3b, 0xc1, 0x87, 0x41, 0x32,
190 0xab, 0xa4, 0xc3, 0xc4, 0xba, 0x75, 0xd, 0xc4, 0xa4, 0x18, 0xc4, 0x9e, 0xea, 0x83
191 ]
192 );
193 }
194
195 #[test]
196 fn parsing_valid_encrypted_image_works() {
197 let path = Path::new("test/test_image.signed.encrypted.ota.bin");
198 let image = parse_image(&path).expect("Failed to parse valid image");
199
200 println!("Image Header:\n{:#x?}", image);
201
202 assert_eq!(image.header.protect_tlv_size, 0);
203 assert_eq!(image.header.image_size, 852544);
204 assert_eq!(
205 image.header.flags,
206 mcuboot_constants::ImageHeaderFlags::IMAGE_F_ENCRYPTED_AES128
207 );
208 assert_eq!(image.header.version.major, 1);
209 assert_eq!(image.header.version.minor, 4);
210 assert_eq!(image.header.version.revision, 2);
211 assert_eq!(image.header.version.build_num, 0);
212 assert_eq!(
213 image.sha256_hash,
214 vec![
215 0x3, 0xb3, 0xbb, 0x6a, 0xfb, 0x5b, 0xce, 0xb2, 0xef, 0x45, 0x20, 0x69, 0x25, 0x38,
216 0xbe, 0xd7, 0xed, 0x1b, 0xb4, 0x9a, 0xfd, 0xd1, 0xab, 0x56, 0x91, 0x1d, 0x13, 0x5f,
217 0x4, 0xa3, 0x41, 0x7a
218 ]
219 );
220 assert_eq!(
221 image.signature_key_hash,
222 vec![
223 0xe3, 0x4, 0x66, 0xf6, 0xb8, 0x47, 0xc, 0x1f, 0x29, 0x7, 0xb, 0x17, 0xf1, 0xe2,
224 0xd3, 0xe9, 0x4d, 0x44, 0x5e, 0x3f, 0x60, 0x80, 0x87, 0xfd, 0xc7, 0x11, 0xe4, 0x38,
225 0x2b, 0xb5, 0x38, 0xb6
226 ]
227 );
228 assert_eq!(
229 image.signature,
230 vec![
231 0x30, 0x45, 0x2, 0x21, 0x0, 0xc2, 0x68, 0xa8, 0x4a, 0x21, 0x3b, 0xc1, 0x97, 0xc7,
232 0x5c, 0x4e, 0x3c, 0xf0, 0x8f, 0x58, 0xe2, 0x79, 0xf1, 0xb1, 0xb4, 0x94, 0x5a, 0x9e,
233 0x8f, 0xb0, 0x1b, 0x21, 0x58, 0x95, 0xaa, 0xe5, 0xdb, 0x2, 0x20, 0x17, 0x83, 0xb8,
234 0x10, 0x7c, 0xb4, 0x49, 0x25, 0xbb, 0x2f, 0x44, 0x2d, 0x6c, 0x7b, 0x29, 0x8f, 0x15,
235 0x3d, 0x6a, 0x7, 0x33, 0xfe, 0x9b, 0x3e, 0xb7, 0x91, 0xe8, 0xa0, 0x19, 0x98, 0x81,
236 0x16
237 ]
238 );
239 }
240}