1 package jwe_test
2
3 import (
4 "crypto"
5 "crypto/ecdsa"
6 "crypto/elliptic"
7 "crypto/rand"
8 "crypto/rsa"
9 "encoding/base64"
10 "fmt"
11 "io/ioutil"
12 "strings"
13 "testing"
14 "time"
15
16 "github.com/lestrrat-go/jwx/internal/json"
17 "github.com/lestrrat-go/jwx/internal/jwxtest"
18
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/x25519"
23 "github.com/stretchr/testify/assert"
24 )
25
26 const (
27 examplePayload = `The true sign of intelligence is not knowledge but imagination.`
28 )
29
30 var rsaPrivKey rsa.PrivateKey
31
32 func init() {
33 var jwkstr = []byte(`
34 {"kty":"RSA",
35 "n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw",
36 "e":"AQAB",
37 "d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ",
38 "p":"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0",
39 "q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc",
40 "dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE",
41 "dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis",
42 "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
43 }`)
44
45 privkey := jwk.NewRSAPrivateKey()
46 if err := json.Unmarshal(jwkstr, privkey); err != nil {
47 panic(err)
48 }
49
50 if err := privkey.Raw(&rsaPrivKey); err != nil {
51 panic(err)
52 }
53 }
54
55 func TestSanityCheck_JWEExamplePayload(t *testing.T) {
56 expected := []byte{
57 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,
58 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99,
59 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108,
60 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105,
61 110, 97, 116, 105, 111, 110, 46,
62 }
63 assert.Equal(t, expected, []byte(examplePayload), "examplePayload OK")
64 }
65
66 func TestParse(t *testing.T) {
67 const s = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`
68 t.Run("Compact format", func(t *testing.T) {
69 t.Run("Normal", func(t *testing.T) {
70 msg, err := jwe.Parse([]byte(s))
71 if !assert.NoError(t, err, "Parsing JWE is successful") {
72 return
73 }
74 if !assert.Len(t, msg.Recipients(), 1, "There is exactly 1 recipient") {
75 return
76 }
77 })
78
79 parts := strings.Split(s, ".")
80 t.Run("Missing parts", func(t *testing.T) {
81 s2 := strings.Join(parts[:4], ".")
82 _, err := jwe.Parse([]byte(s2))
83 if !assert.Error(t, err, `should fail to parse compact format with missing parts`) {
84 return
85 }
86 })
87 t.Run("Invalid header", func(t *testing.T) {
88 s2 := strings.Join(append(append([]string(nil), "!!invalidheader!!"), parts[1:]...), ".")
89 _, err := jwe.Parse([]byte(s2))
90 if !assert.Error(t, err, `should fail to parse compact format with invalid header`) {
91 return
92 }
93 })
94 t.Run("Invalid encrypted key", func(t *testing.T) {
95 s2 := strings.Join(append(append(append([]string(nil), parts[0]), "!!invalidenckey!!"), parts[2:]...), ".")
96 _, err := jwe.Parse([]byte(s2))
97 if !assert.Error(t, err, `should fail to parse compact format with invalid encrypted key`) {
98 return
99 }
100 })
101 t.Run("Invalid initialization vector", func(t *testing.T) {
102 s2 := strings.Join(append(append(append([]string(nil), parts[:2]...), "!!invalidiv!!"), parts[3:]...), ".")
103 _, err := jwe.Parse([]byte(s2))
104 if !assert.Error(t, err, `should fail to parse compact format with invalid initialization vector`) {
105 return
106 }
107 })
108 t.Run("Invalid content", func(t *testing.T) {
109 s2 := strings.Join(append(append(append([]string(nil), parts[:3]...), "!!invalidcontent!!"), parts[4:]...), ".")
110 _, err := jwe.Parse([]byte(s2))
111 if !assert.Error(t, err, `should fail to parse compact format with invalid content`) {
112 return
113 }
114 })
115 t.Run("Invalid tag", func(t *testing.T) {
116 s2 := strings.Join(append(parts[:4], "!!invalidtag!!"), ".")
117 _, err := jwe.Parse([]byte(s2))
118 if !assert.Error(t, err, `should fail to parse compact format with invalid tag`) {
119 return
120 }
121 })
122 })
123 t.Run("JSON format", func(t *testing.T) {
124 msg, err := jwe.Parse([]byte(s))
125 if !assert.NoError(t, err, "Parsing JWE is successful") {
126 return
127 }
128
129 buf, err := jwe.JSON(msg)
130 if !assert.NoError(t, err, "Serializing to JSON format should succeed") {
131 return
132 }
133
134 msg2, err := jwe.Parse(buf)
135 if !assert.NoError(t, err, "Parsing JWE in JSON format should succeed") {
136 return
137 }
138
139 if !assert.Equal(t, msg, msg2, "messages should match") {
140 return
141 }
142 })
143 }
144
145
146
147 func TestParse_RSAES_OAEP_AES_GCM(t *testing.T) {
148 const payload = `The true sign of intelligence is not knowledge but imagination.`
149 const serialized = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`
150 var jwkstr = []byte(`
151 {"kty":"RSA",
152 "n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw",
153 "e":"AQAB",
154 "d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ",
155 "p":"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0",
156 "q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc",
157 "dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE",
158 "dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis",
159 "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
160 }`)
161
162 privkey := jwk.NewRSAPrivateKey()
163 if !assert.NoError(t, json.Unmarshal(jwkstr, privkey), `parsing jwk should succeed`) {
164 return
165 }
166
167 var rawkey rsa.PrivateKey
168 if !assert.NoError(t, privkey.Raw(&rawkey), `obtaining raw key should succeed`) {
169 return
170 }
171
172 msg, err := jwe.ParseString(serialized)
173 if !assert.NoError(t, err, "parse successful") {
174 return
175 }
176
177 {
178 buf, _ := json.MarshalIndent(msg, "", " ")
179 t.Logf("%s", buf)
180 }
181
182 if !assert.Equal(t, 1, len(msg.Recipients()), "message recipients header length is 1") {
183 return
184 }
185
186 plaintext, err := msg.Decrypt(jwa.RSA_OAEP, rawkey)
187 if !assert.NoError(t, err, "Decrypt message succeeded") {
188 return
189 }
190
191 if !assert.Equal(t, payload, string(plaintext), "decrypted value does not match") {
192 return
193 }
194
195 serializers := []struct {
196 Name string
197 Func func(*jwe.Message) ([]byte, error)
198 Expected string
199 }{
200 {
201 Name: "Compact",
202 Func: func(m *jwe.Message) ([]byte, error) { return jwe.Compact(m) },
203 Expected: serialized,
204 },
205 {
206 Name: "JSON",
207 Func: func(m *jwe.Message) ([]byte, error) { return jwe.JSON(m) },
208 Expected: `{"ciphertext":"5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A","iv":"48V1_ALb6US04U3b","protected":"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ","header":{"alg":"RSA-OAEP"},"encrypted_key":"OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg","tag":"XFBoMYUZodetZdvTiFvSkQ"}`,
209 },
210 {
211 Name: "JSON (Pretty)",
212 Func: func(m *jwe.Message) ([]byte, error) { return jwe.JSON(m, jwe.WithPrettyFormat(true)) },
213 Expected: `{
214 "ciphertext": "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A",
215 "iv": "48V1_ALb6US04U3b",
216 "protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
217 "header": {
218 "alg": "RSA-OAEP"
219 },
220 "encrypted_key": "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg",
221 "tag": "XFBoMYUZodetZdvTiFvSkQ"
222 }`,
223 },
224 }
225
226 for _, serializer := range serializers {
227 serializer := serializer
228 for _, compression := range []jwa.CompressionAlgorithm{jwa.NoCompress, jwa.Deflate} {
229 compression := compression
230 compName := compression.String()
231 if compName == "" {
232 compName = "none"
233 }
234 t.Run(serializer.Name+" (compression="+compName+")", func(t *testing.T) {
235 jsonbuf, err := serializer.Func(msg)
236 if !assert.NoError(t, err, "serialize succeeded") {
237 return
238 }
239
240 if !assert.Equal(t, serializer.Expected, string(jsonbuf), "serialize result matches") {
241 jsonbuf, _ = jwe.JSON(msg, jwe.WithPrettyFormat(true))
242 t.Logf("%s", jsonbuf)
243 return
244 }
245
246 encrypted, err := jwe.Encrypt(plaintext, jwa.RSA_OAEP, rawkey.PublicKey, jwa.A256GCM, compression)
247 if !assert.NoError(t, err, "jwe.Encrypt should succeed") {
248 return
249 }
250
251 plaintext, err = jwe.Decrypt(encrypted, jwa.RSA_OAEP, rawkey)
252 if !assert.NoError(t, err, "jwe.Decrypt should succeed") {
253 return
254 }
255
256 if !assert.Equal(t, payload, string(plaintext), "jwe.Decrypt should produce the same plaintext") {
257 return
258 }
259 })
260 }
261 }
262
263
264 t.Run("Marshal/Unmarshal", func(t *testing.T) {
265 buf, err := json.Marshal(msg)
266 if !assert.NoError(t, err, `json.Marshal should succeed`) {
267 return
268 }
269
270 m2 := jwe.NewMessage()
271 if !assert.NoError(t, json.Unmarshal(buf, m2), `json.Unmarshal should succeed`) {
272 t.Logf("%s", buf)
273 return
274 }
275
276 if !assert.Equal(t, msg, m2, `messages should be the same after roundtrip`) {
277 return
278 }
279 })
280 }
281
282
283 func TestRoundtrip_RSAES_OAEP_AES_GCM(t *testing.T) {
284 var plaintext = []byte{
285 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,
286 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99,
287 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108,
288 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105,
289 110, 97, 116, 105, 111, 110, 46,
290 }
291
292 max := 100
293 if testing.Short() {
294 max = 1
295 }
296
297 for i := 0; i < max; i++ {
298 encrypted, err := jwe.Encrypt(plaintext, jwa.RSA_OAEP, &rsaPrivKey.PublicKey, jwa.A256GCM, jwa.NoCompress)
299 if !assert.NoError(t, err, "Encrypt should succeed") {
300 return
301 }
302
303 decrypted, err := jwe.Decrypt(encrypted, jwa.RSA_OAEP, rsaPrivKey)
304 if !assert.NoError(t, err, "Decrypt should succeed") {
305 return
306 }
307
308 if !assert.Equal(t, plaintext, decrypted, "Decrypted content should match") {
309 return
310 }
311 }
312 }
313
314 func TestRoundtrip_RSA1_5_A128CBC_HS256(t *testing.T) {
315 var plaintext = []byte{
316 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,
317 112, 114, 111, 115, 112, 101, 114, 46,
318 }
319
320 max := 100
321 if testing.Short() {
322 max = 1
323 }
324
325 for i := 0; i < max; i++ {
326 encrypted, err := jwe.Encrypt(plaintext, jwa.RSA1_5, &rsaPrivKey.PublicKey, jwa.A128CBC_HS256, jwa.NoCompress)
327 if !assert.NoError(t, err, "Encrypt is successful") {
328 return
329 }
330
331 decrypted, err := jwe.Decrypt(encrypted, jwa.RSA1_5, rsaPrivKey)
332 if !assert.NoError(t, err, "Decrypt successful") {
333 return
334 }
335
336 if !assert.Equal(t, plaintext, decrypted, "Decrypted correct plaintext") {
337 return
338 }
339 }
340 }
341
342
343
344 func TestEncode_A128KW_A128CBC_HS256(t *testing.T) {
345 var plaintext = []byte{
346 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,
347 112, 114, 111, 115, 112, 101, 114, 46,
348 }
349 var sharedkey = []byte{
350 25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82,
351 }
352
353 max := 100
354 if testing.Short() {
355 max = 1
356 }
357
358 for i := 0; i < max; i++ {
359 encrypted, err := jwe.Encrypt(plaintext, jwa.A128KW, sharedkey, jwa.A128CBC_HS256, jwa.NoCompress)
360 if !assert.NoError(t, err, "Encrypt is successful") {
361 return
362 }
363
364 decrypted, err := jwe.Decrypt(encrypted, jwa.A128KW, sharedkey)
365 if !assert.NoError(t, err, "Decrypt successful") {
366 return
367 }
368
369 if !assert.Equal(t, plaintext, decrypted, "Decrypted correct plaintext") {
370 return
371 }
372 }
373 }
374
375
376 func testEncodeECDHWithKey(t *testing.T, privkey interface{}, pubkey interface{}) {
377 plaintext := []byte("Lorem ipsum")
378
379 algorithms := []jwa.KeyEncryptionAlgorithm{
380 jwa.ECDH_ES,
381 jwa.ECDH_ES_A256KW,
382 jwa.ECDH_ES_A192KW,
383 jwa.ECDH_ES_A128KW,
384 }
385
386 for _, alg := range algorithms {
387 alg := alg
388 t.Run(alg.String(), func(t *testing.T) {
389 encrypted, err := jwe.Encrypt(plaintext, alg, pubkey, jwa.A256GCM, jwa.NoCompress)
390 if !assert.NoError(t, err, "Encrypt succeeds") {
391 return
392 }
393
394 t.Logf("encrypted = %s", encrypted)
395
396 msg, err := jwe.Parse(encrypted)
397 if !assert.NoError(t, err, `jwe.Parse should succeed`) {
398 return
399 }
400
401 {
402 buf, _ := json.MarshalIndent(msg, "", " ")
403 t.Logf("%s", buf)
404 }
405 {
406 buf, _ := json.MarshalIndent(msg.ProtectedHeaders(), "", " ")
407 t.Logf("%s", buf)
408 }
409
410 decrypted, err := jwe.Decrypt(encrypted, alg, privkey)
411 if !assert.NoError(t, err, "Decrypt succeeds") {
412 return
413 }
414 t.Logf("%s", decrypted)
415 })
416 }
417 }
418
419 func TestEncode_ECDH(t *testing.T) {
420 curves := []elliptic.Curve{
421 elliptic.P256(),
422 elliptic.P384(),
423 elliptic.P521(),
424 }
425 for _, crv := range curves {
426 crv := crv
427 t.Run(crv.Params().Name, func(t *testing.T) {
428 privkey, err := ecdsa.GenerateKey(crv, rand.Reader)
429 if !assert.NoError(t, err, `ecdsa.GenerateKey should succeed`) {
430 return
431 }
432
433 testEncodeECDHWithKey(t, privkey, &privkey.PublicKey)
434 })
435 }
436 }
437
438 func TestEncode_X25519(t *testing.T) {
439 pubkey, privkey, err := x25519.GenerateKey(rand.Reader)
440 if !assert.NoError(t, err, `x25519.GenerateKey should succeed`) {
441 return
442 }
443
444 testEncodeECDHWithKey(t, privkey, pubkey)
445 }
446
447 func Test_GHIssue207(t *testing.T) {
448 const plaintext = "hi\n"
449 var testcases = []struct {
450 Algorithm jwa.KeyEncryptionAlgorithm
451 Key string
452 Data string
453 Thumbprint string
454 Name string
455 }{
456 {
457 Name: `ECDH-ES`,
458 Key: `{"alg":"ECDH-ES","crv":"P-521","d":"ARxUkIjnB7pjFzM2OIIFcclR-4qbZwv7DoC96cksPKyvVWOkEsZ0CK6deM4AC6G5GClR5TXWMQVC_bNDmfuwPPqF","key_ops":["wrapKey","unwrapKey"],"kty":"EC","x":"ACewmG5j0POUDQw3rIqFQozK_6yXUsfNjiZtWqQOU7MXsSKK9RsRS8ySmeTG14heUpbbnrC9VdYKSOUGkYnYUl2Y","y":"ACkXSOma_FP93R3u5uYX7gUOlM0LDkNsij9dVFPbafF8hlfYEnUGit2o-tt7W0Zq3t38jEhpjUoGgM04JDJ6_m0x"}`,
459 Data: `{"ciphertext":"sp0cLt4Rx1p0Ax0Q1OZj7w","header":{"alg":"ECDH-ES","epk":{"crv":"P-521","kty":"EC","x":"APMKQpje5vu39-eS_LX_g15stqbNZ37GgYimW8PZf7d_OOuAygK2YlINYnPoUybrxkoaLRPhbmxc9MBWFdaY8SXx","y":"AMpq4DFi6w-pfnprO58CkfX-ncXtJ8fvox2Ej8Ey3ZY1xjVUtbDJCDCjY53snYaNCEjnFQPAn-IkAG90p2Xcm8ut"}},"iv":"Fjnb5uUWp9euqp1MK_hT4A","protected":"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0","tag":"6nhiy-vyqwVjpy08jrorTpWqvam66HdKxU36XsE3Z3s"}`,
460 Thumbprint: `0_6x6e2sZKeq3ka0QV0PEkJagqg`,
461 },
462 {
463 Name: `ECDH-ES+A256KW`,
464 Key: `{"alg":"ECDH-ES+A256KW","crv":"P-521","d":"AcH8h_ctsMnopTiCH7wiuM-nAb1CNikC0ubcOZQDLYSVEw93h6_D57aD7DLWbjIsVNzn7Qq8P-kRiTYVoH5GTQVg","key_ops":["wrapKey","unwrapKey"],"kty":"EC","x":"AAQoEbNeiG3ExYj9bJLGFn4h_bFjERfIcmpQMW5KWlFhqcXTFg0g8-5YWjdJXdNmO_2EuaKe7zOvEq8dCFCb12-R","y":"Ad8E2jp6FSCSd8laERqIt67A2T-MIqQE5301jNYb5SMsCSV1rs1McyvhzHaclYcqTUptoA-rW5kNS9N5124XPHky"}`,
465 Data: `{"ciphertext":"evXmzoQ5TWQvEXdpv9ZCBQ","encrypted_key":"ceVsjF-0LhziK75oHRC-C539hlFJMSbub015a3YtIBgCt7c0IRzkzwoOvo_Jf44FXZi0Vd-4fvDjRkZDzx9DcuDd4ASYDLvW","header":{"alg":"ECDH-ES+A256KW","epk":{"crv":"P-521","kty":"EC","x":"Aad7PFl9cct7WcfM3b_LNkhCHfCotW_nRuarX7GACDyyZkr2dd1g6r3rz-8r2-AyOGD9gc2nhrTEjVHT2W7eu65U","y":"Ab0Mj6BK8g3Fok6oyFlkvKOyquEVxeeJOlsyXKYBputPxFT5Gljr2FoJdViAxVspoSiw1K5oG1h59UBJgPWG4GQV"}},"iv":"KsJgq2xyzE1dZi2BM2xf5g","protected":"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0","tag":"b6m_nW9vfk6xJugm_-Uuj4cbAQh9ECelLc1ZBfO86L0"}`,
466 Thumbprint: `G4OtKQL_qr9Q57atNOU6SJnJxB8`,
467 },
468 }
469
470 for _, tc := range testcases {
471 tc := tc
472 t.Run(tc.Name, func(t *testing.T) {
473 webKey, err := jwk.ParseKey([]byte(tc.Key))
474 if !assert.NoError(t, err, `jwk.ParseKey should succeed`) {
475 return
476 }
477
478 thumbprint, err := webKey.Thumbprint(crypto.SHA1)
479 if !assert.NoError(t, err, `jwk.Thumbprint should succeed`) {
480 return
481 }
482
483 if !assert.Equal(t, base64.RawURLEncoding.EncodeToString(thumbprint), tc.Thumbprint, `thumbprints should match`) {
484 return
485 }
486
487 var key ecdsa.PrivateKey
488 if !assert.NoError(t, webKey.Raw(&key), `jwk.Raw should succeed`) {
489 return
490 }
491
492 msg, err := jwe.ParseString(tc.Data)
493 if !assert.NoError(t, err, `jwe.ParseString should succeed`) {
494 return
495 }
496
497 {
498 buf, _ := json.MarshalIndent(msg, "", " ")
499 t.Logf("%s", buf)
500 }
501
502 decrypted, err := msg.Decrypt(((msg.Recipients())[0]).Headers().Algorithm(), &key)
503 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) {
504 return
505 }
506
507 if !assert.Equal(t, string(decrypted), plaintext, `plaintext should match`) {
508 return
509 }
510 })
511 }
512 }
513
514
515 func TestEncode_Direct(t *testing.T) {
516 var testcases = []struct {
517 Algorithm jwa.ContentEncryptionAlgorithm
518 KeySize int
519 }{
520 {jwa.A128CBC_HS256, 32},
521 {jwa.A128GCM, 16},
522 {jwa.A192CBC_HS384, 48},
523 {jwa.A192GCM, 24},
524 {jwa.A256CBC_HS512, 64},
525 {jwa.A256GCM, 32},
526 }
527 plaintext := []byte("Lorem ipsum")
528
529 for _, tc := range testcases {
530 tc := tc
531 t.Run(tc.Algorithm.String(), func(t *testing.T) {
532 key := make([]byte, tc.KeySize)
533 _, err := rand.Read(key)
534 if !assert.NoError(t, err, "Key generation succeeds") {
535 return
536 }
537
538 encrypted, err := jwe.Encrypt(plaintext, jwa.DIRECT, key, tc.Algorithm, jwa.NoCompress)
539 if !assert.NoError(t, err, "Encrypt succeeds") {
540 return
541 }
542
543 decrypted, err := jwe.Decrypt(encrypted, jwa.DIRECT, key)
544 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) {
545 return
546 }
547
548 assert.Equal(t, plaintext, decrypted, `jwe.Decrypt should match input plaintext`)
549 })
550 }
551 }
552
553
554 func TestDecodePredefined_Direct(t *testing.T) {
555 var testcases = []struct {
556 Algorithm jwa.ContentEncryptionAlgorithm
557 Key string
558 Thumbprint string
559 Data string
560 }{
561 {
562 jwa.A128CBC_HS256,
563 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
564 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
565 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
566 },
567 {
568 jwa.A128GCM,
569 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
570 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
571 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
572 },
573 {
574 jwa.A192CBC_HS384,
575 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
576 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
577 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
578 },
579 {
580 jwa.A192GCM,
581 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
582 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
583 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
584 },
585 {
586 jwa.A256CBC_HS512,
587 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
588 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
589 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
590 },
591 {
592 jwa.A256GCM,
593 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`,
594 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`,
595 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`,
596 },
597 }
598 plaintext := "Lorem ipsum"
599
600 for _, tc := range testcases {
601 tc := tc
602 t.Run(tc.Algorithm.String(), func(t *testing.T) {
603 webKey, err := jwk.ParseKey([]byte(tc.Key))
604 if !assert.NoError(t, err, `jwk.ParseKey should succeed`) {
605 return
606 }
607
608 thumbprint, err := webKey.Thumbprint(crypto.SHA1)
609 if !assert.NoError(t, err, `jwk.Thumbprint should succeed`) {
610 return
611 }
612
613 if !assert.Equal(t, base64.RawURLEncoding.EncodeToString(thumbprint), tc.Thumbprint, `thumbprints should match`) {
614 return
615 }
616
617 var key []byte
618 if !assert.NoError(t, webKey.Raw(&key), `jwk.Raw should succeed`) {
619 return
620 }
621
622 msg, err := jwe.ParseString(tc.Data)
623 if !assert.NoError(t, err, `jwe.ParseString should succeed`) {
624 return
625 }
626
627 {
628 buf, _ := json.MarshalIndent(msg, "", " ")
629 t.Logf("%s", buf)
630 }
631
632 decrypted, err := msg.Decrypt(jwa.DIRECT, key)
633 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) {
634 return
635 }
636
637 if !assert.Equal(t, plaintext, string(decrypted), `plaintext should match`) {
638 return
639 }
640 })
641 }
642 }
643
644 func TestGHIssue230(t *testing.T) {
645 t.Parallel()
646
647 const data = `{"ciphertext":"wko","encrypted_key":"","iv":"y-wj7nfa-T8XG58z","protected":"eyJhbGciOiJkaXIiLCJjbGV2aXMiOnsicGluIjoidHBtMiIsInRwbTIiOnsiaGFzaCI6InNoYTI1NiIsImp3a19wcml2IjoiQU80QUlCSTFRYjQ2SHZXUmNSRHVxRXdoN2ZWc3hSNE91MVhsOHBRX2hMMTlPeUc3QUJDVG80S2RqWEZYcEFUOWtLeWptVVJPOTVBaXc4U1o4MGZXRmtDMGdEazJLTXEtamJTZU1wcFZFaFJaWEpxQmhWNXVGZ1V0T0J4eUFjRzFZRjhFMW5Ob1dPWk9Eek5EUkRrOE1ZVWZrWVNpS0ZKb2pPZ0UxSjRIZkRoM0lBelY2MFR6V2NWcXJ0QnlwX2EyZ1V2a0JqcGpTeVF2Nmc2amJMSXpEaG10VnZLMmxDazhlMjUzdG1MSDNPQWk0Q0tZcWFZY0tjTTltSTdTRXBpVldlSjZZVFBEdmtORndpa0tNRjE3czVYQUlFUjZpczNNTVBpNkZTOWQ3ZmdMV25hUkpabDVNNUJDMldxN2NsVmYiLCJqd2tfcHViIjoiQUM0QUNBQUxBQUFFMGdBQUFCQUFJREpTSVhRSVVocjVPaDVkNXZWaWVGUDBmZG9pVFd3S1RicXJRRVRhVmx4QyIsImtleSI6ImVjYyJ9fSwiZW5jIjoiQTI1NkdDTSJ9","tag":"lir7v9YbCCZQKf5-yJ0BTQ"}`
648
649 msg, err := jwe.Parse([]byte(data))
650 if !assert.NoError(t, err, `jwe.Parse should succeed`) {
651 return
652 }
653
654 compact, err := jwe.Compact(msg)
655 if !assert.NoError(t, err, `jwe.Compact should succeed`) {
656 return
657 }
658
659 msg2, err := jwe.Parse(compact)
660 if !assert.NoError(t, err, `jwe.Parse should succeed`) {
661 return
662 }
663
664 if !assert.Equal(t, msg, msg2, `data -> msg -> compact -> msg2 produces msg == msg2`) {
665 t.Logf("msg -> %#v", msg)
666 t.Logf("msg2 -> %#v", msg2)
667 return
668 }
669 }
670
671 func TestReadFile(t *testing.T) {
672 const s = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`
673 t.Parallel()
674
675 f, err := ioutil.TempFile("", "test-read-file-*.jwe")
676 if !assert.NoError(t, err, `ioutil.TempFile should succeed`) {
677 return
678 }
679 defer f.Close()
680
681 fmt.Fprintf(f, "%s", s)
682
683 if _, err := jwe.ReadFile(f.Name()); !assert.NoError(t, err, `jwe.ReadFile should succeed`) {
684 return
685 }
686 }
687
688 func TestCustomField(t *testing.T) {
689
690 jwe.RegisterCustomField(`x-birthday`, time.Time{})
691 defer jwe.RegisterCustomField(`x-birthday`, nil)
692
693 expected := time.Date(2015, 11, 4, 5, 12, 52, 0, time.UTC)
694 bdaybytes, _ := expected.MarshalText()
695
696 plaintext := []byte("Hello, World!")
697 rsakey, err := jwxtest.GenerateRsaJwk()
698 if !assert.NoError(t, err, `jwxtest.GenerateRsaJwk() should succeed`) {
699 return
700 }
701 pubkey, err := jwk.PublicKeyOf(rsakey)
702 if !assert.NoError(t, err, `jwk.PublicKeyOf() should succeed`) {
703 return
704 }
705
706 protected := jwe.NewHeaders()
707 protected.Set(`x-birthday`, string(bdaybytes))
708
709 encrypted, err := jwe.Encrypt(plaintext, jwa.RSA_OAEP, pubkey, jwa.A256GCM, jwa.NoCompress, jwe.WithProtectedHeaders(protected))
710 if !assert.NoError(t, err, `jwe.Encrypt should succeed`) {
711 return
712 }
713
714 t.Run("jwe.Parse + json.Unmarshal", func(t *testing.T) {
715 msg, err := jwe.Parse(encrypted)
716 if !assert.NoError(t, err, `jwe.Parse should succeed`) {
717 return
718 }
719
720 v, ok := msg.ProtectedHeaders().Get(`x-birthday`)
721 if !assert.True(t, ok, `msg.ProtectedHeaders().Get("x-birthday") should succeed`) {
722 return
723 }
724
725 if !assert.Equal(t, expected, v, `values should match`) {
726 return
727 }
728
729
730 buf, err := json.Marshal(msg)
731 if !assert.NoError(t, err, `json.Marshal should succeed`) {
732 return
733 }
734
735 var msg2 jwe.Message
736 if !assert.NoError(t, json.Unmarshal(buf, &msg2), `json.Unmarshal should succeed`) {
737 return
738 }
739
740 v, ok = msg2.ProtectedHeaders().Get(`x-birthday`)
741 if !assert.True(t, ok, `msg2.ProtectedHeaders().Get("x-birthday") should succeed`) {
742 return
743 }
744
745 if !assert.Equal(t, expected, v, `values should match`) {
746 return
747 }
748 })
749 }
750
751 func TestGH554(t *testing.T) {
752 const keyID = `very-secret-key`
753 const plaintext = `hello world!`
754 privkey, err := jwxtest.GenerateEcdsaJwk()
755 if !assert.NoError(t, err, `jwxtest.GenerateEcdsaJwk() should succeed`) {
756 return
757 }
758
759 _ = privkey.Set(jwk.KeyIDKey, keyID)
760
761 pubkey, err := jwk.PublicKeyOf(privkey)
762 if !assert.NoError(t, err, `jwk.PublicKeyOf() should succeed`) {
763 return
764 }
765
766 if !assert.Equal(t, keyID, pubkey.KeyID(), `key ID should match`) {
767 return
768 }
769
770 encrypted, err := jwe.Encrypt([]byte(plaintext), jwa.ECDH_ES, pubkey, jwa.A256GCM, jwa.NoCompress)
771 if !assert.NoError(t, err, `jwk.Encrypt() should succeed`) {
772 return
773 }
774
775 msg, err := jwe.Parse(encrypted)
776 if !assert.NoError(t, err, `jwe.Parse() should succeed`) {
777 return
778 }
779
780 {
781 buf, _ := json.MarshalIndent(msg, "", " ")
782 t.Logf("%s", buf)
783 }
784
785 recipients := msg.Recipients()
786
787
788 kid := recipients[0].Headers().KeyID()
789 if !assert.Equal(t, keyID, kid, `key ID in epk should match`) {
790 return
791 }
792 }
793
View as plain text