...

Source file src/github.com/lestrrat-go/jwx/jwe/jwe_test.go

Documentation: github.com/lestrrat-go/jwx/jwe

     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  // This test parses the example found in https://tools.ietf.org/html/rfc7516#appendix-A.1,
   146  // and checks if we can roundtrip to the same compact serialization format.
   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  	// Test direct marshaling and unmarshaling
   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  // https://tools.ietf.org/html/rfc7516#appendix-A.1.
   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  // https://tools.ietf.org/html/rfc7516#appendix-A.3. Note that cek is dynamically
   343  // generated, so the encrypted values will NOT match that of the RFC.
   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  //nolint:thelper
   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  // tests direct key encryption by encrypting-decrypting a plaintext
   515  func TestEncode_Direct(t *testing.T) {
   516  	var testcases = []struct {
   517  		Algorithm jwa.ContentEncryptionAlgorithm
   518  		KeySize   int // in bytes
   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  // Decrypts messages generated by `jose` tool. It helps check compatibility with other jwx implementations.
   554  func TestDecodePredefined_Direct(t *testing.T) {
   555  	var testcases = []struct {
   556  		Algorithm  jwa.ContentEncryptionAlgorithm
   557  		Key        string // generated with 'jose jwk gen -i '{"alg":"A128GCM"}' -o key.jwk'
   558  		Thumbprint string // generated with 'jose jwk thp -i key.jwk`
   559  		Data       string // generated with 'jose jwe enc -I msg.txt -k key.jwk -o msg.jwe'
   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  	// XXX has global effect!!!
   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() // RFC3339
   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  		// Create JSON from jwe.Message
   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  	// The epk must have the same key ID as the original
   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