**Brief approach — goal:** implement modular exponentiation without data-dependent branches or memory accesses. Use bitmasking for conditional moves and a constant-time table lookup for sliding windows.**Constant-time helpers (conceptual C)**c
#include <stdint.h>
/* constant-time conditional select: if (cond) return a else return b
cond must be 0 or 1 */
static inline uint64_t ct_select_u64(uint64_t a, uint64_t b, uint64_t cond) {
uint64_t mask = -(uint64_t)cond; /* all-ones if cond==1, else 0 */
return (a & mask) | (b & ~mask);
}
/* constant-time modular multiply (toy 64-bit example; replace with big-int) */
static inline uint64_t modmul(uint64_t x, uint64_t y, uint64_t m) {
__uint128_t r = (__uint128_t)x * y;
return (uint64_t)(r % m);
}
**Single-bit square-and-multiply (constant-time)**c
uint64_t modexp_ct(uint64_t base, uint64_t exp, uint64_t mod) {
uint64_t R = 1;
for (int i = 63; i >= 0; --i) {
/* square every loop */
R = modmul(R, R, mod);
/* compute candidate = R * base */
uint64_t candidate = modmul(R, base, mod);
/* choose R = (bit ? candidate : R) in constant time */
uint64_t bit = (exp >> i) & 1;
R = ct_select_u64(candidate, R, bit);
}
return R;
}
**Sliding-window lookup (constant-time)**- Precompute table T[1..(2^w-1)].- For selecting T[k], iterate all entries and use ct_select on each word to build the chosen multi-word bignum — no branching and no index-based loads that leak.Why this is constant time- No data-dependent branches or array indices are used to select values.- All loops iterate fixed counts (bits or table size).- Conditional actions implemented with bitmask ops (constant-time bitwise/arith), not conditional jumps.Compiler optimizations that can break assumptions- Compiler may replace bit-twiddles with branches or optimize away stores/loads it deems dead.- It may reorder or vectorize code introducing data-dependent timing differences.- Link-time or profile-guided optimizations can differ behavior.How to prevent compiler-induced leaks- Use well-tested constant-time primitives from vetted crypto libraries when available (OpenSSL, libsodium).- Mark critical functions as noinline and use "volatile" cautiously to prevent reordering of sensitive memory (volatile alone is not sufficient).- Use compiler barriers or inline assembly: e.g., asm volatile("" ::: "memory") to prevent reordering around sensitive ops.- For select/load of secret-indexed table, implement manual full-table scan with ct_select on each limb so that the compiler cannot change it into an indexed load.- Consider using compiler-provided constant-time intrinsics (if available) or assembly instructions (cmov) via inline asm to ensure no conditional jump.- Test generated assembly for branches and use tools (ctgrind, CacheAudit) and review with -O2 and target compilers/flags used in production.Notes- Replace toy 64-bit mul with constant-time big-integer routines.- Always audit final assembly and include unit tests that detect branches on secrets.