mirror of
https://github.com/logos-storage/constantine.git
synced 2026-05-06 01:29:29 +00:00
* Point decoding: optimized sqrt for p ≡ 5 (mod 8) (Curve25519) * Implement fused sqrt(u/v) for twisted edwards point deserialization * Introduce twisted edwards affine * Allow declaration of curve field elements (and fight against recursive dependencies * Twisted edwards group law + tests * Add support for jubjub and bandersnatch #162 * test twisted edwards scalar mul
307 lines
8.5 KiB
Nim
307 lines
8.5 KiB
Nim
# Constantine
|
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
|
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
import
|
|
../primitives,
|
|
../config/[common, curves],
|
|
../arithmetic,
|
|
../towers,
|
|
./ec_twistededwards_affine
|
|
|
|
# ############################################################
|
|
#
|
|
# Elliptic Curve in Twisted Edwards form
|
|
# with Projective Coordinates
|
|
#
|
|
# ############################################################
|
|
|
|
type ECP_TwEdwards_Prj*[F] = object
|
|
## Elliptic curve point for a curve in Twisted Edwards form
|
|
## ax²+y²=1+dx²y²
|
|
## with a, d ≠ 0 and a ≠ d
|
|
##
|
|
## over a field F
|
|
##
|
|
## in projective coordinate (X, Y, Z)
|
|
## with x = X/Z and y = Y/Z
|
|
## hence (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
x*, y*, z*: F
|
|
|
|
func `==`*(P, Q: ECP_TwEdwards_Prj): SecretBool =
|
|
## Constant-time equality check
|
|
## This is a costly operation
|
|
# Reminder: the representation is not unique
|
|
var a{.noInit.}, b{.noInit.}: ECP_TwEdwards_Prj.F
|
|
|
|
a.prod(P.x, Q.z)
|
|
b.prod(Q.x, P.z)
|
|
result = a == b
|
|
|
|
a.prod(P.y, Q.z)
|
|
b.prod(Q.y, P.z)
|
|
result = result and a == b
|
|
|
|
func isInf*(P: ECP_TwEdwards_Prj): SecretBool {.inline.} =
|
|
## Returns true if P is an infinity point
|
|
## and false otherwise
|
|
result = P.x.isZero() and (P.y == P.z)
|
|
|
|
func setInf*(P: var ECP_TwEdwards_Prj) {.inline.} =
|
|
## Set ``P`` to infinity
|
|
P.x.setZero()
|
|
P.y.setOne()
|
|
P.z.setOne()
|
|
|
|
func ccopy*(P: var ECP_TwEdwards_Prj, Q: ECP_TwEdwards_Prj, ctl: SecretBool) {.inline.} =
|
|
## Constant-time conditional copy
|
|
## If ctl is true: Q is copied into P
|
|
## if ctl is false: Q is not copied and P is unmodified
|
|
## Time and memory accesses are the same whether a copy occurs or not
|
|
for fP, fQ in fields(P, Q):
|
|
ccopy(fP, fQ, ctl)
|
|
|
|
func trySetFromCoordY*[F](
|
|
P: var ECP_TwEdwards_Prj[F],
|
|
y: F): SecretBool =
|
|
## Try to create a point the elliptic curve
|
|
## ax²+y²=1+dx²y² (affine coordinate)
|
|
##
|
|
## The `Z` coordinates is set to 1
|
|
##
|
|
## return true and update `P` if `y` leads to a valid point
|
|
## return false otherwise, in that case `P` is undefined.
|
|
##
|
|
## Note: Dedicated robust procedures for hashing-to-curve
|
|
## will be provided, this is intended for testing purposes.
|
|
##
|
|
## For **test case generation only**,
|
|
## this is preferred to generating random point
|
|
## via random scalar multiplication of the curve generator
|
|
## as the latter assumes:
|
|
## - point addition, doubling work
|
|
## - scalar multiplication works
|
|
## - a generator point is defined
|
|
## i.e. you can't test unless everything is already working
|
|
|
|
var Q{.noInit.}: ECP_TwEdwards_Aff[F]
|
|
result = Q.trySetFromCoordY(y)
|
|
|
|
P.x = Q.x
|
|
P.y = Q.y
|
|
P.z.setOne()
|
|
|
|
func trySetFromCoordsYandZ*[F](
|
|
P: var ECP_TwEdwards_Prj[F],
|
|
y, z: F): SecretBool =
|
|
## Try to create a point the elliptic curve
|
|
## ax²+y²=1+dx²y² (affine coordinate)
|
|
##
|
|
## return true and update `P` if `y` leads to a valid point
|
|
## return false otherwise, in that case `P` is undefined.
|
|
##
|
|
## Note: Dedicated robust procedures for hashing-to-curve
|
|
## will be provided, this is intended for testing purposes.
|
|
##
|
|
## For **test case generation only**,
|
|
## this is preferred to generating random point
|
|
## via random scalar multiplication of the curve generator
|
|
## as the latter assumes:
|
|
## - point addition, doubling work
|
|
## - scalar multiplication works
|
|
## - a generator point is defined
|
|
## i.e. you can't test unless everything is already working
|
|
|
|
var Q{.noInit.}: ECP_TwEdwards_Aff[F]
|
|
result = Q.trySetFromCoordY(y)
|
|
|
|
P.x.prod(Q.x, z)
|
|
P.y.prod(Q.y, z)
|
|
P.z = z
|
|
|
|
func neg*(P: var ECP_TwEdwards_Prj, Q: ECP_TwEdwards_Prj) {.inline.} =
|
|
## Negate ``P``
|
|
P.x.neg(Q.x)
|
|
P.y = Q.y
|
|
P.z = Q.z
|
|
|
|
func neg*(P: var ECP_TwEdwards_Prj) {.inline.} =
|
|
## Negate ``P``
|
|
P.x.neg()
|
|
|
|
func cneg*(P: var ECP_TwEdwards_Prj, ctl: CTBool) {.inline.} =
|
|
## Conditional negation.
|
|
## Negate if ``ctl`` is true
|
|
P.x.cneg(ctl)
|
|
|
|
func sum*[Field](
|
|
r: var ECP_TwEdwards_Prj[Field],
|
|
P, Q: ECP_TwEdwards_Prj[Field]
|
|
) =
|
|
## Elliptic curve point addition for Twisted Edwards curves in projective coordinates
|
|
##
|
|
## R = P + Q
|
|
##
|
|
## Twisted Edwards curves have the following equation in projective coordinates
|
|
## (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
## from the affine equation
|
|
## ax²+y²=1+dx²y²
|
|
##
|
|
## ``r`` is initialized/overwritten with the sum
|
|
## ``r`` may alias P
|
|
##
|
|
## Implementation is constant-time, in particular it will not expose
|
|
## that P == Q or P == -Q or P or Q are the infinity points
|
|
## to simple side-channel attacks (SCA)
|
|
## This is done by using a "complete" or "exception-free" addition law.
|
|
#
|
|
# https://www.hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp
|
|
# Cost: 10M + 1S + 1*a + 1*d + 7add.
|
|
# A = Z1*Z2
|
|
# B = A²
|
|
# C = X1*X2
|
|
# D = Y1*Y2
|
|
# E = d*C*D
|
|
# F = B-E
|
|
# G = B+E
|
|
# X3 = A*F*((X1+Y1)*(X2+Y2)-C-D)
|
|
# Y3 = A*G*(D-a*C)
|
|
# Z3 = F*G
|
|
var
|
|
A{.noInit.}, B{.noInit.}, C{.noInit.}: Field
|
|
D{.noInit.}, E{.noInit.}, F{.noInit.}: Field
|
|
G{.noInit.}: Field
|
|
|
|
A.prod(P.z, Q.z)
|
|
B.square(A)
|
|
C.prod(P.x, Q.x)
|
|
D.prod(P.y, Q.y)
|
|
E.prod(C, D)
|
|
when Field.C.getCoefD() is int:
|
|
# conversion at compile-time
|
|
const coefD = block:
|
|
var d: Field
|
|
d.fromInt F.C.getCoefD()
|
|
d
|
|
E *= coefD
|
|
else:
|
|
E *= Field.C.getCoefD()
|
|
F.diff(B, E)
|
|
G.sum(B, E)
|
|
|
|
# Aliasing: B and E are unused
|
|
# We store (P.x+P.y)*(Q.x+Q.y)
|
|
# so that using r.x or r.y is safe even in case of aliasing
|
|
|
|
B.sum(P.x, P.y)
|
|
E.sum(Q.x, Q.y)
|
|
B *= E # B = (X1+Y1)*(X2+Y2)
|
|
E.sum(C, D) # E = C+D
|
|
|
|
# Y3 = A*G*(D-a*C)
|
|
when Field.C.getCoefA() == -1:
|
|
r.y = E # (D-a*C) = D+C
|
|
else:
|
|
r.y.prod(C, Field.C.getCoefA())
|
|
r.y.diff(D, r.y)
|
|
r.y *= A
|
|
r.y *= G
|
|
|
|
# X3 = A*F*((X1+Y1)*(X2+Y2)-C-D)
|
|
B -= E
|
|
r.x.prod(A, F)
|
|
r.x *= B
|
|
|
|
# Z3 = F*G
|
|
r.z.prod(F, G)
|
|
|
|
func double*[Field](
|
|
r: var ECP_TwEdwards_Prj[Field],
|
|
P: ECP_TwEdwards_Prj[Field]
|
|
) =
|
|
## Elliptic curve point doubling for Twisted Edwards curves in projective coordinates
|
|
##
|
|
## R = [2] P
|
|
##
|
|
## Twisted Edwards curves have the following equation in projective coordinates
|
|
## (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
## from the affine equation
|
|
## ax²+y²=1+dx²y²
|
|
##
|
|
## ``r`` is initialized/overwritten with the sum
|
|
## ``r`` may alias P
|
|
##
|
|
## Implementation is constant-time, in particular it will not expose
|
|
## that P == Q or P == -Q or P or Q are the infinity points
|
|
## to simple side-channel attacks (SCA)
|
|
## This is done by using a "complete" or "exception-free" addition law.
|
|
#
|
|
# https://www.hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp
|
|
# Cost: 3M + 4S + 1*a + 6add + 1*2.
|
|
# B = (X1+Y1)²
|
|
# C = X1²
|
|
# D = Y1²
|
|
# E = a*C
|
|
# F = E+D
|
|
# H = Z1²
|
|
# J = F-2*H
|
|
# X3 = (B-C-D)*J
|
|
# Y3 = F*(E-D)
|
|
# Z3 = F*J
|
|
|
|
var
|
|
D{.noInit.}, E{.noInit.}: Field
|
|
H{.noInit.}, J{.noInit.}: FIeld
|
|
|
|
# (B-C-D) => 2X1Y1, but With squaring and 2 substractions instead of mul + addition
|
|
# In practice, squaring is not cheap enough to compasate the extra substraction cost.
|
|
r.x.prod(P.x, P.y)
|
|
r.x.double()
|
|
|
|
D.square(P.y)
|
|
E.square(P.x)
|
|
E *= Field.C.getCoefA()
|
|
|
|
r.y.sum(E, D) # Ry stores F = E+D
|
|
H.square(P.z)
|
|
H.double()
|
|
J.diff(r.y, H) # J = F-2H
|
|
|
|
r.x *= J # X3 = (B-C-D)*J
|
|
r.z.prod(r.y, J) # Z3 = F*J
|
|
E -= D # C stores E-D
|
|
r.y *= E
|
|
|
|
func double*(P: var ECP_TwEdwards_Prj) {.inline.} =
|
|
## In-place EC doubling
|
|
P.double(P)
|
|
|
|
func diff*(r: var ECP_TwEdwards_Prj,
|
|
P, Q: ECP_TwEdwards_Prj
|
|
) {.inline.} =
|
|
## r = P - Q
|
|
## Can handle r and Q aliasing
|
|
var nQ {.noInit.}: typeof(Q)
|
|
nQ.neg(Q)
|
|
r.sum(P, nQ)
|
|
|
|
func affineFromProjective*[F](
|
|
aff: var ECP_TwEdwards_Prj[F],
|
|
proj: ECP_TwEdwards_Prj[F]) =
|
|
var invZ {.noInit.}: F
|
|
invZ.inv(proj.z)
|
|
|
|
aff.x.prod(proj.x, invZ)
|
|
aff.y.prod(proj.y, invZ)
|
|
|
|
func projectiveFromAffine*[F](
|
|
proj: var ECP_TwEdwards_Prj[F],
|
|
aff: ECP_TwEdwards_Prj[F]) {.inline.} =
|
|
proj.x = aff.x
|
|
proj.y = aff.y
|
|
proj.z.setOne()
|