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, ManifestExtSpec};
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: &ManifestExtSpec) -> Result<()> {
299 for entry_spec in &spec.signed_region {
300 self.add_manifest_extension(ManifestExtEntry::from_spec(
301 entry_spec,
302 spec.source_path(),
303 )?)?;
304 }
305 Ok(())
306 }
307
308 pub fn add_unsigned_manifest_extensions(&mut self, spec: &ManifestExtSpec) -> Result<()> {
313 for entry_spec in &spec.unsigned_region {
314 self.add_manifest_extension(ManifestExtEntry::from_spec(
315 entry_spec,
316 spec.source_path(),
317 )?)?;
318 }
319 Ok(())
320 }
321
322 pub fn add_manifest_extension(&mut self, entry: ManifestExtEntry) -> Result<()> {
324 let manifest = self.borrow_manifest()?;
325
326 let mut ext_table = manifest.extensions.entries;
328
329 let entry_id = entry.header().identifier;
330
331 let ext_table_entry = ext_table
333 .iter_mut()
334 .find(|e| e.identifier == entry_id)
335 .ok_or(ImageError::NoExtensionTableEntry(entry_id))?;
336
337 let offset = if ext_table_entry.offset != 0 {
340 ext_table_entry.offset
341 } else {
342 ensure!(
343 self.size.is_multiple_of(align_of::<u32>()),
344 ImageError::BadExtensionAlignment(entry_id)
345 );
346 self.size.try_into()?
347 };
348 ext_table_entry.offset = offset;
349
350 let ext_bytes = entry.to_vec();
352 let end_index = offset
353 .checked_add(ext_bytes.len().try_into()?)
354 .ok_or(ImageError::ExtensionOverflow)?;
355 let extension_slice = self
356 .data
357 .bytes
358 .get_mut(offset as usize..end_index as usize)
359 .ok_or(ImageError::ExtensionOverflow)?;
360 extension_slice.copy_from_slice(ext_bytes.as_slice());
361 self.size = std::cmp::max(end_index as usize, self.size);
362
363 let manifest = self.borrow_manifest_mut()?;
364 manifest.extensions.entries = ext_table;
365
366 Ok(())
367 }
368
369 pub fn allocate_manifest_extension(&mut self, id: u32, len: usize) -> Result<()> {
375 let offset = self.size as u32;
376 self.borrow_manifest_mut()?
377 .extensions
378 .entries
379 .iter_mut()
380 .find(|e| e.identifier == id)
381 .ok_or(ImageError::NoExtensionTableEntry(id))?
382 .offset = offset;
383
384 self.size = self
385 .size
386 .checked_add(len)
387 .ok_or(ImageError::ExtensionOverflow)?;
388
389 Ok(())
390 }
391
392 pub fn drop_null_extensions(&mut self) -> Result<()> {
397 let manifest = self.borrow_manifest_mut()?;
398
399 manifest.extensions.entries.iter_mut().for_each(|e| {
400 if e.offset == 0 {
401 e.identifier = 0;
402 }
403 });
404
405 Ok(())
406 }
407
408 pub fn update_rsa_signature(&mut self, signature: RsaSignature) -> Result<()> {
410 let manifest = self.borrow_manifest_mut()?;
411
412 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
414 manifest_def.update_signature(ManifestSigverifyBuffer::from_le_bytes(
415 signature.to_le_bytes(),
416 )?);
417 *manifest = manifest_def.try_into()?;
418 Ok(())
419 }
420
421 pub fn update_ecdsa_signature(&mut self, signature: EcdsaRawSignature) -> Result<()> {
423 let manifest = self.borrow_manifest_mut()?;
424
425 if manifest.manifest_version.major != CHIP_MANIFEST_VERSION_MAJOR2 {
440 log::error!(
441 "Invalid manifest version for ECDSA: {:?}",
442 manifest.manifest_version
443 );
444 }
445
446 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
447
448 let signature_bytes = signature
450 .r
451 .iter()
452 .chain(signature.s.iter())
453 .copied()
454 .collect::<Vec<u8>>();
455 let sig_padding = vec![0xa5u8; 384 - 64];
456 let signature_bytes = signature_bytes
457 .iter()
458 .chain(sig_padding.iter())
459 .copied()
460 .collect::<Vec<u8>>();
461
462 manifest_def.update_signature(ManifestSigverifyBuffer::from_le_bytes(signature_bytes)?);
463 *manifest = manifest_def.try_into()?;
464 Ok(())
465 }
466
467 pub fn update_modulus(&mut self, rsa_modulus: Modulus) -> Result<()> {
469 let manifest = self.borrow_manifest_mut()?;
470
471 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
473 manifest_def.update_pub_key(ManifestSigverifyBuffer::from_le_bytes(
474 rsa_modulus.to_le_bytes(),
475 )?);
476 *manifest = manifest_def.try_into()?;
477 Ok(())
478 }
479
480 pub fn update_ecdsa_public_key(&mut self, ecdsa_public_key: EcdsaRawPublicKey) -> Result<()> {
482 let manifest = self.borrow_manifest_mut()?;
483 let mut manifest_def: ManifestSpec = (&*manifest).try_into()?;
484
485 let key_bytes = ecdsa_public_key
487 .x
488 .iter()
489 .chain(ecdsa_public_key.y.iter())
490 .copied()
491 .collect::<Vec<u8>>();
492 let key_padding = vec![0xa5u8; 384 - 64];
493 let key_bytes = key_bytes
494 .iter()
495 .chain(key_padding.iter())
496 .copied()
497 .collect::<Vec<u8>>();
498
499 manifest_def.update_pub_key(ManifestSigverifyBuffer::from_le_bytes(key_bytes)?);
500 *manifest = manifest_def.try_into()?;
501
502 if manifest.manifest_version.major == CHIP_MANIFEST_VERSION_MAJOR1 {
507 manifest.manifest_version.major = CHIP_MANIFEST_VERSION_MAJOR2;
508 }
509 Ok(())
510 }
511
512 pub fn subimages(&self) -> Result<Vec<SubImage<'_>>> {
513 let mut result = Vec::new();
514 let mut offset = 0;
515 while offset < self.size {
516 let m = &self.data.bytes[offset..offset + size_of::<Manifest>()];
517 let manifest = Manifest::ref_from_bytes(m).map_err(|_| ImageError::Parse)?;
518 let kind = ManifestKind(manifest.identifier);
519 let mut size = 1;
520 if kind.is_known_value() {
521 size = manifest.length as usize;
522 result.push(SubImage {
523 kind,
524 offset,
525 manifest,
526 data: &self.data.bytes[offset..offset + size],
527 });
528 }
529 offset += (size + 65535) & !65535;
531 }
532 Ok(result)
533 }
534
535 pub fn bytes(&self) -> &[u8] {
536 &self.data.bytes[..self.size]
537 }
538
539 pub fn borrow_manifest(&self) -> Result<&Manifest> {
540 let manifest_slice = &self.data.bytes[0..size_of::<Manifest>()];
541 let manifest = Manifest::ref_from_bytes(manifest_slice).map_err(|_| ImageError::Parse)?;
542 Ok(manifest)
543 }
544
545 pub fn borrow_manifest_mut(&mut self) -> Result<&mut Manifest> {
546 let manifest_slice = &mut self.data.bytes[0..size_of::<Manifest>()];
547 let manifest = Manifest::mut_from_bytes(manifest_slice).map_err(|_| ImageError::Parse)?;
548 Ok(manifest)
549 }
550
551 pub fn update_length(&mut self) -> Result<usize> {
553 self.borrow_manifest_mut()?.length = self.size as u32;
554 Ok(self.size)
555 }
556
557 pub fn update_signed_region(&mut self, signed_ids: &HashSet<u32>) -> Result<()> {
562 let image_size = self.size as u32;
563 let mut first_unsigned_ext = 0u32;
564 let manifest = self.borrow_manifest_mut()?;
565
566 let mut ext_table = manifest.extensions.entries;
567 ext_table.sort_by(|a, b| a.offset.cmp(&b.offset));
568 for e in ext_table {
569 if e.offset == 0 {
571 continue;
572 }
573 if signed_ids.contains(&e.identifier) {
574 ensure!(
577 first_unsigned_ext == 0,
578 ImageError::MisplacedSignedExtension(e.identifier)
579 );
580 } else if first_unsigned_ext == 0 {
581 first_unsigned_ext = e.offset;
582 }
583 }
584
585 manifest.signed_region_end = if first_unsigned_ext == 0 {
586 image_size
587 } else {
588 first_unsigned_ext
589 };
590
591 Ok(())
592 }
593
594 pub fn map_signed_region<F, R>(&self, f: F) -> Result<R>
596 where
597 F: FnOnce(&[u8]) -> R,
598 {
599 Ok(f(&self.data.bytes[offset_of!(Manifest, usage_constraints)
600 ..self.borrow_manifest()?.signed_region_end as usize]))
601 }
602
603 pub fn compute_digest(&self) -> Result<Sha256Digest> {
605 self.map_signed_region(|v| Sha256Digest::hash(v))
606 }
607}
608
609impl SubImage<'_> {
610 pub fn map_signed_region<F, R>(&self, f: F) -> Result<R>
612 where
613 F: FnOnce(&[u8]) -> R,
614 {
615 Ok(f(&self.data[offset_of!(Manifest, usage_constraints)
616 ..self.manifest.signed_region_end as usize]))
617 }
618
619 pub fn compute_digest(&self) -> Result<Sha256Digest> {
621 self.map_signed_region(|v| Sha256Digest::hash(v))
622 }
623}
624
625impl ImageAssembler {
626 pub fn with_params(size: usize, mirrored: bool) -> Self {
628 ImageAssembler {
629 size,
630 mirrored,
631 ..Default::default()
632 }
633 }
634
635 pub fn new() -> Self {
637 Self::with_params(0x100000, true)
638 }
639
640 pub fn parse(&mut self, chunks: &[impl AsRef<str>]) -> Result<()> {
644 for chunk in chunks {
645 if let Some((file, offset)) = chunk.as_ref().split_once('@') {
646 self.chunks.push(ImageChunk::Offset(
647 PathBuf::from(file),
648 usize::from_str(offset)?,
649 ));
650 } else {
651 self.chunks
652 .push(ImageChunk::Concat(PathBuf::from(chunk.as_ref())));
653 }
654 }
655 Ok(())
656 }
657
658 fn read(path: &Path, buf: &mut [u8]) -> Result<usize> {
660 let mut file = File::open(path)?;
661 let len = file.metadata()?.len() as usize;
662 let n = file.read(buf)?;
663 ensure!(len == n, ImageError::IncompleteRead(len, n));
664 Ok(n)
665 }
666
667 pub fn assemble(&self) -> Result<Vec<u8>> {
669 let size = if self.mirrored {
670 self.size / 2
671 } else {
672 self.size
673 };
674 let mut image = vec![0xff; size];
675 let mut pos = 0;
676 for chunk in &self.chunks {
677 match chunk {
678 ImageChunk::Concat(path) => {
679 let n = Self::read(path, &mut image[pos..])?;
680 pos += n;
681 }
682 ImageChunk::Offset(path, offset) => {
683 let n = Self::read(path, &mut image[*offset..])?;
684 pos = offset + n;
685 }
686 }
687 }
688 if self.mirrored {
689 image.extend_from_within(..size);
690 }
691 Ok(image)
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use crate::util::testdata;
699
700 #[test]
701 fn test_assemble_concat() -> Result<()> {
702 let mut image = ImageAssembler::with_params(16, false);
704 image.parse(&[
705 testdata("image/hello.txt").to_str().unwrap(),
706 testdata("image/world.txt").to_str().unwrap(),
707 ])?;
708 let data = image.assemble()?;
709 assert_eq!(data, b"HelloWorld\xff\xff\xff\xff\xff\xff");
710 Ok(())
711 }
712
713 #[test]
714 fn test_assemble_offset() -> Result<()> {
715 let mut image = ImageAssembler::with_params(16, false);
717 image.parse(&[
718 testdata("image/hello.txt@0").to_str().unwrap(),
719 testdata("image/world.txt@0x8").to_str().unwrap(),
720 ])?;
721 let data = image.assemble()?;
722 assert_eq!(data, b"Hello\xff\xff\xffWorld\xff\xff\xff");
723 Ok(())
724 }
725
726 #[test]
727 fn test_assemble_mirrored() -> Result<()> {
728 let mut image = ImageAssembler::with_params(20, true);
730 image.parse(&[
731 testdata("image/hello.txt").to_str().unwrap(),
732 testdata("image/world.txt").to_str().unwrap(),
733 ])?;
734 let data = image.assemble()?;
735 assert_eq!(data, b"HelloWorldHelloWorld");
736 Ok(())
737 }
738
739 #[test]
740 fn test_assemble_mirrored_offset_error() -> Result<()> {
741 let mut image = ImageAssembler::with_params(16, true);
743 image.parse(&[
744 testdata("image/hello.txt@0").to_str().unwrap(),
745 testdata("image/world.txt@0x5").to_str().unwrap(),
746 ])?;
747 let err = image.assemble().unwrap_err();
748 assert_eq!(
749 err.to_string(),
750 "Incomplete read: expected to read 5 bytes but read 3 bytes"
751 );
752 Ok(())
753 }
754
755 #[test]
756 fn test_load_image() {
757 let image = Image::read_from_file(&testdata("image/test_image.bin")).unwrap();
759 image
760 .write_to_file(&testdata("image/test_image_out.bin"))
761 .unwrap();
762
763 let (mut orig_bytes, mut res_bytes) = (Vec::<u8>::new(), Vec::<u8>::new());
765 File::open(testdata("image/test_image.bin"))
766 .unwrap()
767 .read_to_end(&mut orig_bytes)
768 .unwrap();
769 File::open(testdata("image/test_image_out.bin"))
770 .unwrap()
771 .read_to_end(&mut res_bytes)
772 .unwrap();
773 assert_eq!(orig_bytes, res_bytes);
774 }
775}