Skip to content

Commit 89e0fc6

Browse files
committed
Solving scalar constant-time operations.
1 parent c68669e commit 89e0fc6

File tree

6 files changed

+62
-51
lines changed

6 files changed

+62
-51
lines changed

ecc/goldilocks/decaf.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
101101
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
102102

103103
isValid := isPositiveS && isLessThanP && isQR
104-
b := *((*uint)(unsafe.Pointer(&isValid)))
104+
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
105105
fp.Cmov(&e.p.x, x, b)
106106
fp.Cmov(&e.p.y, y, b)
107107
fp.Cmov(&e.p.ta, x, b)

ecc/goldilocks/isogeny_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
func randomPoint() *Point {
1111
var k Scalar
1212
_, _ = rand.Read(k[:])
13-
return Curve{}.ScalarBaseMult(&k)
13+
P := &Point{}
14+
Curve{}.ScalarBaseMult(P, &k)
15+
return P
1416
}
1517

1618
func TestIsogeny(t *testing.T) {

ecc/goldilocks/point.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (P *Point) UnmarshalBinary(data []byte) error {
123123
fp.Cmov(x, u, uint(signX^(x[0]&1))) // if signX != x mod 2
124124

125125
isValid := isLessThanP && isQR && isValidXSign
126-
b := *((*uint)(unsafe.Pointer(&isValid)))
126+
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
127127
fp.Cmov(&P.x, x, b)
128128
fp.Cmov(&P.y, y, b)
129129
fp.Cmov(&P.ta, x, b)

ecc/goldilocks/scalar.go

+32-18
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@ func (z *scalar64) toScalar(x *Scalar) {
3333
binary.LittleEndian.PutUint64(x[6*8:7*8], z[6])
3434
}
3535

36+
// isZero returns 1 if z=0.
37+
func (z *scalar64) isZero() uint {
38+
z.modOrder()
39+
var z64 uint64
40+
for i := range z {
41+
z64 |= z[i]
42+
}
43+
z32 := uint32(z64&0xFFFFFFFF) | (uint32(z64>>32) & 0xFFFFFFF)
44+
return uint((uint64(z32) - 1) >> 63)
45+
}
46+
47+
// cmov moves x into z if b=1.
48+
func (z *scalar64) cmov(x *scalar64, b uint64) {
49+
m := -(b & 1)
50+
for i := range z {
51+
z[i] = (z[i] &^ m) | (x[i] & m)
52+
}
53+
}
54+
3655
// add calculates z = x + y. Assumes len(z) > max(len(x),len(y)).
3756
func add(z, x, y []uint64) uint64 {
3857
l, L, zz := len(x), len(y), y
@@ -81,14 +100,6 @@ func mulWord(z, x []uint64, y uint64) {
81100
z[len(x)] = carry
82101
}
83102

84-
// Cmov moves x into z if b=1.
85-
func (z *scalar64) Cmov(b uint64, x *scalar64) {
86-
m := uint64(0) - b
87-
for i := range z {
88-
z[i] = (z[i] &^ m) | (x[i] & m)
89-
}
90-
}
91-
92103
// leftShift shifts to the left the words of z returning the more significant word.
93104
func (z *scalar64) leftShift(low uint64) uint64 {
94105
high := z[_N-1]
@@ -108,6 +119,15 @@ func (z *scalar64) reduceOneWord(x uint64) {
108119
add(z[:], z[:], prod)
109120
}
110121

122+
// Sub calculates z = x-y mod order.
123+
func (z *scalar64) sub(x, y *scalar64) {
124+
var t scalar64
125+
c := sub(z[:], x[:], y[:])
126+
sub(t[:], z[:], residue448[:])
127+
z.cmov(&t, c)
128+
z.modOrder()
129+
}
130+
111131
// modOrder reduces z mod order.
112132
func (z *scalar64) modOrder() {
113133
var o64, x scalar64
@@ -116,7 +136,7 @@ func (z *scalar64) modOrder() {
116136
// At most 8 (eight) iterations reduce 3 bits by subtracting.
117137
for i := 0; i < 8; i++ {
118138
c := sub(x[:], z[:], o64[:]) // (c || x) = z-order
119-
z.Cmov(1-c, &x) // if c != 0 { z = x }
139+
z.cmov(&x, 1-c) // if c != 0 { z = x }
120140
}
121141
}
122142

@@ -159,20 +179,17 @@ func (z *Scalar) Add(x, y *Scalar) {
159179
y64.fromScalar(y)
160180
c := add(z64[:], x64[:], y64[:])
161181
add(t[:], z64[:], residue448[:])
162-
z64.Cmov(c, &t)
182+
z64.cmov(&t, c)
163183
z64.modOrder()
164184
z64.toScalar(z)
165185
}
166186

167187
// Sub calculates z = x-y mod order.
168188
func (z *Scalar) Sub(x, y *Scalar) {
169-
var z64, x64, y64, t scalar64
189+
var z64, x64, y64 scalar64
170190
x64.fromScalar(x)
171191
y64.fromScalar(y)
172-
c := sub(z64[:], x64[:], y64[:])
173-
sub(t[:], z64[:], residue448[:])
174-
z64.Cmov(c, &t)
175-
z64.modOrder()
192+
z64.sub(&x64, &y64)
176193
z64.toScalar(z)
177194
}
178195

@@ -195,6 +212,3 @@ func (z *Scalar) Mul(x, y *Scalar) {
195212
z64.modOrder()
196213
z64.toScalar(z)
197214
}
198-
199-
// IsZero returns true if z=0.
200-
func (z *Scalar) IsZero() bool { z.Red(); return *z == Scalar{} }

ecc/goldilocks/twist.go

+13-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package goldilocks
33
import (
44
"crypto/subtle"
55
"math/bits"
6-
"unsafe"
76

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

69-
func recodeScalar(d *[113]int8, k *Scalar) {
70-
var k64 scalar64
71-
k64.fromScalar(k)
68+
func recodeScalar(d *[113]int8, k *scalar64) {
7269
for i := 0; i < 112; i++ {
73-
d[i] = int8((k64[0] & 0x1f) - 16)
74-
subYDiv16(&k64, int64(d[i]))
70+
d[i] = int8((k[0] & 0x1f) - 16)
71+
subYDiv16(k, int64(d[i]))
7572
}
76-
d[112] = int8(k64[0])
73+
d[112] = int8(k[0])
7774
}
7875

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

85-
kk := *k
86-
isZero := kk.IsZero()
87-
isZeroInt := *(*int)(unsafe.Pointer(&isZero))
88-
subtle.ConstantTimeCopy(isZeroInt, kk[:], order[:])
82+
var k64, _k64, order64 scalar64
83+
k64.fromScalar(k)
84+
order64.fromScalar(&order)
85+
k64.cmov(&order64, uint64(k64.isZero()))
86+
87+
isEven := 1 - int(k64[0]&0x1)
88+
_k64.sub(&order64, &k64)
89+
k64.cmov(&_k64, uint64(isEven))
8990

90-
minusK := kk
91-
isEven := 1 - int(kk[0]&0x1)
92-
minusK.Neg()
93-
subtle.ConstantTimeCopy(isEven, kk[:], minusK[:])
94-
recodeScalar(&d, &kk)
91+
recodeScalar(&d, &k64)
9592

9693
P.oddMultiples(TabP[:])
9794
Q := e.Identity()

ecc/goldilocks/twist_basemult.go

+12-14
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,18 @@ func (e twistCurve) ScalarBaseMult(R *twistPoint, k *Scalar) {
2424
panic("not extended")
2525
}
2626

27-
kk := *k
28-
29-
var isZero int
30-
if kk.IsZero() {
31-
isZero = 1
32-
}
33-
34-
subtle.ConstantTimeCopy(isZero, kk[:], order[:])
35-
36-
minusK := kk
37-
isEven := 1 - int(kk[0]&0x1)
38-
minusK.Neg()
39-
subtle.ConstantTimeCopy(isEven, kk[:], minusK[:])
40-
c, err := m.Encode(kk[:])
27+
var k64, _k64, order64 scalar64
28+
k64.fromScalar(k)
29+
order64.fromScalar(&order)
30+
k64.cmov(&order64, uint64(k64.isZero()))
31+
32+
isEven := 1 - int(k64[0]&0x1)
33+
_k64.sub(&order64, &k64)
34+
k64.cmov(&_k64, uint64(isEven))
35+
var scalar Scalar
36+
k64.toScalar(&scalar)
37+
38+
c, err := m.Encode(scalar[:])
4139
if err != nil {
4240
panic(err)
4341
}

0 commit comments

Comments
 (0)