mp_cond_swap_ct: branchless masked XOR

This commit is contained in:
Jeremiah Mackey
2026-05-04 16:36:23 +00:00
parent f3c3687efc
commit 19ff338be9
2 changed files with 84 additions and 7 deletions
+34 -7
View File
@@ -549,21 +549,48 @@ int mp_exch (mp_int * a, mp_int * b)
return MP_OKAY;
}
/* Constant-time conditional swap: must not branch on m (leaks scalar bit).
* m must be 0 or 1. The t parameter is unused; XOR is performed in place
* with a single-digit stack scratch so callers don't need to clear t->dp. */
int mp_cond_swap_ct_ex (mp_int * a, mp_int * b, int c, int m, mp_int * t)
{
(void)c;
int i;
int err;
int imask;
int idiff;
mp_digit mask;
mp_digit d;
(void)t;
if (m == 1)
mp_exch(a, b);
m &= 1;
imask = -m;
mask = (mp_digit)0 - (mp_digit)m;
if ((err = mp_grow(a, c)) != MP_OKAY)
return err;
if ((err = mp_grow(b, c)) != MP_OKAY)
return err;
idiff = (a->used ^ b->used) & imask;
a->used ^= idiff;
b->used ^= idiff;
idiff = (a->sign ^ b->sign) & imask;
a->sign ^= idiff;
b->sign ^= idiff;
for (i = 0; i < c; i++) {
d = (a->dp[i] ^ b->dp[i]) & mask;
a->dp[i] ^= d;
b->dp[i] ^= d;
}
return MP_OKAY;
}
int mp_cond_swap_ct (mp_int * a, mp_int * b, int c, int m)
{
(void)c;
if (m == 1)
mp_exch(a, b);
return MP_OKAY;
return mp_cond_swap_ct_ex(a, b, c, m, NULL);
}
+50
View File
@@ -66406,6 +66406,52 @@ static wc_test_ret_t mp_test_mont(mp_int* a, mp_int* m, mp_int* n, mp_int* r, WC
}
#endif
/* mp_cond_swap_ct resolves to sp_cond_swap_ct in SP-math builds, which is only
* compiled when HAVE_ECC && ECC_TIMING_RESISTANT && !WC_NO_CACHE_RESISTANT.
* Gate the test to match. */
#if !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_SP_MATH_ALL)
#define MP_TEST_COND_SWAP_AVAILABLE
#elif defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT) && \
!defined(WC_NO_CACHE_RESISTANT)
#define MP_TEST_COND_SWAP_AVAILABLE
#endif
#ifdef MP_TEST_COND_SWAP_AVAILABLE
static wc_test_ret_t mp_test_cond_swap(mp_int* a, mp_int* b)
{
int ret;
int c;
mp_zero(a);
mp_zero(b);
ret = mp_set_int(a, 0x12345678);
if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
ret = mp_set_int(b, 0x9abcdef0);
if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
c = (a->used > b->used) ? a->used : b->used;
/* m == 0: no swap. */
ret = mp_cond_swap_ct(a, b, c, 0);
if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
if (a->dp[0] != 0x12345678 || b->dp[0] != 0x9abcdef0)
return WC_TEST_RET_ENC_NC;
/* m == 1: swap. */
ret = mp_cond_swap_ct(a, b, c, 1);
if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
if (a->dp[0] != 0x9abcdef0 || b->dp[0] != 0x12345678)
return WC_TEST_RET_ENC_NC;
/* m == 1 again: swap back. */
ret = mp_cond_swap_ct(a, b, c, 1);
if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
if (a->dp[0] != 0x12345678 || b->dp[0] != 0x9abcdef0)
return WC_TEST_RET_ENC_NC;
return 0;
}
#endif /* MP_TEST_COND_SWAP_AVAILABLE */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mp_test(void)
{
WC_RNG rng;
@@ -66719,6 +66765,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mp_test(void)
if ((ret = mp_test_mont(a, b, r1, r2, &rng)) != 0)
goto done;
#endif
#ifdef MP_TEST_COND_SWAP_AVAILABLE
if ((ret = mp_test_cond_swap(a, b)) != 0)
goto done;
#endif
done: