-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ieee802154_security: Nonce is reused after reboot #16844
Comments
Could SRAM PUF help here at least with the nounce generation? |
I don't see a full story from it, but maybe ... If we went for an entropy approach, its nose component could help to make sure there's enough of it. (At least in cold starts; do we have something to carry over in warm starts?). But no matter how good our source of randomness after a million messages with many reboots inbetween (5 byte = 40 bit ~= 1000**4, at sqrt(n) we have good birthday chances) it still becomes likely. No standard says that these can be random, but outside 6TiSCH I don't think any implementation has hard requirements on the numbers being ascending either (I'm unaware of any monotony based replay protection, but then again I don't know many 6lo implementations in the first place). The PUF itself, as I understand it, would only give us more data that doesn't change across reboots (like the key and the LL address already do). Hm ... I wouldn't see random initialization at startup as a solution to the whole problem, but given that the full solution will need long-term work, it might be a band-aid worth putting on. (Also, running the numbers made me look into flash cycles -- on nrf52 with a 2-page persistence area we get 10k erase cycles * 128 writable words * 2 pages ~= 2 million reboots; I hope to optimize soft reboots so that only hard reboots need flashing. So the randomness approach is probably even practical, but still I wouldn't want to declare it as "just use it that way"). [edit: fixed cycle numbers] |
So with As far as I understand we would need persistent storage to solve the issue. |
We could start with a random frame counter with or without the SRAM PUF -- but the SRAM PUF gives better randomness at a cold start. (How good randomness we have generally depends on hardware support; the SRAM PUF can provide some in those cases where there is no good hardware entropy source). As for backup_ram, what are we talking about here?
In general, the requirements are:
If something can't provide atomicity, it's not too bad -- that can be solved in software using ordered writes and some primitive journaling. If persistence indication is not provided, checksums can fill in. Populating anything other than flash might during flashing could be tricky (I guess the words give it away). There are tricks that can be done with flashing (eg. we could flash keys and a 0xffffffff first-start indicator, and on first start the device clears the persistent-RAM, starts counting there, and invalidates the first-start indicator), but the clearer approach is then probably to flash a key-less (or otherwise invalid) configuration and then populate the persistent-RAM through the debugger, automated through the console or whatever. -- But all these apply only if there is viable long-term backupped RAM available in the first place. |
Exactly!
It depends ... See macro
Sorry the "external" was a bit misleading. It does not matter if we use peripheral or external EEPROM. |
CPU_BACKUP_RAM_NOT_RETAINED
It does not matter if we use peripheral or external EEPROM
Can we create (or, in light of NIH discussions, find and evaluate) an abstraction for the needed persistence that is backed by whatever is there (or is configured; "device breaks if battery backup ever fails" can be OK for some cases)?
(That'd need a full story for the "non-flash data at flash time" problem, but so what).
|
There is also
I'm not sure why it has to break. The device was at some point in a state where it had not joined the network yet. "device needs to be paired again if battery backup ever fails" sounds much better to me. |
Good point, rtc_mem could be another backend.
Because the device can't join the network again even with re-pairing. A repairing (no pun intended) that makes the device joinable would involve rekeying the full network, at which point we're at the complexity levels of CoJP again. But to avoid that breakage ... What we could probably (warning: again we're in unspecified territory) do as a slightly better band-aid is working with absolute time. Quite like the ASNs of 6tisch but less synchronized: If the devices can guarantee that they'll send at most, say 1000 messages per second (which might be realistic from the radio properties), then the key on the PC would come with an absolute timestamp of when it was created, and what's the message rate (is 1000msg/s are a bound already enforced by the physical layer?). Then any device can be rejoined without rekeying if its sequence number is set to the current time in the relevant scale. There's no need for the device to keep monotonous time, just to count -- as long as it adheres to the data rate, its highest sent seqno will always be less than the current time. It'd be still worse than the full 6TiSCH solution, but a key could still live for like 30 years on the usable 5 byte sequence numbers. (Worse because
but still) With that, the remaining limitation would be that once persistence is lost, the device needs to obtain a trusted time again in the key's time scale. |
Reading RFC9030 (yes that's 6TiSCH, but CoJP=RFC9031 doesn't reiterate everything that's there), there's an alternative to keeping sequence numbers locally and rekyeing: It says at the end of this section that the nonce is not necessarily built from the MAC address, but can also be built from the short identifier. That still leaves us with the problem that the device has to know which short identifier it's supposed to be using, which again means we'd need CoJP, but at least this doesn't need extra information to the CoJP (that'd tell it the MAC address), and the CoJP can get away without rekeying the network for as long as it is not running out of short identifers. CoJP when used with EDHOC doesn't need any local persistence, so it's easy again. It may be worth pointing out that there are two kinds of persistence we're having here:
|
Truth be told, I'm not sure where 802.15.4 says short addresses are legal; investigating -- but appreciating help there (@mcr, maybe?). |
Description
The frame counter used with ieee802154_security is initialized with 0 at startup. While it is protected against overflow, it is not protected against being reset, and that reset happens whenever the device restarts.
As the key is flashed into the device in ieee802154_security's normal operation, and the sender LL address is constant per device, the same nonce (varying only through the resetting frame counter) is used in the AES encryption multiple times. Reuse of the same (nonce, key) breaks confidentiality guarantees.
(AES-CCM is used here, so AIU it's not as bad as if GCM were used, when there'd be key leakage).
Steps to reproduce the issue
(All done on microbit-v2; I have high confidence in this working on any 802.15.4 encryption capable device).
USEMODULE+=ieee802154_security
/hello_world/coap
/.well-known/core
Expected results
Actual results
(Note the
20
in the second row where the shared "l" of "hello" and "well" is, as well as the "\x04co" of the "core" / "coap" option; variation is in MIDs (first row bytes 12-13), token (second row, first 4 bytes) and the diverting texts).Versions and cross-references
All since introduction in 2021.01 until current HEAD.
Since #16841, the module in question has been marked as experimental.
Disclosing this has been discussed in the closed security list, and was deemed responsible given the overall circumstances.
CVE-2021-41061 has been assigned to this issue.
Road forward
This is not trivial to fix, as we don't have any committed persistence inside generic devices, and even with 6TiSCH minimal security the problem is just shifted (for section 4.6 requires monotony of ASNs on the device which is equivalent to this problem, although it'd shift the attack difficulty to an active replay of old beacons). Likewise, most advanced modes need persistence, until (with ace-ake-authz) asymmetric negotiation comes into play.
Off my head I don't know any standard solutions that can do with neither asymmetric cryptography nor local persistence; some randomness based scheme could possibly be deployed but it'd be very ad-hoc, custom and eventually not easier than the existing solutions.
I think that the discussion in #16730 can serve as a starting point.
The text was updated successfully, but these errors were encountered: