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