-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
point.go
379 lines (345 loc) · 11.8 KB
/
point.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
// Package secp256k1 is an implementation of the kyber.{Group,Point,Scalar}
////////////////////////////////////////////////////////////////////////////////
// XXX: Do not use in production until this code has been audited.
////////////////////////////////////////////////////////////////////////////////
// interfaces, based on btcd/btcec and kyber/group/mod
//
// XXX: NOT CONSTANT TIME!
package secp256k1
// Implementation of kyber.Point interface for elliptic-curve arithmetic
// operations on secpk256k1.
//
// This is mostly a wrapper of the functionality provided by btcec
import (
"crypto/cipher"
"fmt"
"io"
"math/big"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/util/key"
"golang.org/x/crypto/sha3"
)
// btcec's public interface uses this affine representation for points on the
// curve. This does not naturally accommodate the point at infinity. btcec
// represents it as (0, 0), which is not a point on {y²=x³+7}.
type secp256k1Point struct {
X *fieldElt
Y *fieldElt
}
func newPoint() *secp256k1Point {
return &secp256k1Point{newFieldZero(), newFieldZero()}
}
// String returns a string representation of P
func (P *secp256k1Point) String() string {
return fmt.Sprintf("Secp256k1{X: %s, Y: %s}", P.X, P.Y)
}
// Equal returns true if p and pPrime represent the same point, false otherwise.
func (P *secp256k1Point) Equal(pPrime kyber.Point) bool {
return P.X.Equal(pPrime.(*secp256k1Point).X) &&
P.Y.Equal(pPrime.(*secp256k1Point).Y)
}
// Null sets p to the group-identity value, and returns it.
func (P *secp256k1Point) Null() kyber.Point {
P.X = fieldEltFromInt(0) // btcec representation of null point is (0,0)
P.Y = fieldEltFromInt(0)
return P
}
// Base sets p to a copy of the standard group generator, and returns it.
func (P *secp256k1Point) Base() kyber.Point {
P.X.SetInt(s256.Gx)
P.Y.SetInt(s256.Gy)
return P
}
// Pick sets P to a random point sampled from rand, and returns it.
func (P *secp256k1Point) Pick(rand cipher.Stream) kyber.Point {
for { // Keep trying X's until one fits the curve (~50% probability of
// success each iteration
P.X.Set(newFieldZero().Pick(rand))
maybeRHS := rightHandSide(P.X)
if maybeY := maybeSqrtInField(maybeRHS); maybeY != (*fieldElt)(nil) {
P.Y.Set(maybeY)
// Take the negative with 50% probability
b := make([]byte, 1)
rand.XORKeyStream(b[:], b[:])
if b[0]&1 == 0 {
P.Y.Neg(P.Y)
}
return P
}
}
}
// Set sets P to copies of pPrime's values, and returns it.
func (P *secp256k1Point) Set(pPrime kyber.Point) kyber.Point {
P.X.Set(pPrime.(*secp256k1Point).X)
P.Y.Set(pPrime.(*secp256k1Point).Y)
return P
}
// Clone returns a copy of P.
func (P *secp256k1Point) Clone() kyber.Point {
return &secp256k1Point{X: P.X.Clone(), Y: P.Y.Clone()}
}
// EmbedLen returns the number of bytes of data which can be embedded in a point.
func (*secp256k1Point) EmbedLen() int {
// Reserve the most-significant 8 bits for pseudo-randomness.
// Reserve the least-significant 8 bits for embedded data length.
return (255 - 8 - 8) / 8
}
// Embed encodes a limited amount of specified data in the Point, using r as a
// source of cryptographically secure random data. Implementations only embed
// the first EmbedLen bytes of the given data.
func (P *secp256k1Point) Embed(data []byte, r cipher.Stream) kyber.Point {
numEmbedBytes := P.EmbedLen()
if len(data) > numEmbedBytes {
panic("too much data to embed in a point")
}
numEmbedBytes = len(data)
var x [32]byte
randStart := 1 // First byte to fill with random data
if data != nil {
x[0] = byte(numEmbedBytes) // Encode length in low 8 bits
copy(x[1:1+numEmbedBytes], data) // Copy in data to embed
randStart = 1 + numEmbedBytes
}
maxAttempts := 10000
// Try random x ordinates satisfying the constraints, until one provides
// a point on secp256k1
for numAttempts := 0; numAttempts < maxAttempts; numAttempts++ {
// Fill the rest of the x ordinate with random data
r.XORKeyStream(x[randStart:], x[randStart:])
xOrdinate := newFieldZero().SetBytes(x)
// RHS of secp256k1 equation is x³+7 mod p. Success if square.
// We optimistically don't use btcec.IsOnCurve, here, because we
// hope to assign the intermediate result maybeY to P.Y
secp256k1RHS := rightHandSide(xOrdinate)
if maybeY := maybeSqrtInField(secp256k1RHS); maybeY != (*fieldElt)(nil) {
P.X = xOrdinate // success: found (x,y) s.t. y²=x³+7
P.Y = maybeY
return P
}
}
// Probability 2^{-maxAttempts}, under correct operation.
panic("failed to find point satisfying all constraints")
}
// Data returns data embedded in P, or an error if inconsistent with encoding
func (P *secp256k1Point) Data() ([]byte, error) {
b := P.X.Bytes()
dataLength := int(b[0])
if dataLength > P.EmbedLen() {
return nil, fmt.Errorf("point specifies too much data")
}
return b[1 : dataLength+1], nil
}
// Add sets P to a+b (secp256k1 group operation) and returns it.
func (P *secp256k1Point) Add(a, b kyber.Point) kyber.Point {
X, Y := s256.Add(
a.(*secp256k1Point).X.int(), a.(*secp256k1Point).Y.int(),
b.(*secp256k1Point).X.int(), b.(*secp256k1Point).Y.int())
P.X.SetInt(X)
P.Y.SetInt(Y)
return P
}
// Add sets P to a-b (secp256k1 group operation), and returns it.
func (P *secp256k1Point) Sub(a, b kyber.Point) kyber.Point {
X, Y := s256.Add(
a.(*secp256k1Point).X.int(), a.(*secp256k1Point).Y.int(),
b.(*secp256k1Point).X.int(),
newFieldZero().Neg(b.(*secp256k1Point).Y).int()) // -b_y
P.X.SetInt(X)
P.Y.SetInt(Y)
return P
}
// Neg sets P to -a (in the secp256k1 group), and returns it.
func (P *secp256k1Point) Neg(a kyber.Point) kyber.Point {
P.X = a.(*secp256k1Point).X.Clone()
P.Y = newFieldZero().Neg(a.(*secp256k1Point).Y)
return P
}
// Mul sets P to s*a (in the secp256k1 group, i.e. adding a to itself s times),
// and returns it. If a is nil, it is replaced by the secp256k1 generator.
func (P *secp256k1Point) Mul(s kyber.Scalar, a kyber.Point) kyber.Point {
sBytes, err := s.(*secp256k1Scalar).MarshalBinary()
if err != nil {
panic(fmt.Errorf("failure while marshaling multiplier: %s",
err))
}
var X, Y *big.Int
if a == (*secp256k1Point)(nil) || a == nil {
X, Y = s256.ScalarBaseMult(sBytes)
} else {
X, Y = s256.ScalarMult(a.(*secp256k1Point).X.int(),
a.(*secp256k1Point).Y.int(), sBytes)
}
P.X.SetInt(X)
P.Y.SetInt(Y)
return P
}
// MarshalBinary returns the concatenated big-endian representation of the X
// ordinate and a byte which is 0 if Y is even, 1 if it's odd. Or it returns an
// error on failure.
func (P *secp256k1Point) MarshalBinary() ([]byte, error) {
maybeSqrt := maybeSqrtInField(rightHandSide(P.X))
if maybeSqrt == (*fieldElt)(nil) {
return nil, fmt.Errorf("x³+7 not a square")
}
minusMaybeSqrt := newFieldZero().Neg(maybeSqrt)
if !P.Y.Equal(maybeSqrt) && !P.Y.Equal(minusMaybeSqrt) {
return nil, fmt.Errorf(
"y ≠ ±maybeSqrt(x³+7), so not a point on the curve")
}
rv := make([]byte, P.MarshalSize())
signByte := P.MarshalSize() - 1 // Last byte contains sign of Y.
xordinate := P.X.Bytes()
copyLen := copy(rv[:signByte], xordinate[:])
if copyLen != P.MarshalSize()-1 {
return []byte{}, fmt.Errorf("marshal of x ordinate too short")
}
if P.Y.isEven() {
rv[signByte] = 0
} else {
rv[signByte] = 1
}
return rv, nil
}
// MarshalSize returns the length of the byte representation of P
func (P *secp256k1Point) MarshalSize() int { return 33 }
// MarshalID returns the ID for a secp256k1 point
func (P *secp256k1Point) MarshalID() [8]byte {
return [8]byte{'s', 'p', '2', '5', '6', '.', 'p', 'o'}
}
// UnmarshalBinary sets P to the point represented by contents of buf, or
// returns an non-nil error
func (P *secp256k1Point) UnmarshalBinary(buf []byte) error {
var err error
if len(buf) != P.MarshalSize() {
err = fmt.Errorf("wrong length for marshaled point")
}
if err == nil && !(buf[32] == 0 || buf[32] == 1) {
err = fmt.Errorf("bad sign byte (the last one)")
}
if err != nil {
return err
}
var xordinate [32]byte
copy(xordinate[:], buf[:32])
P.X = newFieldZero().SetBytes(xordinate)
secp256k1RHS := rightHandSide(P.X)
maybeY := maybeSqrtInField(secp256k1RHS)
if maybeY == (*fieldElt)(nil) {
return fmt.Errorf("x ordinate does not correspond to a curve point")
}
isEven := maybeY.isEven()
P.Y.Set(maybeY)
if (buf[32] == 0 && !isEven) || (buf[32] == 1 && isEven) {
P.Y.Neg(P.Y)
} else {
if buf[32] != 0 && buf[32] != 1 {
return fmt.Errorf("parity byte must be 0 or 1")
}
}
return nil
}
// MarshalTo writes the serialized P to w, and returns the number of bytes
// written, or an error on failure.
func (P *secp256k1Point) MarshalTo(w io.Writer) (int, error) {
buf, err := P.MarshalBinary()
if err != nil {
return 0, err
}
return w.Write(buf)
}
// UnmarshalFrom sets P to the secp256k1 point represented by bytes read from r,
// and returns the number of bytes read, or an error on failure.
func (P *secp256k1Point) UnmarshalFrom(r io.Reader) (int, error) {
buf := make([]byte, P.MarshalSize())
n, err := io.ReadFull(r, buf)
if err != nil {
return 0, err
}
return n, P.UnmarshalBinary(buf)
}
// EthereumAddress returns the 160-bit address corresponding to p as public key.
func EthereumAddress(p kyber.Point) (rv [20]byte) {
// The Ethereum address of P is the bottom 160 bits of keccak256(P.X‖P.Y),
// where P.X and P.Y are represented in 32 bytes as big-endian. See equations
// (277, 284) of Ethereum Yellow Paper version 3e36772, or go-ethereum's
// crypto.PubkeyToAddress.
h := sha3.NewLegacyKeccak256()
if _, err := h.Write(LongMarshal(p)); err != nil {
panic(err)
}
copy(rv[:], h.Sum(nil)[12:])
return rv
}
// IsSecp256k1Point returns true if p is a secp256k1Point
func IsSecp256k1Point(p kyber.Point) bool {
switch p.(type) {
case *secp256k1Point:
return true
default:
return false
}
}
// Coordinates returns the coordinates of p
func Coordinates(p kyber.Point) (*big.Int, *big.Int) {
return p.(*secp256k1Point).X.int(), p.(*secp256k1Point).Y.int()
}
// ValidPublicKey returns true iff p can be used in the optimized on-chain
// Schnorr-signature verification. See SchnorrSECP256K1.sol for details.
func ValidPublicKey(p kyber.Point) bool {
if p == (*secp256k1Point)(nil) || p == nil {
return false
}
P, ok := p.(*secp256k1Point)
if !ok {
return false
}
maybeY := maybeSqrtInField(rightHandSide(P.X))
return maybeY != nil && (P.Y.Equal(maybeY) || P.Y.Equal(maybeY.Neg(maybeY)))
}
// Generate generates a public/private key pair, which can be verified cheaply
// on-chain
func Generate(random cipher.Stream) *key.Pair {
p := key.Pair{}
for !ValidPublicKey(p.Public) {
p.Private = (&Secp256k1{}).Scalar().Pick(random)
p.Public = (&Secp256k1{}).Point().Mul(p.Private, nil)
}
return &p
}
// LongMarshal returns the concatenated coordinates serialized as uint256's
func LongMarshal(p kyber.Point) []byte {
xMarshal := p.(*secp256k1Point).X.Bytes()
yMarshal := p.(*secp256k1Point).Y.Bytes()
return append(xMarshal[:], yMarshal[:]...)
}
// LongUnmarshal returns the secp256k1 point represented by m, as a concatenated
// pair of uint256's
func LongUnmarshal(m []byte) (kyber.Point, error) {
if len(m) != 64 {
return nil, fmt.Errorf(
"0x%x does not represent an uncompressed secp256k1Point. Should be length 64, but is length %d",
m, len(m))
}
p := newPoint()
p.X.SetInt(big.NewInt(0).SetBytes(m[:32]))
p.Y.SetInt(big.NewInt(0).SetBytes(m[32:]))
if !ValidPublicKey(p) {
return nil, fmt.Errorf("%s is not a valid secp256k1 point", p)
}
return p, nil
}
// ScalarToPublicPoint returns the public secp256k1 point associated to s
func ScalarToPublicPoint(s kyber.Scalar) kyber.Point {
publicPoint := (&Secp256k1{}).Point()
return publicPoint.Mul(s, nil)
}
// SetCoordinates returns the point (x,y), or panics if an invalid secp256k1Point
func SetCoordinates(x, y *big.Int) kyber.Point {
rv := newPoint()
rv.X.SetInt(x)
rv.Y.SetInt(y)
if !ValidPublicKey(rv) {
panic("point requested from invalid coordinates")
}
return rv
}