-
Notifications
You must be signed in to change notification settings - Fork 17.9k
/
Copy pathecdsa.go
441 lines (404 loc) · 14.6 KB
/
ecdsa.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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ecdsa implements the Elliptic Curve Digital Signature Algorithm, as
// defined in [FIPS 186-5].
//
// Signatures generated by this package are not deterministic, but entropy is
// mixed with the private key and the message, achieving the same level of
// security in case of randomness source failure.
//
// Operations involving private keys are implemented using constant-time
// algorithms, as long as an [elliptic.Curve] returned by [elliptic.P224],
// [elliptic.P256], [elliptic.P384], or [elliptic.P521] is used.
//
// [FIPS 186-5]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
package ecdsa
import (
"crypto"
"crypto/ecdh"
"crypto/elliptic"
"crypto/internal/boring"
"crypto/internal/boring/bbig"
"crypto/internal/fips140/ecdsa"
"crypto/internal/fips140hash"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/sha512"
"crypto/subtle"
"errors"
"io"
"math/big"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)
// PublicKey represents an ECDSA public key.
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
// Any methods implemented on PublicKey might need to also be implemented on
// PrivateKey, as the latter embeds the former and will expose its methods.
// ECDH returns k as a [ecdh.PublicKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPublicKey], or if the
// Curve is not supported by crypto/ecdh.
func (k *PublicKey) ECDH() (*ecdh.PublicKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("ecdsa: unsupported curve by crypto/ecdh")
}
if !k.Curve.IsOnCurve(k.X, k.Y) {
return nil, errors.New("ecdsa: invalid public key")
}
return c.NewPublicKey(elliptic.Marshal(k.Curve, k.X, k.Y))
}
// Equal reports whether pub and x have the same value.
//
// Two keys are only considered to have the same value if they have the same Curve value.
// Note that for example [elliptic.P256] and elliptic.P256().Params() are different
// values, as the latter is a generic not constant time implementation.
func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
xx, ok := x.(*PublicKey)
if !ok {
return false
}
return bigIntEqual(pub.X, xx.X) && bigIntEqual(pub.Y, xx.Y) &&
// Standard library Curve implementations are singletons, so this check
// will work for those. Other Curves might be equivalent even if not
// singletons, but there is no definitive way to check for that, and
// better to err on the side of safety.
pub.Curve == xx.Curve
}
// PrivateKey represents an ECDSA private key.
type PrivateKey struct {
PublicKey
D *big.Int
}
// ECDH returns k as a [ecdh.PrivateKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPrivateKey], or if the
// Curve is not supported by [crypto/ecdh].
func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("ecdsa: unsupported curve by crypto/ecdh")
}
size := (k.Curve.Params().N.BitLen() + 7) / 8
if k.D.BitLen() > size*8 {
return nil, errors.New("ecdsa: invalid private key")
}
return c.NewPrivateKey(k.D.FillBytes(make([]byte, size)))
}
func curveToECDH(c elliptic.Curve) ecdh.Curve {
switch c {
case elliptic.P256():
return ecdh.P256()
case elliptic.P384():
return ecdh.P384()
case elliptic.P521():
return ecdh.P521()
default:
return nil
}
}
// Public returns the public key corresponding to priv.
func (priv *PrivateKey) Public() crypto.PublicKey {
return &priv.PublicKey
}
// Equal reports whether priv and x have the same value.
//
// See [PublicKey.Equal] for details on how Curve is compared.
func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey)
if !ok {
return false
}
return priv.PublicKey.Equal(&xx.PublicKey) && bigIntEqual(priv.D, xx.D)
}
// bigIntEqual reports whether a and b are equal leaking only their bit length
// through timing side-channels.
func bigIntEqual(a, b *big.Int) bool {
return subtle.ConstantTimeCompare(a.Bytes(), b.Bytes()) == 1
}
// Sign signs a hash (which should be the result of hashing a larger message
// with opts.HashFunc()) using the private key, priv. If the hash is longer than
// the bit-length of the private key's curve order, the hash will be truncated
// to that length. It returns the ASN.1 encoded signature, like [SignASN1].
//
// If rand is not nil, the signature is randomized. Most applications should use
// [crypto/rand.Reader] as rand. Note that the returned signature does not
// depend deterministically on the bytes read from rand, and may change between
// calls and/or between versions.
//
// If rand is nil, Sign will produce a deterministic signature according to RFC
// 6979. When producing a deterministic signature, opts.HashFunc() must be the
// function used to produce digest and priv.Curve must be one of
// [elliptic.P224], [elliptic.P256], [elliptic.P384], or [elliptic.P521].
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
if rand == nil {
return signRFC6979(priv, digest, opts)
}
return SignASN1(rand, priv, digest)
}
// GenerateKey generates a new ECDSA private key for the specified curve.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
randutil.MaybeReadByte(rand)
if boring.Enabled && rand == boring.RandReader {
x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name)
if err != nil {
return nil, err
}
return &PrivateKey{PublicKey: PublicKey{Curve: c, X: bbig.Dec(x), Y: bbig.Dec(y)}, D: bbig.Dec(d)}, nil
}
boring.UnreachableExceptTests()
switch c.Params() {
case elliptic.P224().Params():
return generateFIPS(c, ecdsa.P224(), rand)
case elliptic.P256().Params():
return generateFIPS(c, ecdsa.P256(), rand)
case elliptic.P384().Params():
return generateFIPS(c, ecdsa.P384(), rand)
case elliptic.P521().Params():
return generateFIPS(c, ecdsa.P521(), rand)
default:
return generateLegacy(c, rand)
}
}
func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) {
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
}
privateKey, err := ecdsa.GenerateKey(c, rand)
if err != nil {
return nil, err
}
return privateKeyFromFIPS(curve, privateKey)
}
// errNoAsm is returned by signAsm and verifyAsm when the assembly
// implementation is not available.
var errNoAsm = errors.New("no assembly implementation available")
// SignASN1 signs a hash (which should be the result of hashing a larger message)
// using the private key, priv. If the hash is longer than the bit-length of the
// private key's curve order, the hash will be truncated to that length. It
// returns the ASN.1 encoded signature.
//
// The signature is randomized. Most applications should use [crypto/rand.Reader]
// as rand. Note that the returned signature does not depend deterministically on
// the bytes read from rand, and may change between calls and/or between versions.
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
randutil.MaybeReadByte(rand)
if boring.Enabled && rand == boring.RandReader {
b, err := boringPrivateKey(priv)
if err != nil {
return nil, err
}
return boring.SignMarshalECDSA(b, hash)
}
boring.UnreachableExceptTests()
switch priv.Curve.Params() {
case elliptic.P224().Params():
return signFIPS(ecdsa.P224(), priv, rand, hash)
case elliptic.P256().Params():
return signFIPS(ecdsa.P256(), priv, rand, hash)
case elliptic.P384().Params():
return signFIPS(ecdsa.P384(), priv, rand, hash)
case elliptic.P521().Params():
return signFIPS(ecdsa.P521(), priv, rand, hash)
default:
return signLegacy(priv, rand, hash)
}
}
func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) {
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
}
// privateKeyToFIPS is very slow in FIPS mode because it performs a
// Sign+Verify cycle per FIPS 140-3 IG 10.3.A. We should find a way to cache
// it or attach it to the PrivateKey.
k, err := privateKeyToFIPS(c, priv)
if err != nil {
return nil, err
}
// Always using SHA-512 instead of the hash that computed hash is
// technically a violation of draft-irtf-cfrg-det-sigs-with-noise-04 but in
// our API we don't get to know what it was, and this has no security impact.
sig, err := ecdsa.Sign(c, sha512.New, k, rand, hash)
if err != nil {
return nil, err
}
return encodeSignature(sig.R, sig.S)
}
func signRFC6979(priv *PrivateKey, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
if opts == nil {
return nil, errors.New("ecdsa: Sign called with nil opts")
}
h := opts.HashFunc()
if h.Size() != len(hash) {
return nil, errors.New("ecdsa: hash length does not match hash function")
}
switch priv.Curve.Params() {
case elliptic.P224().Params():
return signFIPSDeterministic(ecdsa.P224(), h, priv, hash)
case elliptic.P256().Params():
return signFIPSDeterministic(ecdsa.P256(), h, priv, hash)
case elliptic.P384().Params():
return signFIPSDeterministic(ecdsa.P384(), h, priv, hash)
case elliptic.P521().Params():
return signFIPSDeterministic(ecdsa.P521(), h, priv, hash)
default:
return nil, errors.New("ecdsa: curve not supported by deterministic signatures")
}
}
func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto.Hash, priv *PrivateKey, hash []byte) ([]byte, error) {
k, err := privateKeyToFIPS(c, priv)
if err != nil {
return nil, err
}
h := fips140hash.UnwrapNew(hashFunc.New)
if fips140only.Enabled && !fips140only.ApprovedHash(h()) {
return nil, errors.New("crypto/ecdsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
}
sig, err := ecdsa.SignDeterministic(c, h, k, hash)
if err != nil {
return nil, err
}
return encodeSignature(sig.R, sig.S)
}
func encodeSignature(r, s []byte) ([]byte, error) {
var b cryptobyte.Builder
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
addASN1IntBytes(b, r)
addASN1IntBytes(b, s)
})
return b.Bytes()
}
// addASN1IntBytes encodes in ASN.1 a positive integer represented as
// a big-endian byte slice with zero or more leading zeroes.
func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
for len(bytes) > 0 && bytes[0] == 0 {
bytes = bytes[1:]
}
if len(bytes) == 0 {
b.SetError(errors.New("invalid integer"))
return
}
b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) {
if bytes[0]&0x80 != 0 {
c.AddUint8(0)
}
c.AddBytes(bytes)
})
}
// VerifyASN1 verifies the ASN.1 encoded signature, sig, of hash using the
// public key, pub. Its return value records whether the signature is valid.
//
// The inputs are not considered confidential, and may leak through timing side
// channels, or if an attacker has control of part of the inputs.
func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
if boring.Enabled {
key, err := boringPublicKey(pub)
if err != nil {
return false
}
return boring.VerifyECDSA(key, hash, sig)
}
boring.UnreachableExceptTests()
switch pub.Curve.Params() {
case elliptic.P224().Params():
return verifyFIPS(ecdsa.P224(), pub, hash, sig)
case elliptic.P256().Params():
return verifyFIPS(ecdsa.P256(), pub, hash, sig)
case elliptic.P384().Params():
return verifyFIPS(ecdsa.P384(), pub, hash, sig)
case elliptic.P521().Params():
return verifyFIPS(ecdsa.P521(), pub, hash, sig)
default:
return verifyLegacy(pub, hash, sig)
}
}
func verifyFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], pub *PublicKey, hash, sig []byte) bool {
r, s, err := parseSignature(sig)
if err != nil {
return false
}
k, err := publicKeyToFIPS(c, pub)
if err != nil {
return false
}
if err := ecdsa.Verify(c, k, hash, &ecdsa.Signature{R: r, S: s}); err != nil {
return false
}
return true
}
func parseSignature(sig []byte) (r, s []byte, err error) {
var inner cryptobyte.String
input := cryptobyte.String(sig)
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
!input.Empty() ||
!inner.ReadASN1Integer(&r) ||
!inner.ReadASN1Integer(&s) ||
!inner.Empty() {
return nil, nil, errors.New("invalid ASN.1")
}
return r, s, nil
}
func publicKeyFromFIPS(curve elliptic.Curve, pub *ecdsa.PublicKey) (*PublicKey, error) {
x, y, err := pointToAffine(curve, pub.Bytes())
if err != nil {
return nil, err
}
return &PublicKey{Curve: curve, X: x, Y: y}, nil
}
func privateKeyFromFIPS(curve elliptic.Curve, priv *ecdsa.PrivateKey) (*PrivateKey, error) {
pub, err := publicKeyFromFIPS(curve, priv.PublicKey())
if err != nil {
return nil, err
}
return &PrivateKey{PublicKey: *pub, D: new(big.Int).SetBytes(priv.Bytes())}, nil
}
func publicKeyToFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], pub *PublicKey) (*ecdsa.PublicKey, error) {
Q, err := pointFromAffine(pub.Curve, pub.X, pub.Y)
if err != nil {
return nil, err
}
return ecdsa.NewPublicKey(c, Q)
}
func privateKeyToFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey) (*ecdsa.PrivateKey, error) {
Q, err := pointFromAffine(priv.Curve, priv.X, priv.Y)
if err != nil {
return nil, err
}
return ecdsa.NewPrivateKey(c, priv.D.Bytes(), Q)
}
// pointFromAffine is used to convert the PublicKey to a nistec SetBytes input.
func pointFromAffine(curve elliptic.Curve, x, y *big.Int) ([]byte, error) {
bitSize := curve.Params().BitSize
// Reject values that would not get correctly encoded.
if x.Sign() < 0 || y.Sign() < 0 {
return nil, errors.New("negative coordinate")
}
if x.BitLen() > bitSize || y.BitLen() > bitSize {
return nil, errors.New("overflowing coordinate")
}
// Encode the coordinates and let SetBytes reject invalid points.
byteLen := (bitSize + 7) / 8
buf := make([]byte, 1+2*byteLen)
buf[0] = 4 // uncompressed point
x.FillBytes(buf[1 : 1+byteLen])
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
return buf, nil
}
// pointToAffine is used to convert a nistec Bytes encoding to a PublicKey.
func pointToAffine(curve elliptic.Curve, p []byte) (x, y *big.Int, err error) {
if len(p) == 1 && p[0] == 0 {
// This is the encoding of the point at infinity.
return nil, nil, errors.New("ecdsa: public key point is the infinity")
}
byteLen := (curve.Params().BitSize + 7) / 8
x = new(big.Int).SetBytes(p[1 : 1+byteLen])
y = new(big.Int).SetBytes(p[1+byteLen:])
return x, y, nil
}