1 package jwxtest
2
3 import (
4 "bytes"
5 "context"
6 "crypto/ecdsa"
7 "crypto/ed25519"
8 "crypto/elliptic"
9 "crypto/rand"
10 "crypto/rsa"
11 "encoding/json"
12 "io"
13 "io/ioutil"
14 "os"
15 "strings"
16 "testing"
17
18 "github.com/lestrrat-go/jwx/internal/ecutil"
19 "github.com/lestrrat-go/jwx/jwa"
20 "github.com/lestrrat-go/jwx/jwe"
21 "github.com/lestrrat-go/jwx/jwk"
22 "github.com/lestrrat-go/jwx/jws"
23 "github.com/lestrrat-go/jwx/x25519"
24 "github.com/pkg/errors"
25 "github.com/stretchr/testify/assert"
26 )
27
28 func GenerateRsaKey() (*rsa.PrivateKey, error) {
29 return rsa.GenerateKey(rand.Reader, 2048)
30 }
31
32 func GenerateRsaJwk() (jwk.Key, error) {
33 key, err := GenerateRsaKey()
34 if err != nil {
35 return nil, errors.Wrap(err, `failed to generate RSA private key`)
36 }
37
38 k, err := jwk.New(key)
39 if err != nil {
40 return nil, errors.Wrap(err, `failed to generate jwk.RSAPrivateKey`)
41 }
42
43 return k, nil
44 }
45
46 func GenerateRsaPublicJwk() (jwk.Key, error) {
47 key, err := GenerateRsaJwk()
48 if err != nil {
49 return nil, errors.Wrap(err, `failed to generate jwk.RSAPrivateKey`)
50 }
51
52 return jwk.PublicKeyOf(key)
53 }
54
55 func GenerateEcdsaKey(alg jwa.EllipticCurveAlgorithm) (*ecdsa.PrivateKey, error) {
56 var crv elliptic.Curve
57 if tmp, ok := ecutil.CurveForAlgorithm(alg); ok {
58 crv = tmp
59 } else {
60 return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
61 }
62
63 return ecdsa.GenerateKey(crv, rand.Reader)
64 }
65
66 func GenerateEcdsaJwk() (jwk.Key, error) {
67 key, err := GenerateEcdsaKey(jwa.P521)
68 if err != nil {
69 return nil, errors.Wrap(err, `failed to generate ECDSA private key`)
70 }
71
72 k, err := jwk.New(key)
73 if err != nil {
74 return nil, errors.Wrap(err, `failed to generate jwk.ECDSAPrivateKey`)
75 }
76
77 return k, nil
78 }
79
80 func GenerateEcdsaPublicJwk() (jwk.Key, error) {
81 key, err := GenerateEcdsaJwk()
82 if err != nil {
83 return nil, errors.Wrap(err, `failed to generate jwk.ECDSAPrivateKey`)
84 }
85
86 return jwk.PublicKeyOf(key)
87 }
88
89 func GenerateSymmetricKey() []byte {
90 sharedKey := make([]byte, 64)
91 rand.Read(sharedKey)
92 return sharedKey
93 }
94
95 func GenerateSymmetricJwk() (jwk.Key, error) {
96 key, err := jwk.New(GenerateSymmetricKey())
97 if err != nil {
98 return nil, errors.Wrap(err, `failed to generate jwk.SymmetricKey`)
99 }
100
101 return key, nil
102 }
103
104 func GenerateEd25519Key() (ed25519.PrivateKey, error) {
105 _, priv, err := ed25519.GenerateKey(rand.Reader)
106 return priv, err
107 }
108
109 func GenerateEd25519Jwk() (jwk.Key, error) {
110 key, err := GenerateEd25519Key()
111 if err != nil {
112 return nil, errors.Wrap(err, `failed to generate Ed25519 private key`)
113 }
114
115 k, err := jwk.New(key)
116 if err != nil {
117 return nil, errors.Wrap(err, `failed to generate jwk.OKPPrivateKey`)
118 }
119
120 return k, nil
121 }
122
123 func GenerateX25519Key() (x25519.PrivateKey, error) {
124 _, priv, err := x25519.GenerateKey(rand.Reader)
125 return priv, err
126 }
127
128 func GenerateX25519Jwk() (jwk.Key, error) {
129 key, err := GenerateX25519Key()
130 if err != nil {
131 return nil, errors.Wrap(err, `failed to generate X25519 private key`)
132 }
133
134 k, err := jwk.New(key)
135 if err != nil {
136 return nil, errors.Wrap(err, `failed to generate jwk.OKPPrivateKey`)
137 }
138
139 return k, nil
140 }
141
142 func WriteFile(template string, src io.Reader) (string, func(), error) {
143 file, cleanup, err := CreateTempFile(template)
144 if err != nil {
145 return "", nil, errors.Wrap(err, `failed to create temporary file`)
146 }
147
148 if _, err := io.Copy(file, src); err != nil {
149 defer cleanup()
150 return "", nil, errors.Wrap(err, `failed to copy content to temporary file`)
151 }
152
153 if err := file.Sync(); err != nil {
154 defer cleanup()
155 return "", nil, errors.Wrap(err, `failed to sync file`)
156 }
157 return file.Name(), cleanup, nil
158 }
159
160 func WriteJSONFile(template string, v interface{}) (string, func(), error) {
161 var buf bytes.Buffer
162
163 enc := json.NewEncoder(&buf)
164 if err := enc.Encode(v); err != nil {
165 return "", nil, errors.Wrap(err, `failed to encode object to JSON`)
166 }
167 return WriteFile(template, &buf)
168 }
169
170 func DumpFile(t *testing.T, file string) {
171 buf, err := ioutil.ReadFile(file)
172 if !assert.NoError(t, err, `failed to read file %s for debugging`, file) {
173 return
174 }
175
176 if isHash, isArray := bytes.ContainsRune(buf, '{'), bytes.ContainsRune(buf, '['); isHash || isArray {
177
178
179
180 var v interface{}
181 if isHash {
182 v = map[string]interface{}{}
183 } else {
184 v = []interface{}{}
185 }
186
187 if !assert.NoError(t, json.Unmarshal(buf, &v), `failed to parse contents as JSON`) {
188 return
189 }
190
191 buf, _ = json.MarshalIndent(v, "", " ")
192 t.Logf("=== BEGIN %s (formatted JSON) ===", file)
193 t.Logf("%s", buf)
194 t.Logf("=== END %s (formatted JSON) ===", file)
195 return
196 }
197
198
199
200 t.Logf("=== BEGIN %s (raw) ===", file)
201 t.Logf("%s", buf)
202 t.Logf("=== END %s (raw) ===", file)
203
204 if strings.HasSuffix(file, ".jwe") {
205
206 m, err := jwe.Parse(buf)
207 if !assert.NoError(t, err, `failed to parse JWE encrypted message`) {
208 return
209 }
210
211 buf, _ = json.MarshalIndent(m, "", " ")
212 }
213
214 t.Logf("=== BEGIN %s (formatted JSON) ===", file)
215 t.Logf("%s", buf)
216 t.Logf("=== END %s (formatted JSON) ===", file)
217 }
218
219 func CreateTempFile(template string) (*os.File, func(), error) {
220 file, err := ioutil.TempFile("", template)
221 if err != nil {
222 return nil, nil, errors.Wrap(err, "failed to create temporary file")
223 }
224
225 cleanup := func() {
226 file.Close()
227 os.Remove(file.Name())
228 }
229
230 return file, cleanup, nil
231 }
232
233 func ReadFile(file string) ([]byte, error) {
234 f, err := os.Open(file)
235 if err != nil {
236 return nil, errors.Wrapf(err, `failed to open file %s`, file)
237 }
238 defer f.Close()
239
240 buf, err := ioutil.ReadAll(f)
241 if err != nil {
242 return nil, errors.Wrapf(err, `failed to read from key file %s`, file)
243 }
244
245 return buf, nil
246 }
247
248 func ParseJwkFile(_ context.Context, file string) (jwk.Key, error) {
249 buf, err := ReadFile(file)
250 if err != nil {
251 return nil, errors.Wrapf(err, `failed to read from key file %s`, file)
252 }
253
254 key, err := jwk.ParseKey(buf)
255 if err != nil {
256 return nil, errors.Wrapf(err, `filed to parse JWK in key file %s`, file)
257 }
258
259 return key, nil
260 }
261
262 func DecryptJweFile(ctx context.Context, file string, alg jwa.KeyEncryptionAlgorithm, jwkfile string) ([]byte, error) {
263 key, err := ParseJwkFile(ctx, jwkfile)
264 if err != nil {
265 return nil, errors.Wrapf(err, `failed to parse keyfile %s`, file)
266 }
267
268 buf, err := ReadFile(file)
269 if err != nil {
270 return nil, errors.Wrapf(err, `failed to read from encrypted file %s`, file)
271 }
272
273 var rawkey interface{}
274 if err := key.Raw(&rawkey); err != nil {
275 return nil, errors.Wrap(err, `failed to obtain raw key from JWK`)
276 }
277
278 return jwe.Decrypt(buf, alg, rawkey)
279 }
280
281 func EncryptJweFile(ctx context.Context, payload []byte, keyalg jwa.KeyEncryptionAlgorithm, keyfile string, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm) (string, func(), error) {
282 key, err := ParseJwkFile(ctx, keyfile)
283 if err != nil {
284 return "", nil, errors.Wrapf(err, `failed to parse keyfile %s`, keyfile)
285 }
286
287 var keyif interface{}
288
289 switch keyalg {
290 case jwa.RSA1_5, jwa.RSA_OAEP, jwa.RSA_OAEP_256:
291 var rawkey rsa.PrivateKey
292 if err := key.Raw(&rawkey); err != nil {
293 return "", nil, errors.Wrap(err, `failed to obtain raw key`)
294 }
295 keyif = rawkey.PublicKey
296 case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
297 var rawkey ecdsa.PrivateKey
298 if err := key.Raw(&rawkey); err != nil {
299 return "", nil, errors.Wrap(err, `failed to obtain raw key`)
300 }
301 keyif = rawkey.PublicKey
302 default:
303 var rawkey []byte
304 if err := key.Raw(&rawkey); err != nil {
305 return "", nil, errors.Wrap(err, `failed to obtain raw key`)
306 }
307 keyif = rawkey
308 }
309
310 buf, err := jwe.Encrypt(payload, keyalg, keyif, contentalg, compressalg)
311 if err != nil {
312 return "", nil, errors.Wrap(err, `failed to encrypt payload`)
313 }
314
315 return WriteFile("jwx-test-*.jwe", bytes.NewReader(buf))
316 }
317
318 func VerifyJwsFile(ctx context.Context, file string, alg jwa.SignatureAlgorithm, jwkfile string) ([]byte, error) {
319 key, err := ParseJwkFile(ctx, jwkfile)
320 if err != nil {
321 return nil, errors.Wrapf(err, `failed to parse keyfile %s`, file)
322 }
323
324 buf, err := ReadFile(file)
325 if err != nil {
326 return nil, errors.Wrapf(err, `failed to read from encrypted file %s`, file)
327 }
328
329 var rawkey, pubkey interface{}
330 if err := key.Raw(&rawkey); err != nil {
331 return nil, errors.Wrap(err, `failed to obtain raw key from JWK`)
332 }
333 pubkey = rawkey
334 switch tkey := rawkey.(type) {
335 case *ecdsa.PrivateKey:
336 pubkey = tkey.PublicKey
337 case *rsa.PrivateKey:
338 pubkey = tkey.PublicKey
339 case *ed25519.PrivateKey:
340 pubkey = tkey.Public()
341 }
342
343 return jws.Verify(buf, alg, pubkey)
344 }
345
346 func SignJwsFile(ctx context.Context, payload []byte, alg jwa.SignatureAlgorithm, keyfile string) (string, func(), error) {
347 key, err := ParseJwkFile(ctx, keyfile)
348 if err != nil {
349 return "", nil, errors.Wrapf(err, `failed to parse keyfile %s`, keyfile)
350 }
351
352 buf, err := jws.Sign(payload, alg, key)
353 if err != nil {
354 return "", nil, errors.Wrap(err, `failed to sign payload`)
355 }
356
357 return WriteFile("jwx-test-*.jws", bytes.NewReader(buf))
358 }
359
View as plain text