From ff9dec481309e0bf4245aa90ee678bb26f1f4e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Fri, 17 Apr 2020 22:17:28 +0200 Subject: [PATCH] Initial impl of side-channel resistant scalar mul to securely handle secret keys inputs. --- README.md | 18 ++ constantine/arithmetic/finite_fields.nim | 2 +- constantine/arithmetic/limbs_montgomery.nim | 12 +- constantine/elliptic/README.md | 4 + .../elliptic/ec_weierstrass_projective.nim | 195 ++++++++++++++++++ .../tower_field_extensions/tower_common.nim | 11 + 6 files changed, 240 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3382e76..0cf98f5 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,24 @@ actively hinder you by: A growing number of attack vectors is being collected for your viewing pleasure 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 High-performance is a sought out property. diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index bafe6d2..e46bcc2 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -74,7 +74,7 @@ func toBig*(src: Fp): auto {.noInit.} = func ccopy*(a: var Fp, b: Fp, 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 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 ccopy(a.mres, b.mres, ctl) diff --git a/constantine/arithmetic/limbs_montgomery.nim b/constantine/arithmetic/limbs_montgomery.nim index 00df2e8..683d065 100644 --- a/constantine/arithmetic/limbs_montgomery.nim +++ b/constantine/arithmetic/limbs_montgomery.nim @@ -431,8 +431,18 @@ func montyPowSquarings( ## Updates iteration variables and accumulators # Due to the high number of parameters, # 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 + # 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 if acc_len < window: if e < exponent.len: @@ -516,7 +526,7 @@ func montyPow*( scratchspace[1].ccopy(scratchspace[1+i], ctl) # 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) a.ccopy(scratchspace[0], SecretWord(bits).isNonZero()) diff --git a/constantine/elliptic/README.md b/constantine/elliptic/README.md index b404220..6588184 100644 --- a/constantine/elliptic/README.md +++ b/constantine/elliptic/README.md @@ -117,6 +117,10 @@ We use the complete addition law from Bos2014 for Jacobian coordinates, note tha https://eprint.iacr.org/2014/130 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\ Junfeng Fan,XuGuo, Elke De Mulder, Patrick Schaumont, Bart Preneel and Ingrid Verbauwhede, 2010 https://www.esat.kuleuven.be/cosic/publications/article-1461.pdf diff --git a/constantine/elliptic/ec_weierstrass_projective.nim b/constantine/elliptic/ec_weierstrass_projective.nim index e4c4353..33bbb88 100644 --- a/constantine/elliptic/ec_weierstrass_projective.nim +++ b/constantine/elliptic/ec_weierstrass_projective.nim @@ -265,3 +265,198 @@ func double*[F]( r.x.double() # 18. X3 <- X3 + X3 else: {.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()) diff --git a/constantine/tower_field_extensions/tower_common.nim b/constantine/tower_field_extensions/tower_common.nim index 7fa9906..5803409 100644 --- a/constantine/tower_field_extensions/tower_common.nim +++ b/constantine/tower_field_extensions/tower_common.nim @@ -80,6 +80,17 @@ func isOne*(a: ExtensionField): SecretBool = else: 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 # -------------------------------------------------------------------