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 package ed25519
37
38 import (
39 "bytes"
40 "crypto"
41 cryptoRand "crypto/rand"
42 "crypto/sha512"
43 "crypto/subtle"
44 "errors"
45 "fmt"
46 "io"
47 "strconv"
48
49 "github.com/cloudflare/circl/sign"
50 )
51
52 const (
53
54 ContextMaxSize = 255
55
56 PublicKeySize = 32
57
58 PrivateKeySize = 64
59
60 SignatureSize = 64
61
62 SeedSize = 32
63 )
64
65 const (
66 paramB = 256 / 8
67 )
68
69
70
71 type SignerOptions struct {
72
73
74 crypto.Hash
75
76
77
78 Context string
79
80
81
82 Scheme SchemeID
83 }
84
85
86 type SchemeID uint
87
88 const (
89 ED25519 SchemeID = iota
90 ED25519Ph
91 ED25519Ctx
92 )
93
94
95 type PrivateKey []byte
96
97
98 func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
99 xx, ok := x.(PrivateKey)
100 return ok && subtle.ConstantTimeCompare(priv, xx) == 1
101 }
102
103
104 func (priv PrivateKey) Public() crypto.PublicKey {
105 publicKey := make(PublicKey, PublicKeySize)
106 copy(publicKey, priv[SeedSize:])
107 return publicKey
108 }
109
110
111
112
113 func (priv PrivateKey) Seed() []byte {
114 seed := make([]byte, SeedSize)
115 copy(seed, priv[:SeedSize])
116 return seed
117 }
118
119 func (priv PrivateKey) Scheme() sign.Scheme { return sch }
120
121 func (pub PublicKey) Scheme() sign.Scheme { return sch }
122
123 func (priv PrivateKey) MarshalBinary() (data []byte, err error) {
124 privateKey := make(PrivateKey, PrivateKeySize)
125 copy(privateKey, priv)
126 return privateKey, nil
127 }
128
129 func (pub PublicKey) MarshalBinary() (data []byte, err error) {
130 publicKey := make(PublicKey, PublicKeySize)
131 copy(publicKey, pub)
132 return publicKey, nil
133 }
134
135
136 func (pub PublicKey) Equal(x crypto.PublicKey) bool {
137 xx, ok := x.(PublicKey)
138 return ok && bytes.Equal(pub, xx)
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 func (priv PrivateKey) Sign(
153 rand io.Reader,
154 message []byte,
155 opts crypto.SignerOpts,
156 ) (signature []byte, err error) {
157 var ctx string
158 var scheme SchemeID
159 if o, ok := opts.(SignerOptions); ok {
160 ctx = o.Context
161 scheme = o.Scheme
162 }
163
164 switch true {
165 case scheme == ED25519 && opts.HashFunc() == crypto.Hash(0):
166 return Sign(priv, message), nil
167 case scheme == ED25519Ph && opts.HashFunc() == crypto.SHA512:
168 return SignPh(priv, message, ctx), nil
169 case scheme == ED25519Ctx && opts.HashFunc() == crypto.Hash(0) && len(ctx) > 0:
170 return SignWithCtx(priv, message, ctx), nil
171 default:
172 return nil, errors.New("ed25519: bad hash algorithm")
173 }
174 }
175
176
177
178 func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
179 if rand == nil {
180 rand = cryptoRand.Reader
181 }
182
183 seed := make([]byte, SeedSize)
184 if _, err := io.ReadFull(rand, seed); err != nil {
185 return nil, nil, err
186 }
187
188 privateKey := NewKeyFromSeed(seed)
189 publicKey := make(PublicKey, PublicKeySize)
190 copy(publicKey, privateKey[SeedSize:])
191
192 return publicKey, privateKey, nil
193 }
194
195
196
197
198
199 func NewKeyFromSeed(seed []byte) PrivateKey {
200 privateKey := make(PrivateKey, PrivateKeySize)
201 newKeyFromSeed(privateKey, seed)
202 return privateKey
203 }
204
205 func newKeyFromSeed(privateKey, seed []byte) {
206 if l := len(seed); l != SeedSize {
207 panic("ed25519: bad seed length: " + strconv.Itoa(l))
208 }
209 var P pointR1
210 k := sha512.Sum512(seed)
211 clamp(k[:])
212 reduceModOrder(k[:paramB], false)
213 P.fixedMult(k[:paramB])
214 copy(privateKey[:SeedSize], seed)
215 _ = P.ToBytes(privateKey[SeedSize:])
216 }
217
218 func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) {
219 if l := len(privateKey); l != PrivateKeySize {
220 panic("ed25519: bad private key length: " + strconv.Itoa(l))
221 }
222
223 H := sha512.New()
224 var PHM []byte
225
226 if preHash {
227 _, _ = H.Write(message)
228 PHM = H.Sum(nil)
229 H.Reset()
230 } else {
231 PHM = message
232 }
233
234
235 _, _ = H.Write(privateKey[:SeedSize])
236 h := H.Sum(nil)
237 clamp(h[:])
238 prefix, s := h[paramB:], h[:paramB]
239
240
241 H.Reset()
242
243 writeDom(H, ctx, preHash)
244
245 _, _ = H.Write(prefix)
246 _, _ = H.Write(PHM)
247 r := H.Sum(nil)
248 reduceModOrder(r[:], true)
249
250
251 var P pointR1
252 P.fixedMult(r[:paramB])
253 R := (&[paramB]byte{})[:]
254 if err := P.ToBytes(R); err != nil {
255 panic(err)
256 }
257
258
259 H.Reset()
260
261 writeDom(H, ctx, preHash)
262
263 _, _ = H.Write(R)
264 _, _ = H.Write(privateKey[SeedSize:])
265 _, _ = H.Write(PHM)
266 hRAM := H.Sum(nil)
267
268 reduceModOrder(hRAM[:], true)
269
270
271 S := (&[paramB]byte{})[:]
272 calculateS(S, r[:paramB], hRAM[:paramB], s)
273
274
275 copy(signature[:paramB], R[:])
276 copy(signature[paramB:], S[:])
277 }
278
279
280
281
282
283 func Sign(privateKey PrivateKey, message []byte) []byte {
284 signature := make([]byte, SignatureSize)
285 signAll(signature, privateKey, message, []byte(""), false)
286 return signature
287 }
288
289
290
291
292
293
294
295
296 func SignPh(privateKey PrivateKey, message []byte, ctx string) []byte {
297 if len(ctx) > ContextMaxSize {
298 panic(fmt.Errorf("ed25519: bad context length: %v", len(ctx)))
299 }
300
301 signature := make([]byte, SignatureSize)
302 signAll(signature, privateKey, message, []byte(ctx), true)
303 return signature
304 }
305
306
307
308
309
310
311
312 func SignWithCtx(privateKey PrivateKey, message []byte, ctx string) []byte {
313 if len(ctx) == 0 || len(ctx) > ContextMaxSize {
314 panic(fmt.Errorf("ed25519: bad context length: %v > %v", len(ctx), ContextMaxSize))
315 }
316
317 signature := make([]byte, SignatureSize)
318 signAll(signature, privateKey, message, []byte(ctx), false)
319 return signature
320 }
321
322 func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool {
323 if len(public) != PublicKeySize ||
324 len(signature) != SignatureSize ||
325 !isLessThanOrder(signature[paramB:]) {
326 return false
327 }
328
329 var P pointR1
330 if ok := P.FromBytes(public); !ok {
331 return false
332 }
333
334 H := sha512.New()
335 var PHM []byte
336
337 if preHash {
338 _, _ = H.Write(message)
339 PHM = H.Sum(nil)
340 H.Reset()
341 } else {
342 PHM = message
343 }
344
345 R := signature[:paramB]
346
347 writeDom(H, ctx, preHash)
348
349 _, _ = H.Write(R)
350 _, _ = H.Write(public)
351 _, _ = H.Write(PHM)
352 hRAM := H.Sum(nil)
353 reduceModOrder(hRAM[:], true)
354
355 var Q pointR1
356 encR := (&[paramB]byte{})[:]
357 P.neg()
358 Q.doubleMult(&P, signature[paramB:], hRAM[:paramB])
359 _ = Q.ToBytes(encR)
360 return bytes.Equal(R, encR)
361 }
362
363
364
365
366
367
368
369
370
371
372 func VerifyAny(public PublicKey, message, signature []byte, opts crypto.SignerOpts) bool {
373 var ctx string
374 var scheme SchemeID
375 if o, ok := opts.(SignerOptions); ok {
376 ctx = o.Context
377 scheme = o.Scheme
378 }
379
380 switch true {
381 case scheme == ED25519 && opts.HashFunc() == crypto.Hash(0):
382 return Verify(public, message, signature)
383 case scheme == ED25519Ph && opts.HashFunc() == crypto.SHA512:
384 return VerifyPh(public, message, signature, ctx)
385 case scheme == ED25519Ctx && opts.HashFunc() == crypto.Hash(0) && len(ctx) > 0:
386 return VerifyWithCtx(public, message, signature, ctx)
387 default:
388 return false
389 }
390 }
391
392
393
394
395
396 func Verify(public PublicKey, message, signature []byte) bool {
397 return verify(public, message, signature, []byte(""), false)
398 }
399
400
401
402
403
404
405
406 func VerifyPh(public PublicKey, message, signature []byte, ctx string) bool {
407 return verify(public, message, signature, []byte(ctx), true)
408 }
409
410
411
412
413
414
415
416 func VerifyWithCtx(public PublicKey, message, signature []byte, ctx string) bool {
417 if len(ctx) == 0 || len(ctx) > ContextMaxSize {
418 return false
419 }
420
421 return verify(public, message, signature, []byte(ctx), false)
422 }
423
424 func clamp(k []byte) {
425 k[0] &= 248
426 k[paramB-1] = (k[paramB-1] & 127) | 64
427 }
428
429
430 func isLessThanOrder(x []byte) bool {
431 i := len(order) - 1
432 for i > 0 && x[i] == order[i] {
433 i--
434 }
435 return x[i] < order[i]
436 }
437
438 func writeDom(h io.Writer, ctx []byte, preHash bool) {
439 dom2 := "SigEd25519 no Ed25519 collisions"
440
441 if len(ctx) > 0 {
442 _, _ = h.Write([]byte(dom2))
443 if preHash {
444 _, _ = h.Write([]byte{byte(0x01), byte(len(ctx))})
445 } else {
446 _, _ = h.Write([]byte{byte(0x00), byte(len(ctx))})
447 }
448 _, _ = h.Write(ctx)
449 } else if preHash {
450 _, _ = h.Write([]byte(dom2))
451 _, _ = h.Write([]byte{0x01, 0x00})
452 }
453 }
454
View as plain text