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/v4"
14 "github.com/golang-jwt/jwt/v4/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], "."), 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 parts := strings.Split(data.tokenString, ".")
89 method := jwt.GetSigningMethod(data.alg)
90 sig, err := method.Sign(strings.Join(parts[0:2], "."), rsaPSSKey)
91 if err != nil {
92 t.Errorf("[%v] Error signing token: %v", data.name, err)
93 }
94 if sig == parts[2] {
95 t.Errorf("[%v] Signatures shouldn't match\nnew:\n%v\noriginal:\n%v", data.name, sig, parts[2])
96 }
97 }
98 }
99 }
100
101 func TestRSAPSSSaltLengthCompatibility(t *testing.T) {
102
103 ps256SaltLengthEqualsHash := &jwt.SigningMethodRSAPSS{
104 SigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,
105 Options: &rsa.PSSOptions{
106 SaltLength: rsa.PSSSaltLengthEqualsHash,
107 },
108 }
109
110
111 ps256SaltLengthAuto := &jwt.SigningMethodRSAPSS{
112 SigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,
113 Options: &rsa.PSSOptions{
114 SaltLength: rsa.PSSSaltLengthAuto,
115 },
116 }
117 if !verify(jwt.SigningMethodPS256, makeToken(ps256SaltLengthEqualsHash)) {
118 t.Error("SigningMethodPS256 should accept salt length that is defined in RFC")
119 }
120 if !verify(ps256SaltLengthEqualsHash, makeToken(jwt.SigningMethodPS256)) {
121 t.Error("Sign by SigningMethodPS256 should have salt length that is defined in RFC")
122 }
123 if !verify(jwt.SigningMethodPS256, makeToken(ps256SaltLengthAuto)) {
124 t.Error("SigningMethodPS256 should accept auto salt length to be compatible with previous versions")
125 }
126 if !verify(ps256SaltLengthAuto, makeToken(jwt.SigningMethodPS256)) {
127 t.Error("Sign by SigningMethodPS256 should be accepted by previous versions")
128 }
129 if verify(ps256SaltLengthEqualsHash, makeToken(ps256SaltLengthAuto)) {
130 t.Error("Auto salt length should be not accepted, when RFC salt length is required")
131 }
132 }
133
134 func makeToken(method jwt.SigningMethod) string {
135 token := jwt.NewWithClaims(method, jwt.RegisteredClaims{
136 Issuer: "example",
137 IssuedAt: jwt.NewNumericDate(time.Now()),
138 })
139 privateKey := test.LoadRSAPrivateKeyFromDisk("test/sample_key")
140 signed, err := token.SignedString(privateKey)
141 if err != nil {
142 panic(err)
143 }
144 return signed
145 }
146
147 func verify(signingMethod jwt.SigningMethod, token string) bool {
148 segments := strings.Split(token, ".")
149 err := signingMethod.Verify(strings.Join(segments[:2], "."), segments[2], test.LoadRSAPublicKeyFromDisk("test/sample_key.pub"))
150 return err == nil
151 }
152
View as plain text