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.
Since this is not a security vulnerability no user action is needed for everyday users of the TKey.
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:
FW_RAM
;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.
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.
Firmware TK1-23.03.2 Bellatrix.
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.
We would like to thank the security researcher Sergei Volokitin of Hexplot, who discovered the problem and reported it through our bug bounty program.
Cover image attribution: Gerd Altmann