1use std::borrow::Cow;
6use std::collections::HashSet;
7use std::convert::TryInto;
8use std::fs::File;
9use std::io::{Read, Write};
10use std::mem::{align_of, offset_of, size_of};
11use std::path::{Path, PathBuf};
12
13use anyhow::{Result, bail, ensure};
14use sphincsplus::{SphincsPlus, SpxDomain, SpxPublicKey};
15use thiserror::Error;
16use zerocopy::FromBytes;
17
18use crate::crypto::ecdsa::{EcdsaPublicKey, EcdsaRawPublicKey, EcdsaRawSignature};
19use crate::crypto::rsa::Modulus;
20use crate::crypto::rsa::RsaPublicKey;
21use crate::crypto::rsa::Signature as RsaSignature;
22use crate::crypto::sha256::Sha256Digest;
23use crate::image::manifest::{
24 CHIP_MANIFEST_VERSION_MAJOR1, CHIP_MANIFEST_VERSION_MAJOR2, CHIP_MANIFEST_VERSION_MINOR1,
25 CHIP_ROM_EXT_IDENTIFIER, CHIP_ROM_EXT_SIZE_MAX, MANIFEST_EXT_ID_SPX_KEY,
26 MANIFEST_EXT_ID_SPX_SIGNATURE, Manifest, ManifestKind, SigverifySpxSignature,
27};
28use crate::image::manifest_def::{ManifestSigverifyBuffer, ManifestSpec};
29use crate::image::manifest_ext::{ManifestExtEntry, ManifestExtEntrySpec};
30use crate::util::file::{FromReader, ToWriter};
31use crate::util::parse_int::ParseInt;
32
33#[derive(Debug, Error)]
34pub enum ImageError {
35 #[error("Incomplete read: expected to read {0} bytes but read {1} bytes")]
36 IncompleteRead(usize, usize),
37 #[error("Failed to parse image manifest.")]
38 Parse,
39 #[error("Extension data overflows flash image.")]
40 ExtensionOverflow,
41 #[error("Extension table index is out of bounds.")]
42 BadExtensionTableIndex,
43 #[error("Extension ID 0x{0:x} not in manifest table.")]
44 NoExtensionTableEntry(u32),
45 #[error("Extension 0x{0:x} is not aligned to word boundary.")]
46 BadExtensionAlignment(u32),
47 #[error("Invalid placement of signed extension 0x{0:x}.")]
48 MisplacedSignedExtension(u32),
49 #[error("Invalid manifest major version: {0}. ECDSA support requires major version {1}.")]
50 InvalidManifestVersionforEcdsa(u16, u16),
51}
52
53pub enum MainSignatureParams {
54 Rsa(RsaPublicKey, RsaSignature),
55 Ecdsa(EcdsaRawPublicKey, EcdsaRawSignature),
56}
57
58pub struct SpxSignatureParams {
59 key: SpxPublicKey,
60 signature: [u8; 7856],
61}
62
63pub struct SigverifyParams {
66 pub main_sig_params: MainSignatureParams,
67 pub spx_sig_params: Option<SpxSignatureParams>,
68 pub spx_hash_reversal_bug: bool,
69}
70
71impl SigverifyParams {
72 pub fn new(
73 main_sig_params: MainSignatureParams,
74 spx_sig_params: Option<SpxSignatureParams>,
75 ) -> Self {
76 SigverifyParams {
77 main_sig_params,
78 spx_sig_params,
79 spx_hash_reversal_bug: false,
80 }
81 }
82 pub fn with_hash_reversal_bug(mut self, bug: bool) -> Self {
83 self.spx_hash_reversal_bug = bug;
84 self
85 }
86
87 pub fn verify(&self, digest: &Sha256Digest) -> Result<()> {
89 match &self.main_sig_params {
90 MainSignatureParams::Rsa(key, sig) => {
91 key.verify(digest, sig)?;
92 }
93 MainSignatureParams::Ecdsa(key, sig) => {
94 let ecdsa_key: EcdsaPublicKey = key.try_into()?;
95 ecdsa_key.verify(digest, sig)?;
96 }
97 }
98 Ok(())
99 }
100
101 pub fn spx_verify(&self, b: &[u8], domain: SpxDomain) -> Result<()> {
103 if let Some(spx) = &self.spx_sig_params {
104 let msg = match domain {
105 SpxDomain::PreHashedSha256 => Cow::from(if self.spx_hash_reversal_bug {
106 Sha256Digest::hash(b).to_vec_rev()
107 } else {
108 Sha256Digest::hash(b).to_vec()
109 }),
110 _ => Cow::from(b),
111 };
112 spx.key.verify(domain, &spx.signature, &msg)?;
113 } else {
114 bail!("No SPX signature found");
115 }
116
117 Ok(())
118 }
119}
120
121#[repr(C)]
123#[derive(Debug)]
124pub struct ImageData {
125 pub bytes: [u8; Image::MAX_SIZE],
126 _align: [Manifest; 0],
127}
128
129impl Default for ImageData {
130 fn default() -> Self {
131 ImageData {
132 bytes: [0xFF; Image::MAX_SIZE],
133 _align: [],
134 }
135 }
136}
137
138#[derive(Debug, Default)]
139pub struct Image {
140 data: Box<ImageData>,
148 pub size: usize,
149}
150
151#[derive(Debug)]
152pub struct SubImage<'a> {
153 pub kind: ManifestKind,
154 pub offset: usize,
155 pub manifest: &'a Manifest,
156 pub data: &'a [u8],
157}
158
159#[derive(Debug)]
160pub enum ImageChunk {
161 Concat(PathBuf),
162 Offset(PathBuf, usize),
163}
164
165#[derive(Debug, Default)]
166pub struct ImageAssembler {
167 pub size: usize,
168 pub mirrored: bool,
169 pub chunks: Vec<ImageChunk>,
170}
171
172impl FromReader for Image {
173 fn from_reader(mut r: impl Read) -> Result<Self> {
175 let mut image = Image::default();
176 image.size = r.read(&mut image.data.bytes)?;
177 Ok(image)
178 }
179}
180
181impl ToWriter for Image {
182 fn to_writer(&self, w: &mut impl Write) -> Result<()> {
184 w.write_all(&self.data.bytes[..self.size])?;
185 Ok(())
186 }
187}
188
189impl Image {
190 pub const MAX_SIZE: usize = 1024 * 1024;
191
192 pub fn manifest_sanity_check(&self) -> Result<()> {
196 let manifest = self.borrow_manifest()?;
197 let len = self.data.bytes.len() as u32;
198
199 ensure!(manifest.signed_region_end <= len);
200 ensure!(manifest.length <= len);
201 ensure!(manifest.code_start < len);
202 ensure!(manifest.code_end < len);
203 ensure!(manifest.entry_point < len);
204 ensure!(manifest.extensions.entries.iter().all(|x| x.offset < len));
205
206 if (manifest.identifier == CHIP_ROM_EXT_IDENTIFIER)
209 && (manifest.length > CHIP_ROM_EXT_SIZE_MAX)
210 {
211 log::warn!("ROM_EXT is larger than 64k. Link offsets may need recalculating.");
212 }
213
214 Ok(())
215 }
216
217 fn get_spx_signature(&self) -> Result<Option<SpxSignatureParams>> {
219 let ext_tab = self.borrow_manifest()?.extensions.entries;
220 let key_o = ext_tab
221 .iter()
222 .find(|e| e.identifier == MANIFEST_EXT_ID_SPX_KEY);
223 let sig_o = ext_tab
224 .iter()
225 .find(|e| e.identifier == MANIFEST_EXT_ID_SPX_SIGNATURE);
226
227 match (key_o, sig_o) {
228 (Some(key_e), Some(sig_e)) => {
229 const KEY_SIZE: usize = 32; const SIG_SIZE: usize = std::mem::size_of::<SigverifySpxSignature>();
231
232 let mut key_bytes = [0u8; KEY_SIZE];
233 let mut signature = [0u8; SIG_SIZE];
234 let k_ofs = (key_e.offset + 8) as usize;
235 let s_ofs = (sig_e.offset + 8) as usize;
236
237 key_bytes.copy_from_slice(&self.data.bytes[k_ofs..k_ofs + KEY_SIZE]);
238 signature.copy_from_slice(&self.data.bytes[s_ofs..s_ofs + SIG_SIZE]);
239
240 let key = SpxPublicKey::from_bytes(SphincsPlus::Sha2128sSimple, &key_bytes)?;
241
242 Ok(Some(SpxSignatureParams { key, signature }))
243 }
244 (_, _) => Ok(None),
245 }
246 }
247
248 pub fn get_sigverify_params_from_manifest(&self) -> Result<SigverifyParams> {
249 let manifest = self.borrow_manifest()?;
250 let manifest_def: ManifestSpec = manifest.try_into()?;
251
252 let spx_sig_params = self.get_spx_signature()?;
253
254 let pub_key = manifest_def
255 .pub_key()
256 .ok_or(ImageError::Parse)?
257 .to_le_bytes();
258
259 let signature = manifest_def
260 .signature()
261 .ok_or(ImageError::Parse)?
262 .to_le_bytes();
263
264 if (manifest.manifest_version.major == CHIP_MANIFEST_VERSION_MAJOR1)
265 && (manifest.manifest_version.minor == CHIP_MANIFEST_VERSION_MINOR1)
266 {
267 let rsa_key = RsaPublicKey::new(Modulus::from_le_bytes(pub_key)?)?;
268 let rsa_sig = RsaSignature::from_le_bytes(signature)?;
269 return Ok(SigverifyParams::new(
270 MainSignatureParams::Rsa(rsa_key, rsa_sig),
271 spx_sig_params,
272 ));
273 }
274
275 let ecdsa_pub_key = EcdsaRawPublicKey::read(&mut std::io::Cursor::new(pub_key))?;
276 let ecdsa_sig = EcdsaRawSignature::read(&mut std::io::Cursor::new(signature))?;
277
278 Ok(SigverifyParams::new(
279 MainSignatureParams::Ecdsa(ecdsa_pub_key, ecdsa_sig),
280 spx_sig_params,
281 ))
282 }
283
284 pub fn overwrite_manifest(&mut self, other: ManifestSpec) -> Result<()> {
286 let manifest = self.borrow_manifest_mut()?;
287 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
288 manifest_def.overwrite_fields(other);
289 *manifest = manifest_def.try_into()?;
290 Ok(())
291 }
292
293 pub fn add_signed_manifest_extensions(&mut self, spec: &[ManifestExtEntrySpec]) -> Result<()> {
299 spec.iter()
300 .filter(|entry_spec| entry_spec.is_signed())
301 .try_for_each(|entry_spec| {
302 self.add_manifest_extension(ManifestExtEntry::from_spec(entry_spec)?)
303 })
304 }
305
306 pub fn add_unsigned_manifest_extensions(
311 &mut self,
312 spec: &[ManifestExtEntrySpec],
313 ) -> Result<()> {
314 spec.iter()
315 .filter(|entry_spec| !entry_spec.is_signed())
316 .try_for_each(|entry_spec| {
317 self.add_manifest_extension(ManifestExtEntry::from_spec(entry_spec)?)
318 })
319 }
320
321 pub fn add_manifest_extension(&mut self, entry: ManifestExtEntry) -> Result<()> {
323 let manifest = self.borrow_manifest()?;
324
325 let mut ext_table = manifest.extensions.entries;
327
328 let entry_id = entry.header().identifier;
329
330 let ext_table_entry = ext_table
332 .iter_mut()
333 .find(|e| e.identifier == entry_id)
334 .ok_or(ImageError::NoExtensionTableEntry(entry_id))?;
335
336 let offset = if ext_table_entry.offset != 0 {
339 ext_table_entry.offset
340 } else {
341 ensure!(
342 self.size.is_multiple_of(align_of::<u32>()),
343 ImageError::BadExtensionAlignment(entry_id)
344 );
345 self.size.try_into()?
346 };
347 ext_table_entry.offset = offset;
348
349 let ext_bytes = entry.to_vec();
351 let end_index = offset
352 .checked_add(ext_bytes.len().try_into()?)
353 .ok_or(ImageError::ExtensionOverflow)?;
354 let extension_slice = self
355 .data
356 .bytes
357 .get_mut(offset as usize..end_index as usize)
358 .ok_or(ImageError::ExtensionOverflow)?;
359 extension_slice.copy_from_slice(ext_bytes.as_slice());
360 self.size = std::cmp::max(end_index as usize, self.size);
361
362 let manifest = self.borrow_manifest_mut()?;
363 manifest.extensions.entries = ext_table;
364
365 Ok(())
366 }
367
368 pub fn allocate_manifest_extension(&mut self, id: u32, len: usize) -> Result<()> {
374 let offset = self.size as u32;
375 self.borrow_manifest_mut()?
376 .extensions
377 .entries
378 .iter_mut()
379 .find(|e| e.identifier == id)
380 .ok_or(ImageError::NoExtensionTableEntry(id))?
381 .offset = offset;
382
383 self.size = self
384 .size
385 .checked_add(len)
386 .ok_or(ImageError::ExtensionOverflow)?;
387
388 Ok(())
389 }
390
391 pub fn drop_null_extensions(&mut self) -> Result<()> {
396 let manifest = self.borrow_manifest_mut()?;
397
398 manifest.extensions.entries.iter_mut().for_each(|e| {
399 if e.offset == 0 {
400 e.identifier = 0;
401 }
402 });
403
404 Ok(())
405 }
406
407 pub fn update_rsa_signature(&mut self, signature: RsaSignature) -> Result<()> {
409 let manifest = self.borrow_manifest_mut()?;
410
411 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
413 manifest_def.update_signature(ManifestSigverifyBuffer::from_le_bytes(
414 signature.to_le_bytes(),
415 )?);
416 *manifest = manifest_def.try_into()?;
417 Ok(())
418 }
419
420 pub fn update_ecdsa_signature(&mut self, signature: EcdsaRawSignature) -> Result<()> {
422 let manifest = self.borrow_manifest_mut()?;
423
424 if manifest.manifest_version.major != CHIP_MANIFEST_VERSION_MAJOR2 {
439 log::error!(
440 "Invalid manifest version for ECDSA: {:?}",
441 manifest.manifest_version
442 );
443 }
444
445 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
446
447 let signature_bytes = signature
449 .r
450 .iter()
451 .chain(signature.s.iter())
452 .copied()
453 .collect::<Vec<u8>>();
454 let sig_padding = vec![0xa5u8; 384 - 64];
455 let signature_bytes = signature_bytes
456 .iter()
457 .chain(sig_padding.iter())
458 .copied()
459 .collect::<Vec<u8>>();
460
461 manifest_def.update_signature(ManifestSigverifyBuffer::from_le_bytes(signature_bytes)?);
462 *manifest = manifest_def.try_into()?;
463 Ok(())
464 }
465
466 pub fn update_modulus(&mut self, rsa_modulus: Modulus) -> Result<()> {
468 let manifest = self.borrow_manifest_mut()?;
469
470 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
472 manifest_def.update_pub_key(ManifestSigverifyBuffer::from_le_bytes(
473 rsa_modulus.to_le_bytes(),
474 )?);
475 *manifest = manifest_def.try_into()?;
476 Ok(())
477 }
478
479 pub fn update_ecdsa_public_key(&mut self, ecdsa_public_key: EcdsaRawPublicKey) -> Result<()> {
481 let manifest = self.borrow_manifest_mut()?;
482 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
483
484 let key_bytes = ecdsa_public_key
486 .x
487 .iter()
488 .chain(ecdsa_public_key.y.iter())
489 .copied()
490 .collect::<Vec<u8>>();
491 let key_padding = vec![0xa5u8; 384 - 64];
492 let key_bytes = key_bytes
493 .iter()
494 .chain(key_padding.iter())
495 .copied()
496 .collect::<Vec<u8>>();
497
498 manifest_def.update_pub_key(ManifestSigverifyBuffer::from_le_bytes(key_bytes)?);
499 *manifest = manifest_def.try_into()?;
500
501 if manifest.manifest_version.major == CHIP_MANIFEST_VERSION_MAJOR1 {
506 manifest.manifest_version.major = CHIP_MANIFEST_VERSION_MAJOR2;
507 }
508 Ok(())
509 }
510
511 pub fn subimages(&self) -> Result<Vec<SubImage<'_>>> {
512 let mut result = Vec::new();
513 let mut offset = 0;
514 while offset < self.size {
515 let m = &self.data.bytes[offset..offset + size_of::<Manifest>()];
516 let manifest = Manifest::ref_from_bytes(m).map_err(|_| ImageError::Parse)?;
517 let kind = ManifestKind(manifest.identifier);
518 let mut size = 1;
519 if kind.is_known_value() {
520 size = manifest.length as usize;
521 result.push(SubImage {
522 kind,
523 offset,
524 manifest,
525 data: &self.data.bytes[offset..offset + size],
526 });
527 }
528 offset += (size + 65535) & !65535;
530 }
531 Ok(result)
532 }
533
534 pub fn bytes(&self) -> &[u8] {
535 &self.data.bytes[..self.size]
536 }
537
538 pub fn borrow_manifest(&self) -> Result<&Manifest> {
539 let manifest_slice = &self.data.bytes[0..size_of::<Manifest>()];
540 let manifest = Manifest::ref_from_bytes(manifest_slice).map_err(|_| ImageError::Parse)?;
541 Ok(manifest)
542 }
543
544 pub fn borrow_manifest_mut(&mut self) -> Result<&mut Manifest> {
545 let manifest_slice = &mut self.data.bytes[0..size_of::<Manifest>()];
546 let manifest = Manifest::mut_from_bytes(manifest_slice).map_err(|_| ImageError::Parse)?;
547 Ok(manifest)
548 }
549
550 pub fn update_length(&mut self) -> Result<usize> {
552 self.borrow_manifest_mut()?.length = self.size as u32;
553 Ok(self.size)
554 }
555
556 pub fn update_signed_region(&mut self, signed_ids: &HashSet<u32>) -> Result<()> {
561 let image_size = self.size as u32;
562 let mut first_unsigned_ext = 0u32;
563 let manifest = self.borrow_manifest_mut()?;
564
565 let mut ext_table = manifest.extensions.entries;
566 ext_table.sort_by(|a, b| a.offset.cmp(&b.offset));
567 for e in ext_table {
568 if e.offset == 0 {
570 continue;
571 }
572 if signed_ids.contains(&e.identifier) {
573 ensure!(
576 first_unsigned_ext == 0,
577 ImageError::MisplacedSignedExtension(e.identifier)
578 );
579 } else if first_unsigned_ext == 0 {
580 first_unsigned_ext = e.offset;
581 }
582 }
583
584 manifest.signed_region_end = if first_unsigned_ext == 0 {
585 image_size
586 } else {
587 first_unsigned_ext
588 };
589
590 Ok(())
591 }
592
593 pub fn map_signed_region<F, R>(&self, f: F) -> Result<R>
595 where
596 F: FnOnce(&[u8]) -> R,
597 {
598 Ok(f(&self.data.bytes[offset_of!(Manifest, usage_constraints)
599 ..self.borrow_manifest()?.signed_region_end as usize]))
600 }
601
602 pub fn compute_digest(&self) -> Result<Sha256Digest> {
604 self.map_signed_region(|v| Sha256Digest::hash(v))
605 }
606}
607
608impl SubImage<'_> {
609 pub fn map_signed_region<F, R>(&self, f: F) -> Result<R>
611 where
612 F: FnOnce(&[u8]) -> R,
613 {
614 Ok(f(&self.data[offset_of!(Manifest, usage_constraints)
615 ..self.manifest.signed_region_end as usize]))
616 }
617
618 pub fn compute_digest(&self) -> Result<Sha256Digest> {
620 self.map_signed_region(|v| Sha256Digest::hash(v))
621 }
622}
623
624impl ImageAssembler {
625 pub fn with_params(size: usize, mirrored: bool) -> Self {
627 ImageAssembler {
628 size,
629 mirrored,
630 ..Default::default()
631 }
632 }
633
634 pub fn new() -> Self {
636 Self::with_params(0x100000, true)
637 }
638
639 pub fn parse(&mut self, chunks: &[impl AsRef<str>]) -> Result<()> {
643 for chunk in chunks {
644 if let Some((file, offset)) = chunk.as_ref().split_once('@') {
645 self.chunks.push(ImageChunk::Offset(
646 PathBuf::from(file),
647 usize::from_str(offset)?,
648 ));
649 } else {
650 self.chunks
651 .push(ImageChunk::Concat(PathBuf::from(chunk.as_ref())));
652 }
653 }
654 Ok(())
655 }
656
657 fn read(path: &Path, buf: &mut [u8]) -> Result<usize> {
659 let mut file = File::open(path)?;
660 let len = file.metadata()?.len() as usize;
661 let n = file.read(buf)?;
662 ensure!(len == n, ImageError::IncompleteRead(len, n));
663 Ok(n)
664 }
665
666 pub fn assemble(&self) -> Result<Vec<u8>> {
668 let size = if self.mirrored {
669 self.size / 2
670 } else {
671 self.size
672 };
673 let mut image = vec![0xff; size];
674 let mut pos = 0;
675 for chunk in &self.chunks {
676 match chunk {
677 ImageChunk::Concat(path) => {
678 let n = Self::read(path, &mut image[pos..])?;
679 pos += n;
680 }
681 ImageChunk::Offset(path, offset) => {
682 let n = Self::read(path, &mut image[*offset..])?;
683 pos = offset + n;
684 }
685 }
686 }
687 if self.mirrored {
688 image.extend_from_within(..size);
689 }
690 Ok(image)
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697 use crate::util::testdata;
698
699 #[test]
700 fn test_assemble_concat() -> Result<()> {
701 let mut image = ImageAssembler::with_params(16, false);
703 image.parse(&[
704 testdata("image/hello.txt").to_str().unwrap(),
705 testdata("image/world.txt").to_str().unwrap(),
706 ])?;
707 let data = image.assemble()?;
708 assert_eq!(data, b"HelloWorld\xff\xff\xff\xff\xff\xff");
709 Ok(())
710 }
711
712 #[test]
713 fn test_assemble_offset() -> Result<()> {
714 let mut image = ImageAssembler::with_params(16, false);
716 image.parse(&[
717 testdata("image/hello.txt@0").to_str().unwrap(),
718 testdata("image/world.txt@0x8").to_str().unwrap(),
719 ])?;
720 let data = image.assemble()?;
721 assert_eq!(data, b"Hello\xff\xff\xffWorld\xff\xff\xff");
722 Ok(())
723 }
724
725 #[test]
726 fn test_assemble_mirrored() -> Result<()> {
727 let mut image = ImageAssembler::with_params(20, true);
729 image.parse(&[
730 testdata("image/hello.txt").to_str().unwrap(),
731 testdata("image/world.txt").to_str().unwrap(),
732 ])?;
733 let data = image.assemble()?;
734 assert_eq!(data, b"HelloWorldHelloWorld");
735 Ok(())
736 }
737
738 #[test]
739 fn test_assemble_mirrored_offset_error() -> Result<()> {
740 let mut image = ImageAssembler::with_params(16, true);
742 image.parse(&[
743 testdata("image/hello.txt@0").to_str().unwrap(),
744 testdata("image/world.txt@0x5").to_str().unwrap(),
745 ])?;
746 let err = image.assemble().unwrap_err();
747 assert_eq!(
748 err.to_string(),
749 "Incomplete read: expected to read 5 bytes but read 3 bytes"
750 );
751 Ok(())
752 }
753
754 #[test]
755 fn test_load_image() {
756 let image = Image::read_from_file(&testdata("image/test_image.bin")).unwrap();
758 image
759 .write_to_file(&testdata("image/test_image_out.bin"))
760 .unwrap();
761
762 let (mut orig_bytes, mut res_bytes) = (Vec::<u8>::new(), Vec::<u8>::new());
764 File::open(testdata("image/test_image.bin"))
765 .unwrap()
766 .read_to_end(&mut orig_bytes)
767 .unwrap();
768 File::open(testdata("image/test_image_out.bin"))
769 .unwrap()
770 .read_to_end(&mut res_bytes)
771 .unwrap();
772 assert_eq!(orig_bytes, res_bytes);
773 }
774}