Laser Fault Injection on the TROPIC01 Open-Source Secure Element

ahlCVA1 pts0 comments

Laser Fault Injection on the TROPIC01 Open-Source Secure Element | Ledger DonjonTROPIC01 open-source secure element, achieving arbitrary firmware execution.">TROPIC01 open-source secure element, achieving arbitrary firmware execution.">TROPIC01 open-source secure element, achieving arbitrary firmware execution."><br>Introduction

The TROPIC01 is a secure element designed by Tropic Square with a distinctive philosophy: its hardware design (RTL) and firmware source code are publicly available.1 This transparency is commendable and invites third-party scrutiny, which is exactly what we did.

In this post, we describe how we used laser fault injection (LFI) to bypass the Ed25519 signature verification on the TROPIC01, enabling arbitrary firmware execution. We also discuss why, despite achieving code execution, the chip’s hardware-backed secret storage proved remarkably resilient to further exploitation. We performed this work as part of a security evaluation using commercial TROPIC01 samples running bootloader v2.0.1, and worked on a Coordinated Vulnerability Disclosure with Tropic Square.

Signature Verification

Identifying signature verification in power consumption traces

The TROPIC01 SoC delegates all asymmetric cryptography to a dedicated coprocessor called SPECT (Secure Processor of Elliptic Curves for TROPIC01). SPECT is architecturally separate from the main RISC-V CPU: it has its own instruction set (32 general-purpose 256-bit registers, a dedicated constant ROM, and crypto-optimized instructions like MUL25519, HASH, and REDP), its own boot ROM, and its own updatable firmware.

When monitoring the chip’s power consumption during normal operation, SPECT activity stands out clearly. The coprocessor draws noticeably less current than the CPU subsystem, and signature verification operations involve a lot of computations, and therefore produce a power consumption pattern that is very easy to identify in the oscilloscope traces.

Normal boot power trace. The repeated lower-power activity windows (like the one centered on the 160ms mark) correspond to SPECT-assisted signature verification during boot; in the normal case we observe three such verification patterns before the device goes into idle.<br>At boot time, we observe three distinct SPECT signature verification patterns in the power trace on a normal boot. We interpret these as:

Vendor public key signature verification2

CPU firmware signature verification

SPECT firmware signature verification

Understanding the implementation from source

While the CPU boot ROM is not open source, the SPECT coprocessor boot ROM is. Since the signature verification is executed by SPECT, we can therefore study the exact Ed25519 verification implementation from the published source code.

The ROM for SPECT (boot_main.s) is minimal: it reads a configuration word, checks for the eddsa_verify operation ID (0x4B), and jumps to the verification routine.

The verification itself (eddsa_verify.s) implements standard RFC 8032 Ed25519 verification:

Compute Q1 = S * B (scalar multiplication of the signature scalar with the base point)

Compute E = SHA-512(ENC(R) || ENC(A) || M) mod q (the challenge hash)

Compute Q2 = E * A (scalar multiplication of the challenge with the public key)

Compute Q = Q1 - Q2 (point subtraction)

Compress Q and compare with R

The final comparison is where it gets interesting from a fault injection perspective (eddsa_verify.s, lines 120–156):

; ==============================================================================<br>; Final comparison -> ENC(R) = ENC(S.B - SHA512(R, A, M).A)<br>; ==============================================================================<br>CMPI r31, 0 ; Clear zero flag (r31 is for sure not 0)<br>.ifdef SPECT_ISA_VERSION_1<br>LD r31, ca_ffff<br>SUBP r2, r23, r8<br>CMPA r2, 0<br>.endif<br>.ifdef SPECT_ISA_VERSION_2<br>XOR r2, r23, r8<br>.endif<br>BRZ eddsa_verify_success<br>eddsa_verify_fail:<br>MOVI r2, 1<br>ST r2, eddsa_verify_output_result<br>JMP set_res_word<br>eddsa_verify_success:<br>MOVI r2, 0xBA<br>ROL8 r2, r2<br>ORI r2, r2, 0x11<br>ROL8 r2, r2<br>ORI r2, r2, 0xFA<br>ROL8 r2, r2<br>ORI r2, r2, 0xDE<br>ROL8 r2, r2<br>ORI r2, r2, 0x0B<br>ROL8 r2, r2<br>ORI r2, r2, 0x5E<br>ROL8 r2, r2<br>ORI r2, r2, 0x55<br>ROL8 r2, r2<br>ORI r2, r2, 0xED<br>ST r2, eddsa_verify_output_result<br>JMP set_res_word<br>On ISA v2, the compressed points R and Q are XORed together. If the result is zero (points are equal), the branch BRZ eddsa_verify_success is taken, and the success value is constructed byte-by-byte and stored at the output. On failure, a simple 1 is stored instead.

This creates a particularly attractive target for fault injection: near the end of verification, a small number of instructions decide whether the failure value remains in place or the success value is written instead. For example, skipping the failure-path JMP set_res_word would allow execution to fall through into the success path.

It is worth noting that the SPECT application firmware’s EdDSA signing routine (eddsa_finish.s) includes a self-verification step that...

verification signature spect tropic01 source firmware

Related Articles