add session registration with signature checking

This commit is contained in:
2026-01-29 19:10:07 +00:00
parent 0d92b907d2
commit 923dbe6fa9
7 changed files with 128 additions and 28 deletions
Generated
+21 -3
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "allocator-api2"
version = "0.3.1"
@@ -350,6 +356,7 @@ dependencies = [
"esp-radio",
"esp-rtos",
"esp-storage",
"hashbrown",
"log",
"nb 1.1.0",
"rand_core 0.6.4",
@@ -664,7 +671,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "641e43d6a60244429117ef2fa7a47182120c7561336ea01f6fb08d634f46bae1"
dependencies = [
"allocator-api2",
"allocator-api2 0.3.1",
"cfg-if",
"document-features",
"enumset",
@@ -838,7 +845,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684c4de2f8907b73c9b891fbda65286a86d34fced4b856f36a7896c211f2f265"
dependencies = [
"allocator-api2",
"allocator-api2 0.3.1",
"bt-hci",
"cfg-if",
"document-features",
@@ -901,7 +908,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ec711c8d06e79c67b75d01595539e86b0aac209643af98ca87a12250428b3"
dependencies = [
"allocator-api2",
"allocator-api2 0.3.1",
"cfg-if",
"document-features",
"embassy-executor",
@@ -1041,6 +1048,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "fugit"
version = "0.3.9"
@@ -1120,6 +1133,11 @@ name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2 0.2.21",
"equivalent",
"foldhash",
]
[[package]]
name = "heapless"
+26 -3
View File
@@ -9,6 +9,14 @@ import select
import datetime
from pathlib import Path
import os
import struct
import secrets
import nacl
import nacl.hash
from nacl.encoding import RawEncoder
from nacl.signing import SigningKey
from zeroconf import ServiceBrowser, ServiceListener, Zeroconf
class ZListener(ServiceListener):
@@ -31,9 +39,10 @@ class ZListener(ServiceListener):
class Client():
def __init__(self):
def __init__(self, private_key):
self.selected_service_name = None
self.bytes_recorded = 0
self.private_key = private_key
self.started = False
self.host_addresses = {}
@@ -67,7 +76,7 @@ class Client():
with open(self.output_pathname(), "wb") as outf:
while self.started:
print("sending subscribe")
b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] )
b = self.session_key + bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] )
sock.send(b)
timeout = time.time() + 60
while self.started and time.time() < timeout:
@@ -86,8 +95,20 @@ class Client():
self.selected_service_name = name
self.on_services_changed()
def register_session(self):
session_key = secrets.token_bytes(32)
sock = socket.create_connection(self.host_addresses[self.selected_service_name])
sig = signing_key.sign(session_key, encoder=RawEncoder)
sock.send(b"KEY0" + session_key + sig.signature)
response = sock.recv(200)
print(response.decode(errors="replace"))
sock.close()
return session_key
def start_recording(self):
if self.selected_service_name != None:
self.session_key = self.register_session()
self.started = True
print("starting record")
peer_thread = threading.Thread(target=self.recording_thread_main, daemon = True)
@@ -108,6 +129,8 @@ class Client():
if __name__ == '__main__':
import pprint
with open("privkey.bin", "rb") as f:
signing_key = SigningKey(f.read(), encoder=RawEncoder)
exists = False
@@ -123,7 +146,7 @@ if __name__ == '__main__':
if self.host_addresses.get(fqname, False):
print(f"found {fqname}")
client = MyClient()
client = MyClient(signing_key)
while not exists:
time.sleep(2)
+2 -1
View File
@@ -50,7 +50,7 @@ smoltcp = { version = "0.12.0", default-features = false, features = [
embassy-futures = "0.1.1"
embassy-sync = { version = "0.7" }
embassy-sync = "0.7"
static_cell = "2"
embassy-net = { version = "0.7.1", features = [
@@ -71,3 +71,4 @@ embedded-storage = "0.3.1"
ed25519-dalek = { version = "2", default-features = false, features = ["alloc"] }
rand_core = "0.6"
nb = "1.1.0"
hashbrown = "0.16.1"
+1 -1
View File
@@ -11,7 +11,7 @@ use embedded_io_async::{Read,ReadExactError};
use esp_hal::sha;
use core::convert::Infallible;
use log::{warn, info, error};
use log::{warn, info};
const ERASE_BLOCK_BYTES : usize = 4096;
const ED25519_SIGNATURE_BYTES : usize = 64;
+18 -9
View File
@@ -8,9 +8,11 @@ use embassy_futures::select::select;
use alloc::vec::Vec;
use log::info;
use log::{warn,info};
use crate::ecu::Ecu;
use crate::wifi::SessionStore;
#[derive(Debug)]
enum SubscriptionKind {
@@ -68,7 +70,7 @@ impl Subscriber {
// interval is per subscription: e.g. we want to get the rpm much
// more often than the ecm id
pub async fn loop_udp_thing(stack : Stack<'_>, ecu: &mut Ecu<'_>) {
pub async fn loop_udp_thing(stack : Stack<'_>, ecu: &mut Ecu<'_>, sessions: &SessionStore ) {
let mut rx_buffer = [0u8; 4096];
let mut tx_buffer = [0u8; 4096];
let mut rx_meta = [PacketMetadata::EMPTY; 512];
@@ -96,13 +98,20 @@ pub async fn loop_udp_thing(stack : Stack<'_>, ecu: &mut Ecu<'_>) {
if let Ok((n, meta)) = res {
info!("packet from {:?}", meta.endpoint);
let mut s = Subscriber {
subscriptions: Vec::from([]),
endpoint: meta.endpoint,
until: Instant::now() + Duration::from_secs(60),
};
s.update_subscriptions(&buf[0..n]);
subscriber = Some(s);
if sessions.is_registered(&buf[..32]).await {
let mut s = Subscriber {
subscriptions: Vec::from([]),
endpoint: meta.endpoint,
until: Instant::now() + Duration::from_secs(60),
};
s.update_subscriptions(&buf[32..n]);
subscriber = Some(s);
} else {
warn!("subscription attempt without registered session {:?}",
&buf[..32]);
subscriber = None;
}
}
}
if let Some(ref mut sub) = subscriber {
+59 -11
View File
@@ -7,6 +7,9 @@ use alloc::vec::Vec;
use alloc::string::String;
use alloc::format;
use hashbrown::HashMap;
use ed25519_dalek::{VerifyingKey, Verifier, Signature};
use embassy_executor::Spawner;
use embassy_net::{
@@ -14,8 +17,10 @@ use embassy_net::{
StackResources
};
use embassy_futures::join::join3;
use embassy_sync::mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Duration, Timer};
use embassy_time::{Duration, Timer, Instant};
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{
@@ -89,6 +94,43 @@ fn dns_records(my_ip_address: [u8;4], serial: Option<usize>) -> Vec<mdns::Resour
Vec::from(records)
}
pub struct SessionStore {
map : Mutex<CriticalSectionRawMutex, HashMap<[u8; 32], Instant>>,
public_key: VerifyingKey
}
impl SessionStore {
pub fn new() -> Self {
let m = HashMap::new();
let verifying_key_bytes = include_bytes!("../../pubkey.bin");
let public_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify");
Self { map : Mutex::new(m), public_key }
}
pub async fn register(&self, key: &[u8], signature: &[u8]) -> bool {
info!("pretending to register {:?}", key);
if self.public_key.verify(key, &Signature::try_from(signature).expect("64 bytes")).is_ok() {
let mut m = self.map.lock().await;
m.insert(key.try_into().unwrap(), Instant::now() + Duration::from_secs(86400));
true
} else {
warn!("wrong key");
false
}
}
pub async fn is_registered(&self, key: &[u8]) -> bool {
let m = self.map.lock().await;
match m.get(key) {
Some(expiry) => *expiry > Instant::now(),
None => false
}
}
}
pub async fn start_wifi(spawner: &Spawner,
flasher: crate::ota::Flasher<'_>,
wifi_peripheral: esp_hal::peripherals::WIFI<'static>,
@@ -140,9 +182,13 @@ pub async fn start_wifi(spawner: &Spawner,
let mut serial = None;
let session_store = SessionStore::new();
join3(
crate::streamer::loop_udp_thing(stack, ecu),
run_tcp_handler(stack, flasher),
crate::streamer::loop_udp_thing(stack, ecu, &session_store),
run_tcp_handler(stack, &session_store, flasher),
async {
loop {
let mdns_thread = mdns::responder(
@@ -213,13 +259,8 @@ async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
}
fn register_session_key(key : &[u8]) {
info!("got session key{:?}", key);
}
async fn run_tcp_handler(stack : Stack<'_>,
sessions : &SessionStore,
mut flasher : crate::ota::Flasher<'_>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
@@ -248,8 +289,15 @@ async fn run_tcp_handler(stack : Stack<'_>,
b"KEY0" => {
info!("sessionkey");
if let Ok(_) = socket.read_exact(&mut buf).await {
let (_sig, key) = buf.split_at(64);
register_session_key(&key);
let (key, sig) = buf.split_at(32);
if sessions.register(key, sig).await {
info!("registered? {}", sessions.is_registered(key).await);
info!("write {:?}", socket.write(b"BYE\n").await);
let _ = socket.flush().await;
} else {
warn!("couldn't register key")
}
socket.close();
}
},
_ => {
+1
View File
@@ -141,6 +141,7 @@ X as PoC, add the signed thing into the rust binary and verify the signature at
X verify signature before flashing
- maintain a hash of session keys for udp listener and expire them eventually
- update the subscription request parsing to expect a session key, validate it
- udp client needs to send the key
- wifi provisioning over ble
- add the 12v->5v buck converter, make a cable to plug into diag port
- ride