1// +build go1.4
2
3package jwt_test
4
5import (
6	"crypto/rsa"
7	"io/ioutil"
8	"strings"
9	"testing"
10	"time"
11
12	"github.com/golang-jwt/jwt/v4"
13	"github.com/golang-jwt/jwt/v4/test"
14)
15
16var rsaPSSTestData = []struct {
17	name        string
18	tokenString string
19	alg         string
20	claims      map[string]interface{}
21	valid       bool
22}{
23	{
24		"Basic PS256",
25		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9w",
26		"PS256",
27		map[string]interface{}{"foo": "bar"},
28		true,
29	},
30	{
31		"Basic PS384",
32		"eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.w7-qqgj97gK4fJsq_DCqdYQiylJjzWONvD0qWWWhqEOFk2P1eDULPnqHRnjgTXoO4HAw4YIWCsZPet7nR3Xxq4ZhMqvKW8b7KlfRTb9cH8zqFvzMmybQ4jv2hKc3bXYqVow3AoR7hN_CWXI3Dv6Kd2X5xhtxRHI6IL39oTVDUQ74LACe-9t4c3QRPuj6Pq1H4FAT2E2kW_0KOc6EQhCLWEhm2Z2__OZskDC8AiPpP8Kv4k2vB7l0IKQu8Pr4RcNBlqJdq8dA5D3hk5TLxP8V5nG1Ib80MOMMqoS3FQvSLyolFX-R_jZ3-zfq6Ebsqr0yEb0AH2CfsECF7935Pa0FKQ",
33		"PS384",
34		map[string]interface{}{"foo": "bar"},
35		true,
36	},
37	{
38		"Basic PS512",
39		"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.GX1HWGzFaJevuSLavqqFYaW8_TpvcjQ8KfC5fXiSDzSiT9UD9nB_ikSmDNyDILNdtjZLSvVKfXxZJqCfefxAtiozEDDdJthZ-F0uO4SPFHlGiXszvKeodh7BuTWRI2wL9-ZO4mFa8nq3GMeQAfo9cx11i7nfN8n2YNQ9SHGovG7_T_AvaMZB_jT6jkDHpwGR9mz7x1sycckEo6teLdHRnH_ZdlHlxqknmyTu8Odr5Xh0sJFOL8BepWbbvIIn-P161rRHHiDWFv6nhlHwZnVzjx7HQrWSGb6-s2cdLie9QL_8XaMcUpjLkfOMKkDOfHo6AvpL7Jbwi83Z2ZTHjJWB-A",
40		"PS512",
41		map[string]interface{}{"foo": "bar"},
42		true,
43	},
44	{
45		"basic PS256 invalid: foo => bar",
46		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9W",
47		"PS256",
48		map[string]interface{}{"foo": "bar"},
49		false,
50	},
51}
52
53func TestRSAPSSVerify(t *testing.T) {
54	var err error
55
56	key, _ := ioutil.ReadFile("test/sample_key.pub")
57	var rsaPSSKey *rsa.PublicKey
58	if rsaPSSKey, err = jwt.ParseRSAPublicKeyFromPEM(key); err != nil {
59		t.Errorf("Unable to parse RSA public key: %v", err)
60	}
61
62	for _, data := range rsaPSSTestData {
63		parts := strings.Split(data.tokenString, ".")
64
65		method := jwt.GetSigningMethod(data.alg)
66		err := method.Verify(strings.Join(parts[0:2], "."), parts[2], rsaPSSKey)
67		if data.valid && err != nil {
68			t.Errorf("[%v] Error while verifying key: %v", data.name, err)
69		}
70		if !data.valid && err == nil {
71			t.Errorf("[%v] Invalid key passed validation", data.name)
72		}
73	}
74}
75
76func TestRSAPSSSign(t *testing.T) {
77	var err error
78
79	key, _ := ioutil.ReadFile("test/sample_key")
80	var rsaPSSKey *rsa.PrivateKey
81	if rsaPSSKey, err = jwt.ParseRSAPrivateKeyFromPEM(key); err != nil {
82		t.Errorf("Unable to parse RSA private key: %v", err)
83	}
84
85	for _, data := range rsaPSSTestData {
86		if data.valid {
87			parts := strings.Split(data.tokenString, ".")
88			method := jwt.GetSigningMethod(data.alg)
89			sig, err := method.Sign(strings.Join(parts[0:2], "."), rsaPSSKey)
90			if err != nil {
91				t.Errorf("[%v] Error signing token: %v", data.name, err)
92			}
93			if sig == parts[2] {
94				t.Errorf("[%v] Signatures shouldn't match\nnew:\n%v\noriginal:\n%v", data.name, sig, parts[2])
95			}
96		}
97	}
98}
99
100func TestRSAPSSSaltLengthCompatibility(t *testing.T) {
101	// Fails token verify, if salt length is auto.
102	ps256SaltLengthEqualsHash := &jwt.SigningMethodRSAPSS{
103		SigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,
104		Options: &rsa.PSSOptions{
105			SaltLength: rsa.PSSSaltLengthEqualsHash,
106		},
107	}
108
109	// Behaves as before https://github.com/dgrijalva/jwt-go/issues/285 fix.
110	ps256SaltLengthAuto := &jwt.SigningMethodRSAPSS{
111		SigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,
112		Options: &rsa.PSSOptions{
113			SaltLength: rsa.PSSSaltLengthAuto,
114		},
115	}
116	if !verify(jwt.SigningMethodPS256, makeToken(ps256SaltLengthEqualsHash)) {
117		t.Error("SigningMethodPS256 should accept salt length that is defined in RFC")
118	}
119	if !verify(ps256SaltLengthEqualsHash, makeToken(jwt.SigningMethodPS256)) {
120		t.Error("Sign by SigningMethodPS256 should have salt length that is defined in RFC")
121	}
122	if !verify(jwt.SigningMethodPS256, makeToken(ps256SaltLengthAuto)) {
123		t.Error("SigningMethodPS256 should accept auto salt length to be compatible with previous versions")
124	}
125	if !verify(ps256SaltLengthAuto, makeToken(jwt.SigningMethodPS256)) {
126		t.Error("Sign by SigningMethodPS256 should be accepted by previous versions")
127	}
128	if verify(ps256SaltLengthEqualsHash, makeToken(ps256SaltLengthAuto)) {
129		t.Error("Auto salt length should be not accepted, when RFC salt length is required")
130	}
131}
132
133func makeToken(method jwt.SigningMethod) string {
134	token := jwt.NewWithClaims(method, jwt.RegisteredClaims{
135		Issuer:   "example",
136		IssuedAt: jwt.NewNumericDate(time.Now()),
137	})
138	privateKey := test.LoadRSAPrivateKeyFromDisk("test/sample_key")
139	signed, err := token.SignedString(privateKey)
140	if err != nil {
141		panic(err)
142	}
143	return signed
144}
145
146func verify(signingMethod jwt.SigningMethod, token string) bool {
147	segments := strings.Split(token, ".")
148	err := signingMethod.Verify(strings.Join(segments[:2], "."), segments[2], test.LoadRSAPublicKeyFromDisk("test/sample_key.pub"))
149	return err == nil
150}
151