1
2
3
4
5
6
7
8
9
10
11
12
13 package ed25519
14
15 import (
16 "bytes"
17 "crypto"
18 "crypto/internal/edwards25519"
19 cryptorand "crypto/rand"
20 "crypto/sha512"
21 "crypto/subtle"
22 "errors"
23 "io"
24 "strconv"
25 )
26
27 const (
28
29 PublicKeySize = 32
30
31 PrivateKeySize = 64
32
33 SignatureSize = 64
34
35 SeedSize = 32
36 )
37
38
39 type PublicKey []byte
40
41
42
43
44
45 func (pub PublicKey) Equal(x crypto.PublicKey) bool {
46 xx, ok := x.(PublicKey)
47 if !ok {
48 return false
49 }
50 return subtle.ConstantTimeCompare(pub, xx) == 1
51 }
52
53
54 type PrivateKey []byte
55
56
57 func (priv PrivateKey) Public() crypto.PublicKey {
58 publicKey := make([]byte, PublicKeySize)
59 copy(publicKey, priv[32:])
60 return PublicKey(publicKey)
61 }
62
63
64 func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
65 xx, ok := x.(PrivateKey)
66 if !ok {
67 return false
68 }
69 return subtle.ConstantTimeCompare(priv, xx) == 1
70 }
71
72
73
74
75 func (priv PrivateKey) Seed() []byte {
76 return bytes.Clone(priv[:SeedSize])
77 }
78
79
80
81
82
83
84
85
86
87
88 func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
89 hash := opts.HashFunc()
90 context := ""
91 if opts, ok := opts.(*Options); ok {
92 context = opts.Context
93 }
94 switch {
95 case hash == crypto.SHA512:
96 if l := len(message); l != sha512.Size {
97 return nil, errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa(l))
98 }
99 if l := len(context); l > 255 {
100 return nil, errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa(l))
101 }
102 signature := make([]byte, SignatureSize)
103 sign(signature, priv, message, domPrefixPh, context)
104 return signature, nil
105 case hash == crypto.Hash(0) && context != "":
106 if l := len(context); l > 255 {
107 return nil, errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa(l))
108 }
109 signature := make([]byte, SignatureSize)
110 sign(signature, priv, message, domPrefixCtx, context)
111 return signature, nil
112 case hash == crypto.Hash(0):
113 return Sign(priv, message), nil
114 default:
115 return nil, errors.New("ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)")
116 }
117 }
118
119
120
121 type Options struct {
122
123 Hash crypto.Hash
124
125
126
127 Context string
128 }
129
130
131 func (o *Options) HashFunc() crypto.Hash { return o.Hash }
132
133
134
135
136
137
138 func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
139 if rand == nil {
140 rand = cryptorand.Reader
141 }
142
143 seed := make([]byte, SeedSize)
144 if _, err := io.ReadFull(rand, seed); err != nil {
145 return nil, nil, err
146 }
147
148 privateKey := NewKeyFromSeed(seed)
149 publicKey := make([]byte, PublicKeySize)
150 copy(publicKey, privateKey[32:])
151
152 return publicKey, privateKey, nil
153 }
154
155
156
157
158
159 func NewKeyFromSeed(seed []byte) PrivateKey {
160
161 privateKey := make([]byte, PrivateKeySize)
162 newKeyFromSeed(privateKey, seed)
163 return privateKey
164 }
165
166 func newKeyFromSeed(privateKey, seed []byte) {
167 if l := len(seed); l != SeedSize {
168 panic("ed25519: bad seed length: " + strconv.Itoa(l))
169 }
170
171 h := sha512.Sum512(seed)
172 s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
173 if err != nil {
174 panic("ed25519: internal error: setting scalar failed")
175 }
176 A := (&edwards25519.Point{}).ScalarBaseMult(s)
177
178 publicKey := A.Bytes()
179
180 copy(privateKey, seed)
181 copy(privateKey[32:], publicKey)
182 }
183
184
185
186 func Sign(privateKey PrivateKey, message []byte) []byte {
187
188
189 signature := make([]byte, SignatureSize)
190 sign(signature, privateKey, message, domPrefixPure, "")
191 return signature
192 }
193
194
195
196 const (
197
198 domPrefixPure = ""
199
200
201 domPrefixPh = "SigEd25519 no Ed25519 collisions\x01"
202
203
204 domPrefixCtx = "SigEd25519 no Ed25519 collisions\x00"
205 )
206
207 func sign(signature, privateKey, message []byte, domPrefix, context string) {
208 if l := len(privateKey); l != PrivateKeySize {
209 panic("ed25519: bad private key length: " + strconv.Itoa(l))
210 }
211 seed, publicKey := privateKey[:SeedSize], privateKey[SeedSize:]
212
213 h := sha512.Sum512(seed)
214 s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
215 if err != nil {
216 panic("ed25519: internal error: setting scalar failed")
217 }
218 prefix := h[32:]
219
220 mh := sha512.New()
221 if domPrefix != domPrefixPure {
222 mh.Write([]byte(domPrefix))
223 mh.Write([]byte{byte(len(context))})
224 mh.Write([]byte(context))
225 }
226 mh.Write(prefix)
227 mh.Write(message)
228 messageDigest := make([]byte, 0, sha512.Size)
229 messageDigest = mh.Sum(messageDigest)
230 r, err := edwards25519.NewScalar().SetUniformBytes(messageDigest)
231 if err != nil {
232 panic("ed25519: internal error: setting scalar failed")
233 }
234
235 R := (&edwards25519.Point{}).ScalarBaseMult(r)
236
237 kh := sha512.New()
238 if domPrefix != domPrefixPure {
239 kh.Write([]byte(domPrefix))
240 kh.Write([]byte{byte(len(context))})
241 kh.Write([]byte(context))
242 }
243 kh.Write(R.Bytes())
244 kh.Write(publicKey)
245 kh.Write(message)
246 hramDigest := make([]byte, 0, sha512.Size)
247 hramDigest = kh.Sum(hramDigest)
248 k, err := edwards25519.NewScalar().SetUniformBytes(hramDigest)
249 if err != nil {
250 panic("ed25519: internal error: setting scalar failed")
251 }
252
253 S := edwards25519.NewScalar().MultiplyAdd(k, s, r)
254
255 copy(signature[:32], R.Bytes())
256 copy(signature[32:], S.Bytes())
257 }
258
259
260
261 func Verify(publicKey PublicKey, message, sig []byte) bool {
262 return verify(publicKey, message, sig, domPrefixPure, "")
263 }
264
265
266
267
268
269
270
271
272
273 func VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) error {
274 switch {
275 case opts.Hash == crypto.SHA512:
276 if l := len(message); l != sha512.Size {
277 return errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa(l))
278 }
279 if l := len(opts.Context); l > 255 {
280 return errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa(l))
281 }
282 if !verify(publicKey, message, sig, domPrefixPh, opts.Context) {
283 return errors.New("ed25519: invalid signature")
284 }
285 return nil
286 case opts.Hash == crypto.Hash(0) && opts.Context != "":
287 if l := len(opts.Context); l > 255 {
288 return errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa(l))
289 }
290 if !verify(publicKey, message, sig, domPrefixCtx, opts.Context) {
291 return errors.New("ed25519: invalid signature")
292 }
293 return nil
294 case opts.Hash == crypto.Hash(0):
295 if !verify(publicKey, message, sig, domPrefixPure, "") {
296 return errors.New("ed25519: invalid signature")
297 }
298 return nil
299 default:
300 return errors.New("ed25519: expected opts.Hash zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)")
301 }
302 }
303
304 func verify(publicKey PublicKey, message, sig []byte, domPrefix, context string) bool {
305 if l := len(publicKey); l != PublicKeySize {
306 panic("ed25519: bad public key length: " + strconv.Itoa(l))
307 }
308
309 if len(sig) != SignatureSize || sig[63]&224 != 0 {
310 return false
311 }
312
313 A, err := (&edwards25519.Point{}).SetBytes(publicKey)
314 if err != nil {
315 return false
316 }
317
318 kh := sha512.New()
319 if domPrefix != domPrefixPure {
320 kh.Write([]byte(domPrefix))
321 kh.Write([]byte{byte(len(context))})
322 kh.Write([]byte(context))
323 }
324 kh.Write(sig[:32])
325 kh.Write(publicKey)
326 kh.Write(message)
327 hramDigest := make([]byte, 0, sha512.Size)
328 hramDigest = kh.Sum(hramDigest)
329 k, err := edwards25519.NewScalar().SetUniformBytes(hramDigest)
330 if err != nil {
331 panic("ed25519: internal error: setting scalar failed")
332 }
333
334 S, err := edwards25519.NewScalar().SetCanonicalBytes(sig[32:])
335 if err != nil {
336 return false
337 }
338
339
340 minusA := (&edwards25519.Point{}).Negate(A)
341 R := (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(k, minusA, S)
342
343 return bytes.Equal(sig[:32], R.Bytes())
344 }
345
View as plain text