eculocate

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

commit cc9bf7897cdd8653b897d83c6bbae9383d43e3ec
parent 1d03cfdd39c17de3c009320b2a4ecf678e8202dc
Author: Daniel Barlow <dan@telent.net>
Date:   Wed, 21 Jan 2026 18:32:57 +0000

rearrange OTA message format to sign each block

Diffstat:
Mecu-esp32/src/wifi.rs | 65+++++++++++++++++++++++++++++++++++++----------------------------
Motaclient.py | 31+++++++++++++++++++++++--------
Mshell.nix | 2+-
3 files changed, 61 insertions(+), 37 deletions(-)

diff --git a/ecu-esp32/src/wifi.rs b/ecu-esp32/src/wifi.rs @@ -219,7 +219,7 @@ async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>, len: usize) -> usize { let mut n = 0; while n < len { - match socket.read(&mut buf[n..]).await { + match socket.read(&mut buf[n..len]).await { Ok(0) => { warn!("read EOF"); break; @@ -236,48 +236,61 @@ async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>, n } +const ERASE_BLOCK_BYTES : usize = 4096; +const ED25519_SIGNATURE_BYTES : usize = 64; + async fn write_ota(ota : &mut Ota<FlashStorage<'_>>, - sock: &mut embassy_net::tcp::TcpSocket<'_>, - len: u32) { - let mut buf = [0u8; 4096]; + sock: &mut embassy_net::tcp::TcpSocket<'_>) { + let mut buf = [0u8; ERASE_BLOCK_BYTES + ED25519_SIGNATURE_BYTES]; let mut buf_offset = 0; + let len = { + let mut len_bytes = [0u8; 4]; + if read_all(sock, &mut len_bytes, 4).await == 4 { + u32::from_be_bytes(len_bytes) + } else { + 0 + } + }; + + info!("ota length {}", len); if let Err(e) = ota.ota_begin(len, 0) { - error!("ota_begin faled {:?}", e); + error!("ota_begin failed {:?}", e); return; - } + }; + - let mut len = len; - while len > 0 { + let mut remaining = len; + while remaining > 0 { + // info!("remaining {}, offset {}", remaining, buf_offset); match sock.read(&mut buf[buf_offset..]).await { Ok(0) => { info!("unexpected eof on read"); break; } Ok(chunksize) => { - // info!("read {} at {}, {} remaining", chunksize, buf_offset, len); + // info!("read {} at {}, {} remaining", chunksize, buf_offset, remaining); buf_offset += chunksize; - len -= chunksize as u32; + remaining -= chunksize as u32; } Err(x) => { warn!("error reading ota image: {:?}", x); break; } } - if buf_offset == 4096 { - info!("{} remaining", len); - if let Err(_) = ota.ota_write_chunk(&buf) { + if buf_offset == ED25519_SIGNATURE_BYTES + ERASE_BLOCK_BYTES { + if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..]) { break; } buf_offset = 0; }; - if len <= 0 { - if let Err(_) = ota.ota_write_chunk(&buf[0..buf_offset]) { + if remaining <= 0 { + if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..buf_offset]) { break; } }; } - if len == 0 { + if remaining == 0 { info!("wrote all bytes"); if ota.ota_flush(false, false).is_ok() { // ignore crc, no rollback info!("write {:?}", sock.write(b"BYE\n").await); @@ -287,7 +300,7 @@ async fn write_ota(ota : &mut Ota<FlashStorage<'_>>, esp_hal::system::software_reset(); } } else { - warn!("stream ended early or flash failed with {} bytes left", len); + warn!("stream ended early or flash failed with {} bytes left", remaining); } } @@ -299,10 +312,11 @@ fn register_session_key(key : &[u8]) { async fn run_tcp_handler(stack : Stack<'_>, flash_peripheral: esp_hal::peripherals::FLASH<'static>) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; - let mut buf = [0u8; 68]; + let mut buf = [0u8; 96]; let flash = FlashStorage::new(flash_peripheral); let mut ota = Ota::new(flash).unwrap(); + loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(Duration::from_secs(10))); @@ -314,24 +328,19 @@ async fn run_tcp_handler(stack : Stack<'_>, flash_peripheral: esp_hal::periphera info!("Received connection from {:?}", socket.remote_endpoint()); - let n = read_all(&mut socket, &mut buf, 68).await; + let n = read_all(&mut socket, &mut buf, 4).await; - if n == 68 { + if n == 4 { info!("got a thing"); match &buf[0..4] { b"ROM0" => { info!("OTA request"); - let mut len_bytes = [0u8; 4]; - let _n = read_all(&mut socket, &mut len_bytes[..], 4).await; - let len = u32::from_be_bytes(len_bytes); - info!("reading {} bytes", len); - write_ota(&mut ota, &mut socket, len).await; + write_ota(&mut ota, &mut socket).await; }, b"KEY0" => { info!("sessionkey"); - let _sig = &buf[4..68]; - let mut key = [0u8; 32]; // 256 bits - if read_all(&mut socket, &mut key, 32).await == 32 { + if read_all(&mut socket, &mut buf, 96).await == 64 { + let (_sig, key) = buf.split_at(64); register_session_key(&key); } }, diff --git a/otaclient.py b/otaclient.py @@ -1,6 +1,14 @@ import socket import struct import sys +import nacl +import os + +from nacl.signing import SigningKey +from nacl.encoding import RawEncoder + +with open("privkey.bin", "rb") as f: + signing_key = SigningKey(f.read(), encoder=RawEncoder) sock = socket.create_connection(("eculocate.local", 5000)) @@ -8,17 +16,24 @@ with open(sys.argv[1], "rb") as f: rom_image = f.read() image_len = len(rom_image) +print(f"raw {image_len}") +# 64 byte signature needed for each 4096 byte block +send_len = image_len + (1+int(image_len/4096)) * 64 +print(f"expanded {send_len}") -# Build payload -payload = ( - b"ROM0" + - (b"z" * 64) + - struct.pack(">L", image_len) + - rom_image +header = ( + b"ROM0" + struct.pack(">L", send_len) ) -# Send payload -sock.sendall(payload) +sock.send(header) + +for i in range(0, image_len, 4096): + chunk = rom_image[i : i + 4096] + sig = signing_key.sign(chunk, encoder=RawEncoder) + print(f"offset {i}") + sock.send(sig.signature) + sock.send(chunk) + # Read a line from the socket (until newline) response = b"" diff --git a/shell.nix b/shell.nix @@ -5,7 +5,7 @@ # load external libraries that you need in your rust project here ]; python = (pkgs.python3.withPackages (python-pkgs: [ - python-pkgs.ed25519 + python-pkgs.pynacl ])); in pkgs.mkShell rec {