Initial impl of side-channel resistant scalar mul to securely handle secret keys inputs.

This commit is contained in:
Mamy André-Ratsimbazafy 2020-04-17 22:17:28 +02:00
parent 44350d08af
commit ff9dec4813
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
6 changed files with 240 additions and 2 deletions

View File

@ -90,6 +90,24 @@ actively hinder you by:
A growing number of attack vectors is being collected for your viewing pleasure A growing number of attack vectors is being collected for your viewing pleasure
at https://github.com/mratsim/constantine/wiki/Constant-time-arithmetics at https://github.com/mratsim/constantine/wiki/Constant-time-arithmetics
### Disclaimer
Constantine's authors do their utmost to implement a secure cryptographic library
in particular against remote attack vectors like timing attacks.
Please note that Constantine is provided as-is without guarantees.
Use at your own risks.
Thorough evaluation of your threat model, the security of any cryptographic library you are considering,
and the secrets you put in jeopardy is strongly advised before putting data at risk.
The author would like to remind users that the best code can only mitigate
but not protect against human failures which are the weakest link and largest
backdoors to secrets exploited today.
### Security disclosure
TODO
## Performance ## Performance
High-performance is a sought out property. High-performance is a sought out property.

View File

@ -74,7 +74,7 @@ func toBig*(src: Fp): auto {.noInit.} =
func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) = func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) =
## Constant-time conditional copy ## Constant-time conditional copy
## If ctl is true: b is copied into a ## If ctl is true: b is copied into a
## if ctl is false: b is not copied and a is untouched ## if ctl is false: b is not copied and a is unmodified
## Time and memory accesses are the same whether a copy occurs or not ## Time and memory accesses are the same whether a copy occurs or not
ccopy(a.mres, b.mres, ctl) ccopy(a.mres, b.mres, ctl)

View File

