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