From 9bd9bf9cdba971bef7d7c7adb3a85ad3795244d3 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Aug 2026 10:21:27 +1000 Subject: [PATCH 1/3] tools: gateway: handle device key retrieval failure Detect and handle the case where the device key cannot be retrieved from the database. This prevents the gateway from crashing when the retrieval fails. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/gateway.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/infuse_iot/tools/gateway.py b/src/infuse_iot/tools/gateway.py index 309aa75..c2b582f 100644 --- a/src/infuse_iot/tools/gateway.py +++ b/src/infuse_iot/tools/gateway.py @@ -75,7 +75,9 @@ def notification_broadcast(self, notification: ClientNotification): if self.server: self.server.broadcast(notification) - def query_device_key(self, infuse_id: int, cb_event: threading.Event | None = None): + def query_device_key(self, infuse_id: int, cb_event: threading.Event | None = None) -> bool: + """Query device key, returns True on success, False on failure""" + def security_state_done(pkt: PacketReceived, _rc: int, response: bytes, challenge): decoded = defs.security_state.response.vla_from_buffer_copy(response) self.ddb.observe_security_state( @@ -98,28 +100,32 @@ def public_keys_done(pkt: PacketReceived, rc: int, response: bytes, _): if key.id == defs.rpc_enum_key_id.SECONDARY_REMOTE_PUBLIC_KEY: self.ddb.observe_secondary_remote_public_key(infuse_id, bytes(key.key)) - def run_cmd_pkt(cmd_pkt: PacketOutputRouted): + def run_cmd_pkt(cmd_pkt: PacketOutputRouted) -> bool: encrypted = cmd_pkt.to_serial(self.ddb) # Write to serial port Console.log_tx(cmd_pkt.ptype, len(encrypted)) self.port.write(encrypted) if cb_event is not None: # Wait for the response - cb_event.wait(1.0) + return cb_event.wait(1.0) + return True # Run security_state RPC challenge = random.randbytes(16) cmd_pkt = self.rpc.generate_addressed( infuse_id, defs.security_state.COMMAND_ID, challenge, Auth.NETWORK, security_state_done, challenge ) - run_cmd_pkt(cmd_pkt) + if not run_cmd_pkt(cmd_pkt): + return False if self.ddb.has_local_root: # Query other public keys from the device cmd_pkt = self.rpc.generate_addressed( infuse_id, defs.security_public_keys.COMMAND_ID, b"\x00", Auth.NETWORK, public_keys_done, None ) - run_cmd_pkt(cmd_pkt) + if not run_cmd_pkt(cmd_pkt): + return False + return True class SerialRxThread(SignaledThread): @@ -271,7 +277,9 @@ def _handle_epacket_send(self, req: GatewayRequestEpacketSend): for hop in routed.route: if hop.auth == Auth.DEVICE and not self._common.ddb.has_shared_key(hop.infuse_id): cb_event = threading.Event() - self._common.query_device_key(hop.infuse_id, cb_event) + if not self._common.query_device_key(hop.infuse_id, cb_event): + Console.log_error("Failed to query device key for {infuse_id:016x}") + return # Encode and encrypt payload encrypted = routed.to_serial(self._common.ddb) From 69a95840334dad8de4558233282456ce38cd7abb Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Aug 2026 10:22:54 +1000 Subject: [PATCH 2/3] tools: gateway: proactively query device key Proactively query device key when connecting to a device if it is not already known. This fixes tools that don't need the command characteristic (e.g. bt_log) to still be able to decode device key authorised packets. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/gateway.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/infuse_iot/tools/gateway.py b/src/infuse_iot/tools/gateway.py index c2b582f..ce3bea9 100644 --- a/src/infuse_iot/tools/gateway.py +++ b/src/infuse_iot/tools/gateway.py @@ -331,6 +331,11 @@ def _bt_connect_cb(self, _pkt: PacketReceived, rc: int, response: bytes, _): self._common.port.write(encrypted) return + if not self._common.ddb.has_shared_key(infuse_id): + # Pro-actively query key information + cb_event = threading.Event() + self._common.query_device_key(infuse_id, cb_event) + # Notify connection success self._connected_notification(infuse_id) @@ -349,7 +354,7 @@ def _handle_conn_request(self, req: GatewayRequestConnectionRequest): subs = 0 bt_char = defs.rpc_enum_infuse_bt_characteristic - if req.data_types & req.DataType.COMMAND: + if req.data_types & req.DataType.COMMAND or not self._common.ddb.has_shared_key(req.infuse_id): subs |= bt_char.COMMAND if req.data_types & req.DataType.DATA: subs |= bt_char.DATA From 3863836605c420905520acb87c0607ada3c2506c Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Aug 2026 10:25:48 +1000 Subject: [PATCH 3/3] tools: ota_upgrade: don't exit on failing time set Don't exit the program if the time set fails. Instead terminate the connection and continue scanning for other devices. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/ota_upgrade.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/infuse_iot/tools/ota_upgrade.py b/src/infuse_iot/tools/ota_upgrade.py index 02d7108..1d9b59b 100644 --- a/src/infuse_iot/tools/ota_upgrade.py +++ b/src/infuse_iot/tools/ota_upgrade.py @@ -324,12 +324,12 @@ def run(self): source.infuse_id, GatewayRequestConnectionRequest.DataType.COMMAND, self._conn_timeout ) as mtu: # Set time on the remote device to keep keys in sync - if not self.set_device_time(mtu, source.infuse_id): - break - if self._single_diff: - self.run_file_copy(live, mtu, source) - else: - self.run_file_upload(live, mtu, source) + if self.set_device_time(mtu, source.infuse_id): + # Upload the patch file + if self._single_diff: + self.run_file_copy(live, mtu, source) + else: + self.run_file_upload(live, mtu, source) except ConnectionRefusedError: self.state_update(live, "Scanning")