github.com/lestrrat-go/jwx/v2@v2.0.21/jwe/jwe_test.go (about) 1 package jwe_test 2 3 import ( 4 "bytes" 5 "context" 6 "crypto" 7 "crypto/ecdsa" 8 "crypto/elliptic" 9 "crypto/rand" 10 "crypto/rsa" 11 "encoding/base64" 12 "fmt" 13 "os" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/lestrrat-go/jwx/v2/internal/json" 19 "github.com/lestrrat-go/jwx/v2/internal/jwxtest" 20 21 "github.com/lestrrat-go/jwx/v2/jwa" 22 "github.com/lestrrat-go/jwx/v2/jwe" 23 "github.com/lestrrat-go/jwx/v2/jwk" 24 "github.com/lestrrat-go/jwx/v2/x25519" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 const ( 30 examplePayload = `The true sign of intelligence is not knowledge but imagination.` 31 ) 32 33 var rsaPrivKey rsa.PrivateKey 34 35 func init() { 36 var jwkstr = []byte(` 37 {"kty":"RSA", 38 "n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw", 39 "e":"AQAB", 40 "d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ", 41 "p":"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0", 42 "q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc", 43 "dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE", 44 "dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis", 45 "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY" 46 }`) 47 48 privkey, err := jwk.ParseKey(jwkstr) 49 if err != nil { 50 panic(err) 51 } 52 53 if err := privkey.Raw(&rsaPrivKey); err != nil { 54 panic(err) 55 } 56 } 57 58 func TestSanityCheck_JWEExamplePayload(t *testing.T) { 59 expected := []byte{ 60 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32, 61 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99, 62 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108, 63 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105, 64 110, 97, 116, 105, 111, 110, 46, 65 } 66 assert.Equal(t, expected, []byte(examplePayload), "examplePayload OK") 67 } 68 69 func TestParse(t *testing.T) { 70 const s = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ` 71 t.Run("Compact format", func(t *testing.T) { 72 t.Run("Normal", func(t *testing.T) { 73 msg, err := jwe.Parse([]byte(s)) 74 if !assert.NoError(t, err, "Parsing JWE is successful") { 75 return 76 } 77 if !assert.Len(t, msg.Recipients(), 1, "There is exactly 1 recipient") { 78 return 79 } 80 }) 81 82 parts := strings.Split(s, ".") 83 t.Run("Missing parts", func(t *testing.T) { 84 s2 := strings.Join(parts[:4], ".") 85 _, err := jwe.Parse([]byte(s2)) 86 if !assert.Error(t, err, `should fail to parse compact format with missing parts`) { 87 return 88 } 89 }) 90 t.Run("Invalid header", func(t *testing.T) { 91 s2 := strings.Join(append(append([]string(nil), "!!invalidheader!!"), parts[1:]...), ".") 92 _, err := jwe.Parse([]byte(s2)) 93 if !assert.Error(t, err, `should fail to parse compact format with invalid header`) { 94 return 95 } 96 }) 97 t.Run("Invalid encrypted key", func(t *testing.T) { 98 s2 := strings.Join(append(append(append([]string(nil), parts[0]), "!!invalidenckey!!"), parts[2:]...), ".") 99 _, err := jwe.Parse([]byte(s2)) 100 if !assert.Error(t, err, `should fail to parse compact format with invalid encrypted key`) { 101 return 102 } 103 }) 104 t.Run("Invalid initialization vector", func(t *testing.T) { 105 s2 := strings.Join(append(append(append([]string(nil), parts[:2]...), "!!invalidiv!!"), parts[3:]...), ".") 106 _, err := jwe.Parse([]byte(s2)) 107 if !assert.Error(t, err, `should fail to parse compact format with invalid initialization vector`) { 108 return 109 } 110 }) 111 t.Run("Invalid content", func(t *testing.T) { 112 s2 := strings.Join(append(append(append([]string(nil), parts[:3]...), "!!invalidcontent!!"), parts[4:]...), ".") 113 _, err := jwe.Parse([]byte(s2)) 114 if !assert.Error(t, err, `should fail to parse compact format with invalid content`) { 115 return 116 } 117 }) 118 t.Run("Invalid tag", func(t *testing.T) { 119 s2 := strings.Join(append(parts[:4], "!!invalidtag!!"), ".") 120 _, err := jwe.Parse([]byte(s2)) 121 if !assert.Error(t, err, `should fail to parse compact format with invalid tag`) { 122 return 123 } 124 }) 125 }) 126 t.Run("JSON format", func(t *testing.T) { 127 msg, err := jwe.Parse([]byte(s)) 128 if !assert.NoError(t, err, "Parsing JWE is successful") { 129 return 130 } 131 132 buf, err := json.Marshal(msg) 133 if !assert.NoError(t, err, "Serializing to JSON format should succeed") { 134 return 135 } 136 137 msg2, err := jwe.Parse(buf) 138 if !assert.NoError(t, err, "Parsing JWE in JSON format should succeed") { 139 return 140 } 141 142 if !assert.Equal(t, msg, msg2, "messages should match") { 143 return 144 } 145 }) 146 } 147 148 // This test parses the example found in https://tools.ietf.org/html/rfc7516#appendix-A.1, 149 // and checks if we can roundtrip to the same compact serialization format. 150 func TestParse_RSAES_OAEP_AES_GCM(t *testing.T) { 151 const payload = `The true sign of intelligence is not knowledge but imagination.` 152 const serialized = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ` 153 var jwkstr = []byte(` 154 {"kty":"RSA", 155 "n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw", 156 "e":"AQAB", 157 "d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ", 158 "p":"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0", 159 "q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc", 160 "dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE", 161 "dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis", 162 "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY" 163 }`) 164 165 privkey, err := jwk.ParseKey(jwkstr) 166 if !assert.NoError(t, err, `parsing jwk should succeed`) { 167 return 168 } 169 170 var rawkey rsa.PrivateKey 171 if !assert.NoError(t, privkey.Raw(&rawkey), `obtaining raw key should succeed`) { 172 return 173 } 174 175 msg := jwe.NewMessage() 176 plaintext, err := jwe.Decrypt([]byte(serialized), jwe.WithKey(jwa.RSA_OAEP, rawkey), jwe.WithMessage(msg)) 177 if !assert.NoError(t, err, "jwe.Decrypt should be successful") { 178 return 179 } 180 181 if !assert.Equal(t, 1, len(msg.Recipients()), "message recipients header length is 1") { 182 return 183 } 184 185 if !assert.Equal(t, payload, string(plaintext), "decrypted value does not match") { 186 return 187 } 188 189 templates := []*struct { 190 Name string 191 Options []jwe.EncryptOption 192 Expected string 193 }{ 194 { 195 Name: "Compact", 196 Options: []jwe.EncryptOption{jwe.WithCompact()}, 197 Expected: serialized, 198 }, 199 { 200 Name: "JSON", 201 Options: []jwe.EncryptOption{jwe.WithJSON()}, 202 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"}`, 203 }, 204 { 205 Name: "JSON (Pretty)", 206 Options: []jwe.EncryptOption{jwe.WithJSON(jwe.WithPretty(true))}, 207 Expected: `{ 208 "ciphertext": "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A", 209 "iv": "48V1_ALb6US04U3b", 210 "protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ", 211 "header": { 212 "alg": "RSA-OAEP" 213 }, 214 "encrypted_key": "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg", 215 "tag": "XFBoMYUZodetZdvTiFvSkQ" 216 }`, 217 }, 218 } 219 220 ntmpl := len(templates) 221 testcases := make([]struct { 222 Name string 223 Options []jwe.EncryptOption 224 Expected string 225 }, ntmpl*2) 226 227 for i, tmpl := range templates { 228 options := make([]jwe.EncryptOption, len(tmpl.Options)) 229 copy(options, tmpl.Options) 230 231 for j, compression := range []jwa.CompressionAlgorithm{jwa.NoCompress, jwa.Deflate} { 232 compName := compression.String() 233 if compName == "" { 234 compName = "none" 235 } 236 tc := testcases[i+j] 237 tc.Name = tmpl.Name + " (compression=" + compName + ")" 238 tc.Expected = tmpl.Expected 239 tc.Options = append(options, jwe.WithCompress(compression)) 240 } 241 } 242 243 for _, tc := range testcases { 244 tc := tc 245 t.Run(tc.Name, func(t *testing.T) { 246 options := tc.Options 247 options = append(options, jwe.WithKey(jwa.RSA_OAEP, rawkey.PublicKey)) 248 249 encrypted, err := jwe.Encrypt(plaintext, options...) 250 if !assert.NoError(t, err, "jwe.Encrypt should succeed") { 251 return 252 } 253 t.Logf("%s", encrypted) 254 255 t.Run("WithKey", func(t *testing.T) { 256 plaintext, err = jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, rawkey)) 257 if !assert.NoError(t, err, "jwe.Decrypt should succeed") { 258 return 259 } 260 261 if !assert.Equal(t, payload, string(plaintext), "jwe.Decrypt should produce the same plaintext") { 262 return 263 } 264 }) 265 t.Run("WithKeySet", func(t *testing.T) { 266 pkJwk, err := jwk.FromRaw(rawkey) 267 if !assert.NoError(t, err, `jwk.New should succeed`) { 268 return 269 } 270 // Keys are not going to be selected without an algorithm 271 _ = pkJwk.Set(jwe.AlgorithmKey, jwa.RSA_OAEP) 272 set := jwk.NewSet() 273 set.AddKey(pkJwk) 274 275 var used interface{} 276 plaintext, err = jwe.Decrypt(encrypted, jwe.WithKeySet(set, jwe.WithRequireKid(false)), jwe.WithKeyUsed(&used)) 277 if !assert.NoError(t, err, "jwe.Decrypt should succeed") { 278 return 279 } 280 281 if !assert.Equal(t, payload, string(plaintext), "jwe.Decrypt should produce the same plaintext") { 282 return 283 } 284 285 if !assert.Equal(t, pkJwk, used) { 286 return 287 } 288 }) 289 }) 290 } 291 292 // Test direct marshaling and unmarshaling 293 t.Run("Marshal/Unmarshal", func(t *testing.T) { 294 buf, err := json.Marshal(msg) 295 if !assert.NoError(t, err, `json.Marshal should succeed`) { 296 return 297 } 298 299 m2 := jwe.NewMessage() 300 if !assert.NoError(t, json.Unmarshal(buf, m2), `json.Unmarshal should succeed`) { 301 t.Logf("%s", buf) 302 return 303 } 304 305 if !assert.Equal(t, msg, m2, `messages should be the same after roundtrip`) { 306 return 307 } 308 }) 309 } 310 311 // https://tools.ietf.org/html/rfc7516#appendix-A.1. 312 func TestRoundtrip_RSAES_OAEP_AES_GCM(t *testing.T) { 313 var plaintext = []byte{ 314 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32, 315 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99, 316 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108, 317 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105, 318 110, 97, 116, 105, 111, 110, 46, 319 } 320 321 max := 100 322 if testing.Short() { 323 max = 1 324 } 325 326 for i := 0; i < max; i++ { 327 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(jwa.RSA_OAEP, &rsaPrivKey.PublicKey)) 328 if !assert.NoError(t, err, "Encrypt should succeed") { 329 return 330 } 331 332 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, rsaPrivKey)) 333 if !assert.NoError(t, err, "Decrypt should succeed") { 334 return 335 } 336 337 if !assert.Equal(t, plaintext, decrypted, "Decrypted content should match") { 338 return 339 } 340 } 341 } 342 343 func TestRoundtrip_RSA1_5_A128CBC_HS256(t *testing.T) { 344 var plaintext = []byte{ 345 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 346 112, 114, 111, 115, 112, 101, 114, 46, 347 } 348 349 max := 100 350 if testing.Short() { 351 max = 1 352 } 353 354 for i := 0; i < max; i++ { 355 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(jwa.RSA1_5, &rsaPrivKey.PublicKey), jwe.WithContentEncryption(jwa.A128CBC_HS256)) 356 if !assert.NoError(t, err, "Encrypt is successful") { 357 return 358 } 359 360 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA1_5, rsaPrivKey)) 361 if !assert.NoError(t, err, "Decrypt successful") { 362 return 363 } 364 365 if !assert.Equal(t, plaintext, decrypted, "Decrypted correct plaintext") { 366 return 367 } 368 } 369 } 370 371 // https://tools.ietf.org/html/rfc7516#appendix-A.3. Note that cek is dynamically 372 // generated, so the encrypted values will NOT match that of the RFC. 373 func TestEncode_A128KW_A128CBC_HS256(t *testing.T) { 374 var plaintext = []byte{ 375 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 376 112, 114, 111, 115, 112, 101, 114, 46, 377 } 378 var sharedkey = []byte{ 379 25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82, 380 } 381 382 max := 100 383 if testing.Short() { 384 max = 1 385 } 386 387 for i := 0; i < max; i++ { 388 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(jwa.A128KW, sharedkey), jwe.WithContentEncryption(jwa.A128CBC_HS256)) 389 if !assert.NoError(t, err, "Encrypt is successful") { 390 return 391 } 392 393 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.A128KW, sharedkey)) 394 if !assert.NoError(t, err, "Decrypt successful") { 395 return 396 } 397 398 if !assert.Equal(t, plaintext, decrypted, "Decrypted correct plaintext") { 399 return 400 } 401 } 402 } 403 404 //nolint:thelper 405 func testEncodeECDHWithKey(t *testing.T, privkey interface{}, pubkey interface{}) { 406 plaintext := []byte("Lorem ipsum") 407 408 algorithms := []jwa.KeyEncryptionAlgorithm{ 409 jwa.ECDH_ES, 410 jwa.ECDH_ES_A256KW, 411 jwa.ECDH_ES_A192KW, 412 jwa.ECDH_ES_A128KW, 413 } 414 415 for _, alg := range algorithms { 416 alg := alg 417 t.Run(alg.String(), func(t *testing.T) { 418 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(alg, pubkey)) 419 if !assert.NoError(t, err, "Encrypt succeeds") { 420 return 421 } 422 423 _, err = jwe.Parse(encrypted) 424 if !assert.NoError(t, err, `jwe.Parse should succeed`) { 425 return 426 } 427 428 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(alg, privkey)) 429 if !assert.NoError(t, err, "Decrypt succeeds") { 430 return 431 } 432 t.Logf("%s", decrypted) 433 }) 434 } 435 } 436 437 func TestEncode_ECDH(t *testing.T) { 438 curves := []elliptic.Curve{ 439 elliptic.P256(), 440 elliptic.P384(), 441 elliptic.P521(), 442 } 443 for _, crv := range curves { 444 crv := crv 445 t.Run(crv.Params().Name, func(t *testing.T) { 446 privkey, err := ecdsa.GenerateKey(crv, rand.Reader) 447 if !assert.NoError(t, err, `ecdsa.GenerateKey should succeed`) { 448 return 449 } 450 451 testEncodeECDHWithKey(t, privkey, &privkey.PublicKey) 452 }) 453 } 454 } 455 456 func TestEncode_X25519(t *testing.T) { 457 pubkey, privkey, err := x25519.GenerateKey(rand.Reader) 458 if !assert.NoError(t, err, `x25519.GenerateKey should succeed`) { 459 return 460 } 461 462 testEncodeECDHWithKey(t, privkey, pubkey) 463 } 464 465 func Test_GHIssue207(t *testing.T) { 466 const plaintext = "hi\n" 467 var testcases = []struct { 468 Algorithm jwa.KeyEncryptionAlgorithm 469 Key string 470 Data string 471 Thumbprint string 472 Name string 473 }{ 474 { 475 Name: `ECDH-ES`, 476 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"}`, 477 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"}`, 478 Thumbprint: `0_6x6e2sZKeq3ka0QV0PEkJagqg`, 479 }, 480 { 481 Name: `ECDH-ES+A256KW`, 482 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"}`, 483 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"}`, 484 Thumbprint: `G4OtKQL_qr9Q57atNOU6SJnJxB8`, 485 }, 486 } 487 488 for _, tc := range testcases { 489 tc := tc 490 t.Run(tc.Name, func(t *testing.T) { 491 webKey, err := jwk.ParseKey([]byte(tc.Key)) 492 if !assert.NoError(t, err, `jwk.ParseKey should succeed`) { 493 return 494 } 495 496 thumbprint, err := webKey.Thumbprint(crypto.SHA1) 497 if !assert.NoError(t, err, `jwk.Thumbprint should succeed`) { 498 return 499 } 500 501 if !assert.Equal(t, base64.RawURLEncoding.EncodeToString(thumbprint), tc.Thumbprint, `thumbprints should match`) { 502 return 503 } 504 505 var key ecdsa.PrivateKey 506 if !assert.NoError(t, webKey.Raw(&key), `jwk.Raw should succeed`) { 507 return 508 } 509 510 decrypted, err := jwe.Decrypt([]byte(tc.Data), jwe.WithKeyProvider(jwe.KeyProviderFunc(func(_ context.Context, sink jwe.KeySink, r jwe.Recipient, _ *jwe.Message) error { 511 sink.Key(r.Headers().Algorithm(), &key) 512 return nil 513 }))) 514 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) { 515 return 516 } 517 518 if !assert.Equal(t, string(decrypted), plaintext, `plaintext should match`) { 519 return 520 } 521 }) 522 } 523 } 524 525 // tests direct key encryption by encrypting-decrypting a plaintext 526 func TestEncode_Direct(t *testing.T) { 527 var testcases = []struct { 528 Algorithm jwa.ContentEncryptionAlgorithm 529 KeySize int // in bytes 530 }{ 531 {jwa.A128CBC_HS256, 32}, 532 {jwa.A128GCM, 16}, 533 {jwa.A192CBC_HS384, 48}, 534 {jwa.A192GCM, 24}, 535 {jwa.A256CBC_HS512, 64}, 536 {jwa.A256GCM, 32}, 537 } 538 plaintext := []byte("Lorem ipsum") 539 540 for _, tc := range testcases { 541 tc := tc 542 t.Run(tc.Algorithm.String(), func(t *testing.T) { 543 key := make([]byte, tc.KeySize) 544 /* 545 _, err := rand.Read(key) 546 if !assert.NoError(t, err, "Key generation succeeds") { 547 return 548 }*/ 549 for n := 0; n < len(key); { 550 w := copy(key[n:], []byte(`12345678`)) 551 n += w 552 } 553 554 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(jwa.DIRECT, key), jwe.WithContentEncryption(tc.Algorithm)) 555 if !assert.NoError(t, err, `jwe.Encrypt should succeed`) { 556 return 557 } 558 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.DIRECT, key)) 559 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) { 560 return 561 } 562 563 assert.Equal(t, plaintext, decrypted, `jwe.Decrypt should match input plaintext`) 564 }) 565 } 566 } 567 568 // Decrypts messages generated by `jose` tool. It helps check compatibility with other jwx implementations. 569 func TestDecodePredefined_Direct(t *testing.T) { 570 var testcases = []struct { 571 Algorithm jwa.ContentEncryptionAlgorithm 572 Key string // generated with 'jose jwk gen -i '{"alg":"A128GCM"}' -o key.jwk' 573 Thumbprint string // generated with 'jose jwk thp -i key.jwk` 574 Data string // generated with 'jose jwe enc -I msg.txt -k key.jwk -o msg.jwe' 575 }{ 576 { 577 jwa.A128CBC_HS256, 578 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 579 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 580 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 581 }, 582 { 583 jwa.A128GCM, 584 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 585 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 586 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 587 }, 588 { 589 jwa.A192CBC_HS384, 590 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 591 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 592 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 593 }, 594 { 595 jwa.A192GCM, 596 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 597 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 598 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 599 }, 600 { 601 jwa.A256CBC_HS512, 602 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 603 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 604 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 605 }, 606 { 607 jwa.A256GCM, 608 `{"alg":"A128GCM","k":"9hexZKVSV9pZhPNzgXiD8g","key_ops":["encrypt","decrypt"],"kty":"oct"}`, 609 `RwW22IemrIJLFwlqZ-OQUe_Lnbo`, 610 `{"ciphertext":"FX_px9cuyO_hZfo","encrypted_key":"","header":{"alg":"dir"},"iv":"Z9CRJCFPtpEI5Pwq","protected":"eyJlbmMiOiJBMTI4R0NNIn0","tag":"1iq0MNDX40XVtqGYinhUtQ"}`, 611 }, 612 } 613 plaintext := "Lorem ipsum" 614 615 for _, tc := range testcases { 616 tc := tc 617 t.Run(tc.Algorithm.String(), func(t *testing.T) { 618 webKey, err := jwk.ParseKey([]byte(tc.Key)) 619 if !assert.NoError(t, err, `jwk.ParseKey should succeed`) { 620 return 621 } 622 623 thumbprint, err := webKey.Thumbprint(crypto.SHA1) 624 if !assert.NoError(t, err, `jwk.Thumbprint should succeed`) { 625 return 626 } 627 628 if !assert.Equal(t, base64.RawURLEncoding.EncodeToString(thumbprint), tc.Thumbprint, `thumbprints should match`) { 629 return 630 } 631 632 var key []byte 633 if !assert.NoError(t, webKey.Raw(&key), `jwk.Raw should succeed`) { 634 return 635 } 636 637 decrypted, err := jwe.Decrypt([]byte(tc.Data), jwe.WithKey(jwa.DIRECT, key)) 638 if !assert.NoError(t, err, `jwe.Decrypt should succeed`) { 639 return 640 } 641 642 if !assert.Equal(t, plaintext, string(decrypted), `plaintext should match`) { 643 return 644 } 645 }) 646 } 647 } 648 649 func TestGHIssue230(t *testing.T) { 650 t.Parallel() 651 652 const data = `{"ciphertext":"wko","encrypted_key":"","iv":"y-wj7nfa-T8XG58z","protected":"eyJhbGciOiJkaXIiLCJjbGV2aXMiOnsicGluIjoidHBtMiIsInRwbTIiOnsiaGFzaCI6InNoYTI1NiIsImp3a19wcml2IjoiQU80QUlCSTFRYjQ2SHZXUmNSRHVxRXdoN2ZWc3hSNE91MVhsOHBRX2hMMTlPeUc3QUJDVG80S2RqWEZYcEFUOWtLeWptVVJPOTVBaXc4U1o4MGZXRmtDMGdEazJLTXEtamJTZU1wcFZFaFJaWEpxQmhWNXVGZ1V0T0J4eUFjRzFZRjhFMW5Ob1dPWk9Eek5EUkRrOE1ZVWZrWVNpS0ZKb2pPZ0UxSjRIZkRoM0lBelY2MFR6V2NWcXJ0QnlwX2EyZ1V2a0JqcGpTeVF2Nmc2amJMSXpEaG10VnZLMmxDazhlMjUzdG1MSDNPQWk0Q0tZcWFZY0tjTTltSTdTRXBpVldlSjZZVFBEdmtORndpa0tNRjE3czVYQUlFUjZpczNNTVBpNkZTOWQ3ZmdMV25hUkpabDVNNUJDMldxN2NsVmYiLCJqd2tfcHViIjoiQUM0QUNBQUxBQUFFMGdBQUFCQUFJREpTSVhRSVVocjVPaDVkNXZWaWVGUDBmZG9pVFd3S1RicXJRRVRhVmx4QyIsImtleSI6ImVjYyJ9fSwiZW5jIjoiQTI1NkdDTSJ9","tag":"lir7v9YbCCZQKf5-yJ0BTQ"}` 653 654 msg, err := jwe.Parse([]byte(data)) 655 if !assert.NoError(t, err, `jwe.Parse should succeed`) { 656 return 657 } 658 659 compact, err := jwe.Compact(msg) 660 if !assert.NoError(t, err, `jwe.Compact should succeed`) { 661 return 662 } 663 664 msg2, err := jwe.Parse(compact) 665 if !assert.NoError(t, err, `jwe.Parse should succeed`) { 666 return 667 } 668 669 if !assert.Equal(t, msg, msg2, `data -> msg -> compact -> msg2 produces msg == msg2`) { 670 t.Logf("msg -> %#v", msg) 671 t.Logf("msg2 -> %#v", msg2) 672 return 673 } 674 } 675 676 func TestReadFile(t *testing.T) { 677 const s = `eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ` 678 679 f, err := os.CreateTemp("", "test-read-file-*.jwe") 680 if !assert.NoError(t, err, `os.CreateTemp should succeed`) { 681 return 682 } 683 defer f.Close() 684 685 fmt.Fprintf(f, "%s", s) 686 687 if _, err := jwe.ReadFile(f.Name()); !assert.NoError(t, err, `jwe.ReadFile should succeed`) { 688 return 689 } 690 } 691 692 func TestCustomField(t *testing.T) { 693 // XXX has global effect!!! 694 jwe.RegisterCustomField(`x-birthday`, time.Time{}) 695 defer jwe.RegisterCustomField(`x-birthday`, nil) 696 697 expected := time.Date(2015, 11, 4, 5, 12, 52, 0, time.UTC) 698 bdaybytes, _ := expected.MarshalText() // RFC3339 699 700 plaintext := []byte("Hello, World!") 701 rsakey, err := jwxtest.GenerateRsaJwk() 702 if !assert.NoError(t, err, `jwxtest.GenerateRsaJwk() should succeed`) { 703 return 704 } 705 pubkey, err := jwk.PublicKeyOf(rsakey) 706 if !assert.NoError(t, err, `jwk.PublicKeyOf() should succeed`) { 707 return 708 } 709 710 protected := jwe.NewHeaders() 711 protected.Set(`x-birthday`, string(bdaybytes)) 712 713 encrypted, err := jwe.Encrypt(plaintext, jwe.WithKey(jwa.RSA_OAEP, pubkey), jwe.WithProtectedHeaders(protected)) 714 if !assert.NoError(t, err, `jwe.Encrypt should succeed`) { 715 return 716 } 717 718 t.Run("jwe.Parse + json.Unmarshal", func(t *testing.T) { 719 msg, err := jwe.Parse(encrypted) 720 if !assert.NoError(t, err, `jwe.Parse should succeed`) { 721 return 722 } 723 724 v, ok := msg.ProtectedHeaders().Get(`x-birthday`) 725 if !assert.True(t, ok, `msg.ProtectedHeaders().Get("x-birthday") should succeed`) { 726 return 727 } 728 729 if !assert.Equal(t, expected, v, `values should match`) { 730 return 731 } 732 733 // Create JSON from jwe.Message 734 buf, err := json.Marshal(msg) 735 if !assert.NoError(t, err, `json.Marshal should succeed`) { 736 return 737 } 738 739 var msg2 jwe.Message 740 if !assert.NoError(t, json.Unmarshal(buf, &msg2), `json.Unmarshal should succeed`) { 741 return 742 } 743 744 v, ok = msg2.ProtectedHeaders().Get(`x-birthday`) 745 if !assert.True(t, ok, `msg2.ProtectedHeaders().Get("x-birthday") should succeed`) { 746 return 747 } 748 749 if !assert.Equal(t, expected, v, `values should match`) { 750 return 751 } 752 }) 753 } 754 755 func TestGH554(t *testing.T) { 756 const keyID = `very-secret-key` 757 const plaintext = `hello world!` 758 privkey, err := jwxtest.GenerateEcdsaJwk() 759 if !assert.NoError(t, err, `jwxtest.GenerateEcdsaJwk() should succeed`) { 760 return 761 } 762 763 _ = privkey.Set(jwk.KeyIDKey, keyID) 764 765 pubkey, err := jwk.PublicKeyOf(privkey) 766 if !assert.NoError(t, err, `jwk.PublicKeyOf() should succeed`) { 767 return 768 } 769 770 if !assert.Equal(t, keyID, pubkey.KeyID(), `key ID should match`) { 771 return 772 } 773 774 encrypted, err := jwe.Encrypt([]byte(plaintext), jwe.WithKey(jwa.ECDH_ES, pubkey)) 775 if !assert.NoError(t, err, `jwk.Encrypt() should succeed`) { 776 return 777 } 778 779 msg, err := jwe.Parse(encrypted) 780 if !assert.NoError(t, err, `jwe.Parse() should succeed`) { 781 return 782 } 783 784 recipients := msg.Recipients() 785 786 // The epk must have the same key ID as the original 787 kid := recipients[0].Headers().KeyID() 788 if !assert.Equal(t, keyID, kid, `key ID in epk should match`) { 789 return 790 } 791 } 792 793 func TestGH803(t *testing.T) { 794 privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 795 require.NoError(t, err, `ecdsa.GenerateKey should succeed`) 796 797 payload := []byte("Lorem Ipsum") 798 apu := []byte(`Alice`) 799 apv := []byte(`Bob`) 800 hdrs := jwe.NewHeaders() 801 hdrs.Set(jwe.AgreementPartyUInfoKey, apu) 802 hdrs.Set(jwe.AgreementPartyVInfoKey, apv) 803 encrypted, err := jwe.Encrypt( 804 payload, 805 jwe.WithJSON(), 806 jwe.WithKey(jwa.ECDH_ES, privateKey.PublicKey, jwe.WithPerRecipientHeaders(hdrs)), 807 jwe.WithContentEncryption(jwa.A128GCM), 808 ) 809 require.NoError(t, err, `jwe.Encrypt should succeed`) 810 811 var msg jwe.Message 812 decrypted, err := jwe.Decrypt( 813 encrypted, 814 jwe.WithKey(jwa.ECDH_ES, privateKey), 815 jwe.WithMessage(&msg), 816 ) 817 require.NoError(t, err, `jwe.Decrypt should succeed`) 818 require.Equal(t, payload, decrypted, `decrypt messages match`) 819 require.Equal(t, apu, msg.ProtectedHeaders().AgreementPartyUInfo()) 820 require.Equal(t, apv, msg.ProtectedHeaders().AgreementPartyVInfo()) 821 } 822 823 func TestGH840(t *testing.T) { 824 // Go 1.19+ panics if elliptic curve operations are called against 825 // a point that's _NOT_ on the curve 826 untrustedJWK := []byte(`{ 827 "kty": "EC", 828 "crv": "P-256", 829 "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqx7D4", 830 "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 831 "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" 832 }`) 833 834 privkey, err := jwk.ParseKey(untrustedJWK) 835 require.NoError(t, err, `jwk.ParseKey should succeed`) 836 837 pubkey, err := privkey.PublicKey() 838 require.NoError(t, err, `privkey.PublicKey should succeed`) 839 840 const payload = `Lorem ipsum` 841 _, err = jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.ECDH_ES_A128KW, pubkey)) 842 require.Error(t, err, `jwe.Encrypt should fail (instead of panic)`) 843 } 844 845 type dummyKeyEncrypterDecrypter struct { 846 key []byte 847 } 848 849 func (kd *dummyKeyEncrypterDecrypter) DecryptKey(_ jwa.KeyEncryptionAlgorithm, cek []byte, _ jwe.Recipient, _ *jwe.Message) ([]byte, error) { 850 return bytes.TrimSuffix(cek, kd.key), nil 851 } 852 853 func (kd *dummyKeyEncrypterDecrypter) Algorithm() jwa.KeyEncryptionAlgorithm { 854 return jwa.A128GCMKW 855 } 856 857 func (kd *dummyKeyEncrypterDecrypter) EncryptKey(key []byte) ([]byte, error) { 858 return append(key, kd.key...), nil 859 } 860 861 var _ jwe.KeyEncrypter = (*dummyKeyEncrypterDecrypter)(nil) 862 863 func TestGH924(t *testing.T) { 864 sharedKey := []byte("abra-kadabra") 865 866 ked := &dummyKeyEncrypterDecrypter{key: sharedKey} 867 868 payload := []byte("Lorem Ipsum") 869 encrypted, err := jwe.Encrypt( 870 payload, 871 jwe.WithJSON(), 872 jwe.WithKey(jwa.A128GCMKW, ked), 873 jwe.WithContentEncryption(jwa.A128GCM), 874 ) 875 require.NoError(t, err, `jwe.Encrypt should succeed`) 876 877 var msg jwe.Message 878 decrypted, err := jwe.Decrypt( 879 encrypted, 880 jwe.WithKey(jwa.A128GCMKW, ked), 881 jwe.WithMessage(&msg), 882 ) 883 require.NoError(t, err, `jwe.Decrypt should succeed`) 884 require.Equal(t, payload, decrypted, `decrypt messages match`) 885 } 886 887 func TestGH1001(t *testing.T) { 888 rawKey, err := jwxtest.GenerateRsaKey() 889 require.NoError(t, err, `jwxtest.GenerateRsaKey should succeed`) 890 891 encrypted, err := jwe.Encrypt([]byte("Lorem Ipsum"), jwe.WithKey(jwa.RSA_OAEP, rawKey.PublicKey)) 892 require.NoError(t, err, `jwe.Encrypt should succeed`) 893 var cek []byte 894 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, rawKey), jwe.WithCEK(&cek)) 895 require.NoError(t, err, `jwe.Decrypt should succeed`) 896 897 require.Equal(t, "Lorem Ipsum", string(decrypted), `decrypted message should match`) 898 require.NotNil(t, cek, `cek should not be nil`) 899 900 reEncrypted, err := jwe.EncryptStatic([]byte("Lorem Ipsum"), cek, jwe.WithKey(jwa.RSA_OAEP, rawKey.PublicKey)) 901 require.NoError(t, err, `jwe.EncryptStatic should succeed`) 902 903 // sanity. empty CEKs should be rejected 904 _, err = jwe.EncryptStatic([]byte("Lorem Ipsum"), nil, jwe.WithKey(jwa.RSA_OAEP, rawKey.PublicKey)) 905 require.Error(t, err, `jwe.Encryptstatic should fail with empty cek`) 906 907 cek = []byte(nil) 908 decrypted, err = jwe.Decrypt(reEncrypted, jwe.WithKey(jwa.RSA_OAEP, rawKey), jwe.WithCEK(&cek)) 909 require.NoError(t, err, `jwe.Decrypt should succeed`) 910 911 require.Equal(t, "Lorem Ipsum", string(decrypted), `decrypted message should match`) 912 require.NotNil(t, cek, `cek should not be nil`) 913 } 914 915 func TestGHSA_7f9x_gw85_8grf(t *testing.T) { 916 token := []byte("eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjoyMDAwMDAwMDAwLCJwMnMiOiJNNzczSnlmV2xlX2FsSXNrc0NOTU9BIn0=.S8B1kXdIR7BM6i_TaGsgqEOxU-1Sgdakp4mHq7UVhn-_REzOiGz2gg.gU_LfzhBXtQdwYjh.9QUIS-RWkLc.m9TudmzUoCzDhHsGGfzmCA") 917 key, err := jwk.FromRaw([]byte(`abcdefg`)) 918 require.NoError(t, err, `jwk.FromRaw should succeed`) 919 920 { 921 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 922 defer cancel() 923 924 done := make(chan struct{}) 925 go func(t *testing.T, done chan struct{}) { 926 _, err := jwe.Decrypt(token, jwe.WithKey(jwa.PBES2_HS256_A128KW, key)) 927 require.Error(t, err, `jwe.Decrypt should fail`) 928 close(done) 929 }(t, done) 930 931 select { 932 case <-done: 933 case <-ctx.Done(): 934 require.Fail(t, "jwe.Decrypt should not block") 935 } 936 } 937 938 // NOTE: HAS GLOBAL EFFECT 939 // Should allow for timeout to occur 940 jwe.Settings(jwe.WithMaxPBES2Count(100000000000000000)) 941 942 // put it back to normal after the test 943 defer jwe.Settings(jwe.WithMaxPBES2Count(10000)) 944 { 945 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 946 defer cancel() 947 948 done := make(chan struct{}) 949 go func(done chan struct{}) { 950 _, _ = jwe.Decrypt(token, jwe.WithKey(jwa.PBES2_HS256_A128KW, key)) 951 close(done) 952 }(done) 953 954 select { 955 case <-done: 956 require.Fail(t, "jwe.Decrypt should block") 957 case <-ctx.Done(): 958 // timeout occurred as it should 959 } 960 } 961 } 962 963 func TestMaxBufferSize(t *testing.T) { 964 // NOTE: This has GLOBAL EFFECT 965 jwe.Settings(jwe.WithMaxBufferSize(1)) 966 defer jwe.Settings(jwe.WithMaxBufferSize(0)) 967 968 key, err := jwxtest.GenerateRsaJwk() 969 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 970 971 _, err = jwe.Encrypt([]byte("Lorem Ipsum"), jwe.WithContentEncryption(jwa.A128CBC_HS256), jwe.WithKey(jwa.RSA_OAEP, key)) 972 require.Error(t, err, `jwe.Encrypt should fail`) 973 } 974 975 func TestMaxDecompressBufferSize(t *testing.T) { 976 // This payload size is intentionally set to a small value to avoid 977 // causing problems for regular users and CI/CD systems. If you wish to 978 // verify that root issue is fixed, you may want to try increasing the 979 // payload size to a larger value. 980 const payloadSize = 1 << 16 981 982 privkey, err := rsa.GenerateKey(rand.Reader, 2048) 983 require.NoError(t, err, `rsa.GenerateKey should succeed`) 984 985 pubkey := &privkey.PublicKey 986 987 wrongPrivkey, err := rsa.GenerateKey(rand.Reader, 2048) 988 require.NoError(t, err, `rsa.GenerateKey should succeed`) 989 wrongPubkey := &wrongPrivkey.PublicKey 990 991 payload := strings.Repeat("x", payloadSize) 992 993 testcases := []struct { 994 Name string 995 GlobalMaxSize int64 996 PublicKey *rsa.PublicKey 997 Error bool 998 ProcessDecryptOptions func([]jwe.DecryptOption) []jwe.DecryptOption 999 }{ 1000 // This should work, because we set the MaxSize to be large (==payload size) 1001 { 1002 Name: "same as payload size", 1003 GlobalMaxSize: payloadSize, 1004 PublicKey: pubkey, 1005 }, 1006 // This should fail, because we set the GlobalMaxSize to be smaller than the payload size 1007 { 1008 Name: "smaller than payload size", 1009 GlobalMaxSize: payloadSize - 1, 1010 PublicKey: pubkey, 1011 Error: true, 1012 }, 1013 // This should fail, because the public key does not match the 1014 // private key used to decrypt the payload. In essence this way 1015 // we do NOT trigger the root cause of this issue, but we bail out early 1016 { 1017 Name: "Wrong PublicKey", 1018 GlobalMaxSize: payloadSize, 1019 PublicKey: wrongPubkey, 1020 Error: true, 1021 }, 1022 { 1023 Name: "global=payloadSize-1, per-call=payloadSize", 1024 GlobalMaxSize: payloadSize - 1, 1025 PublicKey: pubkey, 1026 ProcessDecryptOptions: func(options []jwe.DecryptOption) []jwe.DecryptOption { 1027 return append(options, jwe.WithMaxDecompressBufferSize(payloadSize)) 1028 }, 1029 }, 1030 // This should be the last test case to put the value back to default :) 1031 { 1032 Name: "Default 10MB globally", 1033 GlobalMaxSize: 10 * 1024 * 1024, 1034 PublicKey: pubkey, 1035 }, 1036 } 1037 for _, tc := range testcases { 1038 tc := tc 1039 t.Run(tc.Name, func(t *testing.T) { 1040 jwe.Settings(jwe.WithMaxDecompressBufferSize(tc.GlobalMaxSize)) 1041 1042 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.RSA_OAEP, tc.PublicKey), jwe.WithContentEncryption("A128CBC-HS256"), jwe.WithCompress(jwa.Deflate)) 1043 1044 require.NoError(t, err, `jwe.Encrypt should succeed`) 1045 1046 decryptOptions := []jwe.DecryptOption{jwe.WithKey(jwa.RSA_OAEP, privkey)} 1047 1048 if fn := tc.ProcessDecryptOptions; fn != nil { 1049 decryptOptions = fn(decryptOptions) 1050 } 1051 _, err = jwe.Decrypt(encrypted, decryptOptions...) 1052 if tc.Error { 1053 require.Error(t, err, `jwe.Decrypt should fail`) 1054 } else { 1055 require.NoError(t, err, `jwe.Decrypt should succeed`) 1056 } 1057 }) 1058 } 1059 }