1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/jwt/internal/jwt_mac_impl.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "absl/status/status.h"
27 #include "absl/strings/escaping.h"
28 #include "absl/strings/str_split.h"
29 #include "tink/jwt/internal/json_util.h"
30 #include "tink/jwt/internal/jwt_format.h"
31 #include "tink/jwt/jwt_mac.h"
32 #include "tink/jwt/jwt_validator.h"
33 #include "tink/jwt/raw_jwt.h"
34 #include "tink/jwt/verified_jwt.h"
35 #include "tink/subtle/hmac_boringssl.h"
36 #include "tink/util/constants.h"
37 #include "tink/util/enums.h"
38 #include "tink/util/errors.h"
39 #include "tink/util/protobuf_helper.h"
40 #include "tink/util/secret_data.h"
41 #include "tink/util/test_matchers.h"
42 #include "tink/util/test_util.h"
43
44 using ::crypto::tink::test::IsOk;
45 using ::crypto::tink::test::IsOkAndHolds;
46 using ::testing::Eq;
47 using ::testing::Not;
48
49 namespace crypto {
50 namespace tink {
51 namespace jwt_internal {
52
53 namespace {
54
CreateJwtMac()55 util::StatusOr<std::unique_ptr<JwtMacInternal>> CreateJwtMac() {
56 std::string key_value;
57 if (!absl::WebSafeBase64Unescape(
58 "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1"
59 "qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
60 &key_value)) {
61 return util::Status(absl::StatusCode::kInvalidArgument,
62 "failed to parse key");
63 }
64 crypto::tink::util::StatusOr<std::unique_ptr<Mac>> mac =
65 subtle::HmacBoringSsl::New(
66 util::Enums::ProtoToSubtle(google::crypto::tink::HashType::SHA256),
67 32, util::SecretDataFromStringView(key_value));
68 if (!mac.ok()) {
69 return mac.status();
70 }
71 std::unique_ptr<JwtMacInternal> jwt_mac = absl::make_unique<JwtMacImpl>(
72 *std::move(mac), "HS256", /*kid=*/absl::nullopt);
73 return std::move(jwt_mac);
74 }
75
TEST(JwtMacImplTest,CreateAndValidateToken)76 TEST(JwtMacImplTest, CreateAndValidateToken) {
77 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac = CreateJwtMac();
78 ASSERT_THAT(jwt_mac, IsOk());
79
80 absl::Time now = absl::Now();
81 util::StatusOr<RawJwt> raw_jwt = RawJwtBuilder()
82 .SetTypeHeader("typeHeader")
83 .SetJwtId("id123")
84 .SetNotBefore(now - absl::Seconds(300))
85 .SetIssuedAt(now)
86 .SetExpiration(now + absl::Seconds(300))
87 .Build();
88 ASSERT_THAT(raw_jwt, IsOk());
89 EXPECT_TRUE(raw_jwt->HasTypeHeader());
90 EXPECT_THAT(raw_jwt->GetTypeHeader(), IsOkAndHolds("typeHeader"));
91
92 util::StatusOr<std::string> compact =
93 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
94 ASSERT_THAT(compact, IsOk());
95
96 util::StatusOr<JwtValidator> validator =
97 JwtValidatorBuilder().ExpectTypeHeader("typeHeader").Build();
98 ASSERT_THAT(validator, IsOk());
99
100 util::StatusOr<VerifiedJwt> verified_jwt =
101 (*jwt_mac)->VerifyMacAndDecodeWithKid(*compact, *validator,
102 /*kid=*/absl::nullopt);
103 ASSERT_THAT(verified_jwt, IsOk());
104 EXPECT_THAT(verified_jwt->GetTypeHeader(), IsOkAndHolds("typeHeader"));
105 EXPECT_THAT(verified_jwt->GetJwtId(), IsOkAndHolds("id123"));
106
107 util::StatusOr<JwtValidator> validator2 =
108 JwtValidatorBuilder().ExpectIssuer("unknown").Build();
109 ASSERT_THAT(validator2, IsOk());
110 EXPECT_FALSE((*jwt_mac)
111 ->VerifyMacAndDecodeWithKid(*compact, *validator2,
112 /*kid=*/absl::nullopt)
113 .ok());
114 }
115
TEST(JwtMacImplTest,CreateAndValidateTokenWithKid)116 TEST(JwtMacImplTest, CreateAndValidateTokenWithKid) {
117 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac = CreateJwtMac();
118 ASSERT_THAT(jwt_mac, IsOk());
119
120 absl::Time now = absl::Now();
121 util::StatusOr<RawJwt> raw_jwt = RawJwtBuilder()
122 .SetTypeHeader("typeHeader")
123 .SetJwtId("id123")
124 .SetNotBefore(now - absl::Seconds(300))
125 .SetIssuedAt(now)
126 .SetExpiration(now + absl::Seconds(300))
127 .Build();
128 ASSERT_THAT(raw_jwt, IsOk());
129 EXPECT_TRUE(raw_jwt->HasTypeHeader());
130 EXPECT_THAT(raw_jwt->GetTypeHeader(), IsOkAndHolds("typeHeader"));
131
132 util::StatusOr<std::string> compact =
133 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, "kid-123");
134 ASSERT_THAT(compact, IsOk());
135
136 util::StatusOr<JwtValidator> validator =
137 JwtValidatorBuilder().ExpectTypeHeader("typeHeader").Build();
138 ASSERT_THAT(validator, IsOk());
139
140 util::StatusOr<VerifiedJwt> verified_jwt =
141 (*jwt_mac)->VerifyMacAndDecodeWithKid(*compact, *validator,
142 /*kid=*/"kid-123");
143 ASSERT_THAT(verified_jwt, IsOk());
144 EXPECT_THAT(verified_jwt->GetTypeHeader(), IsOkAndHolds("typeHeader"));
145 EXPECT_THAT(verified_jwt->GetJwtId(), IsOkAndHolds("id123"));
146
147 // with kid=absl::nullopt, the kid header in the token is ignored.
148 EXPECT_THAT((*jwt_mac)
149 ->VerifyMacAndDecodeWithKid(*compact, *validator,
150 /*kid=*/absl::nullopt)
151 .status(),
152 IsOk());
153
154 // with a different kid, the verification fails.
155 EXPECT_THAT((*jwt_mac)
156 ->VerifyMacAndDecodeWithKid(*compact, *validator,
157 /*kid=*/"other-kid")
158 .status(),
159 Not(IsOk()));
160
161 // parse header to make sure the kid value is set correctly.
162 std::vector<absl::string_view> parts = absl::StrSplit(*compact, '.');
163 ASSERT_THAT(parts.size(), Eq(3));
164 std::string json_header;
165 ASSERT_TRUE(DecodeHeader(parts[0], &json_header));
166 util::StatusOr<google::protobuf::Struct> header =
167 JsonStringToProtoStruct(json_header);
168 ASSERT_THAT(header, IsOk());
169 EXPECT_THAT(header->fields().find("kid")->second.string_value(),
170 Eq("kid-123"));
171 }
172
TEST(JwtMacImplTest,ValidateFixedToken)173 TEST(JwtMacImplTest, ValidateFixedToken) {
174 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac = CreateJwtMac();
175 ASSERT_THAT(jwt_mac, IsOk());
176
177 // token that expired in 2011
178 std::string compact =
179 "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleH"
180 "AiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
181 "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
182 util::StatusOr<JwtValidator> validator_1970 =
183 JwtValidatorBuilder()
184 .ExpectTypeHeader("JWT")
185 .ExpectIssuer("joe")
186 .SetFixedNow(absl::FromUnixSeconds(12345))
187 .Build();
188 ASSERT_THAT(validator_1970, IsOk());
189
190 // verification succeeds because token was valid 1970
191 util::StatusOr<VerifiedJwt> verified_jwt =
192 (*jwt_mac)->VerifyMacAndDecodeWithKid(compact, *validator_1970,
193 /*kid=*/absl::nullopt);
194 ASSERT_THAT(verified_jwt, IsOk());
195 EXPECT_THAT(verified_jwt->GetIssuer(), IsOkAndHolds("joe"));
196 EXPECT_THAT(verified_jwt->GetBooleanClaim("http://example.com/is_root"),
197 IsOkAndHolds(true));
198
199 // verification fails because token is expired
200 util::StatusOr<JwtValidator> validator_now = JwtValidatorBuilder().Build();
201 ASSERT_THAT(validator_now, IsOk());
202 EXPECT_FALSE((*jwt_mac)
203 ->VerifyMacAndDecodeWithKid(compact, *validator_now,
204 /*kid=*/absl::nullopt)
205 .ok());
206
207 // verification fails because token was modified
208 std::string modified_compact =
209 "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleH"
210 "AiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
211 "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXi";
212 EXPECT_FALSE((*jwt_mac)
213 ->VerifyMacAndDecodeWithKid(
214 modified_compact, *validator_1970, /*kid=*/absl::nullopt)
215 .ok());
216 }
217
TEST(JwtMacImplTest,ValidateInvalidTokens)218 TEST(JwtMacImplTest, ValidateInvalidTokens) {
219 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac = CreateJwtMac();
220 ASSERT_THAT(jwt_mac, IsOk());
221
222 util::StatusOr<JwtValidator> validator = JwtValidatorBuilder().Build();
223 ASSERT_THAT(validator, IsOk());
224
225 EXPECT_FALSE((*jwt_mac)
226 ->VerifyMacAndDecodeWithKid("eyJhbGciOiJIUzI1NiJ9.e30.abc.",
227 *validator,
228 /*kid=*/absl::nullopt)
229 .ok());
230 EXPECT_FALSE((*jwt_mac)
231 ->VerifyMacAndDecodeWithKid("eyJhbGciOiJIUzI1NiJ9?.e30.abc",
232 *validator,
233 /*kid=*/absl::nullopt)
234 .ok());
235 EXPECT_FALSE((*jwt_mac)
236 ->VerifyMacAndDecodeWithKid("eyJhbGciOiJIUzI1NiJ9.e30?.abc",
237 *validator,
238 /*kid=*/absl::nullopt)
239 .ok());
240 EXPECT_FALSE((*jwt_mac)
241 ->VerifyMacAndDecodeWithKid("eyJhbGciOiJIUzI1NiJ9.e30.abc?",
242 *validator,
243 /*kid=*/absl::nullopt)
244 .ok());
245 EXPECT_FALSE((*jwt_mac)
246 ->VerifyMacAndDecodeWithKid("eyJhbGciOiJIUzI1NiJ9.e30",
247 *validator,
248 /*kid=*/absl::nullopt)
249 .ok());
250 }
251
252 } // namespace
253 } // namespace jwt_internal
254 } // namespace tink
255 } // namespace crypto
256