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:
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 {