Skip to content

Commit

Permalink
Solving scalar constant-time operations.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jun 1, 2020
1 parent c68669e commit 89e0fc6
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 51 deletions.
2 changes: 1 addition & 1 deletion ecc/goldilocks/decaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den

isValid := isPositiveS && isLessThanP && isQR
b := *((*uint)(unsafe.Pointer(&isValid)))
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
fp.Cmov(&e.p.x, x, b)
fp.Cmov(&e.p.y, y, b)
fp.Cmov(&e.p.ta, x, b)
Expand Down
4 changes: 3 additions & 1 deletion ecc/goldilocks/isogeny_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
func randomPoint() *Point {
var k Scalar
_, _ = rand.Read(k[:])
return Curve{}.ScalarBaseMult(&k)
P := &Point{}
Curve{}.ScalarBaseMult(P, &k)
return P
}

func TestIsogeny(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion ecc/goldilocks/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (P *Point) UnmarshalBinary(data []byte) error {
fp.Cmov(x, u, uint(signX^(x[0]&1))) // if signX != x mod 2

isValid := isLessThanP && isQR && isValidXSign
b := *((*uint)(unsafe.Pointer(&isValid)))
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
fp.Cmov(&P.x, x, b)
fp.Cmov(&P.y, y, b)
fp.Cmov(&P.ta, x, b)
Expand Down
50 changes: 32 additions & 18 deletions ecc/goldilocks/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ func (z *scalar64) toScalar(x *Scalar) {
binary.LittleEndian.PutUint64(x[6*8:7*8], z[6])
}

// isZero returns 1 if z=0.
func (z *scalar64) isZero() uint {
z.modOrder()
var z64 uint64
for i := range z {
z64 |= z[i]
}
z32 := uint32(z64&0xFFFFFFFF) | (uint32(z64>>32) & 0xFFFFFFF)
return uint((uint64(z32) - 1) >> 63)
}

// cmov moves x into z if b=1.
func (z *scalar64) cmov(x *scalar64, b uint64) {
m := -(b & 1)
for i := range z {
z[i] = (z[i] &^ m) | (x[i] & m)
}
}

// add calculates z = x + y. Assumes len(z) > max(len(x),len(y)).
func add(z, x, y []uint64) uint64 {
l, L, zz := len(x), len(y), y
Expand Down Expand Up @@ -81,14 +100,6 @@ func mulWord(z, x []uint64, y uint64) {
z[len(x)] = carry
}

// Cmov moves x into z if b=1.
func (z *scalar64) Cmov(b uint64, x *scalar64) {
m := uint64(0) - b
for i := range z {
z[i] = (z[i] &^ m) | (x[i] & m)
}
}

// leftShift shifts to the left the words of z returning the more significant word.
func (z *scalar64) leftShift(low uint64) uint64 {
high := z[_N-1]
Expand All @@ -108,6 +119,15 @@ func (z *scalar64) reduceOneWord(x uint64) {
add(z[:], z[:], prod)
}

// Sub calculates z = x-y mod order.
func (z *scalar64) sub(x, y *scalar64) {
var t scalar64
c := sub(z[:], x[:], y[:])
sub(t[:], z[:], residue448[:])
z.cmov(&t, c)
z.modOrder()
}

// modOrder reduces z mod order.
func (z *scalar64) modOrder() {
var o64, x scalar64
Expand All @@ -116,7 +136,7 @@ func (z *scalar64) modOrder() {
// At most 8 (eight) iterations reduce 3 bits by subtracting.
for i := 0; i < 8; i++ {
c := sub(x[:], z[:], o64[:]) // (c || x) = z-order
z.Cmov(1-c, &x) // if c != 0 { z = x }
z.cmov(&x, 1-c) // if c != 0 { z = x }
}
}

Expand Down Expand Up @@ -159,20 +179,17 @@ func (z *Scalar) Add(x, y *Scalar) {
y64.fromScalar(y)
c := add(z64[:], x64[:], y64[:])
add(t[:], z64[:], residue448[:])
z64.Cmov(c, &t)
z64.cmov(&t, c)
z64.modOrder()
z64.toScalar(z)
}

// Sub calculates z = x-y mod order.
func (z *Scalar) Sub(x, y *Scalar) {
var z64, x64, y64, t scalar64
var z64, x64, y64 scalar64
x64.fromScalar(x)
y64.fromScalar(y)
c := sub(z64[:], x64[:], y64[:])
sub(t[:], z64[:], residue448[:])
z64.Cmov(c, &t)
z64.modOrder()
z64.sub(&x64, &y64)
z64.toScalar(z)
}

Expand All @@ -195,6 +212,3 @@ func (z *Scalar) Mul(x, y *Scalar) {
z64.modOrder()
z64.toScalar(z)
}

// IsZero returns true if z=0.
func (z *Scalar) IsZero() bool { z.Red(); return *z == Scalar{} }
29 changes: 13 additions & 16 deletions ecc/goldilocks/twist.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package goldilocks
import (
"crypto/subtle"
"math/bits"
"unsafe"

"github.com/cloudflare/circl/internal/conv"
"github.com/cloudflare/circl/math"
Expand Down Expand Up @@ -66,14 +65,12 @@ func subYDiv16(x *scalar64, y int64) {
x[6] = (x6 >> 4)
}

func recodeScalar(d *[113]int8, k *Scalar) {
var k64 scalar64
k64.fromScalar(k)
func recodeScalar(d *[113]int8, k *scalar64) {
for i := 0; i < 112; i++ {
d[i] = int8((k64[0] & 0x1f) - 16)
subYDiv16(&k64, int64(d[i]))
d[i] = int8((k[0] & 0x1f) - 16)
subYDiv16(k, int64(d[i]))
}
d[112] = int8(k64[0])
d[112] = int8(k[0])
}

// ScalarMult calculates R = kP.
Expand All @@ -82,16 +79,16 @@ func (e twistCurve) ScalarMult(R *twistPoint, k *Scalar, P *twistPoint) {
var S preTwistPointProy
var d [113]int8

kk := *k
isZero := kk.IsZero()
isZeroInt := *(*int)(unsafe.Pointer(&isZero))
subtle.ConstantTimeCopy(isZeroInt, kk[:], order[:])
var k64, _k64, order64 scalar64
k64.fromScalar(k)
order64.fromScalar(&order)
k64.cmov(&order64, uint64(k64.isZero()))

isEven := 1 - int(k64[0]&0x1)
_k64.sub(&order64, &k64)
k64.cmov(&_k64, uint64(isEven))

minusK := kk
isEven := 1 - int(kk[0]&0x1)
minusK.Neg()
subtle.ConstantTimeCopy(isEven, kk[:], minusK[:])
recodeScalar(&d, &kk)
recodeScalar(&d, &k64)

P.oddMultiples(TabP[:])
Q := e.Identity()
Expand Down
26 changes: 12 additions & 14 deletions ecc/goldilocks/twist_basemult.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,18 @@ func (e twistCurve) ScalarBaseMult(R *twistPoint, k *Scalar) {
panic("not extended")
}

kk := *k

var isZero int
if kk.IsZero() {
isZero = 1
}

subtle.ConstantTimeCopy(isZero, kk[:], order[:])

minusK := kk
isEven := 1 - int(kk[0]&0x1)
minusK.Neg()
subtle.ConstantTimeCopy(isEven, kk[:], minusK[:])
c, err := m.Encode(kk[:])
var k64, _k64, order64 scalar64
k64.fromScalar(k)
order64.fromScalar(&order)
k64.cmov(&order64, uint64(k64.isZero()))

isEven := 1 - int(k64[0]&0x1)
_k64.sub(&order64, &k64)
k64.cmov(&_k64, uint64(isEven))
var scalar Scalar
k64.toScalar(&scalar)

c, err := m.Encode(scalar[:])
if err != nil {
panic(err)
}
Expand Down

0 comments on commit 89e0fc6

Please sign in to comment.