Skip to content

Commit

Permalink
EC multiplication constant time (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
krebernisak authored and gregoryguillou committed Dec 8, 2022
1 parent c8063aa commit 948dc06
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 52 deletions.
29 changes: 29 additions & 0 deletions caigo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ func TestGeneral_Signature(t *testing.T) {
hash *big.Int
rIn *big.Int
sIn *big.Int
rOut *big.Int
sOut *big.Int
raw string
}{
{
Expand All @@ -169,6 +171,27 @@ func TestGeneral_Signature(t *testing.T) {
rIn: types.StrToBig("2849277527182985104629156126825776904262411756563556603659114084811678482647"),
sIn: types.StrToBig("3156340738553451171391693475354397094160428600037567299774561739201502791079"),
},
// Example ref: https://github.com/starkware-libs/crypto-cpp/blob/master/src/starkware/crypto/ecdsa_test.cc
// NOTICE: s component of the {r, s} signature is not available at source, but was manually computed/confirmed as
// `s := sc.InvModCurveSize(w)` for w: 0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e
{
private: types.HexToBN("0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc"),
hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"),
rIn: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"),
sIn: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"),
},
{
publicX: types.HexToBN("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43"),
hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"),
rIn: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"),
sIn: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"),
},
{
private: types.HexToBN("0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc"),
hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"),
rOut: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"),
sOut: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"),
},
}

var err error
Expand All @@ -190,6 +213,12 @@ func TestGeneral_Signature(t *testing.T) {
if err != nil {
t.Errorf("Could not sign good hash: %v\n", err)
}
if tt.rOut != nil && tt.rOut.Cmp(tt.rIn) != 0 {
t.Errorf("Signature {r!, s} mismatch: %x != %x\n", tt.rIn, tt.rOut)
}
if tt.sOut != nil && tt.sOut.Cmp(tt.sIn) != 0 {
t.Errorf("Signature {r, s!} mismatch: %x != %x\n", tt.sIn, tt.sOut)
}
}

if !Curve.Verify(tt.hash, tt.rIn, tt.sIn, tt.publicX, tt.publicY) {
Expand Down
105 changes: 63 additions & 42 deletions curve.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ func init() {
//
// (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py)
func (sc StarkCurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
// As elliptic curves form a group, there is an additive identity that is the equivalent of 0
// If 𝑃=0 or 𝑄=0, then 𝑃+𝑄=𝑄 or 𝑃+𝑄=𝑃, respectively
// NOTICE: the EC multiplication algorithm is using using `StarkCurve.rewriteScalar` trick
// to avoid this condition and provide constant-time execution.
if len(x1.Bits()) == 0 && len(y1.Bits()) == 0 {
return x2, y2
}
if len(x2.Bits()) == 0 && len(y2.Bits()) == 0 {
return x1, y1
}

yDelta := new(big.Int).Sub(y1, y2)
xDelta := new(big.Int).Sub(x1, x2)

Expand Down Expand Up @@ -166,11 +177,7 @@ func (sc StarkCurve) IsOnCurve(x, y *big.Int) bool {
right = right.Add(right, sc.B)
right = right.Mod(right, sc.P)

if left.Cmp(right) == 0 {
return true
} else {
return false
}
return left.Cmp(right) == 0
}

// (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py)
Expand Down Expand Up @@ -227,48 +234,62 @@ func (sc StarkCurve) MimicEcMultAir(mout, x1, y1, x2, y2 *big.Int) (x *big.Int,
// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p.
// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point).
//
// (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py)
func (sc StarkCurve) EcMult(m, x1, y1 *big.Int) (x, y *big.Int) {
var _ecMult func(m, x1, y1 *big.Int) (x, y *big.Int)

_add := func(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
yDelta := new(big.Int).Sub(y1, y2)
xDelta := new(big.Int).Sub(x1, x2)

m := DivMod(yDelta, xDelta, sc.P)

xm := new(big.Int).Mul(m, m)

x = new(big.Int).Sub(xm, x1)
x = x.Sub(x, x2)
x = x.Mod(x, sc.P)
// (ref: https://www.semanticscholar.org/paper/Elliptic-Curves-and-Side-Channel-Analysis-Joye/7fc91d3684f1ab63b97d125161daf57af60f2ad9/figure/1)
// (ref: https://cosade.telecom-paristech.fr/presentations/s2_p2.pdf)
func (sc StarkCurve) ecMult_DoubleAndAlwaysAdd(m, x1, y1 *big.Int) (x, y *big.Int) {
var _ecMult = func(m, x1, y1 *big.Int) (x, y *big.Int) {
// Two-index table initialization, Q[0] <- P
q := [2]struct {
x *big.Int
y *big.Int
}{
{
x: x1,
y: y1,
},
{
x: nil,
y: nil,
},
}

y = new(big.Int).Sub(x1, x)
y = y.Mul(m, y)
y = y.Sub(y, y1)
y = y.Mod(y, sc.P)
// Run the algorithm, expects the most-significant bit is 1
for i := sc.N.BitLen() - 2; i >= 0; i-- {
q[0].x, q[0].y = sc.Double(q[0].x, q[0].y) // Q[0] <- 2Q[0]
q[1].x, q[1].y = sc.Add(q[0].x, q[0].y, x1, y1) // Q[1] <- Q[0] + P
b := m.Bit(i) // b <- bit at position i
q[0].x, q[0].y = q[b].x, q[b].y // Q[0] <- Q[b]
}

return x, y
return q[0].x, q[0].y
}

// alpha is our Y
_ecMult = func(m, x1, y1 *big.Int) (x, y *big.Int) {
if m.BitLen() == 1 {
return x1, y1
}
mk := new(big.Int).Mod(m, big.NewInt(2))
if mk.Cmp(big.NewInt(0)) == 0 {
h := new(big.Int).Div(m, big.NewInt(2))
c, d := sc.Double(x1, y1)
return _ecMult(h, c, d)
}
n := new(big.Int).Sub(m, big.NewInt(1))
e, f := _ecMult(n, x1, y1)
return _add(e, f, x1, y1)
}
return _ecMult(sc.rewriteScalar(m), x1, y1)
}

x, y = _ecMult(m, x1, y1)
return x, y
// Rewrites k into an equivalent scalar, such that the first bit (the most-significant
// bit for the Double-And-Always-Add or Montgomery algo) is 1.
//
// The k scalar rewriting obtains an equivalent scalar K = 2^n + (k - 2^n mod q),
// such that k·G == K·G and K has the n-th bit set to 1. The scalars are equal modulo
// the group order, k mod q == K mod q.
//
// Notice: The EC multiplication algorithms are typically presented as starting with the state (O, P0),
// where O is the identity element (or neutral point) of the curve. However, the neutral point is at infinity,
// which causes problems for some formulas (non constant-time execution for the naive implementation).
// The ladder then starts after the first step, when the state no longer contains the neutral point.
// (ref: https://www.shiftleft.org/papers/ladder/ladder-tches.pdf)
func (sc StarkCurve) rewriteScalar(k *big.Int) *big.Int {
size := new(big.Int).Lsh(big.NewInt(1), uint(sc.BitSize)) // 2ˆn
mod := new(big.Int).Mod(size, sc.N) // 2ˆn mod q
diff := new(big.Int).Sub(k, mod) // (k - 2ˆn mod q)
return new(big.Int).Add(size, diff) // 2ˆn + (k - 2ˆn mod q)
}

// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p.
// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point).
func (sc StarkCurve) EcMult(m, x1, y1 *big.Int) (x, y *big.Int) {
return sc.ecMult_DoubleAndAlwaysAdd(m, x1, y1)
}

// Finds a nonnegative integer 0 <= x < p such that (m * x) % p == n
Expand Down
Loading

0 comments on commit 948dc06

Please sign in to comment.