chore(crypto): improve constant-time comparison
What changed, and why it matters
This commit hardens how Trezor compares secret values (passwords, PINs, cryptographic tags, and checksums) so that an attacker cannot learn information by measuring how long the comparison takes. It also adds a fault-injection check: if a glitch skips the comparison loop, the device is supposed to detect that and halt. The change is defensive and reduces the risk of side-channel and fault attacks, but it does not by itself fix a known, directly exploitable bug.
Treat this as a security-hardening commit rather than an urgent vulnerability fix. Verify that production firmware images use the real tc_fault_handler() and not the no-op default. Review the consteq() implementation for compiler optimizations that could reintroduce short-circuiting or remove the volatile counter. Consider adding tests that simulate loop-skipping faults and timing measurements to validate constant-time behavior.
Security signals we found
Replaces non-constant-time memcmp with constant-time consteq in cryptographic tag verification paths (AES-GCM, Poly1305, base58 checksums, ed25519 signature verification, SLIP25 MAC)
Adds volatile loop counter and loop-completion fault check in consteq to mitigate fault-injection skipping of the comparison
Introduces tc_fault_handler() abstraction; production builds map it to a fatal error, while a no-op fallback is provided for tests/unconfigured builds
Removes duplicated constant-time helpers (bip39.c, ed25519-donna) in favor of a single audited implementation
Evidence from the diff
The patch centralizes constant-time equality into a new consteq() implementation and replaces memcmp/short-circuit comparisons across AES-GCM, Poly1305, base58, BIP39, ed25519, Monero, NEM, storage mnemonic digest, and SLIP25 MAC checks. The new consteq() uses a volatile loop counter and a post-loop fault-handler check intended to detect skipped-iteration glitch attacks. A default no-op fault handler is provided for builds that do not supply a real handler; production firmware (core and legacy) wires tc_fault_handler() to ensure(secfalse, msg), which halts the device.
Changed components
crypto/consteq.ccrypto/consteq.hcrypto/fault_handler.hcrypto/fault_handler_noop.ccrypto/aes/aesgcm.ccrypto/base58.ccrypto/bip39.ccrypto/chacha20poly1305/poly1305-donna.ccrypto/ed25519-donna/ed25519-donna-impl-base.ccrypto/ed25519-donna/ed25519-donna-impl-base.hcrypto/ed25519-donna/ed25519.ccrypto/monero/base58.ccrypto/nem.clegacy/firmware/config.clegacy/firmware/fsm_msg_coin.hcore/embed/rtl/error_handling.ccore/embed/upymod/modtrezorcrypto/modtrezorcrypto-monero.hlegacy/common.cInspect captured patch +156 / −67
diff --git a/core/embed/rtl/error_handling.c b/core/embed/rtl/error_handling.c
index aad8e6a4..58d5fdbe 100644
--- a/core/embed/rtl/error_handling.c
+++ b/core/embed/rtl/error_handling.c
@@ -18,6 +18,7 @@
*/
#include <trezor_rtl.h>
+#include "fault_handler.h"
#ifndef TREZOR_EMULATOR
// Stack check guard value set in startup code.
@@ -78,3 +79,5 @@ void __attribute__((noreturn)) __fatal_error(const char *msg, const char *file,
system_exit_fatal(msg, file, line);
while (1);
}
+
+void tc_fault_handler(const char *msg) { ensure(secfalse, msg); }
diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-monero.h b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-monero.h
index aa461e9a..67c1de60 100644
--- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-monero.h
+++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-monero.h
@@ -24,6 +24,7 @@
#include "../trezorobj.h"
#include "bignum.h"
+#include "consteq.h"
#include "memzero.h"
#include "monero/monero.h"
@@ -1073,7 +1074,7 @@ STATIC mp_obj_t mod_trezorcrypto_ct_equals(const mp_obj_t a, const mp_obj_t b) {
return MP_OBJ_NEW_SMALL_INT(0);
}
- int r = ed25519_verify(buff_a.buf, buff_b.buf, buff_a.len);
+ int r = consteq(buff_a.buf, buff_b.buf, buff_a.len);
return MP_OBJ_NEW_SMALL_INT(r);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ct_equals_obj,
diff --git a/crypto/Makefile b/crypto/Makefile
index 4b4aec82..d970202f 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -136,6 +136,8 @@ SRCS += tls_prf.c
SRCS += hash_to_curve.c
SRCS += buffer.c der.c
SRCS += elligator2.c
+SRCS += consteq.c
+SRCS += fault_handler_noop.c
OBJS = $(SRCS:.c=.o)
OBJS += secp256k1-zkp.o
diff --git a/crypto/aes/aesgcm.c b/crypto/aes/aesgcm.c
index f9831188..96b029fb 100644
--- a/crypto/aes/aesgcm.c
+++ b/crypto/aes/aesgcm.c
@@ -30,6 +30,7 @@ Issue Date: 30/03/2011
*/
#include "aesgcm.h"
+#include "consteq.h"
#include "mode_hdr.h"
/* This GCM implementation needs a Galois Field multiplier for GF(2^128).
@@ -539,7 +540,7 @@ ret_type gcm_decrypt_message( /* decrypt an entire message */
gcm_auth_header(hdr, hdr_len, ctx);
gcm_decrypt(msg, msg_len, ctx);
rr = gcm_compute_tag(local_tag, tag_len, ctx);
- return (rr != RETURN_GOOD || memcmp(tag, local_tag, tag_len)) ? RETURN_ERROR : RETURN_GOOD;
+ return (rr != RETURN_GOOD || !consteq(tag, local_tag, tag_len)) ? RETURN_ERROR : RETURN_GOOD;
}
#if defined(__cplusplus)
diff --git a/crypto/base58.c b/crypto/base58.c
index 407feade..ad8c78cd 100644
--- a/crypto/base58.c
+++ b/crypto/base58.c
@@ -24,6 +24,7 @@
#include "base58.h"
#include <stdbool.h>
#include <string.h>
+#include "consteq.h"
#include "memzero.h"
#include "ripemd160.h"
#include "sha2.h"
@@ -134,7 +135,7 @@ int b58check(const void *bin, size_t binsz, HasherType hasher_type,
unsigned i = 0;
if (binsz < 4) return -4;
hasher_Raw(hasher_type, bin, binsz - 4, buf);
- if (memcmp(&binc[binsz - 4], buf, 4)) return -1;
+ if (!consteq(&binc[binsz - 4], buf, 4)) return -1;
// Check number of zeros is correct AFTER verifying checksum (to avoid
// possibility of accessing base58str beyond the end)
diff --git a/crypto/bip39.c b/crypto/bip39.c
index 33d0b57d..dcbf6446 100644
--- a/crypto/bip39.c
+++ b/crypto/bip39.c
@@ -25,6 +25,7 @@
#include <string.h>
#include "bip39.h"
+#include "consteq.h"
#include "hmac.h"
#include "memzero.h"
#include "options.h"
@@ -239,23 +240,6 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase,
#endif
}
-/**
- * @brief Constant-time memory comparison.
- * Compares 'n' bytes, but unlike memcmp, it does not short-circuit,
- * thus preventing timing attacks.
- * @return `true` if the memory areas are equal, `false` otherwise.
- */
-static bool constant_time_memeq(const void *s1, const void *s2, size_t n) {
- const unsigned char *p1 = s1;
- const unsigned char *p2 = s2;
- int diff = 0;
- for (size_t i = 0; i < n; i++) {
- // Accumulate differences using OR to prevent early termination
- diff |= p1[i] ^ p2[i];
- }
- return diff == 0;
-}
-
/**
* @brief Constant-time linear search for a mnemonic word. Make sure the `word`
* argument is provided within at least 9 characters big buffer to avoid
@@ -268,7 +252,7 @@ found_word mnemonic_find_word(const char *word) {
const char *dict_word = BIP39_WORDLIST_ENGLISH[i];
size_t dict_word_len = strlen(dict_word);
bool is_match = // 0 or 1 - 1 is match
- constant_time_memeq(word, dict_word, dict_word_len + 1);
+ consteq(word, dict_word, dict_word_len + 1);
int8_t match_mask = -is_match; // 0x00 or 0xFF - 0xFF is match
result_index =
(match_mask & i) + (~match_mask & result_index); // take one of the two
diff --git a/crypto/chacha20poly1305/poly1305-donna.c b/crypto/chacha20poly1305/poly1305-donna.c
index bb6e7a3e..42f44cfe 100644
--- a/crypto/chacha20poly1305/poly1305-donna.c
+++ b/crypto/chacha20poly1305/poly1305-donna.c
@@ -1,4 +1,5 @@
#include "poly1305-donna.h"
+#include "consteq.h"
#include "poly1305-donna-32.h"
void
@@ -48,12 +49,7 @@ poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const
int
poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) {
- size_t i = 0;
- unsigned int dif = 0;
- for (i = 0; i < 16; i++)
- dif |= (mac1[i] ^ mac2[i]);
- dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1);
- return (dif & 1);
+ return consteq(mac1, mac2, 16);
}
diff --git a/crypto/consteq.c b/crypto/consteq.c
index d9287780..d0eca998 100644
--- a/crypto/consteq.c
+++ b/crypto/consteq.c
@@ -1,14 +1,46 @@
+/**
+ * Copyright (c) Trezor Company s.r.o.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "consteq.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include "fault_handler.h"
bool consteq(const void *s1, const void *s2, size_t n) {
- const unsigned char *p1 = s1;
- const unsigned char *p2 = s2;
- int diff = 0;
- for (size_t i = 0; i < n; i++) {
+ const uint8_t *p1 = s1;
+ const uint8_t *p2 = s2;
+ size_t diff = 0;
+ volatile size_t i = 0;
+
+ for (i = 0; i < n; i++) {
// Accumulate differences using OR to prevent early termination
diff |= p1[i] ^ p2[i];
}
- return diff == 0;
+
+ // Check loop completion in case of a fault injection attack.
+ if (i != n) {
+ tc_fault_handler("consteq loop completion check");
+ }
+
+ return (bool)(1 & ((diff - 1) >> 8));
}
diff --git a/crypto/consteq.h b/crypto/consteq.h
index d2abdfd6..1312938a 100644
--- a/crypto/consteq.h
+++ b/crypto/consteq.h
@@ -1,3 +1,25 @@
+/**
+ * Copyright (c) Trezor Company s.r.o.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT HMAC_SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
#ifndef __CONSTEQ_H__
#define __CONSTEQ_H__
diff --git a/crypto/ed25519-donna/ed25519-donna-impl-base.c b/crypto/ed25519-donna/ed25519-donna-impl-base.c
index 5ef7b438..4debc26c 100644
--- a/crypto/ed25519-donna/ed25519-donna-impl-base.c
+++ b/crypto/ed25519-donna/ed25519-donna-impl-base.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include "consteq.h"
#include "ed25519-donna.h"
#include "memzero.h"
#include "options.h"
@@ -29,16 +30,6 @@ static const bignum25519 ALIGN(16) fe_fffb4 = {
0x2b39186, 0x14640ed, 0x14930a7, 0x04509fa, 0x3b91bf0, 0x0f7432e, 0x07a443f, 0x17f24d8, 0x031067d, 0x0690fcc}; /* sqrt(sqrt(-1) * A * (A + 2)) */
-/*
- Timing safe memory compare
-*/
-int ed25519_verify(const unsigned char *x, const unsigned char *y, size_t len) {
- size_t differentbits = 0;
- while (len--)
- differentbits |= (*x++ ^ *y++);
- return (int) (1 & ((differentbits - 1) >> 8));
-}
-
/*
conversions
*/
@@ -235,10 +226,10 @@ int ge25519_unpack_negative_vartime(ge25519 *r, const unsigned char p[32]) {
curve25519_mul(t, t, den);
curve25519_sub_reduce(root, t, num);
curve25519_contract(check, root);
- if (!ed25519_verify(check, zero, 32)) {
+ if (!consteq(check, zero, 32)) {
curve25519_add_reduce(t, t, num);
curve25519_contract(check, t);
- if (!ed25519_verify(check, zero, 32))
+ if (!consteq(check, zero, 32))
return 0;
curve25519_mul(r->x, r->x, ge25519_sqrtneg1);
}
diff --git a/crypto/ed25519-donna/ed25519-donna-impl-base.h b/crypto/ed25519-donna/ed25519-donna-impl-base.h
index 342a6448..b1bf0cd8 100644
--- a/crypto/ed25519-donna/ed25519-donna-impl-base.h
+++ b/crypto/ed25519-donna/ed25519-donna-impl-base.h
@@ -1,8 +1,3 @@
-/*
- Timing safe memory compare
-*/
-int ed25519_verify(const unsigned char *x, const unsigned char *y, size_t len);
-
/*
conversions
*/
diff --git a/crypto/ed25519-donna/ed25519.c b/crypto/ed25519-donna/ed25519.c
index 7fda5892..a2680765 100644
--- a/crypto/ed25519-donna/ed25519.c
+++ b/crypto/ed25519-donna/ed25519.c
@@ -14,12 +14,13 @@
#define ED25519_FN(fn) fn
#endif
-#include "ed25519-donna.h"
#include "ed25519.h"
+#include "ed25519-donna.h"
+#include "consteq.h"
#include "ed25519-hash-custom.h"
-#include "rand.h"
#include "memzero.h"
+#include "rand.h"
/*
Generates a (extsk[0..31]) and aExt (extsk[32..63])
@@ -179,7 +180,7 @@ ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen, const ed2551
ge25519_pack(checkR, &R);
/* check that R = SB - H(R,A,m)A */
- return ed25519_verify(RS, checkR, 32) ? 0 : -1;
+ return consteq(RS, checkR, 32) ? 0 : -1;
}
int
diff --git a/crypto/fault_handler.h b/crypto/fault_handler.h
new file mode 100644
index 00000000..34bed78e
--- /dev/null
+++ b/crypto/fault_handler.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) Trezor Company s.r.o.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __FAULT_HANDLER_H__
+#define __FAULT_HANDLER_H__
+
+void tc_fault_handler(const char *msg);
+
+#endif
diff --git a/crypto/fault_handler_noop.c b/crypto/fault_handler_noop.c
new file mode 100644
index 00000000..cb7718c6
--- /dev/null
+++ b/crypto/fault_handler_noop.c
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) Trezor Company s.r.o.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma message( \
+ "NOT SUITABLE FOR PRODUCTION USE! Provide a real tc_fault_handler() that halts execution on fault.")
+
+void tc_fault_handler(const char *msg) {
+ (void)msg; // no-op by default; override in platform code
+}
diff --git a/crypto/monero/base58.c b/crypto/monero/base58.c
index 14ec8279..08802a3a 100644
--- a/crypto/monero/base58.c
+++ b/crypto/monero/base58.c
@@ -38,6 +38,7 @@
#include <sys/types.h>
#include "../base58.h"
#include "../byte_order.h"
+#include "consteq.h"
#include "int-util.h"
#include "sha2.h"
@@ -274,8 +275,7 @@ int xmr_base58_addr_decode_check(const char *addr, size_t sz, uint64_t *tag,
}
hasher_Raw(HASHER_SHA3K, buf, buflen - addr_checksum_size, hash);
- if (memcmp(hash, buf + buflen - addr_checksum_size, addr_checksum_size) !=
- 0) {
+ if (!consteq(hash, buf + buflen - addr_checksum_size, addr_checksum_size)) {
return 0;
}
diff --git a/crypto/nem.c b/crypto/nem.c
index 57e18557..51474346 100644
--- a/crypto/nem.c
+++ b/crypto/nem.c
@@ -25,6 +25,7 @@
#include <string.h>
#include "base32.h"
+#include "consteq.h"
#include "ed25519-donna/ed25519-keccak.h"
#include "memzero.h"
#include "ripemd160.h"
@@ -170,7 +171,7 @@ bool nem_validate_address_raw(const uint8_t *address, uint8_t network) {
uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0};
keccak_256(address, 1 + RIPEMD160_DIGEST_LENGTH, hash);
- bool valid = (memcmp(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4) == 0);
+ bool valid = consteq(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4);
memzero(hash, sizeof(hash));
return valid;
diff --git a/legacy/common.c b/legacy/common.c
index 8ff7843e..2d8b6808 100644
--- a/legacy/common.c
+++ b/legacy/common.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <unistd.h>
#include "bitmaps.h"
+#include "fault_handler.h"
#include "firmware/usb.h"
#include "hmac_drbg.h"
#include "layout.h"
@@ -130,3 +131,5 @@ void show_pin_too_many_screen(void) {
error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.",
NULL);
}
+
+void tc_fault_handler(const char *msg) { ensure(secfalse, msg); }
diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile
index 26a659ba..df0d97a8 100644
--- a/legacy/firmware/Makefile
+++ b/legacy/firmware/Makefile
@@ -125,6 +125,8 @@ OBJS += ../vendor/trezor-crypto/chacha20poly1305/chacha_merged.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/poly1305-donna.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/rfc7539.o
+OBJS += ../vendor/trezor-crypto/consteq.o
+
OBJS += ../vendor/trezor-crypto/nem.o
OBJS += ../vendor/QR-Code-generator/c/qrcodegen.o
diff --git a/legacy/firmware/config.c b/legacy/firmware/config.c
index ebe344b1..60c34810 100644
--- a/legacy/firmware/config.c
+++ b/legacy/firmware/config.c
@@ -29,6 +29,7 @@
#include "bip39.h"
#include "common.h"
#include "config.h"
+#include "consteq.h"
#include "curves.h"
#include "debug.h"
#include "fsm.h"
@@ -797,14 +798,10 @@ bool config_containsMnemonic(const char *mnemonic) {
uint8_t digest_input[SHA256_DIGEST_LENGTH] = {0};
sha256_Raw((const uint8_t *)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN),
digest_input);
-
- uint8_t diff = 0;
- for (size_t i = 0; i < sizeof(digest_input); i++) {
- diff |= (digest_stored[i] - digest_input[i]);
- }
+ bool result = consteq(digest_stored, digest_input, sizeof(digest_input));
memzero(digest_stored, sizeof(digest_stored));
memzero(digest_input, sizeof(digest_input));
- return diff == 0;
+ return result;
}
/* Check whether pin matches storage. The pin must be
diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h
index a8ac3bba..7ce468bf 100644
--- a/legacy/firmware/fsm_msg_coin.h
+++ b/legacy/firmware/fsm_msg_coin.h
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "consteq.h"
void fsm_msgGetPublicKey(const GetPublicKey *msg) {
RESP_INIT(PublicKey);
@@ -907,12 +908,8 @@ void fsm_msgUnlockPath(const UnlockPath *msg) {
// Require confirmation to access SLIP25 paths unless already authorized.
if (msg->has_mac) {
- uint8_t diff = 0;
- for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
- diff |= (msg->mac.bytes[i] - resp->mac.bytes[i]);
- }
-
- if (msg->mac.size != SHA256_DIGEST_LENGTH || diff != 0) {
+ if (msg->mac.size != SHA256_DIGEST_LENGTH ||
+ !consteq(msg->mac.bytes, resp->mac.bytes, SHA256_DIGEST_LENGTH)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid MAC"));
layoutHome();
return;
diff --git a/storage/tests/c/Makefile b/storage/tests/c/Makefile
index b8cdc370..8116f96f 100644
--- a/storage/tests/c/Makefile
+++ b/storage/tests/c/Makefile
@@ -30,6 +30,8 @@ SRC += crypto/chacha20poly1305/chacha_merged.c
SRC += crypto/hmac.c
SRC += crypto/sha2.c
SRC += crypto/memzero.c
+SRC += crypto/consteq.c
+SRC += crypto/fault_handler_noop.c
OBJ = $(SRC:%.c=build/%.o)
OBJ_QW = $(SRC:%.c=build_qw/%.o)
diff --git a/storage/tests/c3/Makefile b/storage/tests/c3/Makefile
index b727fc20..7bef97ae 100644
--- a/storage/tests/c3/Makefile
+++ b/storage/tests/c3/Makefile
@@ -27,6 +27,8 @@ SRC += crypto/chacha20poly1305/chacha_merged.c
SRC += crypto/hmac.c
SRC += crypto/sha2.c
SRC += crypto/memzero.c
+SRC += crypto/consteq.c
+SRC += crypto/fault_handler_noop.c
OBJ = $(SRC:%.c=build/%.o)
Why this scored 63/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.