docs: per-device encryption key guide for production - #95
Closed
quechau wants to merge 6 commits into
Closed
Conversation
A self-contained procedure for giving each LoRa device its own AES key instead of the single key currently shared by the whole fleet. Written for production and commissioning engineers, and covering all three device families in one place: Optical Power Meter, Droplet V2 and FGA Gen2. Everything described works with the module as shipped - no firmware or module change is needed for the feature itself. Contents: - Why it matters: every un-keyed device carries the same compile-time key, so extracting it from one device exposes the whole fleet. - How AES protects a frame: byte-level layout drawn as packet diagrams, what is encrypted and what is not, and why the address must stay in the clear - that is precisely what lets each device have a different key without touching the radio protocol. - Why every LoRaRAW address carries C0 in its second byte: it is a protocol version marker, not identity, so only three bytes distinguish devices. Includes the exception engineers will actually meet - legacy MicroEdge units do not follow the convention, and because they encrypt the address itself they cannot be given a per-device key at all. - A worked example following one genuine frame end to end. Part A shows the device assembling it: four readings bit-packed into 23 bytes, the three-byte inner header, padding to a whole AES block, encryption and CMAC. Part B shows Rubix CE taking the same frame apart: splitting the fields, reading address and signal strength with no key at all, decrypting, and decoding back to the four readings it started from. Cross-checked against the module's own republished frame on MQTT, and closed by decrypting with the wrong key to show what failure looks like - which doubles as the technique for working out which key a silent device is actually using. - A procedure overview diagram before the per-device sections, showing where the STM32 and ESP32 paths diverge and where the device is briefly unreachable. - Bench setup per device family. The STM32 boards need SWD and UART at the same time - flash or verify over SWD, talk AT over UART - so they are wired through an ICQMCU module that breaks out both an ST-LINK V3 and a UART adapter. The ESP32 needs only a USB cable. - Entering AT mode on STM32, which is where most people get stuck. The firmware samples the supply rail once at startup and only accepts AT commands above 2900 mV; on battery it reads about 1930 mV, so commands are silently ignored. Two ways in are given: power the board from a 3.3 V supply (the production path, stock firmware), or flash the build from the optical-power-meter branch feat/debug-print-aes-key (commit aeb725a), which adds a 3-second UART entry window that works on battery. The second is marked as bench tooling, not for shipped units. - The key-format trap, stated in the terms an operator sees: a plain AES key (without dashes) for the STM32 boards and the Rubix CE field, a dashed AES key for the ESP32 console. Each rejects the other's form. The FGA error message this produces is quoted verbatim so it can be searched for, with one-line conversions in both directions. - The two failure modes behind most silent devices: AT+SAVE is mandatory on STM32 because AT+AES= only writes RAM, and the AT parser drops characters if a key is sent as a burst rather than at roughly 20-25ms per character. - Verification, troubleshooting that starts by reading the key back from the device rather than trusting the write, and a per-device checklist including a power-cycle read-back so a write that did not persist is caught at the bench instead of hours later in the field. - Known limitations stated plainly: keys sit unprotected in device flash, re-keying breaks the link until both ends are updated, a factory reset silently reverts a device to the shared key, legacy MicroEdge devices cannot be keyed at all, and nothing prevents the same key being reused across devices. Every command, both format conversions and the full end-to-end walkthrough were run against a live board before being written down; the log lines and decryption output in the document are copied from that run. All thirteen diagrams render cleanly under mermaid-cli.
quechau
force-pushed
the
docs/per-device-key-guide
branch
from
August 26, 2026 08:28
fee81c6 to
561a39c
Compare
Move Per-Key-Loraraw.md into docs/md/ and add docs/generate.py, which turns a Mermaid-using Markdown file into a Google-Docs-ready .docx. pandoc cannot render Mermaid on its own, so the script pre-renders each ```mermaid block to a PNG with mermaid-cli (white background, 2x scale for legibility inside a document), rewrites the block as an image reference, then runs pandoc to produce the .docx with a table of contents. Layout: docs/md/<name>.md source docs/images/<name>-N.png rendered diagrams (generated) docs/<name>.docx output (generated) The .docx and the rendered PNGs are build artifacts, so they are ignored locally via .git/info/exclude rather than committed - the Markdown source is the thing under version control. Verified: Per-Key-Loraraw.docx builds with all 13 diagrams embedded as images and the full text intact.
Output location moves from docs/ to docs/docs/, keeping generated documents in their own subfolder separate from the source and the script. Layout is now: docs/generate.py the script docs/md/<name>.md source docs/images/<name>-N.png rendered diagrams (generated) docs/docs/<name>.docx output (generated) Image references stay relative to docs/, and pandoc resolves them via --resource-path regardless of where the .docx is written, so the images are still embedded correctly.
Per-Key-Loraraw.md explains how per-device keys work - the background and the protocol. This adds Per-Key-Loraraw-Guideline.md, the practical companion: a tester reads it top to bottom, does each step, and ticks each box, with no theory in the way. It uses real photos and dashboard screenshots (committed under docs/images/): - Bench wiring for each STM32 board (Optical Power Meter, Droplet V2), showing the ST-Link + UART 2-in-1 module and the 3.3 V wire that is required to enter AT mode - the single most common reason AT commands appear dead. - FGA Gen2 wiring - a single USB-C, no AT mode, but the key must be dashed. - The Rubix CE flow end to end: where to find the Loraraw device manager, how to read the Device Key column (a value means per-device, empty means shared), and how to enter the key when adding or editing a device, including which Model to pick per device family. Also documents running Rubix CE itself: the binary comes from the rubix-ce-builds releases page. The generator picks up the second document automatically. It now resolves both image styles - rendered diagrams (images/<name>-N.png) and the committed screenshots the source references as ../images/... - by listing both directories on pandoc's resource-path. Rendered diagrams stay build artifacts (ignored); the screenshots are source and are committed. Verified: both documents' 15 diagrams render clean, all 9 referenced screenshots resolve, and Per-Key-Loraraw-Guideline.docx builds with 11 images embedded (2 diagrams + 9 screenshots).
Every step that produces output now shows an actual capture from a bench run, so a tester knows exactly what a correct result looks like rather than a paraphrase. - Section 4 (Verify) now carries a full real PASS log for a single Optical Power Meter frame - the enter/decode/decrypt-ok/decoded-values lines and the MQTT payload with the four UVP readings - and a real FAIL log for the same device with a mismatched key. Also notes that the "failed to find point ... UVP-1" lines are point creation, not an encryption error. - AT+SAVE (2A.4) shows the real flash-write lines and CRC around the save. - The SWD flash read-back (2A.6) shows a real two-record dump and points out which page is the live one. - The ESP32 dashes trap (2B.3) shows the verbatim "Data length ... 11 bytes ... NOT within the allowed range" error the console prints for plain hex. All captures are from real hardware. The 15 diagrams across both documents still render clean; the guideline .docx rebuilds with 11 images embedded.
Include the built Word documents in the repo so members can download and open them - or import to Google Docs - without needing pandoc and mermaid-cli installed locally. docs/docs/Per-Key-Loraraw.docx background / how it works docs/docs/Per-Key-Loraraw-Guideline.docx hands-on test procedure (QA/QC) Both are regenerated from docs/md/ by docs/generate.py and carry the latest content, including the real captured logs added to the guideline. The rendered Mermaid PNGs under docs/images/ remain build artifacts and stay ignored - the diagrams are already embedded inside the .docx files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Per-Key-Loraraw.md- a self-contained procedure for giving each LoRa device its own AES key instead of the single key currently shared by the whole fleet.Written for production and commissioning engineers, covering all three device families in one place: Optical Power Meter, Droplet V2 and FGA Gen2.
Documentation only. No code changes, and nothing in the module needs to change for the procedure to work - the per-device key mechanism has been in place since
26597487(Feb 2025). What was missing was a written procedure.Why
Every device that has never been re-keyed ships with the same compile-time AES key. Extracting it from one device, or reading it from the firmware source, exposes traffic for every device in the field. The capability to fix that already exists; this document makes it usable on the production line.
What is in it
How AES protects a frame - byte-level layout, what is encrypted and what is not, and why the address must stay in the clear. That ordering is precisely what allows per-device keys without touching the radio protocol.
Why every LoRaRAW address carries
C0in its second byte - it is a protocol version marker, not identity, so only three bytes distinguish devices. Includes the exception engineers will actually meet: legacy MicroEdge units do not follow the convention, and because they encrypt the address itself they cannot be given a per-device key at all.A worked example following one real frame end to end. Part A shows the device assembling it - four readings bit-packed into 23 bytes, the three-byte inner header, padding to a whole AES block, encryption and CMAC. Part B shows Rubix CE taking the same frame apart - splitting the fields, reading address and signal strength with no key at all, decrypting, and decoding back to the four readings it started from. Cross-checked against the module's own republished frame on MQTT.
Bench setup per device family. The STM32 boards need SWD and UART simultaneously, so they are wired through an ICQMCU module breaking out an ST-LINK V3 and a UART adapter. The ESP32 needs only a USB cable.
Entering AT mode on STM32, which is where most people get stuck. The firmware samples the supply rail once at startup and only accepts AT commands above 2900 mV; on battery it reads about 1930 mV, so commands are silently ignored. Two ways in are documented, including the production path using a 3.3 V supply and stock firmware.
The key-format trap - a plain AES key without dashes for the STM32 boards and the Rubix CE field, a dashed AES key for the ESP32 console. Each rejects the other's form. The FGA error message is quoted verbatim so it can be searched for, with conversions in both directions.
The two failure modes behind most silent devices -
AT+SAVEis mandatory on STM32 becauseAT+AES=only writes RAM, and the AT parser drops characters if a key is sent as a burst rather than at roughly 20-25 ms per character.Verification, troubleshooting and a per-device checklist, including a power-cycle read-back so a write that did not persist is caught at the bench instead of hours later in the field.
Known limitations stated plainly - keys sit unprotected in device flash, re-keying breaks the link until both ends are updated, a factory reset silently reverts a device to the shared key, legacy MicroEdge devices cannot be keyed at all, and nothing currently prevents the same key being reused across devices.
Verification
Every command, both format conversions and the full end-to-end walkthrough were run against a live board before being written down. The log lines and decryption output in the document are copied from that run rather than reconstructed.
All thirteen diagrams render cleanly under
mermaid-cli.