1/*-
2 * Copyright 2014 Square Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package jose
18
19import (
20	"crypto/x509"
21	"encoding/base64"
22	"strings"
23	"testing"
24
25	"github.com/stretchr/testify/assert"
26)
27
28const trustedCA = `
29-----BEGIN CERTIFICATE-----
30MIIE6DCCAtCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUcnVz
31dGVkQ0EwHhcNMTgwMzI4MTg0MzA0WhcNMzgwMzI4MTg0MzA0WjAUMRIwEAYDVQQD
32EwlUcnVzdGVkQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsHcd3
33uaKBilWQUe2epNf86xvq2HZV+JDULjJlKfUQAkpG+huHDEMiPEFPSlQK17bFj7gc
34qOx/INeeCU2nBVtZDtlm3U0jfQWO2F2kZgH1JWnEArrAWWy3BP/NYv7apBLcl7nD
35hkL4USVUnXF8mtuegiSMI2YT7TVchGzYMjrj/j+oRuDm1GF1OxoIMeUuVmqyJ6jK
36Kxv9YVmCB+e/QaUltkPGwxl2dKWdBwECXDgSr7hcZhT8ANmgFR1dJjLCy0Us12yw
375eKUANDlfNP+z9urykoAwHXpBlmga1ze45aL+p+7K+8sl/PgMqKO7VdT5GBsOCzf
38xaBDG5Qy92Di34Sc27ZZz0mfaIy5kySnceBclMyWb8vdhEGkyHVsGpWc63JBmtg+
39bKeh876m7KVLfiykfpMqHUhq/ImQwiQTwX2RonFK5gP+XU0I9V+4rE0iqucbcvCS
40HuHzhf6B+TybhalRsvOZ6GB/SokF5YCmf8ylAq4be/HSxnJQcBhpSSQp0zz4ZKOD
41ikXuwf29yhWZ0lgIyaZpT9H4QecWNcyx4UcqO3wQAGjxadTG3gzjLu/OJwPkw+bK
42RvXWSBZjlQ9+JPmrHH+oKMgHshR4TQmtmXqXLaarrAe+HXCZEiBKFOqPgeo2RMxr
43LAO+MYIsVtEz39gISRhEvqcAls01sV1l7oGicQIDAQABo0UwQzAOBgNVHQ8BAf8E
44BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUy9Nqk0mDRwC5tcmN
45xQ1YWO5MAhgwDQYJKoZIhvcNAQELBQADggIBAHbpsqY+tPSj8BSky+acZoZjF7fZ
46Ae3MKVogwm5tecmwTKf5xDj9J99ZpGvcWCKtoxxWw0LZ+JI/aeqANSRDXZIelcIw
47yZefw06z/coQJwAIy1RSoKJPV72mkG0Es9w2HxSEoLaZ9tql0TyV8D/QseUM8Yt/
48nNtShRoj6iMnZjhmut5pLfrLWHwQkt4fguBpL7rtydS/wAsOmnJ7lmOrU6zrBJzD
49vEER3AJtgdIt4GvKf4MupKLgKvYDB4sUQVmMyAS78B9+WZDDRTClsx+/Oc1ggkWz
508X7EmIw+3U9V2hd67qZ81EwcSB8ixV06E7ZcbhnJs7ds7swqUjwMArFWuzqO4cjW
512BnyVzCO9pymFLI7qol32xCEgaQlOVS/kFHP3meygfeaeYe902sJw6NevOA4e0AO
52AKR8FDfGRXJ9cOmYzeHeWKex8yt1Ul6+N8SXzjOhf39JM0QqTfHN7pPfFthTAFOs
539rI/buJteJqR1WxgVk/jY4wLGEOcEyO6Y/Uj5iWWTvm5G/C1yZfSg+NvWoytxZ7P
543S0qtEfmT4UwuHBsd5ZfEZoxb+GbqL/nhrKz/0B9LyKS0SJP9+mz7nSORz7t35Uc
55BhiG6T9W7P/NRW4Tqb2tEN1VwU6eP5SEf7c7C1VVaepk0fvc1p5dl67IERqPucPD
56dT2rDsCMBV7SXMUM
57-----END CERTIFICATE-----`
58
59const intermediateCA = `
60-----BEGIN CERTIFICATE-----
61MIIEHTCCAgWgAwIBAgIQXzZsEQv0cvSRLJAkS9FmWTANBgkqhkiG9w0BAQsFADAU
62MRIwEAYDVQQDEwlUcnVzdGVkQ0EwHhcNMTgwMzI4MTg0MzMzWhcNMzgwMzI4MTg0
63MzAzWjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTCCASIwDQYJKoZIhvcNAQEB
64BQADggEPADCCAQoCggEBAN3aYpH/1yEYf/kHuHWyO3AO4tgwlYYLhCDT2GvaPdaE
65cqhe/VuYiqx3xY7IRDqsW2rau/OXgW6KzLHdRZHogK07hUj1Lfr7X+Oqbp22IV4y
66dyiL7jwK9AtVXvDuuv5ET+oRfV82j0uhyk0ueGD9r6C/h+6NTzHBD+3xo6Yuc0Vk
67BfY5zIyhaFqlm1aRYvupDRjC/63uBgAlrGxy2LyiTMVnYMuxoJM5ahDepz3sqjuN
68WVyPhfGwIezjXuXRdEvlmWX05XLnsTdP4zu4fHq9Z7c3TKWWONM3z64ECAZmGQVf
69MAcEDX7qP0gZX5PCT+0WcvTgTWE4Q+WIh5AmYyxQ04cCAwEAAaNmMGQwDgYDVR0P
70AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMAYlq86RZzT
71WxLpYE7KTTM7DHOuMB8GA1UdIwQYMBaAFMvTapNJg0cAubXJjcUNWFjuTAIYMA0G
72CSqGSIb3DQEBCwUAA4ICAQBmYRpQoWEm5g16kwUrpwWrH7OIqqMtUhM1dcskECfk
73i3/hcsV+MQRkGHLIItucYIWqs7oOQIglsyGcohAbnvE1PVtKKojUHC0lfbjgIenD
74Pbvz15QB6A3KLDR82QbQGeGniACy924p66zlfPwHJbkMo5ZaqtNqI//EIa2YCpyy
75okhFXaSFmPWXXrTOCsEEsFJKsoSCH1KUpTcwACGkkilNseg1edZB6/lBDwybxVuY
76+dbUlHip3r5tFcP66Co3tKAaEcVY0AsZ/8GKwH+IM2AR6q7jdn9Gp2OX4E1ul9Wy
77+hW5GHMmfixkgTVwRowuKgkCPEKV2/Xy3k9rlSpnKr2NpYYq0mu6An9HYt8THQ+e
78wGZHwWufuDFDWuzlu7CxFOjpXLKv8qqVnwSFC91S3HsPAzPKLC9ZMEC+iQs2Vkes
79Os0nFLZeMaMGAO5W6xiyQ5p94oo0bqa1XbmSV1bNp1HWuNEGIiZKrEUDxfYuDc6f
80C6hJZKsjJkMkBeadlQAlLcjIx1rDV171CKLLTxy/dT5kv4p9UrJlnleyMVG6S/3d
816nX/WLSgZIMYbOwiZVVPlSrobuG38ULJMCSuxndxD0l+HahJaH8vYXuR67A0XT+b
82TEe305AI6A/9MEaRrActBnq6/OviQgBsKAvtTv1FmDbnpZsKeoFuwc3OPdTveQdC
83RA==
84-----END CERTIFICATE-----`
85
86func TestEmbeddedHMAC(t *testing.T) {
87	// protected: {"alg":"HS256", "jwk":{"kty":"oct", "k":"MTEx"}}, aka HMAC key.
88	msg := `{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ","protected":"eyJhbGciOiJIUzI1NiIsICJqd2siOnsia3R5Ijoib2N0IiwgImsiOiJNVEV4In19","signature":"lvo41ZZsuHwQvSh0uJtEXRR3vmuBJ7in6qMoD7p9jyo"}`
89
90	_, err := ParseSigned(msg)
91	if err == nil {
92		t.Error("should not allow parsing JWS with embedded JWK with HMAC key")
93	}
94}
95
96func TestCompactParseJWS(t *testing.T) {
97	// Should parse
98	msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl"
99	_, err := ParseSigned(msg)
100	if err != nil {
101		t.Error("Unable to parse valid message:", err)
102	}
103
104	// Should parse (detached signature missing payload)
105	msg = "eyJhbGciOiJYWVoifQ..c2lnbmF0dXJl"
106	_, err = ParseSigned(msg)
107	if err != nil {
108		t.Error("Unable to parse valid message:", err)
109	}
110
111	// Messages that should fail to parse
112	failures := []string{
113		// Not enough parts
114		"eyJhbGciOiJYWVoifQ.cGF5bG9hZA",
115		// Invalid signature
116		"eyJhbGciOiJYWVoifQ.cGF5bG9hZA.////",
117		// Invalid payload
118		"eyJhbGciOiJYWVoifQ.////.c2lnbmF0dXJl",
119		// Invalid header
120		"////.eyJhbGciOiJYWVoifQ.c2lnbmF0dXJl",
121		// Invalid header
122		"cGF5bG9hZA.cGF5bG9hZA.c2lnbmF0dXJl",
123	}
124
125	for i := range failures {
126		_, err = ParseSigned(failures[i])
127		if err == nil {
128			t.Error("Able to parse invalid message")
129		}
130	}
131}
132
133func TestFullParseJWS(t *testing.T) {
134	// Messages that should succeed to parse
135	successes := []string{
136		"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"},{\"protected\":\"e30\",\"signature\":\"CUJD\"}]}",
137	}
138
139	for i := range successes {
140		_, err := ParseSigned(successes[i])
141		if err != nil {
142			t.Error("Unble to parse valid message", err, successes[i])
143		}
144	}
145
146	// Messages that should fail to parse
147	failures := []string{
148		// Empty
149		"{}",
150		// Invalid JSON
151		"{XX",
152		// Invalid protected header
153		"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
154		// Invalid protected header
155		"{\"payload\":\"CUJD\",\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}",
156		// Invalid protected header
157		"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"###\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
158		// Invalid payload
159		"{\"payload\":\"###\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
160		// Invalid payload
161		"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"###\"}]}",
162	}
163
164	for i := range failures {
165		_, err := ParseSigned(failures[i])
166		if err == nil {
167			t.Error("Able to parse invalid message", err, failures[i])
168		}
169	}
170}
171
172func TestRejectUnprotectedJWSNonce(t *testing.T) {
173	// No need to test compact, since that's always protected
174
175	// Flattened JSON
176	input := `{
177		"header": { "nonce": "should-cause-an-error" },
178		"payload": "does-not-matter",
179		"signature": "does-not-matter"
180	}`
181	_, err := ParseSigned(input)
182	if err == nil {
183		t.Error("JWS with an unprotected nonce parsed as valid.")
184	} else if err != ErrUnprotectedNonce {
185		t.Errorf("Improper error for unprotected nonce: %v", err)
186	}
187
188	// Full JSON
189	input = `{
190		"payload": "does-not-matter",
191 		"signatures": [{
192 			"header": { "nonce": "should-cause-an-error" },
193			"signature": "does-not-matter"
194		}]
195	}`
196	_, err = ParseSigned(input)
197	if err == nil {
198		t.Error("JWS with an unprotected nonce parsed as valid.")
199	} else if err != ErrUnprotectedNonce {
200		t.Errorf("Improper error for unprotected nonce: %v", err)
201	}
202}
203
204func TestVerifyFlattenedWithIncludedUnprotectedKey(t *testing.T) {
205	input := `{
206			"header": {
207					"alg": "RS256",
208					"jwk": {
209							"e": "AQAB",
210							"kty": "RSA",
211							"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
212					}
213			},
214			"payload": "Zm9vCg",
215			"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
216	}`
217
218	jws, err := ParseSigned(input)
219	if err != nil {
220		t.Fatal("Unable to parse valid message", err)
221	}
222	if len(jws.Signatures) != 1 {
223		t.Error("Too many or too few signatures.")
224	}
225	sig := jws.Signatures[0]
226	if sig.Header.JSONWebKey == nil {
227		t.Error("No JWK in signature header.")
228	}
229	payload, err := jws.Verify(sig.Header.JSONWebKey)
230	if err != nil {
231		t.Errorf("Signature did not validate: %v", err)
232	}
233	if string(payload) != "foo\n" {
234		t.Errorf("Payload was incorrect: '%s' should have been 'foo\\n'", string(payload))
235	}
236}
237
238// Test verification of a detached signature
239func TestDetachedVerifyJWS(t *testing.T) {
240	rsaPublicKey, err := x509.ParsePKIXPublicKey(fromBase64Bytes(`
241		MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO
242		91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ
243		t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd
244		NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6
245		CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G
246		zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`))
247	if err != nil {
248		t.Fatal(err)
249	}
250
251	sampleMessages := []string{
252		"eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA",
253		"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
254	}
255
256	for _, msg := range sampleMessages {
257		obj, err := ParseSigned(msg)
258		if err != nil {
259			t.Error("unable to parse message", msg, err)
260			continue
261		}
262		payload := obj.payload
263		obj.payload = nil
264		err = obj.DetachedVerify(payload, rsaPublicKey)
265		if err != nil {
266			t.Error("unable to verify message", msg, err)
267			continue
268		}
269		idx, _, err := obj.DetachedVerifyMulti(payload, rsaPublicKey)
270		if idx != 0 || err != nil {
271			t.Error("unable to verify message", msg, err)
272			continue
273		}
274	}
275}
276
277func TestVerifyFlattenedWithPrivateProtected(t *testing.T) {
278	// The protected field contains a Private Header Parameter name, per
279	// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
280	// Base64-decoded, it's '{"nonce":"8HIepUNFZUa-exKTrXVf4g"}'
281	input := `{"header":{"alg":"RS256","jwk":{"kty":"RSA","n":"7ixeydcbxxppzxrBphrW1atUiEZqTpiHDpI-79olav5XxAgWolHmVsJyxzoZXRxmtED8PF9-EICZWBGdSAL9ZTD0hLUCIsPcpdgT_LqNW3Sh2b2caPL2hbMF7vsXvnCGg9varpnHWuYTyRrCLUF9vM7ES-V3VCYTa7LcCSRm56Gg9r19qar43Z9kIKBBxpgt723v2cC4bmLmoAX2s217ou3uCpCXGLOeV_BesG4--Nl3pso1VhCfO85wEWjmW6lbv7Kg4d7Jdkv5DjDZfJ086fkEAYZVYGRpIgAvJBH3d3yKDCrSByUEud1bWuFjQBmMaeYOrVDXO_mbYg5PwUDMhw","e":"AQAB"}},"protected":"eyJub25jZSI6IjhISWVwVU5GWlVhLWV4S1RyWFZmNGcifQ","payload":"eyJjb250YWN0IjpbIm1haWx0bzpmb29AYmFyLmNvbSJdfQ","signature":"AyvVGMgXsQ1zTdXrZxE_gyO63pQgotL1KbI7gv6Wi8I7NRy0iAOkDAkWcTQT9pcCYApJ04lXfEDZfP5i0XgcFUm_6spxi5mFBZU-NemKcvK9dUiAbXvb4hB3GnaZtZiuVnMQUb_ku4DOaFFKbteA6gOYCnED_x7v0kAPHIYrQnvIa-KZ6pTajbV9348zgh9TL7NgGIIsTcMHd-Jatr4z1LQ0ubGa8tS300hoDhVzfoDQaEetYjCo1drR1RmdEN1SIzXdHOHfubjA3ZZRbrF_AJnNKpRRoIwzu1VayOhRmdy1qVSQZq_tENF4VrQFycEL7DhG7JLoXC4T2p1urwMlsw"}`
282
283	jws, err := ParseSigned(input)
284	if err != nil {
285		t.Error("Unable to parse valid message.")
286	}
287	if len(jws.Signatures) != 1 {
288		t.Error("Too many or too few signatures.")
289	}
290	sig := jws.Signatures[0]
291	if sig.Header.JSONWebKey == nil {
292		t.Error("No JWK in signature header.")
293	}
294	payload, err := jws.Verify(sig.Header.JSONWebKey)
295	if err != nil {
296		t.Errorf("Signature did not validate: %v", err)
297	}
298	expected := "{\"contact\":[\"mailto:foo@bar.com\"]}"
299	if string(payload) != expected {
300		t.Errorf("Payload was incorrect: '%s' should have been '%s'", string(payload), expected)
301	}
302}
303
304// Test vectors generated with nimbus-jose-jwt
305func TestSampleNimbusJWSMessagesRSA(t *testing.T) {
306	rsaPublicKey, err := x509.ParsePKIXPublicKey(fromBase64Bytes(`
307		MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO
308		91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ
309		t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd
310		NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6
311		CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G
312		zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`))
313	if err != nil {
314		panic(err)
315	}
316
317	rsaSampleMessages := []string{
318		"eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA",
319		"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
320		"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
321		"eyJhbGciOiJQUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.UTtxjsv_6x4CdlAmZfAW6Lun3byMjJbcwRp_OlPH2W4MZaZar7aql052mIB_ddK45O9VUz2aphYVRvKPZY8WHmvlTUU30bk0z_cDJRYB9eIJVMOiRCYj0oNkz1iEZqsP0YgngxwuUDv4Q4A6aJ0Bo5E_rZo3AnrVHMHUjPp_ZRRSBFs30tQma1qQ0ApK4Gxk0XYCYAcxIv99e78vldVRaGzjEZmQeAVZx4tGcqZP20vG1L84nlhSGnOuZ0FhR8UjRFLXuob6M7EqtMRoqPgRYw47EI3fYBdeSivAg98E5S8R7R1NJc7ef-l03RvfUSY0S3_zBq_4PlHK6A-2kHb__w",
322		"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
323		"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
324	}
325
326	for _, msg := range rsaSampleMessages {
327		obj, err := ParseSigned(msg)
328		if err != nil {
329			t.Error("unable to parse message", msg, err)
330			continue
331		}
332		payload, err := obj.Verify(rsaPublicKey)
333		if err != nil {
334			t.Error("unable to verify message", msg, err)
335			continue
336		}
337		if string(payload) != "Lorem ipsum dolor sit amet" {
338			t.Error("payload is not what we expected for msg", msg)
339		}
340	}
341}
342
343// Test vectors generated with nimbus-jose-jwt
344func TestSampleNimbusJWSMessagesEC(t *testing.T) {
345	ecPublicKeyP256, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIg62jq6FyL1otEj9Up7S35BUrwGF9TVrAzrrY1rHUKZqYIGEg67u/imjgadVcr7y9Q32I0gB8W8FHqbqt696rA=="))
346	if err != nil {
347		panic(err)
348	}
349	ecPublicKeyP384, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPXsVlqCtN2oTY+F+hFZm3M0ldYpb7IeeJM5wYmT0k1RaqzBFDhDMNnYK5Q5x+OyssZrAtHgYDFw02AVJhhng/eHRp7mqmL/vI3wbxJtrLKYldIbBA+9fYBQcKeibjlu5"))
350	if err != nil {
351		panic(err)
352	}
353	ecPublicKeyP521, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAa2w3MMJ5FWD6tSf68G+Wy5jIhWXOD3IA7pE5IC/myQzo1lWcD8KS57SM6nm4POtPcxyLmDhL7FLuh8DKoIZyvtAAdK8+tOQP7XXRlT2bkvzIuazp05It3TAPu00YzTIpKfDlc19Y1lvf7etrbFqhShD92B+hHmhT4ddrdbPCBDW8hvU="))
354	if err != nil {
355		panic(err)
356	}
357
358	ecPublicKeys := []interface{}{ecPublicKeyP256, ecPublicKeyP384, ecPublicKeyP521}
359
360	ecSampleMessages := []string{
361		"eyJhbGciOiJFUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.MEWJVlvGRQyzMEGOYm4rwuiwxrX-6LjnlbaRDAuhwmnBm2Gtn7pRpGXRTMFZUXsSGDz2L1p-Hz1qn8j9bFIBtQ",
362		"eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.nbdjPnJPYQtVNNdBIx8-KbFKplTxrz-hnW5UNhYUY7SBkwHK4NZnqc2Lv4DXoA0aWHq9eiypgOh1kmyPWGEmqKAHUx0xdIEkBoHk3ZsbmhOQuq2jL_wcMUG6nTWNhLrB",
363		"eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g",
364	}
365
366	for i, msg := range ecSampleMessages {
367		obj, err := ParseSigned(msg)
368		if err != nil {
369			t.Error("unable to parse message", msg, err)
370			continue
371		}
372		payload, err := obj.Verify(ecPublicKeys[i])
373		if err != nil {
374			t.Error("unable to verify message", msg, err)
375			continue
376		}
377		if string(payload) != "Lorem ipsum dolor sit amet" {
378			t.Error("payload is not what we expected for msg", msg)
379		}
380	}
381}
382
383// Test vectors generated with nimbus-jose-jwt
384func TestSampleNimbusJWSMessagesHMAC(t *testing.T) {
385	hmacTestKey := fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D")
386
387	hmacSampleMessages := []string{
388		"eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM",
389		"eyJhbGciOiJIUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.sBu44lXOJa4Nd10oqOdYH2uz3lxlZ6o32QSGHaoGdPtYTDG5zvSja6N48CXKqdAh",
390		"eyJhbGciOiJIUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.M0yR4tmipsORIix-BitIbxEPGaxPchDfj8UNOpKuhDEfnb7URjGvCKn4nOlyQ1z9mG1FKbwnqR1hOVAWSzAU_w",
391	}
392
393	for _, msg := range hmacSampleMessages {
394		obj, err := ParseSigned(msg)
395		if err != nil {
396			t.Error("unable to parse message", msg, err)
397			continue
398		}
399		payload, err := obj.Verify(hmacTestKey)
400		if err != nil {
401			t.Error("unable to verify message", msg, err)
402			continue
403		}
404		if string(payload) != "Lorem ipsum dolor sit amet" {
405			t.Error("payload is not what we expected for msg", msg)
406		}
407	}
408}
409
410func TestHeaderFieldsCompact(t *testing.T) {
411	msg := "eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g"
412
413	obj, err := ParseSigned(msg)
414	if err != nil {
415		t.Fatal("unable to parse message", msg, err)
416	}
417	if obj.Signatures[0].Header.Algorithm != "ES512" {
418		t.Error("merged header did not contain expected alg value")
419	}
420	if obj.Signatures[0].Protected.Algorithm != "ES512" {
421		t.Error("protected header did not contain expected alg value")
422	}
423	if obj.Signatures[0].Unprotected.Algorithm != "" {
424		t.Error("unprotected header contained an alg value")
425	}
426}
427
428func TestHeaderFieldsFull(t *testing.T) {
429	msg := `{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ","protected":"eyJhbGciOiJFUzUxMiJ9","header":{"custom":"test"},"signature":"AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g"}`
430
431	obj, err := ParseSigned(msg)
432	if err != nil {
433		t.Fatal("unable to parse message", msg, err)
434	}
435	if obj.Signatures[0].Header.Algorithm != "ES512" {
436		t.Error("merged header did not contain expected alg value")
437	}
438	if obj.Signatures[0].Protected.Algorithm != "ES512" {
439		t.Error("protected header did not contain expected alg value")
440	}
441	if obj.Signatures[0].Unprotected.Algorithm != "" {
442		t.Error("unprotected header contained an alg value")
443	}
444	if obj.Signatures[0].Unprotected.ExtraHeaders["custom"] != "test" {
445		t.Error("unprotected header did not contain custom header value")
446	}
447}
448
449// Test vectors generated with nimbus-jose-jwt
450func TestErrorMissingPayloadJWS(t *testing.T) {
451	_, err := (&rawJSONWebSignature{}).sanitized()
452	if err == nil {
453		t.Error("was able to parse message with missing payload")
454	}
455	if !strings.Contains(err.Error(), "missing payload") {
456		t.Errorf("unexpected error message, should contain 'missing payload': %s", err)
457	}
458}
459
460// Test that a null value in the header doesn't panic
461func TestNullHeaderValue(t *testing.T) {
462	msg := `{
463   "payload":
464    "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF
465     tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
466   "protected":"eyJhbGciOiJFUzI1NiIsIm5vbmNlIjpudWxsfQ",
467   "header":
468    {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
469   "signature":
470    "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS
471     lSApmWQxfKTUJqPP3-Kg6NU1Q"
472  }`
473
474	defer func() {
475		if r := recover(); r != nil {
476			t.Errorf("ParseSigned panic'd when parsing a message with a null protected header value")
477		}
478	}()
479	ParseSigned(msg)
480}
481
482// Test for bug:
483// https://github.com/square/go-jose/issues/157
484func TestEmbedJWKBug(t *testing.T) {
485	signerKey := SigningKey{
486		Key: &JSONWebKey{
487			Key:   rsaTestKey,
488			KeyID: "rsa-test-key",
489		},
490		Algorithm: RS256,
491	}
492
493	signer, err := NewSigner(signerKey, &SignerOptions{EmbedJWK: true})
494	if err != nil {
495		t.Fatal(err)
496	}
497
498	signerNoEmbed, err := NewSigner(signerKey, &SignerOptions{EmbedJWK: false})
499	if err != nil {
500		t.Fatal(err)
501	}
502
503	jws, err := signer.Sign([]byte("Lorem ipsum dolor sit amet"))
504	if err != nil {
505		t.Fatal(err)
506	}
507
508	jwsNoEmbed, err := signerNoEmbed.Sign([]byte("Lorem ipsum dolor sit amet"))
509	if err != nil {
510		t.Fatal(err)
511	}
512
513	// This used to panic with:
514	// json: error calling MarshalJSON for type *jose.JSONWebKey: square/go-jose: unknown key type '%!s(<nil>)'
515	output := jws.FullSerialize()
516	outputNoEmbed := jwsNoEmbed.FullSerialize()
517
518	// Expected output with embed set to true is a JWS with the public JWK embedded, with kid header empty.
519	// Expected output with embed set to false is that we set the kid header for key identification instead.
520	parsed, err := ParseSigned(output)
521	if err != nil {
522		t.Fatal(err)
523	}
524
525	parsedNoEmbed, err := ParseSigned(outputNoEmbed)
526	if err != nil {
527		t.Fatal(err)
528	}
529
530	if parsed.Signatures[0].Header.KeyID != "" {
531		t.Error("expected kid field in protected header to be empty")
532	}
533	if parsed.Signatures[0].Header.JSONWebKey.KeyID != "rsa-test-key" {
534		t.Error("expected rsa-test-key to be kid in embedded JWK in protected header")
535	}
536	if parsedNoEmbed.Signatures[0].Header.KeyID != "rsa-test-key" {
537		t.Error("expected kid field in protected header to be rsa-test-key")
538	}
539	if parsedNoEmbed.Signatures[0].Header.JSONWebKey != nil {
540		t.Error("expected no embedded JWK to be present")
541	}
542}
543
544func TestJWSWithCertificateChain(t *testing.T) {
545	signerKey := SigningKey{
546		Key:       rsaTestKey,
547		Algorithm: RS256,
548	}
549
550	certs := []string{
551		// CN=TrustedSigner, signed by IntermediateCA
552		"MIIDLDCCAhSgAwIBAgIQNsV1i7m3kXGugqOQuuC7FzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTAeFw0xODAzMjgxODQzNDlaFw0zODAzMjgxODQzMDJaMBgxFjAUBgNVBAMTDVRydXN0ZWRTaWduZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLpvmOEDRxzQJUKHLkLQSsFDo9eGnolSERa6fz1E1F4wmk6nieHqssPd28C6Vb1sHJFne/j93DXNrx7W9Gy9fQvWa+VNHfGuYAodaS2pyV4VUPWMXI2a+qjxW85orq34XtcHzU+qm+ekR5W06ypW+xewbXJW//P9ulrsv3bDoDFaiggHY/u3p5CRSB9mg+Pbpf6E/k/N85sFJUsRE9hzgwg27Kqhh6p3hP3QnA+0WZRcWhwG0gykoD6layRLCPVcxlTSUdpyStDiK8w2whLJQfixCBGLS3/tB/GKb726bxTQK72OLzIMtOo4ZMtTva7bcA2PRgwfRz7bJg4DXz7oHTAgMBAAGjcTBvMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFCpZEyJGAyK//NsYSSC4xkOqNnh3MB8GA1UdIwQYMBaAFMAYlq86RZzTWxLpYE7KTTM7DHOuMA0GCSqGSIb3DQEBCwUAA4IBAQBSIln6jPFkctPC17le0O+wkCStFOuqUM9cjwPuj4xBQ47RxmC0Pjv52N3TuVH7slmMykITQO/vVqQZguf+N5u4BCh223qWiu1muYBTfBPXCPgJjJ79bUL/dy9QEocOfPiIqTFC6xHKeSUCu6qi5jCPFynOaoVvlNPZEb2MR+QrkKVzg09aDEfk6J+wE6eH9+kNOtwvd/z2a2t2hterURtJEnYt7AQGviEpUf1gbHxCE9f3FW5iJGdgcshrk5ZwUfxvND2x4qFq2fYQRxNBnkO+TSYzwYoAItcGAUvlZFH+rdsq3N+UpRptXRkj5iMq59VlcXFOT675EkkNREgromWn",
553		// CN=IntermediateCA, signed by TrustedCA
554		"MIIEHTCCAgWgAwIBAgIQXzZsEQv0cvSRLJAkS9FmWTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUcnVzdGVkQ0EwHhcNMTgwMzI4MTg0MzMzWhcNMzgwMzI4MTg0MzAzWjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3aYpH/1yEYf/kHuHWyO3AO4tgwlYYLhCDT2GvaPdaEcqhe/VuYiqx3xY7IRDqsW2rau/OXgW6KzLHdRZHogK07hUj1Lfr7X+Oqbp22IV4ydyiL7jwK9AtVXvDuuv5ET+oRfV82j0uhyk0ueGD9r6C/h+6NTzHBD+3xo6Yuc0VkBfY5zIyhaFqlm1aRYvupDRjC/63uBgAlrGxy2LyiTMVnYMuxoJM5ahDepz3sqjuNWVyPhfGwIezjXuXRdEvlmWX05XLnsTdP4zu4fHq9Z7c3TKWWONM3z64ECAZmGQVfMAcEDX7qP0gZX5PCT+0WcvTgTWE4Q+WIh5AmYyxQ04cCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMAYlq86RZzTWxLpYE7KTTM7DHOuMB8GA1UdIwQYMBaAFMvTapNJg0cAubXJjcUNWFjuTAIYMA0GCSqGSIb3DQEBCwUAA4ICAQBmYRpQoWEm5g16kwUrpwWrH7OIqqMtUhM1dcskECfki3/hcsV+MQRkGHLIItucYIWqs7oOQIglsyGcohAbnvE1PVtKKojUHC0lfbjgIenDPbvz15QB6A3KLDR82QbQGeGniACy924p66zlfPwHJbkMo5ZaqtNqI//EIa2YCpyyokhFXaSFmPWXXrTOCsEEsFJKsoSCH1KUpTcwACGkkilNseg1edZB6/lBDwybxVuY+dbUlHip3r5tFcP66Co3tKAaEcVY0AsZ/8GKwH+IM2AR6q7jdn9Gp2OX4E1ul9Wy+hW5GHMmfixkgTVwRowuKgkCPEKV2/Xy3k9rlSpnKr2NpYYq0mu6An9HYt8THQ+ewGZHwWufuDFDWuzlu7CxFOjpXLKv8qqVnwSFC91S3HsPAzPKLC9ZMEC+iQs2VkesOs0nFLZeMaMGAO5W6xiyQ5p94oo0bqa1XbmSV1bNp1HWuNEGIiZKrEUDxfYuDc6fC6hJZKsjJkMkBeadlQAlLcjIx1rDV171CKLLTxy/dT5kv4p9UrJlnleyMVG6S/3d6nX/WLSgZIMYbOwiZVVPlSrobuG38ULJMCSuxndxD0l+HahJaH8vYXuR67A0XT+bTEe305AI6A/9MEaRrActBnq6/OviQgBsKAvtTv1FmDbnpZsKeoFuwc3OPdTveQdCRA==",
555	}
556
557	testCases := []struct {
558		// Cert chain to embed in message
559		chain []string
560		// Intermediates & root certificate to verify against
561		intermediates []string
562		root          string
563		// Should this test case verify?
564		success bool
565	}{
566		{certs, nil, trustedCA, true},
567		{certs, []string{intermediateCA}, trustedCA, true},
568		{certs[0:1], nil, intermediateCA, true},
569		{certs[0:1], nil, trustedCA, false},
570		{[]string{}, nil, trustedCA, false},
571	}
572
573	for i, testCase := range testCases {
574		signer, err := NewSigner(signerKey, &SignerOptions{
575			ExtraHeaders: map[HeaderKey]interface{}{HeaderKey("x5c"): testCase.chain},
576		})
577		if err != nil {
578			t.Fatal(err)
579		}
580
581		signed, err := signer.Sign([]byte("Lorem ipsum dolor sit amet"))
582		if err != nil {
583			t.Fatal(err)
584		}
585
586		parsed, err := ParseSigned(signed.FullSerialize())
587		if err != nil {
588			t.Fatal(err)
589		}
590
591		opts := x509.VerifyOptions{
592			DNSName: "TrustedSigner",
593			Roots:   x509.NewCertPool(),
594		}
595
596		ok := opts.Roots.AppendCertsFromPEM([]byte(testCase.root))
597		if !ok {
598			t.Fatal("failed to parse trusted root certificate")
599		}
600
601		if len(testCase.intermediates) > 0 {
602			opts.Intermediates = x509.NewCertPool()
603			for _, intermediate := range testCase.intermediates {
604				ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate))
605				if !ok {
606					t.Fatal("failed to parse trusted root certificate")
607				}
608			}
609		}
610
611		chains, err := parsed.Signatures[0].Protected.Certificates(opts)
612		if testCase.success && (len(chains) == 0 || err != nil) {
613			t.Fatalf("failed to verify certificate chain for test case %d: %s", i, err)
614		}
615		if !testCase.success && (len(chains) != 0 && err == nil) {
616			t.Fatalf("incorrectly verified certificate chain for test case %d (should fail)", i)
617		}
618	}
619}
620
621func TestDetachedCompactSerialization(t *testing.T) {
622	msg := "eyJhbGciOiJSUzI1NiJ9.JC4wMg.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM"
623	exp := "eyJhbGciOiJSUzI1NiJ9..W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM"
624
625	obj, err := ParseSigned(msg)
626	if err != nil {
627		t.Fatal(err)
628	}
629
630	ser, err := obj.DetachedCompactSerialize()
631	if err != nil {
632		t.Fatal(err)
633	}
634
635	if ser != exp {
636		t.Fatalf("got '%s', expected '%s'", ser, exp)
637	}
638
639	obj, err = ParseDetached(ser, []byte("$.02"))
640	if err != nil {
641		t.Fatal(err)
642	}
643
644	ser, err = obj.CompactSerialize()
645	if err != nil {
646		t.Fatal(err)
647	}
648
649	if ser != msg {
650		t.Fatalf("got '%s', expected '%s'", ser, msg)
651	}
652}
653
654func TestJWSComputeAuthDataBase64(t *testing.T) {
655	jws := JSONWebSignature{}
656
657	_, err := jws.computeAuthData([]byte{0x01}, &Signature{
658		original: &rawSignatureInfo{
659			Protected: newBuffer([]byte("{!invalid-json}")),
660		},
661	})
662	// Invalid header, should return error
663	assert.NotNil(t, err)
664
665	payload := []byte{0x01}
666	encodedPayload := base64.RawURLEncoding.EncodeToString(payload)
667
668	b64TrueHeader := newBuffer([]byte(`{"alg":"RSA-OAEP","enc":"A256GCM","b64":true}`))
669	b64FalseHeader := newBuffer([]byte(`{"alg":"RSA-OAEP","enc":"A256GCM","b64":false}`))
670
671	data, err := jws.computeAuthData(payload, &Signature{
672		original: &rawSignatureInfo{
673			Protected: b64TrueHeader,
674		},
675	})
676	assert.Nil(t, err)
677	// Payload should be b64 encoded
678	assert.Len(t, data, len(b64TrueHeader.base64())+len(encodedPayload)+1)
679
680	data, err = jws.computeAuthData(payload, &Signature{
681		original: &rawSignatureInfo{
682			Protected: b64FalseHeader,
683		},
684	})
685	assert.Nil(t, err)
686	// Payload should *not* be b64 encoded
687	assert.Len(t, data, len(b64FalseHeader.base64())+len(payload)+1)
688}
689