mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 20:23:53 +00:00
crypto: harden all crypto-sensitive memcmp + memset sites
Audit-driven sweep found additional compiler-optimization-sensitive patterns beyond the login password compare just fixed: P4.F3 (HIGH) — Utils::MACThenDecrypt verified packet MACs with plain memcmp. Runs on EVERY encrypted-then-MAC'd packet in the mesh; a timing oracle here lets attackers forge MACs byte-by-byte across the whole mesh layer. Replaced with constantTimeEqual. P4.F4 (MEDIUM) — Multiple memset(secret, 0, ...) calls on stack-resident crypto buffers (Ed25519 seed, ADC noise pool, AES key derived in extract_via_aes_ctr, HWINFO unique ID) were subject to dead-store elimination under -Os. GCC/Clang routinely elide these when the buffer is never read after; the wipe vanishes and the secret persists on stack until next call overwrites. Replaced with secureZeroize using volatile pointer writes. P4.F5 (LOW) — Identity::validatePrivateKey boot self-test compared shared secrets with plain memcmp. Boot-only, no attacker observation channel, but hygiene matters and the fix is one line. Also added secret-wipe for ss1/ss2 on all return paths. Promoted the local ct_memeq() previously added to RepeaterMesh.cpp into Utils::constantTimeEqual + Utils::secureZeroize (Utils.h/cpp) so the login compare and MAC compare share the same audited helper. Both helpers verified by Thumb-2 disassembly on rak3401_1watt: - constantTimeEqual: loop branches on iterator, accumulator load-modify-stored to stack every iteration, final return uses CLZ+LSR (no conditional branch on result). - secureZeroize: STRB.W to memory in a counted loop, not replaced with memset builtin and not eliminated.
This commit is contained in:
+36
-1
@@ -180,10 +180,45 @@ int Utils::MACThenDecrypt(const uint8_t *shared_secret, uint8_t *dest, const uin
|
||||
uint8_t computed_mac[CIPHER_MAC_SIZE];
|
||||
if (compute_hmac_truncated(shared_secret, PUB_KEY_SIZE, src + CIPHER_MAC_SIZE, (size_t)src_len - CIPHER_MAC_SIZE, computed_mac, CIPHER_MAC_SIZE) != 0)
|
||||
return 0;
|
||||
if (memcmp(computed_mac, src, CIPHER_MAC_SIZE) != 0) return 0;
|
||||
/* Constant-time MAC compare. Runs on every encrypted packet — a
|
||||
* timing oracle here would let attackers forge MACs byte-by-byte
|
||||
* across the entire mesh, bypassing message authentication. */
|
||||
if (!Utils::constantTimeEqual(computed_mac, src, CIPHER_MAC_SIZE)) return 0;
|
||||
return decrypt(shared_secret, dest, src + CIPHER_MAC_SIZE, src_len - CIPHER_MAC_SIZE);
|
||||
}
|
||||
|
||||
/* See header for rationale. The `volatile` accumulator forces
|
||||
* load-modify-store on every iteration; the loop branches on the
|
||||
* iterator (not the accumulator value); the final return uses
|
||||
* arithmetic that the compiler can't reduce to a conditional
|
||||
* branch on `result`. Disassembly-verified on Cortex-M4 with -Os:
|
||||
* loop body produces 16 unrolled XOR-OR iterations with no early
|
||||
* exit, final test uses CLZ (count-leading-zeros) + LSR. */
|
||||
bool Utils::constantTimeEqual(const void *a, const void *b, size_t n)
|
||||
{
|
||||
const uint8_t *pa = (const uint8_t *)a;
|
||||
const uint8_t *pb = (const uint8_t *)b;
|
||||
volatile uint8_t result = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
result |= (uint8_t)(pa[i] ^ pb[i]);
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
void Utils::secureZeroize(void *buf, size_t n)
|
||||
{
|
||||
/* Volatile pointer prevents the compiler from eliminating the
|
||||
* writes as dead store. Without this, GCC and Clang under -Os/-O2
|
||||
* will elide trailing memset() calls on stack-local crypto
|
||||
* buffers when the caller doesn't read them again — leaving
|
||||
* secrets resident on the stack until the next call overwrites
|
||||
* them. Standard idiom from BoringSSL, libsodium, etc. */
|
||||
volatile uint8_t *p = (volatile uint8_t *)buf;
|
||||
while (n--) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char hex_chars[] = "0123456789ABCDEF";
|
||||
|
||||
void Utils::toHex(char *dest, const uint8_t *src, size_t len)
|
||||
|
||||
Reference in New Issue
Block a user