Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

precomp square root in constant time #358

Open
advaita-saha opened this issue Feb 12, 2024 · 2 comments
Open

precomp square root in constant time #358

advaita-saha opened this issue Feb 12, 2024 · 2 comments
Labels
constant time ⏳ Enhancement is suitable for secret data

Comments

@advaita-saha
Copy link
Collaborator

The square root with precomp optimisation is not currently in constant-time

PR #354 adds the precomp optimisation to the square-root for Banderwagon/Bandersnatch.
The implementation needs to be changed to a constant-time implementation and all the _vatime functions to be changes/removed related to the optimised square root.

Once the implementation is in constant-time, a check for the curve type should be added here and the precomp sqrt to be called

func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool =
## Compute the square root and ivnerse square root of ``a``
##
## This returns true if ``a`` is square and sqrt/invsqrt contains the square root/inverse square root
##
## The result is undefined otherwise
##
## The square root, if it exist is multivalued,
## i.e. both x² == (-x)²
## This procedure returns a deterministic result
sqrt_invsqrt(sqrt, invsqrt, a)
var test {.noInit.}: Fp[C]
test.square(sqrt)
result = test == a

The problem which need to be fixed

So the problem is in the access of the LUT table in constantine/math/constants/banderwagon_sqrt.nim and constantine/math/constants/bandersnatch_sqrt.nim. We need to fetch values corresponding to the key, but if the key is not present we need to return zero. This is currently done using the tableLUT.getOrDefault(key, 0). But this is function is not in constant time.
If this access is made in constant-time then the whole implementation will be in constant-time

@advaita-saha advaita-saha added the constant time ⏳ Enhancement is suitable for secret data label Feb 12, 2024
@rupam-04
Copy link

I came up with a quite simple solution to this issue, but I am not sure if its actually in constant time. While reading the code I came across a certain key value pair in the table (65534: 0). The problem was the time for accessing a value in a table with its key was not the same as returning 0. We can kind of avoid this issue with the following implementation:

func sqrtAlg_NegDlogInSmallDyadicSubgroup(x: Fp): int {.tags:[VarTime], raises: [KeyError].} =
  let key = cast[int](x.mres.limbs[0] and SecretWord 0xFFFF)
  if Fp.C.sqrtDlog(dlogLUT).hasKey(key):
    return Fp.C.sqrtDlog(dlogLUT)[key]
  else:
    return Fp.C.sqrtDlog(dlogLUT)[65534]

Since in both the cases of the if we are accessing a table element, it is supposed to be in constant time I think. So this might be a workaround for now.

@mratsim
Copy link
Owner

mratsim commented Feb 13, 2024

Unfortunately this is still susceptible to cache-timing attacks, like Percival: https://koclab.cs.ucsb.edu/teaching/cren/project/2010/gallegos.pdf

image

The data in Fp.C.sqrtDlog(dlogLUT)[65534] would be read often and be hot in cache and so would result in timing differences compared to a "real" key.

Beyond timing differences, this would also result in different power draw or electromagnetic profile depending on the data taken, while on a PC it would be drowned in noise, this can be read on embedded devices like a Raspberry Pi or a Phone.

This technique is often called "double-and-always-add" using a dummy point in elliptic curve scalar multiplication and there is a lot of litterature on attacks it cannot prevent:

In Aburúa, Valencia, López paper, 7 ways to defeat that approach are listed:
image


In general, to achieve constant-time property, the data access pattern MUST NOT depend on secret data. Hence you need to always access the whole table when dealing with lookup tables:

func secretLookup*[T; S: Ct](table: openArray[T], index: S): T =
## Return table[index]
## This is constant-time, whatever the `index`, its value is not leaked
## This is also protected against cache-timing attack by always scanning the whole table
var val: S
for i in 0 ..< table.len:
let selector = S(i) == index
selector.ccopy(val, S table[i])
return T(val)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
constant time ⏳ Enhancement is suitable for secret data
Projects
None yet
Development

No branches or pull requests

3 participants