1
2
3 package jwt_test
4
5 import (
6 "crypto/rsa"
7 "io/ioutil"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/golang-jwt/jwt"
13 "github.com/golang-jwt/jwt/test"
14 )
15
16 var 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
53 func 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
76 func 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
100 func TestRSAPSSSaltLengthCompatibility(t *testing.T) {
101
102 ps256SaltLengthEqualsHash := &jwt.SigningMethodRSAPSS{
103 SigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,
104 Options: &rsa.PSSOptions{
105 SaltLength: rsa.PSSSaltLengthEqualsHash,
106 },
107 }
108
109
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
133 func makeToken(method jwt.SigningMethod) string {
134 token := jwt.NewWithClaims(method, jwt.StandardClaims{
135 Issuer: "example",
136 IssuedAt: time.Now().Unix(),
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
146 func 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
View as plain text