@ -431,8 +431,18 @@ func montyPowSquarings(
## Updates iteration variables and accumulators ## Updates iteration variables and accumulators
# Due to the high number of parameters, # Due to the high number of parameters,
# forcing this inline actually reduces the code size # forcing this inline actually reduces the code size
#
# ⚠️: Extreme care should be used to not leak
# the exponent bits nor its real bitlength
# i.e. if the exponent is zero but encoded in a
# 256-bit integer, only "256" should leak
# as for some application like RSA
# the exponent might be the user secret key.
# Get the next bits # Get the next bits
# acc/acc_len must be uint to avoid Nim runtime checks leaking bits
# acc/acc_len must be uint to avoid Nim runtime checks leaking bits
# e is public
var k = window var k = window
if acc_len < window: if acc_len < window:
if e < exponent.len: if e < exponent.len:
@ -516,7 +526,7 @@ func montyPow*(
scratchspace[1].ccopy(scratchspace[1+i], ctl) scratchspace[1].ccopy(scratchspace[1+i], ctl)
# Multiply with the looked-up value # Multiply with the looked-up value
# we keep the product only if the exponent bits are not all zero # we keep the product only if the exponent bits are not all zeroes
scratchspace[0].montyMul(a, scratchspace[1], M, m0ninv, canUseNoCarryMontyMul) scratchspace[0].montyMul(a, scratchspace[1], M, m0ninv, canUseNoCarryMontyMul)
a.ccopy(scratchspace[0], SecretWord(bits).isNonZero()) a.ccopy(scratchspace[0], SecretWord(bits).isNonZero())

View File

@ -117,6 +117,10 @@ We use the complete addition law from Bos2014 for Jacobian coordinates, note tha
https://eprint.iacr.org/2014/130 https://eprint.iacr.org/2014/130
https://www.imsc.res.in/~ecc14/slides/costello.pdf https://www.imsc.res.in/~ecc14/slides/costello.pdf
- Remote Timing Attacks are Still Practical\
Billy Bob Brumley and Nicola Tuveri\
https://eprint.iacr.org/2011/232
- State-of-the-art of secure ECC implementations:a survey on known side-channel attacks and countermeasures\ - State-of-the-art of secure ECC implementations:a survey on known side-channel attacks and countermeasures\
Junfeng Fan,XuGuo, Elke De Mulder, Patrick Schaumont, Bart Preneel and Ingrid Verbauwhede, 2010 Junfeng Fan,XuGuo, Elke De Mulder, Patrick Schaumont, Bart Preneel and Ingrid Verbauwhede, 2010
https://www.esat.kuleuven.be/cosic/publications/article-1461.pdf https://www.esat.kuleuven.be/cosic/publications/article-1461.pdf

View File

@ -265,3 +265,198 @@ func double*[F](
r.x.double() # 18. X3 <- X3 + X3 r.x.double() # 18. X3 <- X3 + X3
else: else:
{.error: "Not implemented.".} {.error: "Not implemented.".}
# ############################################################
# #
# Scalar Multiplication #
# #
# ############################################################
#
# Scalar multiplication is a key algorithm for cryptographic protocols:
# - it is slow,
# - it is performance critical as it is used to generate signatures and authenticate messages
# - it is a high-value target as the "scalar" is very often the user secret key
#
# A safe scalar multiplication MUST:
# - Use no branching (to prevent timing and simple power analysis attacks)
# - Always do the same memory accesses (in particular for table lookups) (to prevent cache-timing attacks)
# - Not expose the bitlength of the exponent (use the curve order bitlength instead)
#
# Constantine does not make an extra effort to defend against the smart-cards
# and embedded device attacks:
# - Differential Power-Analysis which may allow for example retrieving bit content depending on the cost of writing 0 or 1
# (Address-bit DPA by Itoh, Izu and Takenaka)
# - Electro-Magnetic which can be used in a similar way to power analysis but based on EM waves
# - Fault Attacks which can be used by actively introducing faults (via a laser for example) in an algorithm
#
# The current security efforts are focused on preventing attacks
# that are effective remotely including through the network,
# a colocated VM or a malicious process on your phone.
#
# - Survey for Performance & Security Problems of Passive Side-channel Attacks Countermeasures in ECC\
# Rodrigo Abarúa, Claudio Valencia, and Julio López, 2019\
# https://eprint.iacr.org/2019/010
#
# - State-of-the-art of secure ECC implementations:a survey on known side-channel attacks and countermeasures\
# Junfeng Fan,XuGuo, Elke De Mulder, Patrick Schaumont, Bart Preneel and Ingrid Verbauwhede, 2010
# https://www.esat.kuleuven.be/cosic/publications/article-1461.pdf
template checkScalarMulScratchspaceLen(len: int) =
## CHeck that there is a minimum of scratchspace to hold the temporaries
debug:
assert len >= 2, "Internal Error: the scratchspace for scalar multiplication should be equal or greater than 2"
func getWindowLen(bufLen: int): uint =
## Compute the maximum window size that fits in the scratchspace buffer
checkScalarMulScratchspaceLen(bufLen)
result = 4
while (1 shl result) + 1 > bufLen:
dec result
func scalarMulPrologue(
P: var ECP_SWei_Proj,
scratchspace: var openarray[ECP_SWei_Proj]
): uint =
## Setup the scratchspace
## Returns the fixed-window size for scalar mul with window optimization
result = result.scratchspace.len.getWindowLen()
# Precompute window content, special case for window = 1
# (i.e scratchspace has only space for 2 temporaries)
# The content scratchspace[2+k] is set at [k]P
# with scratchspace[0] untouched
if result == 1:
scratchspace[1] = P
else:
scratchspace[2] = P
for k in 2 ..< 1 shl result:
scratchspace[k+1].sum(scratchspace[k], P)
# Set a to infinity
P.setInf()
func scalarMulDoubling(
P: var ECP_SWei_Proj,
exponent: openArray[byte],
tmp: var ECP_SWei_Proj,
window: uint,
acc, acc_len: var uint,
e: var int
) =
## Doubling steps of doubling and add for scalar multiplication
## Get the next k bits in range [1, window)
## and double k times
## Returns the niumber of doubling done and the corresponding bits.
##
## Updates iteration variables and accumulators
#
# ⚠️: Extreme care should be used to not leak
# the exponent bits nor its real bitlength
# i.e. if the exponent is zero but encoded in a
# 256-bit integer, only "256" should leak
# as for most applications like ECDSA or BLS signature schemes
# the scalar is the user secret key.
# Get the next bits
# acc/acc_len must be uint to avoid Nim runtime checks leaking bits
# e is public
var k = window
if acc_len < window:
if e < exponent.len:
acc = (acc shl 8) or exponent[e].uint
inc e
acc_len += 8
else: # Drained all exponent bits
k = acc_len
let bits = (acc shr (acc_len - k)) and ((1'u32 shl k) - 1)
acc_len -= k
# We have k bits and can do k doublings
for i in 0 ..< k:
tmp.double(P)
P = tmp
return (k, bits)
func scalarMul*(
P: var ECP_SWei_Proj,
scalar: openArray[byte],
scratchspace: var openArray[ECP_SWei_Proj]
) =
## Elliptic Curve Scalar Multiplication
##
## P <- [k] P
##
## This uses fixed-window optimization if possible
## `scratchspace` MUST be of size 2 .. 2^4
##
## This is suitable to use with secret `scalar`, in particular
## to derive a public key from a private key or
## to sign a message.
##
## Particular care has been given to defend against the following side-channel attacks:
## - timing attacks: all exponents of the same length
## will take the same time including
## a "zero" exponent of length 256-bit
## - cache-timing attacks: Constantine does use a precomputed table
## but when extracting a value from the table
## the whole table is always accessed with the same pattern
## preventing malicious attacks through CPU cache delay analysis.
## - simple power-analysis and electromagnetic attacks: Constantine always do the same
## double and add sequences and those cannot be analyzed to distinguish
## the exponent 0 and 1.
##
## I.e. As far as the author know, Constantine implements all countermeasures to the known
## **remote** attacks on ECC implementations.
##
## Disclaimer:
## Constantine is provided as-is without any guarantees.
## Use at your own risks.
## Thorough evaluation of your threat model, the security of any cryptographic library you are considering,
## and the secrets you put in jeopardy is strongly advised before putting data at risk.
## The author would like to remind users that the best code can only mitigate
## but not protect against human failures which are the weakest links and largest
## backdoors to secrets exploited today.
##
## Constantine is resistant to
## - Fault Injection attacks: Constantine does not have branches that could
## be used to skip some additions and reveal which were dummy and which were real.
## Dummy operations are like the double-and-add-always timing attack countermeasure.
##
##
## Constantine DOES NOT defend against Address-Bit Differential Power Analysis attacks by default,
## which allow differentiating between writing a 0 or a 1 to a memory cell.
## This is a threat for smart-cards and embedded devices (for example to handle authentication to a cable or satellite service)
## Constantine can be extended to use randomized projective coordinates to foil this attack.
let window = scalarMulPrologue(P, scratchspace)
# We process bits with from most to least significant.
# At each loop iteration with have acc_len bits in acc.
# To maintain constant-time the number of iterations
# or the number of operations or memory accesses should be the same
# regardless of acc & acc_len
var
acc, acc_len: uint
e = 0
while acc_len > 0 or e < scalar.len:
let (k, bits) = scalarMulDoubling(
P, scalar, scratchspace[0],
window, acc, acc_len, e
)
# Window lookup: we set scratchspace[1] to the lookup value
# If the window length is 1 it's already set.
if window > 1:
# otherwise we need a constant-time lookup
# in particular we need the same memory accesses, we can't
# just index the openarray with the bits to avoid cache attacks.
for i in 1 ..< 1 shl k:
let ctl = SecretWord(i) == SecretWord(bits)
scratchspace[1].ccopy(scratchspace[1+i], ctl)
# Multiply with the looked-up value
# we need to keep the product only ig the exponent bits are not all zeroes
scratchspace[0].sum(P, scratchspace[1])
P.ccopy(scratchspace[0], SecretWord(bits).isNonZero())

View File

@ -80,6 +80,17 @@ func isOne*(a: ExtensionField): SecretBool =
else: else:
result = result and fA.isZero() result = result and fA.isZero()
# Copies
# -------------------------------------------------------------------
func ccopy*(a: var ExtensionField, b: ExtensionField, ctl: SecretBool) =
## Constant-time conditional copy
## If ctl is true: b is copied into a
## if ctl is false: b is not copied and a is unmodified
## Time and memory accesses are the same whether a copy occurs or not
for fA, fB in fields(a, b):
ccopy(fA, fB, ctl)
# Abelian group # Abelian group
# ------------------------------------------------------------------- # -------------------------------------------------------------------