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_hmac_key_manager.h"
18
19 #include <memory>
20 #include <sstream>
21 #include <string>
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 "absl/time/time.h"
30 #include "tink/core/key_manager_impl.h"
31 #include "tink/jwt/internal/json_util.h"
32 #include "tink/jwt/internal/jwt_format.h"
33 #include "tink/mac.h"
34 #include "tink/util/istream_input_stream.h"
35 #include "tink/util/secret_data.h"
36 #include "tink/util/status.h"
37 #include "tink/util/statusor.h"
38 #include "tink/util/test_matchers.h"
39 #include "tink/util/test_util.h"
40
41 namespace crypto {
42 namespace tink {
43 namespace jwt_internal {
44
45 using ::crypto::tink::test::IsOk;
46 using ::crypto::tink::test::IsOkAndHolds;
47 using ::crypto::tink::test::StatusIs;
48 using ::crypto::tink::util::IstreamInputStream;
49 using ::crypto::tink::util::StatusOr;
50 using ::google::crypto::tink::JwtHmacAlgorithm;
51 using ::google::crypto::tink::JwtHmacKey;
52 using ::google::crypto::tink::JwtHmacKeyFormat;
53 using ::testing::Eq;
54 using ::testing::Not;
55 using ::testing::SizeIs;
56
57 namespace {
58
TEST(JwtHmacKeyManagerTest,Basics)59 TEST(JwtHmacKeyManagerTest, Basics) {
60 EXPECT_EQ(JwtHmacKeyManager().get_version(), 0);
61 EXPECT_EQ(JwtHmacKeyManager().get_key_type(),
62 "type.googleapis.com/google.crypto.tink.JwtHmacKey");
63 EXPECT_EQ(JwtHmacKeyManager().key_material_type(),
64 google::crypto::tink::KeyData::SYMMETRIC);
65 }
66
TEST(JwtHmacKeyManagerTest,ValidateEmptyKey)67 TEST(JwtHmacKeyManagerTest, ValidateEmptyKey) {
68 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(JwtHmacKey()), Not(IsOk()));
69 }
70
TEST(JwtHmacKeyManagerTest,ValidateEmptyKeyFormat)71 TEST(JwtHmacKeyManagerTest, ValidateEmptyKeyFormat) {
72 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(JwtHmacKeyFormat()),
73 Not(IsOk()));
74 }
75
TEST(RawJwtHmacKeyManagerTest,ValidateHS256KeyFormat)76 TEST(RawJwtHmacKeyManagerTest, ValidateHS256KeyFormat) {
77 JwtHmacKeyFormat key_format;
78 key_format.set_algorithm(JwtHmacAlgorithm::HS256);
79 key_format.set_key_size(32);
80 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), IsOk());
81 key_format.set_key_size(31);
82 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), Not(IsOk()));
83 }
84
TEST(RawJwtHmacKeyManagerTest,ValidateHS384KeyFormat)85 TEST(RawJwtHmacKeyManagerTest, ValidateHS384KeyFormat) {
86 JwtHmacKeyFormat key_format;
87 key_format.set_algorithm(JwtHmacAlgorithm::HS384);
88 key_format.set_key_size(48);
89 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), IsOk());
90 key_format.set_key_size(47);
91 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), Not(IsOk()));
92 }
93
TEST(RawJwtHmacKeyManagerTest,ValidateHS512KeyFormat)94 TEST(RawJwtHmacKeyManagerTest, ValidateHS512KeyFormat) {
95 JwtHmacKeyFormat key_format;
96 key_format.set_algorithm(JwtHmacAlgorithm::HS512);
97 key_format.set_key_size(64);
98 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), IsOk());
99 key_format.set_key_size(63);
100 EXPECT_THAT(JwtHmacKeyManager().ValidateKeyFormat(key_format), Not(IsOk()));
101 }
102
TEST(JwtHmacKeyManagerTest,CreateKey)103 TEST(JwtHmacKeyManagerTest, CreateKey) {
104 JwtHmacKeyFormat key_format;
105 key_format.set_key_size(32);
106 key_format.set_algorithm(JwtHmacAlgorithm::HS256);
107 util::StatusOr<google::crypto::tink::JwtHmacKey> key =
108 JwtHmacKeyManager().CreateKey(key_format);
109 ASSERT_THAT(key, IsOk());
110 EXPECT_EQ(key->version(), 0);
111 EXPECT_EQ(key->algorithm(), key_format.algorithm());
112 EXPECT_THAT(key->key_value(), SizeIs(key_format.key_size()));
113
114 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(*key), IsOk());
115 }
116
TEST(JwtHmacKeyManagerTest,ValidateKeyWithUnknownAlgorithm_fails)117 TEST(JwtHmacKeyManagerTest, ValidateKeyWithUnknownAlgorithm_fails) {
118 JwtHmacKey key;
119 key.set_version(0);
120 key.set_algorithm(JwtHmacAlgorithm::HS_UNKNOWN);
121 key.set_key_value("0123456789abcdef0123456789abcdef");
122
123 EXPECT_FALSE(JwtHmacKeyManager().ValidateKey(key).ok());
124 }
125
TEST(JwtHmacKeyManagerTest,ValidateHS256Key)126 TEST(JwtHmacKeyManagerTest, ValidateHS256Key) {
127 JwtHmacKey key;
128 key.set_version(0);
129 key.set_algorithm(JwtHmacAlgorithm::HS256);
130 key.set_key_value("0123456789abcdef0123456789abcdef"); // 32 bytes
131 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(key), IsOk());
132 key.set_key_value("0123456789abcdef0123456789abcde"); // 31 bytes
133 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(key), Not(IsOk()));
134 }
135
TEST(JwtHmacKeyManagerTest,ValidateHS384Key)136 TEST(JwtHmacKeyManagerTest, ValidateHS384Key) {
137 JwtHmacKey key;
138 key.set_version(0);
139 key.set_algorithm(JwtHmacAlgorithm::HS384);
140 key.set_key_value(
141 "0123456789abcdef0123456789abcdef0123456789abcdef"); // 48 bytes
142 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(key), IsOk());
143 key.set_key_value(
144 "0123456789abcdef0123456789abcdef0123456789abcde"); // 47 bytes
145 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(key), Not(IsOk()));
146 }
147
TEST(JwtHmacKeyManagerTest,ValidateHS512Key)148 TEST(JwtHmacKeyManagerTest, ValidateHS512Key) {
149 JwtHmacKey key;
150 key.set_version(0);
151 key.set_algorithm(JwtHmacAlgorithm::HS512);
152 key.set_key_value( // 64 bytes
153 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
154 key.set_key_value( // 63 bytes
155 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde");
156 EXPECT_THAT(JwtHmacKeyManager().ValidateKey(key), Not(IsOk()));
157 }
158
159
TEST(JwtHmacKeyManagerTest,DeriveKeyIsNotImplemented)160 TEST(JwtHmacKeyManagerTest, DeriveKeyIsNotImplemented) {
161 JwtHmacKeyFormat format;
162 format.set_version(0);
163 format.set_key_size(32);
164 format.set_algorithm(JwtHmacAlgorithm::HS256);
165
166 IstreamInputStream input_stream{
167 absl::make_unique<std::stringstream>("0123456789abcdefghijklmnop")};
168
169 ASSERT_THAT(JwtHmacKeyManager().DeriveKey(format, &input_stream).status(),
170 StatusIs(absl::StatusCode::kUnimplemented));
171 }
172
TEST(JwtHmacKeyManagerTest,GetAndUsePrimitive)173 TEST(JwtHmacKeyManagerTest, GetAndUsePrimitive) {
174 JwtHmacKeyFormat key_format;
175 key_format.set_key_size(32);
176 key_format.set_algorithm(JwtHmacAlgorithm::HS256);
177 util::StatusOr<google::crypto::tink::JwtHmacKey> key =
178 JwtHmacKeyManager().CreateKey(key_format);
179 ASSERT_THAT(key, IsOk());
180
181 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac =
182 JwtHmacKeyManager().GetPrimitive<JwtMacInternal>(*key);
183 ASSERT_THAT(jwt_mac, IsOk());
184
185 util::StatusOr<RawJwt> raw_jwt =
186 RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
187 ASSERT_THAT(raw_jwt, IsOk());
188
189 util::StatusOr<std::string> compact =
190 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
191 ASSERT_THAT(compact, IsOk());
192 util::StatusOr<JwtValidator> validator = JwtValidatorBuilder()
193 .ExpectIssuer("issuer")
194 .AllowMissingExpiration()
195 .Build();
196 ASSERT_THAT(validator, IsOk());
197
198 util::StatusOr<VerifiedJwt> verified_jwt =
199 (*jwt_mac)->VerifyMacAndDecodeWithKid(*compact, *validator,
200 /*kid=*/absl::nullopt);
201 ASSERT_THAT(verified_jwt, IsOk());
202 util::StatusOr<std::string> issuer = verified_jwt->GetIssuer();
203 EXPECT_THAT(issuer, IsOkAndHolds("issuer"));
204 }
205
TEST(JwtHmacKeyManagerTest,GetAndUsePrimitiveWithKid)206 TEST(JwtHmacKeyManagerTest, GetAndUsePrimitiveWithKid) {
207 JwtHmacKeyFormat key_format;
208 key_format.set_key_size(32);
209 key_format.set_algorithm(JwtHmacAlgorithm::HS256);
210 util::StatusOr<google::crypto::tink::JwtHmacKey> key =
211 JwtHmacKeyManager().CreateKey(key_format);
212 ASSERT_THAT(key, IsOk());
213
214 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac =
215 JwtHmacKeyManager().GetPrimitive<JwtMacInternal>(*key);
216 ASSERT_THAT(jwt_mac, IsOk());
217
218 util::StatusOr<RawJwt> raw_jwt =
219 RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
220 ASSERT_THAT(raw_jwt, IsOk());
221
222 util::StatusOr<std::string> token_with_kid =
223 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/"kid-123");
224 ASSERT_THAT(token_with_kid, IsOk());
225 util::StatusOr<std::string> token_without_kid =
226 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
227 ASSERT_THAT(token_without_kid, IsOk());
228
229 util::StatusOr<JwtValidator> validator = JwtValidatorBuilder()
230 .ExpectIssuer("issuer")
231 .AllowMissingExpiration()
232 .Build();
233 ASSERT_THAT(validator, IsOk());
234
235 // A token with kid only fails if the wrong kid is passed.
236 ASSERT_THAT((*jwt_mac)
237 ->VerifyMacAndDecodeWithKid(*token_with_kid, *validator,
238 /*kid=*/absl::nullopt)
239 .status(),
240 IsOk());
241 ASSERT_THAT((*jwt_mac)
242 ->VerifyMacAndDecodeWithKid(*token_with_kid, *validator,
243 /*kid=*/"kid-123")
244 .status(),
245 IsOk());
246 ASSERT_THAT((*jwt_mac)
247 ->VerifyMacAndDecodeWithKid(*token_with_kid, *validator,
248 /*kid=*/"wrong-kid")
249 .status(),
250 Not(IsOk()));
251
252 // A token without kid is only valid if no kid is passed.
253 ASSERT_THAT((*jwt_mac)
254 ->VerifyMacAndDecodeWithKid(*token_without_kid, *validator,
255 /*kid=*/absl::nullopt)
256 .status(),
257 IsOk());
258 ASSERT_THAT(
259 (*jwt_mac)
260 ->VerifyMacAndDecodeWithKid(*token_without_kid, *validator, "kid-123")
261 .status(),
262 Not(IsOk()));
263 }
264
TEST(JwtHmacKeyManagerTest,GetAndUsePrimitiveWithCustomKid)265 TEST(JwtHmacKeyManagerTest, GetAndUsePrimitiveWithCustomKid) {
266 JwtHmacKeyFormat key_format;
267 key_format.set_key_size(32);
268 key_format.set_algorithm(JwtHmacAlgorithm::HS256);
269 util::StatusOr<JwtHmacKey> key = JwtHmacKeyManager().CreateKey(key_format);
270 ASSERT_THAT(key, IsOk());
271 key->mutable_custom_kid()->set_value(
272 "Lorem ipsum dolor sit amet, consectetur adipiscing elit");
273
274 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac =
275 JwtHmacKeyManager().GetPrimitive<JwtMacInternal>(*key);
276 ASSERT_THAT(jwt_mac, IsOk());
277
278 util::StatusOr<RawJwt> raw_jwt =
279 RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
280 ASSERT_THAT(raw_jwt, IsOk());
281
282 util::StatusOr<std::string> compact =
283 (*jwt_mac)->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
284 ASSERT_THAT(compact, IsOk());
285 util::StatusOr<JwtValidator> validator = JwtValidatorBuilder()
286 .ExpectIssuer("issuer")
287 .AllowMissingExpiration()
288 .Build();
289 ASSERT_THAT(validator, IsOk());
290 // parse header and check "kid"
291 std::vector<absl::string_view> parts = absl::StrSplit(*compact, '.');
292 ASSERT_THAT(parts.size(), Eq(3));
293 std::string json_header;
294 ASSERT_TRUE(DecodeHeader(parts[0], &json_header));
295 util::StatusOr<google::protobuf::Struct> header =
296 JsonStringToProtoStruct(json_header);
297 ASSERT_THAT(header, IsOk());
298 auto it = header->fields().find("kid");
299 ASSERT_FALSE(it == header->fields().end());
300 EXPECT_THAT(it->second.string_value(),
301 Eq("Lorem ipsum dolor sit amet, consectetur adipiscing elit"));
302
303 // validate token
304 util::StatusOr<VerifiedJwt> verified_jwt =
305 (*jwt_mac)->VerifyMacAndDecodeWithKid(*compact, *validator,
306 /*kid=*/absl::nullopt);
307 ASSERT_THAT(verified_jwt, IsOk());
308 util::StatusOr<std::string> issuer = verified_jwt->GetIssuer();
309 ASSERT_THAT(issuer, IsOk());
310 EXPECT_THAT(*issuer, testing::Eq("issuer"));
311
312 // passing a kid when custom_kid is set should fail
313 EXPECT_THAT((*jwt_mac)
314 ->ComputeMacAndEncodeWithKid(*raw_jwt, /*kid=*/"kid123")
315 .status(),
316 Not(IsOk()));
317 }
318
TEST(JwtHmacKeyManagerTest,ValidateTokenWithFixedKey)319 TEST(JwtHmacKeyManagerTest, ValidateTokenWithFixedKey) {
320 JwtHmacKey key;
321 key.set_version(0);
322 key.set_algorithm(JwtHmacAlgorithm::HS256);
323
324 std::string key_value;
325 ASSERT_TRUE(absl::WebSafeBase64Unescape(
326 "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1"
327 "qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
328 &key_value));
329 key.set_key_value(key_value);
330 util::StatusOr<std::unique_ptr<JwtMacInternal>> jwt_mac =
331 JwtHmacKeyManager().GetPrimitive<JwtMacInternal>(key);
332 ASSERT_THAT(jwt_mac, IsOk());
333
334 std::string compact =
335 "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleH"
336 "AiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
337 "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
338 util::StatusOr<JwtValidator> validator =
339 JwtValidatorBuilder()
340 .ExpectTypeHeader("JWT")
341 .ExpectIssuer("joe")
342 .SetFixedNow(absl::FromUnixSeconds(12345))
343 .Build();
344 ASSERT_THAT(validator, IsOk());
345
346 util::StatusOr<VerifiedJwt> verified_jwt =
347 (*jwt_mac)->VerifyMacAndDecodeWithKid(compact, *validator,
348 /*kid=*/absl::nullopt);
349 ASSERT_THAT(verified_jwt, IsOk());
350 EXPECT_THAT(verified_jwt->GetIssuer(), IsOkAndHolds("joe"));
351 EXPECT_THAT(verified_jwt->GetBooleanClaim("http://example.com/is_root"),
352 IsOkAndHolds(true));
353
354 util::StatusOr<JwtValidator> validator_now = JwtValidatorBuilder().Build();
355 ASSERT_THAT(validator_now, IsOk());
356 EXPECT_THAT((*jwt_mac)
357 ->VerifyMacAndDecodeWithKid(compact, *validator_now,
358 /*kid=*/absl::nullopt)
359 .status(),
360 Not(IsOk()));
361
362 std::string modified_compact =
363 "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleH"
364 "AiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
365 "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXi";
366 EXPECT_THAT((*jwt_mac)
367 ->VerifyMacAndDecodeWithKid(modified_compact, *validator,
368 /*kid=*/absl::nullopt)
369 .status(),
370 Not(IsOk()));
371 }
372
373 } // namespace
374 } // namespace jwt_internal
375 } // namespace tink
376 } // namespace crypto
377