The overall function is still not constant-time, but it just got a lot
less leaky.
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
The path using builtin should be OK, as it should be using dedicated CPU
instructions which are constant time.
This fixes the no-builing path.
GCC gained support for __has_builtin in version 10. We're still testing
with older GCC on the CI, so the non-builtin path is tested on the CI.
https://gcc.gnu.org/gcc-10/changes.html
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
In the next refactoring we'll have:
- pretty different paths for N odd or even,
- possibly different paths for A <= 0, in [0, N) or above,
- possibly special cases when A % N is 0 or 1.
Pick two small moduli of different parities (3 and 4)
and go over the range [-(N+1), 2N-1] with A.
This should ensure we naturally run into all special cases.
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
That rule is common to the whole module and not a likely mistake to
make. Also, the test was not really precise as G, I, T were oversized.
Better remove it than give a false sense of security.
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
With this data, the loop only settles to its final state u == 0 and v ==
GCD(A, N) at the last iteration. However it already has v == GCD(A, N)
at the previous iteration. Concretely, this means that if in
mbedtls_mpi_core_gcd_modinv_odd() we change the main loop as follows
- for (size_t i = 0; i < (A_limbs + N_limbs) * biL; i++) {
+ for (size_t i = 0; i < (A_limbs + N_limbs) * biL - 2; i++) {
then this test case would fail. Ideally we'd like a test case that would
fail with -1 above but I've not been able to find one and I have no idea
whether that's possible.
Experimentally I've systematically tried small values (8 bit) and
noticed the case A = 2^n and N significantly larger then A is promising,
so I explored that further. Clearly we want A and N's bitlength to be a
multiple of biL because the bound in the paper is with bitlenths while
we use limbs * biL.
Anyway, I ended up with the following Python script.
import secrets
import math
bil = 64
def bitlimbs(x):
return (x.bit_length() + bil - 1) // bil * bil
def sict_gcd(p, a):
assert p >= a >= 0
assert p & 1 != 0 or a & 1 != 0
u, v = a, p
for i in range(2 * p.bit_length()):
s, z = u & 1, v & 1
t1 = (s ^ z) * v + (2 * s * z - 1) * u
t2 = (s * v + (2 - 2 * s - z) * u) >> 1
if t2 >= t1:
u, v = t1, t2
else:
u, v = t2, t1
if u == 0: # v == 1 ideally, but can't get it
return bitlimbs(a) + bitlimbs(p) - (i + 1)
return 0
a = 2 ** (bil - 1)
m = 1000
while m != 0:
n = secrets.randbits(2 * bil) | 1
d = sict_gcd(n, a)
if d < m:
m = d
print(d)
g = math.gcd(a, n)
i = pow(a, -1, n)
print(f'mpi_core_gcd_modinv_odd:"{a:x}":"{n:x}":"{g:x}":"{i:x}"')
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>