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

EC multiplication constant time (#172) #177

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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