1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
//! Application image manipulation. //! //! This isn't technically part of the GBL container format, but can be very //! useful nonetheless. use crate::crypto; use crate::error::Error; use crate::utils::Blob; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use num_traits::FromPrimitive; use std::borrow::Cow; use std::io::{self, Write}; use std::u32; use uuid::Uuid; /// An application info structure. /// /// This structure is generally ignored by the bootloader and can be used to /// implement custom checks. For example, the `version` field can be used to /// encode the firmware revision and prevent firmware downgrades. /// /// Can be passed to [`Gbl::from_parts`] to create a fully custom GBL container. /// /// [`Gbl::from_parts`]: struct.Gbl.html#method.from_parts #[derive(Debug, Copy, Clone)] pub struct AppInfo { /// Application type. Bitfield. type_: u32, /// Application version (not the version of the struct layout). version: u32, capabilities: u32, product_id: Uuid, } impl AppInfo { /// The length of the `AppInfo` structure in a raw byte stream. const LENGTH: usize = 28; /// Parse `AppInfo` from the contents of a corresponding GBL tag. pub(crate) fn parse(mut bytes: &[u8]) -> Result<Self, Error> { if bytes.len() != 28 { return Err(Error::parse_err(format!( "invalid length for app info: {} bytes (expected 28)", bytes.len() ))); } let type_ = bytes.read_u32::<LittleEndian>()?; let version = bytes.read_u32::<LittleEndian>()?; let capabilities = bytes.read_u32::<LittleEndian>()?; let mut product_id = [0; 16]; product_id.copy_from_slice(bytes); Ok(Self { type_, version, capabilities, product_id: Uuid::from_bytes(product_id), }) } pub(crate) fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { w.write_u32::<LittleEndian>(self.type_)?; w.write_u32::<LittleEndian>(self.version)?; w.write_u32::<LittleEndian>(self.capabilities)?; w.write_all(self.product_id.as_bytes())?; Ok(()) } /// Creates a new application info section from its raw components. /// /// Note that these values are basically ignored by the bootloader, but can /// be used by the application before initiating a firmware upgrade. /// /// # Parameters /// /// * `type_`: Type of the application (might be a bitfield). /// * `version`: App version (can be used to prevent firmware downgrades). /// * `capabilities`: Capability bitfield (can be used for anything). /// * `product_id`: A UUID identifying the application. pub fn new(type_: u32, version: u32, capabilities: u32, product_id: Uuid) -> Self { Self { type_, version, capabilities, product_id, } } /// Returns the application type field. pub fn type_(&self) -> u32 { self.type_ } /// Returns the app version field. pub fn version(&self) -> u32 { self.version } /// Returns the capabilities field. pub fn capabilities(&self) -> u32 { self.capabilities } /// Returns the product ID field. pub fn product_id(&self) -> Uuid { self.product_id } } #[derive(FromPrimitive, Debug, Copy, Clone)] enum SignatureType { None = 0, EcdsaP256 = 1, Crc32 = 2, } /// Application property structure. /// /// This must be present in an application binary and is located using its /// 16-Byte magic number. The bootloader uses this structure to find and verify /// the app signature during secure boot. #[derive(Debug)] pub struct AppProperties { /// Position of the `AppProperties` struct itself (starting at the magic /// bytes). position: u32, /// Version of this struct. /// /// We have observed values of `0x100` and `0x101`, with a compatible /// layout, so the assumption is that the low byte is a minor/patch version /// that is incremented when stuff gets added to this struct. The other byte /// is always `0x01` in the observed files and is interpreted as the major /// version of the struct, so other versions larger than 1 are rejected as /// unsupported. version: u32, signature_type: SignatureType, signature_location: u32, app_info: AppInfo, } impl AppProperties { const MAGIC: &'static [u8; 16] = &[ 0x13, 0xB7, 0x79, 0xFA, 0xC9, 0x25, 0xDD, 0xB7, 0xAD, 0xF3, 0xCF, 0xE0, 0xF1, 0xB6, 0x14, 0xB8, ]; /// Number of bytes occupied by the app properties in the image. const LENGTH: usize = 16 /* magic */ + 3 * 4 /* version, sig type/loc */ + AppInfo::LENGTH; /// Supported major version number. /// /// We have only observed 1 so far. const MAJOR_VERSION_SUPPORTED: u32 = 1; const MAX_RAW_LEN: u32 = 0x80000000; /// Extracts the application property structure from a raw app image. pub(crate) fn extract(image: &[u8]) -> Result<Self, Error> { let complete_image = image; if image.len() > Self::MAX_RAW_LEN as usize { return Err(Error::parse_err(format!( "could not get application properties from binary image (image larger than limit of {} bytes)", Self::MAX_RAW_LEN ))); } if let Some(pos) = image .windows(Self::MAGIC.len()) .position(|win| win == Self::MAGIC) { if image.len() < pos + Self::LENGTH { return Err(Error::parse_err( "could not get application properties from binary image (image too small)", )); } info!( "app properties at {:#X}: {:?}", pos, Blob(&image[pos..pos + Self::LENGTH]) ); let mut image = &image[pos + Self::MAGIC.len()..]; let version = image.read_u32::<LittleEndian>()?; let (v_major, v_minor) = (version >> 8, version & 0xff); if v_major > Self::MAJOR_VERSION_SUPPORTED { return Err(Error::parse_err(format!( "unsupported app property struct version {}.{} (expected {}.x)", v_major, v_minor, Self::MAJOR_VERSION_SUPPORTED ))); } let signature_type = image.read_u32::<LittleEndian>()?; let signature_location = image.read_u32::<LittleEndian>()?; let app_info = AppInfo::parse(&image[..AppInfo::LENGTH])?; let signature_type = SignatureType::from_u32(signature_type).ok_or_else(|| { Error::parse_err(format!( "invalid signature type {} in app properties", signature_type, )) })?; let signature_len = match signature_type { SignatureType::None => 0, SignatureType::Crc32 => { // FIXME return Err(Error::parse_err(r#"CRC-32 "signatures" not yet supported"#)); } SignatureType::EcdsaP256 => 64, }; if signature_location != 0 { if signature_location as usize + signature_len > complete_image.len() { return Err(Error::parse_err(format!( "invalid signature location {:#010X} (length is {} bytes, image length is {:#010X})", signature_location, signature_len, complete_image.len(), ))); } if let SignatureType::None = signature_type { return Err(Error::parse_err(format!( "non-zero signature location {:#010X} but signature type 'None'", signature_location, ))); } } let this = Self { position: pos as u32, version, signature_type, signature_location, app_info, }; debug!("parsed app properties: {:?}", this); Ok(this) } else { Err(Error::parse_err( "could not find application info in binary image", )) } } /// Writes the app properties, including the magic string, to `w`. fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { w.write_all(Self::MAGIC)?; w.write_u32::<LittleEndian>(self.version)?; w.write_u32::<LittleEndian>(self.signature_type as u32)?; w.write_u32::<LittleEndian>(self.signature_location)?; self.app_info.write(w)?; Ok(()) } pub(crate) fn app_info(&self) -> &AppInfo { &self.app_info } } /// A flash image containing application data. /// /// It is expected that every application embeds an "application properties" /// data structure, which will be located by [`AppImage::parse`] and contains an /// [`AppInfo`] structure which can be accessed using [`AppImage::app_info`]. /// /// Can be turned into a [`Gbl`] by calling [`Gbl::from_app_image`] or into a /// [`ProgramData`] section using `From`/`Into` conversion. The resulting /// section will be flashed at offset 0. It is the user's responsibility to /// ensure that the whole image fits in the target device's flash memory /// (this library is completely device-agnostic and thus cannot check that /// device-specific constraints are satisfied). /// /// [`Gbl`]: struct.Gbl.html /// [`Gbl::from_app_image`]: struct.Gbl.html#method.from_app_image /// [`AppImage::parse`]: #method.parse /// [`AppInfo`]: struct.AppInfo.html /// [`AppImage::app_info`]: #method.app_info /// [`ProgramData`]: struct.ProgramData.html #[derive(Debug)] pub struct AppImage<'a> { raw: Blob<Cow<'a, [u8]>>, app_props_pos: u32, app_props: AppProperties, } impl<'a> AppImage<'a> { /// Parses a raw application image by extracting the application properties. /// /// # Errors /// /// Returns an error if `image` does not contain a valid application /// properties structure. pub fn parse<T: AsRef<[u8]> + ?Sized>(image: &'a T) -> Result<Self, Error> { let image = image.as_ref(); let props = AppProperties::extract(image)?; Ok(Self { raw: Blob(image.into()), app_props_pos: props.position, app_props: props, }) } /// Consumes `self` and returns the raw bytes making up the application /// image. pub fn into_raw(self) -> Cow<'a, [u8]> { self.raw.0 } /// Returns the [`AppInfo`] structure extracted from the application image. /// /// [`AppInfo`]: struct.AppInfo.html pub fn app_info(&self) -> &AppInfo { &self.app_props.app_info() } /// Returns whether a cryptographic signature is embedded into the /// application image. pub fn is_signed(&self) -> bool { match ( self.app_props.signature_location, &self.app_props.signature_type, ) { (0, _) => false, (_, SignatureType::EcdsaP256) => true, _ => false, } } /// If present, returns the embedded ECDSA P-256 signature blob. pub fn ecdsa_signature(&self) -> Option<[u8; 64]> { match ( self.app_props.signature_location, &self.app_props.signature_type, ) { (0, _) => None, (pos, SignatureType::EcdsaP256) => { let mut signature = [0; 64]; signature.copy_from_slice(&self.raw[pos as usize..pos as usize + 64]); Some(signature) } _ => None, } } /// Signs this application image. /// /// The signature will be embedded into the flashed data, and the /// application properties structure will be updated to point at the /// signature. If the signature pointer is non-zero and the signature type /// indicates an ECDSA P-256 signature, an existing signature is assumed to /// be present at that offset and will be overwritten with a new signature. /// /// If no signature is present, this will append the new signature to the /// raw image, enlarging it. /// /// If secure boot is enabled in the bootloader, a valid signature must be /// present for boot to continue. /// /// # Errors /// /// Returns an error if: /// /// * `pem_private_key` is malformed or does not contain an ECDSA P-256 /// private key. /// * there is a problem accessing the system's random number generator. pub fn sign(mut self, pem_private_key: &str) -> Result<Self, Error> { // The data that appears to be signed is the raw app image, with the // app properties already pointing to the new signature position, but // not including the space allocated for that signature. let sig_pos = self.make_ecdsa_signature_space() as usize; let signature = crypto::create_signature(pem_private_key, &self.raw[..sig_pos])?; self.raw.0.to_mut()[sig_pos..sig_pos + 64].copy_from_slice(&signature); Ok(self) } /// Returns the existing signature space, or allocates a new signature at /// the end of the image. /// /// When allocating new space for the signature, this will update the /// application property structure inside the raw data. /// /// Returns the position of the signature data. fn make_ecdsa_signature_space(&mut self) -> u32 { if let SignatureType::EcdsaP256 = self.app_props.signature_type { if self.app_props.signature_location != 0 { // signature already present return self.app_props.signature_location; } } // Allocate a new signature at the very end of the app image let location = self.raw.len() as u32; // Reallocate raw image buffer, allocating 0s for signature let mut raw = vec![0; self.raw.len() + 64]; raw[..self.raw.len()].copy_from_slice(&self.raw); // Modify app props accordingly and write them back self.app_props.signature_type = SignatureType::EcdsaP256; self.app_props.signature_location = location; let props_pos = self.app_props_pos as usize; self.app_props .write(&mut &mut raw[props_pos..props_pos + AppProperties::LENGTH]) .expect("serializing properties to byte slice failed"); // Now use the larger buffer as the raw image self.raw = Blob(Cow::Owned(raw)); location } }