eculocate

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ota.rs (6004B)


      1 use nb::block;
      2 
      3 use embassy_time::{Timer};
      4 
      5 use ed25519_dalek::{VerifyingKey, Verifier, Signature};
      6 
      7 use esp_hal_ota::Ota;
      8 use esp_storage::FlashStorage;
      9 use embedded_io_async::{Read,ReadExactError};
     10 
     11 use esp_hal::sha;
     12 use core::convert::Infallible;
     13 
     14 use log::{warn, info};
     15 
     16 use alloc::string::String;
     17 
     18 
     19 const ERASE_BLOCK_BYTES : usize  = 4096;
     20 const ED25519_SIGNATURE_BYTES : usize = 64;
     21 const SHA256_BYTES : usize = 32;
     22 
     23 pub struct Flasher<'a> {
     24     peripheral: esp_hal::peripherals::FLASH<'a>,
     25     sha_engine : sha::Sha<'a>
     26 }
     27 
     28 #[derive(Debug)]
     29 pub enum FlashError {
     30     UnexpectedEof,
     31     OtaFailed,
     32     InvalidSignature,
     33     Infallible
     34 }
     35 
     36 impl<E> From<ReadExactError<E>> for FlashError {
     37     fn from(_e : ReadExactError<E>) -> Self {
     38 	Self::UnexpectedEof
     39     }
     40 }
     41 
     42 impl From<esp_hal_ota::OtaError> for FlashError {
     43     fn from(_e :  esp_hal_ota::OtaError) -> Self {
     44 	Self::OtaFailed
     45     }
     46 }
     47 
     48 impl From<Infallible> for FlashError {
     49     fn from(_e :  Infallible) -> Self {
     50 	Self::Infallible
     51     }
     52 }
     53 
     54 
     55 impl Flasher<'_> {
     56     pub fn new(flash_peripheral: esp_hal::peripherals::FLASH<'static>,
     57                sha_peripheral: esp_hal::peripherals::SHA<'static>) -> Self {
     58 	let sha_engine = sha::Sha::new(sha_peripheral);
     59 
     60 	Self { peripheral: flash_peripheral, sha_engine }
     61     }
     62 
     63     pub async fn write(&mut self, sock: &mut embassy_net::tcp::TcpSocket<'_>) -> Result<(), FlashError> {
     64 	let storage = FlashStorage::new(self.peripheral.reborrow());
     65 	let mut ota = Ota::new(storage).unwrap();
     66 	write_ota(&mut ota, &mut self.sha_engine, sock).await
     67     }
     68 
     69     pub fn var(&mut self, name: &str) -> Option<String> {
     70 	let storage = FlashStorage::new(self.peripheral.reborrow());
     71 	let mut nvs =
     72             esp_nvs::Nvs::new(0x9000, 0x4000, storage).expect("failed to create nvs");
     73 	let ns = const { esp_nvs::Key::from_str("eculocate") };
     74 	match nvs.get(&ns, &esp_nvs::Key::from_str(name)) {
     75 	    Ok(s) => Some(s),
     76 	    Err(e) => None
     77 	}
     78     }
     79 
     80     pub fn set_var(&mut self, name: &str, value: Option<&str>) ->  Result<(), esp_nvs::error::Error> {
     81 	let storage = FlashStorage::new(self.peripheral.reborrow());
     82 	let mut nvs =
     83             esp_nvs::Nvs::new(0x9000, 0x4000, storage).expect("failed to create nvs");
     84 	let ns = const { esp_nvs::Key::from_str("eculocate") };
     85 	match value {
     86 	    Some(v) => {
     87 		nvs.set(&ns, &esp_nvs::Key::from_str(name), v)
     88 	    }
     89 	    None => {
     90 		nvs.delete(&ns, &esp_nvs::Key::from_str(name))
     91 	    }
     92 	}
     93     }
     94 }
     95 
     96 
     97 async fn write_ota(ota : &mut Ota<FlashStorage<'_>>,
     98                    sha_engine : &mut sha::Sha<'_>,
     99                    sock: &mut embassy_net::tcp::TcpSocket<'_>) -> Result<(), FlashError> {
    100     let mut buf = [0u8; SHA256_BYTES + ERASE_BLOCK_BYTES];
    101     let mut buf_offset = 0;
    102     let mut signed;
    103     let mut signature = [0u8; ED25519_SIGNATURE_BYTES ];
    104     let mut next_block_hash = [0u8; SHA256_BYTES];
    105 
    106     // ed25519 signature of first block
    107     // first block is sha256 of second block, plus length as u32
    108     // second+ block is sha256 of successor block, plus data
    109 
    110     let mut block0 = [0u8; SHA256_BYTES + 4];
    111 
    112     sock.read_exact(&mut signature).await?;
    113     sock.read_exact(&mut block0[..SHA256_BYTES + 4]).await?;
    114 
    115     let verifying_key_bytes = include_bytes!("../../pubkey.bin");
    116     let verifying_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify");
    117     signed = verifying_key.verify(&mut block0,
    118                                   &Signature::try_from(&signature).expect("sig")).is_ok();
    119 
    120     info!("initial sig checks out? {:?}", signed);
    121 
    122     let (sha, len_bytes) = block0.split_at(SHA256_BYTES);
    123     let len = u32::from_be_bytes(len_bytes.try_into().expect("4 bytes"));
    124     next_block_hash.clone_from_slice(sha);
    125 
    126     info!("ota length {}", len);
    127 
    128     ota.ota_begin(len, 0)?;
    129 
    130     // total payload to expect is image size plus per-block hashes
    131     let mut remaining = len + (len/(ERASE_BLOCK_BYTES as u32)+ 1) * (SHA256_BYTES as u32);
    132 
    133     while remaining > 0 {
    134         // info!("remaining {}, offset {}", remaining, buf_offset);
    135         match sock.read(&mut buf[buf_offset..]).await {
    136             Ok(0) => {
    137                 info!("unexpected eof on read");
    138                 break;
    139             }
    140             Ok(chunksize) => {
    141                 buf_offset += chunksize;
    142                 remaining -= chunksize as u32;
    143             }
    144             Err(x) => {
    145                 warn!("error reading ota image: {:?}", x);
    146                 break;
    147             }
    148         }
    149 
    150         if remaining <= 0 || buf_offset == SHA256_BYTES + ERASE_BLOCK_BYTES {
    151             let mut actual_hash = [0u8; SHA256_BYTES];
    152             let mut hasher = sha_engine.start::<sha::Sha256>();
    153             let mut p = &buf[..buf_offset];
    154             while !p.is_empty() {
    155                 match  block!(hasher.update(p)) {
    156                     Ok(remaining) => { p = remaining; }
    157                     _ => ()
    158                 }
    159             }
    160             block!(hasher.finish(&mut actual_hash))?;
    161 
    162             signed = signed && next_block_hash == actual_hash;
    163 
    164             if signed {
    165                 if let Err(e) =  ota.ota_write_chunk(&buf[SHA256_BYTES..buf_offset]) {
    166 		    warn!("error writing chunk {:?}", e);
    167                     break;
    168                 }
    169             } else {
    170                 // accept the rest of the update, just don't flash it.
    171                 warn!("signature invalid with {remaining} left");
    172             }
    173 
    174             buf_offset = 0;
    175             next_block_hash.copy_from_slice(&buf[0..SHA256_BYTES]);
    176         }
    177     }
    178     if remaining == 0 {
    179         info!("wrote all bytes");
    180         info!("write {:?}",  sock.write(b"BYE\n").await);
    181         let _ = sock.flush().await;
    182         sock.close();
    183         if signed {
    184 	    ota.ota_flush(false, false)?;
    185             Timer::after_millis(1000).await;
    186             esp_hal::system::software_reset();
    187 	} else {
    188 	    return Err(FlashError::InvalidSignature)
    189 	}
    190     } else {
    191         warn!("stream ended early or flash failed with {} bytes left", remaining);
    192 	Err(FlashError::UnexpectedEof)
    193     }
    194 }