What changed, and why it matters
This commit fixes a bug in the COLDCARD Mk4 bootloader where the same encryption counter value (nonce) was reused for both a verification check value and the real secret data. The fix changes the last byte of the nonce to a distinct value (0x80) when encrypting/decrypting the check value, so it no longer overlaps with the counter range used for actual secrets. Reusing a counter with AES-CTR-style encryption can weaken confidentiality guarantees and, in some cryptographic designs, allow an attacker who sees two encrypted blocks with the same nonce to learn information or tamper with data. The change is small and defensive, and the changelog credits an external reporter.
Treat this as a security-hardening fix that may have exploitable cryptographic implications under specific fault or side-channel conditions. Review whether any prior firmware version allowed the check-value ciphertext to be observed or manipulated by an attacker with physical or glitching access, because CTR nonce reuse can enable keystream recovery and plaintext disclosure. Coordinate with the credited reporter to understand the practical impact and consider issuing a security advisory if a concrete exploit path exists.
Security signals we found
Nonce/counter reuse in AES counter mode
Domain separation between check value and real secret
Bootloader cryptographic secret handling
External security researcher credited in changelog
Changelog labels change as 'Bugfix' with security-relevant wording
Evidence from the diff
In stm32/mk4-bootloader/se2.c, se2_encrypt_secret() and se2_decrypt_secret() use AES in a counter mode (likely AES-CTR via the aes_done/aes_add helper) keyed by aes_key and parameterized by a 16-byte nonce. Before this patch, the check_value (32 zero bytes used as an integrity/availability check) and the real secret were encrypted under the same nonce whose last byte was set to offset/AES_BLOCK_SIZE. Because the check_value is always 32 zero bytes at a fixed logical position, using the same counter range as a real secret means ciphertext blocks could collide or leak XOR relationships if the same key/nonce/counter combination is ever applied to both plaintexts. The patch sets nonce[15] = 0x80 for the check_value operation, reserving counters 0x80-0xFF (or simply a distinct domain) for the check, and then resets nonce[15] = offset/AES_BLOCK_SIZE for the real data. This is a domain-separation fix: it prevents nonce/counter reuse between the check block and secret blocks.
Changed components
stm32/mk4-bootloader/se2.cCOLDCARD Mk4 bootloaderSecure Element (SE1/SE2) secret encryption/decryptionPIN digest / secret storage encryption pathInspect captured patch +5 / −0
### releases/Next-ChangeLog.md
@@ -55,6 +55,8 @@ This lists the new changes that have not yet been published in a normal release.
"Transaction modified". Thanks to FreeZ Agent for the report and PoC.
- Bugfix: Reject duplicate cosigner keys, and cosigner keys the device already holds,
during multisig wallet enrollment. Thanks to drk1wi for reporting this.
+- Bugfix: Separate the SE1 check nonce from the PIN digest. Thanks to
+ [@instagibbs](https://github.com/instagibbs) for reporting this issue.
# Mk Specific Changes
### stm32/mk4-bootloader/se2.c
@@ -1195,6 +1195,7 @@ se2_encrypt_secret(const uint8_t secret[], int secret_len, int offset,
if(check_value) {
// encrypt the check value: 32 zeros
+ nonce[15] = 0x80; // separate counter range from encrypted secrets
aes_init(&ctx);
ctx.num_pending = 32;
aes_done(&ctx, check_value, 32, aes_key, nonce);
@@ -1239,6 +1240,7 @@ se2_decrypt_secret(uint8_t secret[], int secret_len, int offset,
if(check_value) {
// decrypt the check value
+ nonce[15] = 0x80; // separate counter range from encrypted secrets
aes_init(&ctx);
aes_add(&ctx, check_value, 32);
uint8_t got[32];
@@ -1254,6 +1256,7 @@ se2_decrypt_secret(uint8_t secret[], int secret_len, int offset,
}
// decrypt the real data
+ nonce[15] = offset / AES_BLOCK_SIZE;
aes_init(&ctx);
aes_add(&ctx, main_slot, secret_len);
aes_done(&ctx, secret, secret_len, aes_key, nonce);Why this scored 60/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.