-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathed448.go
More file actions
221 lines (194 loc) · 6.58 KB
/
ed448.go
File metadata and controls
221 lines (194 loc) · 6.58 KB
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
// Package ed448 provides Ed448 signing, verification, and JWK support for the jwx library.
//
// Ed448 is not included in the main jwx module because Go's standard library
// does not support Ed448, requiring the external github.com/cloudflare/circl
// module. To avoid adding this dependency for all users, Ed448 support is
// provided as a separate module.
//
// To enable Ed448 support, import this package for its side effects:
//
// import _ "github.com/lestrrat-go/jwx-circl-ed448"
//
// This registers Ed448 signing/verification (via dsig-circl-ed448), JWK key
// import/export, and algorithm-for-key-type mappings. After importing,
// jwa.EdDSAEd448() can be used with jws.Sign, jws.Verify, jwk.Import, etc.
package ed448
import (
"bytes"
"fmt"
"github.com/cloudflare/circl/sign/ed448"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwk/jwkunsafe"
"github.com/lestrrat-go/jwx/v3/jws"
"github.com/lestrrat-go/jwx/v3/jws/jwsbb"
// Import dsig-circl-ed448 for side effects: registers Ed448 sign/verify
// with dsig as a Custom family algorithm.
_ "github.com/lestrrat-go/dsig-circl-ed448"
)
func init() {
// Register Ed448 as a known signature algorithm in jwa
jwa.RegisterSignatureAlgorithm(jwa.EdDSAEd448())
// Register Ed448 as valid algorithm for OKP key type
jws.RegisterAlgorithmForKeyType(jwa.OKP(), jwa.EdDSAEd448())
// Register Ed448 algorithm for Ed448 curve (enables curve-specific algorithm filtering)
jws.RegisterAlgorithmForCurve(jwa.Ed448(), jwa.EdDSAEd448())
// Register signer/verifier that handle JWK key unwrapping.
// The dsig-circl-ed448 signer only accepts raw ed448 keys,
// so we need this layer to convert JWK keys before dispatch.
if err := jws.RegisterSigner(jwa.EdDSAEd448(), ed448Signer{}); err != nil {
panic(fmt.Sprintf("jwx-circl-ed448: failed to register signer: %s", err))
}
if err := jws.RegisterVerifier(jwa.EdDSAEd448(), ed448Verifier{}); err != nil {
panic(fmt.Sprintf("jwx-circl-ed448: failed to register verifier: %s", err))
}
// Register JWK exporter for OKP:Ed448 keys (JWK → raw ed448 key)
jwk.RegisterKeyExporter(jwk.KeyKind("OKP:Ed448"), jwk.KeyExportFunc(exportEd448Key))
// Register raw key importer for Ed448 keys
jwk.RegisterOKPRawKeyImporter(importEd448RawKey)
// Register jwk.Import handlers for Ed448 key types (raw ed448 key → JWK)
f := jwk.KeyImportFunc(importOKPEd448Key)
jwk.RegisterKeyImporter(ed448.PublicKey(nil), f)
jwk.RegisterKeyImporter(ed448.PrivateKey(nil), f)
}
// --- Signer/Verifier (JWK key unwrapping) ---
type ed448Signer struct{}
func (ed448Signer) Algorithm() jwa.SignatureAlgorithm { return jwa.EdDSAEd448() }
func (ed448Signer) Sign(key any, payload []byte) ([]byte, error) {
var privkey ed448.PrivateKey
if err := ed448PrivateKey(&privkey, key); err != nil {
return nil, fmt.Errorf(`ed448.Sign: %w`, err)
}
return jwsbb.Sign(privkey, "Ed448", payload, nil)
}
type ed448Verifier struct{}
func (ed448Verifier) Verify(key any, payload, signature []byte) error {
var pubkey ed448.PublicKey
if err := ed448PublicKey(&pubkey, key); err != nil {
return fmt.Errorf(`ed448.Verify: %w`, err)
}
return jwsbb.Verify(pubkey, "Ed448", payload, signature)
}
// --- Key conversion ---
func ed448PrivateKey(dst *ed448.PrivateKey, src any) error {
if jwkKey, ok := src.(jwk.Key); ok {
var raw ed448.PrivateKey
if err := jwk.Export(jwkKey, &raw); err != nil {
return fmt.Errorf(`failed to produce ed448.PrivateKey from %T: %w`, src, err)
}
*dst = raw
return nil
}
switch src := src.(type) {
case ed448.PrivateKey:
*dst = src
case *ed448.PrivateKey:
*dst = *src
default:
return fmt.Errorf(`expected ed448.PrivateKey, got %T`, src)
}
return nil
}
func ed448PublicKey(dst *ed448.PublicKey, src any) error {
if jwkKey, ok := src.(jwk.Key); ok {
pk, err := jwk.PublicRawKeyOf(jwkKey)
if err != nil {
return fmt.Errorf(`failed to produce public key from %T: %w`, src, err)
}
src = pk
}
switch key := src.(type) {
case ed448.PrivateKey:
src = key.Public()
case *ed448.PrivateKey:
src = key.Public()
}
switch src := src.(type) {
case ed448.PublicKey:
*dst = src
case *ed448.PublicKey:
*dst = *src
default:
return fmt.Errorf(`expected ed448.PublicKey, got %T`, src)
}
return nil
}
// --- JWK key export (JWK → raw ed448 key) ---
func exportEd448Key(key jwk.Key, _ any) (any, error) {
switch key := key.(type) {
case jwk.OKPPrivateKey:
x, ok := key.X()
if !ok {
return nil, fmt.Errorf(`missing "x" field`)
}
d, ok := key.D()
if !ok {
return nil, fmt.Errorf(`missing "d" field`)
}
if len(d) != ed448.SeedSize {
return nil, fmt.Errorf(`ed448: wrong private key seed size %d (expected %d)`, len(d), ed448.SeedSize)
}
ret := ed448.NewKeyFromSeed(d)
pub := ret.Public().(ed448.PublicKey) //nolint:forcetypeassert
if !bytes.Equal(x, pub) {
return nil, fmt.Errorf(`ed448: invalid x value given d value`)
}
return ret, nil
case jwk.OKPPublicKey:
x, ok := key.X()
if !ok {
return nil, fmt.Errorf(`missing "x" field`)
}
if len(x) != ed448.PublicKeySize {
return nil, fmt.Errorf(`ed448: wrong public key size %d (expected %d)`, len(x), ed448.PublicKeySize)
}
return ed448.PublicKey(x), nil
default:
return nil, jwk.ContinueError()
}
}
// --- JWK raw key import ---
func importEd448RawKey(key any) (jwa.EllipticCurveAlgorithm, []byte, []byte, bool) {
switch k := key.(type) {
case ed448.PublicKey:
return jwa.Ed448(), []byte(k), nil, true
case ed448.PrivateKey:
pub := k.Public().(ed448.PublicKey) //nolint:forcetypeassert
return jwa.Ed448(), []byte(pub), k.Seed(), true
}
return jwa.InvalidEllipticCurve(), nil, nil, false
}
func importOKPEd448Key(src any) (jwk.Key, error) {
switch k := src.(type) {
case ed448.PrivateKey:
key, err := jwkunsafe.NewKey(jwa.OKP())
if err != nil {
return nil, fmt.Errorf(`failed to create OKP private key: %w`, err)
}
pub := k.Public().(ed448.PublicKey) //nolint:forcetypeassert
if err := key.Set(jwk.OKPCrvKey, jwa.Ed448()); err != nil {
return nil, err
}
if err := key.Set(jwk.OKPXKey, []byte(pub)); err != nil {
return nil, err
}
if err := key.Set(jwk.OKPDKey, k.Seed()); err != nil {
return nil, err
}
return key, nil
case ed448.PublicKey:
key, err := jwkunsafe.NewPublicKey(jwa.OKP())
if err != nil {
return nil, fmt.Errorf(`failed to create OKP public key: %w`, err)
}
if err := key.Set(jwk.OKPCrvKey, jwa.Ed448()); err != nil {
return nil, err
}
if err := key.Set(jwk.OKPXKey, []byte(k)); err != nil {
return nil, err
}
return key, nil
default:
return nil, fmt.Errorf(`cannot convert key type %T to OKP jwk.Key`, src)
}
}