On January 15 we received a potential vulnerability report about the TKey firmware through our bug bounty program. The reporter had discovered that code that should erase memory where sensitive data is stored, is optimised away by the compiler. We deem the firmware problem a fairly benign bug because no sensitive data is leaked and the memory is erased and hardware protected anyway.
In this blog post we go through the details about this part of the firmware, why the eager optimiser wasn’t a vulnerability, and how we fixed the bug.
User action
Since this is not a security vulnerability no user action is needed for everyday users of the TKey.
Details
One of the Tkey firmware’s tasks is to do an unconditional measurement of device applications. We call the result Compund Device Identifier (CDI), a combination of this particular TKey device (Unique Device Secret, UDS), the integrity of the device application, and an optional User Supplied Secret (USS).
The Unique Device Secret (UDS) is the unchanging base of all secrets in the TKey. It is of utmost importance to protect the UDS.
The entire firmware stack and all its variables are stored in a
special firmware RAM (FW_RAM
), which is protected by hardware and
inaccessible to the application when it has been started. This is an
entirely different memory compared to what the application runs in.
The UDS itself can only be read once in firmware mode per power cycle,
protected on word boundaries.
During the CDI computation, the Unique Device Secret lives for a short
moment in the FW_RAM
to be able to reach it byte by byte for the
hash function. This lives both in the local_uds
variable and the
BLAKE2s context secure_ctx
. The local_uds
is successfully cleared
by the code but the clearing of the secure_ctx
fails in affected
versions because of compiler optimisation.
However, on the very next line of code the context is updated again and the internal buffer where the copy of the UDS was held is completely overwritten with the measured application digest.
Directly after that the entire FW_RAM
is unconditionally cleared by
inline assembly. Then the firmware switches to application mode just
before jumping to the device app. Switching to application mode
protects the already cleared FW_RAM
from the device application with
hardware protections.
In conclusion, the UDS is to be protected by:
-
hardware controlled one-time read access;
-
clearing the temporary copy after use;
-
immediately overwriting the context copy;
-
clearing the entire
FW_RAM
; -
hardware-protected CPU mode (switching from firmware mode to application mode).
One of these, number 2, failed because of compiler optimisation, and
the UDS lives slightly longer in the special FW_RAM
than intended in
the affected versions of the firmware.
The fix
Instead of doing a simple memset()
we add a new function that is
forcing the compiler to use it with a volatile
declaration of the
data:
void secure_wipe(void *v, size_t n)
{
volatile uint8_t *p = (volatile uint8_t *)v;
while (n--)
*p++ = 0;
}
Then we use secure_wipe()
instead of memset()
:
secure_wipe(&secure_ctx, sizeof(secure_ctx));
Just to be sure we use secure_wipe()
also on the local_uds
variable whose clearing wasn’t optimised out.
We then verified, by disassembling the resulting binary, that the code to clear the memory was indeed present.
Affected version
Firmware TK1-23.03.2 Bellatrix.
New Version
Firmware TK1-24.03 Bellatrix. (https://github.com/tillitis/tillitis-key1/releases/tag/TK1-24.03)
All orders shipped from Tillitis with the TKey (SKU: TK-1) and serial number (UDI) starting from 0x0133708200000001 will have the new version of the firmware.
As always, we recommend users with TKey Unlocked (SKU: TK-1U) to use the latest released version when programming their TKey Unlocked.
Credits
We would like to thank the security researcher Sergei Volokitin of Hexplot, who discovered the problem and reported it through our bug bounty program.