use super::{Encoding, Receiver};
use crate::{envelope::Envelope, update};
#[derive(Debug)]
pub struct PreparedUpdate {
plan: TransferPlan,
}
#[derive(Debug, Clone)]
pub struct TransferPlan {
receiver: Receiver,
class: crate::DriveClass,
image: Vec<u8>,
transfer: Option<Vec<u8>>,
prepare: crate::cdb::Prepare,
mode: update::flash::ExecutionMode,
}
#[derive(Debug)]
pub enum ExecuteError<E> {
Receiver(super::Error<E>),
Flash(update::FlashError<E>),
}
impl<E: core::fmt::Debug> core::fmt::Display for ExecuteError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Receiver(error) => {
write!(f, "receiver revalidation failed before writing: {error}")
}
Self::Flash(error) => write!(f, "{error}"),
}
}
}
impl<E: core::fmt::Debug + 'static> std::error::Error for ExecuteError<E> {}
impl Receiver {
pub fn prepare(
&self,
envelope: &Envelope,
installed: &update::InstalledFirmware,
) -> Result<PreparedUpdate, update::PrepareError> {
let update = update::PreparedUpdate::prepare(envelope.image(), installed)?;
Ok(PreparedUpdate {
plan: TransferPlan::new(
self,
envelope.image(),
update.class(),
update::flash::ExecutionMode::Flash,
)?,
})
}
}
impl PreparedUpdate {
pub fn plan(&self) -> &TransferPlan {
&self.plan
}
pub fn flash<R, W, H>(
&self,
io: update::FlashTransport<'_, R, W>,
runtime: &mut H,
) -> Result<update::Verified, ExecuteError<W::Error>>
where
W: crate::drive::Transport,
R: crate::drive::Transport<Error = W::Error>,
H: update::FlashRuntime<W::Error>,
{
self.plan.execute(io, runtime)
}
}
impl TransferPlan {
pub(crate) fn new(
receiver: &Receiver,
image: &[u8],
class: crate::DriveClass,
mode: update::flash::ExecutionMode,
) -> Result<Self, update::PrepareError> {
let prepare = receiver
.profile
.as_ref()
.ok_or(update::PrepareError::ReceiverEntry)?
.prepare;
let transfer = if receiver.encoding() == Encoding::Encrypted {
let mut wire = image.to_vec();
crate::enc::encrypt(&mut wire).expect("validated image is whole AES blocks");
Some(wire)
} else {
None
};
Ok(Self {
receiver: receiver.clone(),
class,
image: image.to_vec(),
transfer,
prepare,
mode,
})
}
pub(crate) fn execute<R, W, H>(
&self,
io: update::FlashTransport<'_, R, W>,
runtime: &mut H,
) -> Result<update::Verified, ExecuteError<W::Error>>
where
W: crate::drive::Transport,
R: crate::drive::Transport<Error = W::Error>,
H: update::FlashRuntime<W::Error>,
{
self.receiver
.revalidate(io.reads)
.map_err(ExecuteError::Receiver)?;
update::flash::Payload {
class: self.class,
image: self.image(),
transfer: self.transfer(),
}
.flash(io, runtime, self.mode, self.prepare)
.map_err(ExecuteError::Flash)
}
pub fn prepare_command(&self) -> crate::cdb::Prepare {
self.prepare
}
pub fn class(&self) -> crate::DriveClass {
self.class
}
pub fn receiver(&self) -> &Receiver {
&self.receiver
}
pub fn image(&self) -> &[u8] {
&self.image
}
pub fn transfer(&self) -> &[u8] {
self.transfer.as_deref().unwrap_or(&self.image)
}
pub fn steps(&self) -> std::vec::Vec<update::Step> {
let mut steps = self.class.steps();
if self.mode == update::flash::ExecutionMode::Recovery {
steps.drain(..2);
}
for step in &mut steps {
if step.label == "PREPARE" {
step.cdb = self.prepare.command();
}
}
steps
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
fixtures::{self, BU3},
layout, Identity, INQUIRY_LEN,
};
#[test]
fn input_envelope_never_selects_the_running_receivers_wire_encoding() {
let image = fixtures::image(&BU3, 7);
let identity = Identity::parse(
&[0; INQUIRY_LEN],
layout::BANNER.of(&image).unwrap(),
layout::DESCRIPTOR.of(&image).unwrap(),
)
.unwrap();
let installed =
update::InstalledFirmware::new(identity, &fixtures::drive_read(&image)).unwrap();
let mut ciphertext = image.clone();
crate::enc::encrypt(&mut ciphertext).unwrap();
for encoding in [Encoding::Plaintext, Encoding::Encrypted] {
let receiver = Receiver {
identity: super::super::DetectedMode {
mode: super::super::Mode::Firmware,
inquiry: [0; INQUIRY_LEN],
},
encoding,
site: super::super::Evidence::new(0x98000, &[1, 2]),
transform: None,
profile: Some(super::super::profile::Profile::testing()),
};
for input in [&image, &ciphertext] {
let prepared = receiver
.prepare(&Envelope::load(input).unwrap(), &installed)
.unwrap();
assert_eq!(prepared.plan().image(), image);
assert_eq!(
prepared.plan().transfer(),
if encoding == Encoding::Encrypted {
&ciphertext
} else {
&image
}
);
assert_eq!(prepared.plan().receiver(), &receiver);
let steps = prepared.plan().steps();
assert_eq!(
steps[..3].iter().map(|s| s.label).collect::<Vec<_>>(),
["PROBE", "READY", "PREPARE"]
);
let recovery = TransferPlan::new(
&receiver,
&image,
prepared.plan().class(),
update::flash::ExecutionMode::Recovery,
)
.unwrap();
let recovery_steps = recovery.steps();
assert_eq!(recovery_steps[0].label, "PREPARE");
assert_eq!(recovery_steps.len() + 2, steps.len());
for (recovery, flash) in recovery_steps.iter().zip(&steps[2..]) {
assert_eq!(recovery.cdb, flash.cdb);
assert_eq!(recovery.direction, flash.direction);
assert_eq!(recovery.length, flash.length);
}
}
}
}
}