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_format.h"
18
19 #include <string>
20 #include <vector>
21
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "tink/jwt/internal/json_util.h"
25 #include "tink/util/test_matchers.h"
26 #include "tink/util/test_util.h"
27
28 using ::crypto::tink::test::IsOk;
29 using ::crypto::tink::test::IsOkAndHolds;
30 using ::google::crypto::tink::OutputPrefixType;
31 using testing::Eq;
32
33 namespace crypto {
34 namespace tink {
35 namespace jwt_internal {
36
TEST(JwtFormat,EncodeDecodeHeader)37 TEST(JwtFormat, EncodeDecodeHeader) {
38 std::string header = R"({"alg":"HS256"})";
39 std::string output;
40 ASSERT_TRUE(DecodeHeader(EncodeHeader(header), &output));
41 EXPECT_THAT(output, Eq(header));
42 }
43
TEST(JwtFormat,EncodeFixedHeader)44 TEST(JwtFormat, EncodeFixedHeader) {
45 // Null-terminted example from https://tools.ietf.org/html/rfc7519#section-3.1
46 char header[] = {123, 34, 116, 121, 112, 34, 58, 34, 74, 87,
47 84, 34, 44, 13, 10, 32, 34, 97, 108, 103,
48 34, 58, 34, 72, 83, 50, 53, 54, 34, 125, 0};
49 EXPECT_THAT(EncodeHeader(header),
50 Eq("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"));
51 }
52
TEST(JwtFormat,DecodedHeaderWithLineFeedFails)53 TEST(JwtFormat, DecodedHeaderWithLineFeedFails) {
54 std::string output;
55 ASSERT_FALSE(
56 DecodeHeader("eyJ0eXAiOiJKV1Qi\nLA0KICJhbGciOiJIUzI1NiJ9", &output));
57 }
58
TEST(JwtFormat,EncodeDecodePayload)59 TEST(JwtFormat, EncodeDecodePayload) {
60 std::string payload = R"({"iss":"issuer"})";
61 std::string output;
62 ASSERT_TRUE(DecodePayload(EncodePayload(payload), &output));
63 EXPECT_THAT(output, Eq(payload));
64 }
65
TEST(JwtFormat,EncodeFixedPayload)66 TEST(JwtFormat, EncodeFixedPayload) {
67 // Null-terminted example from https://tools.ietf.org/html/rfc7519#section-3.1
68 char payload[] = {123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34,
69 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51,
70 48, 48, 56, 49, 57, 51, 56, 48, 44, 13, 10, 32,
71 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109,
72 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114,
73 111, 111, 116, 34, 58, 116, 114, 117, 101, 125, 0};
74 EXPECT_THAT(EncodeHeader(payload),
75 Eq("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0"
76 "dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"));
77 }
78
TEST(JwtFormat,DecodeInvalidPayload_fails)79 TEST(JwtFormat, DecodeInvalidPayload_fails) {
80 std::string output;
81 ASSERT_FALSE(DecodePayload("eyJmb28iO?JiYXIifQ", &output));
82 }
83
TEST(JwtFormat,DecodeAndValidateFixedHeaderHS256)84 TEST(JwtFormat, DecodeAndValidateFixedHeaderHS256) {
85 // Example from https://tools.ietf.org/html/rfc7515#appendix-A.1
86 std::string encoded_header = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9";
87
88 std::string json_header;
89 ASSERT_TRUE(DecodeHeader(encoded_header, &json_header));
90 EXPECT_THAT(json_header, Eq("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}"));
91
92 util::StatusOr<google::protobuf::Struct> header =
93 JsonStringToProtoStruct(json_header);
94 EXPECT_THAT(header, IsOk());
95
96 EXPECT_THAT(ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt),
97 IsOk());
98 EXPECT_FALSE(
99 ValidateHeader(*header, "RS256", absl::nullopt, absl::nullopt).ok());
100 }
101
TEST(JwtFormat,DecodeAndValidateFixedHeaderRS256)102 TEST(JwtFormat, DecodeAndValidateFixedHeaderRS256) {
103 // Example from https://tools.ietf.org/html/rfc7515#appendix-A.2
104 std::string encoded_header = "eyJhbGciOiJSUzI1NiJ9";
105
106 std::string json_header;
107 ASSERT_TRUE(DecodeHeader(encoded_header, &json_header));
108 EXPECT_THAT(json_header, Eq(R"({"alg":"RS256"})"));
109
110 util::StatusOr<google::protobuf::Struct> header =
111 JsonStringToProtoStruct(json_header);
112 EXPECT_THAT(header, IsOk());
113
114 EXPECT_THAT(ValidateHeader(*header, "RS256", absl::nullopt, absl::nullopt),
115 IsOk());
116 EXPECT_FALSE(
117 ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt).ok());
118 }
119
TEST(JwtFormat,CreateValidateHeader)120 TEST(JwtFormat, CreateValidateHeader) {
121 util::StatusOr<std::string> encoded_header =
122 CreateHeader("PS384", absl::nullopt, absl::nullopt);
123 EXPECT_THAT(encoded_header, IsOk());
124
125 std::string json_header;
126 ASSERT_TRUE(DecodeHeader(*encoded_header, &json_header));
127
128 util::StatusOr<google::protobuf::Struct> header =
129 JsonStringToProtoStruct(json_header);
130 EXPECT_THAT(header, IsOk());
131
132 EXPECT_THAT(ValidateHeader(*header, "PS384", absl::nullopt, absl::nullopt),
133 IsOk());
134 EXPECT_FALSE(
135 ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt).ok());
136 }
137
TEST(JwtFormat,CreateValidateHeaderWithTypeAndKid)138 TEST(JwtFormat, CreateValidateHeaderWithTypeAndKid) {
139 util::StatusOr<std::string> encoded_header =
140 CreateHeader("PS384", "JWT", "kid-1234");
141 EXPECT_THAT(encoded_header, IsOk());
142
143 std::string json_header;
144 ASSERT_TRUE(DecodeHeader(*encoded_header, &json_header));
145
146 util::StatusOr<google::protobuf::Struct> header =
147 JsonStringToProtoStruct(json_header);
148 EXPECT_THAT(header, IsOk());
149
150 EXPECT_THAT(GetTypeHeader(*header), Eq("JWT"));
151 EXPECT_THAT(ValidateHeader(*header, "PS384", absl::nullopt, absl::nullopt),
152 IsOk());
153 EXPECT_FALSE(
154 ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt).ok());
155
156 auto it = header->fields().find("kid");
157 EXPECT_FALSE(it == header->fields().end());
158 const google::protobuf::Value& value = it->second;
159 EXPECT_THAT(value.kind_case(), Eq(google::protobuf::Value::kStringValue));
160 EXPECT_THAT(value.string_value(), Eq("kid-1234"));
161 }
162
TEST(JwtFormat,ValidateEmptyHeaderFails)163 TEST(JwtFormat, ValidateEmptyHeaderFails) {
164 google::protobuf::Struct empty_header;
165 EXPECT_FALSE(
166 ValidateHeader(empty_header, "HS256", absl::nullopt, absl::nullopt).ok());
167 }
168
TEST(JwtFormat,ValidateHeaderWithUnknownTypeOk)169 TEST(JwtFormat, ValidateHeaderWithUnknownTypeOk) {
170 std::string json_header = R"({"alg":"HS256","typ":"unknown"})";
171 util::StatusOr<google::protobuf::Struct> header =
172 JsonStringToProtoStruct(json_header);
173 EXPECT_THAT(header, IsOk());
174
175 EXPECT_THAT(ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt),
176 IsOk());
177 }
178
TEST(JwtFormat,ValidateHeaderRejectsCrit)179 TEST(JwtFormat, ValidateHeaderRejectsCrit) {
180 std::string json_header =
181 R"({"alg":"HS256","crit":["http://example.invalid/UNDEFINED"],)"
182 R"("http://example.invalid/UNDEFINED":true})";
183 util::StatusOr<google::protobuf::Struct> header =
184 JsonStringToProtoStruct(json_header);
185 EXPECT_THAT(header, IsOk());
186 EXPECT_FALSE(
187 ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt).ok());
188 }
189
TEST(JwtFormat,ValidateHeaderWithUnknownEntry)190 TEST(JwtFormat, ValidateHeaderWithUnknownEntry) {
191 std::string json_header = R"({"alg":"HS256","unknown":"header"})";
192 util::StatusOr<google::protobuf::Struct> header =
193 JsonStringToProtoStruct(json_header);
194 EXPECT_THAT(header, IsOk());
195 EXPECT_THAT(ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt),
196 IsOk());
197 }
198
TEST(JwtFormat,ValidateHeaderWithInvalidAlgTypFails)199 TEST(JwtFormat, ValidateHeaderWithInvalidAlgTypFails) {
200 std::string json_header = R"({"alg":true})";
201 util::StatusOr<google::protobuf::Struct> header =
202 JsonStringToProtoStruct(json_header);
203 EXPECT_THAT(header, IsOk());
204 EXPECT_FALSE(
205 ValidateHeader(*header, "HS256", absl::nullopt, absl::nullopt).ok());
206 }
207
TEST(JwtFormat,ValidateHeaderWithTinkKid)208 TEST(JwtFormat, ValidateHeaderWithTinkKid) {
209 std::string json_header = R"({"alg":"HS256","kid":"tink_kid"})";
210 util::StatusOr<google::protobuf::Struct> header =
211 JsonStringToProtoStruct(json_header);
212 EXPECT_THAT(header, IsOk());
213 EXPECT_THAT(ValidateHeader(*header, "HS256", "tink_kid", absl::nullopt),
214 IsOk());
215 EXPECT_FALSE(
216 ValidateHeader(*header, "HS256", "other_tink_kid", absl::nullopt).ok());
217 }
218
TEST(JwtFormat,ValidateHeaderWithTinkKidMissingFails)219 TEST(JwtFormat, ValidateHeaderWithTinkKidMissingFails) {
220 std::string json_header = R"({"alg":"HS256"})";
221 util::StatusOr<google::protobuf::Struct> header =
222 JsonStringToProtoStruct(json_header);
223 EXPECT_THAT(header, IsOk());
224 // If tink_kid is set, then the kid is required in the header.
225 EXPECT_FALSE(
226 ValidateHeader(*header, "HS256", "tink_kid", absl::nullopt).ok());
227 }
228
TEST(JwtFormat,ValidateHeaderWithCustomKid)229 TEST(JwtFormat, ValidateHeaderWithCustomKid) {
230 std::string json_header = R"({"alg":"HS256","kid":"custom_kid"})";
231 util::StatusOr<google::protobuf::Struct> header =
232 JsonStringToProtoStruct(json_header);
233 EXPECT_THAT(header, IsOk());
234 EXPECT_THAT(ValidateHeader(*header, "HS256", absl::nullopt, "custom_kid"),
235 IsOk());
236 EXPECT_FALSE(
237 ValidateHeader(*header, "HS256", absl::nullopt, "other_custom_kid").ok());
238 }
239
TEST(JwtFormat,ValidateHeaderWithCustomKidMissingFails)240 TEST(JwtFormat, ValidateHeaderWithCustomKidMissingFails) {
241 std::string json_header = R"({"alg":"HS256"})";
242 util::StatusOr<google::protobuf::Struct> header =
243 JsonStringToProtoStruct(json_header);
244 EXPECT_THAT(header, IsOk());
245 // If custom_kid is set, then the kid is not required in the header.
246 EXPECT_THAT(ValidateHeader(*header, "HS256", absl::nullopt, "custom_kid"),
247 IsOk());
248 }
249
TEST(JwtFormat,ValidateHeaderWithTinkAndCustomKidFails)250 TEST(JwtFormat, ValidateHeaderWithTinkAndCustomKidFails) {
251 std::string json_header = R"({"alg":"HS256","kid":"tink_kid"})";
252 util::StatusOr<google::protobuf::Struct> header =
253 JsonStringToProtoStruct(json_header);
254 EXPECT_THAT(header, IsOk());
255 EXPECT_FALSE(ValidateHeader(*header, "HS256", "kid", "kid").ok());
256 }
257
TEST(JwtFormat,GetKidWithTinkOutputPrefixType)258 TEST(JwtFormat, GetKidWithTinkOutputPrefixType) {
259 uint32_t keyId = 0x1ac6a944;
260 std::string kid = "GsapRA";
261 EXPECT_THAT(GetKid(keyId, OutputPrefixType::TINK), Eq(kid));
262 EXPECT_THAT(GetKeyId(kid), Eq(keyId));
263 }
264
TEST(JwtFormat,GetKeyId)265 TEST(JwtFormat, GetKeyId) {
266 uint32_t keyId = 0x1ac6a944;
267 std::string kid = "GsapRA";
268 EXPECT_THAT(GetKeyId(kid), Eq(keyId));
269 }
270
TEST(JwtFormat,GetKidWithRawOutputPrefixTypeIsNotPresent)271 TEST(JwtFormat, GetKidWithRawOutputPrefixTypeIsNotPresent) {
272 uint32_t keyId = 0x1ac6a944;
273 EXPECT_THAT(GetKid(keyId, OutputPrefixType::RAW), Eq(absl::nullopt));
274 }
275
TEST(JwtFormat,KeyIdKidConversion)276 TEST(JwtFormat, KeyIdKidConversion) {
277 EXPECT_THAT(GetKeyId(*GetKid(0x12345678, OutputPrefixType::TINK)),
278 Eq(0x12345678));
279 EXPECT_THAT(GetKeyId(*GetKid(0, OutputPrefixType::TINK)), Eq(0));
280 EXPECT_THAT(GetKeyId(*GetKid(100, OutputPrefixType::TINK)), Eq(100));
281 EXPECT_THAT(GetKeyId(*GetKid(2147483647, OutputPrefixType::TINK)),
282 Eq(2147483647));
283 EXPECT_THAT(GetKeyId(*GetKid(0xffffffff, OutputPrefixType::TINK)),
284 Eq(0xffffffff));
285 }
286
TEST(JwtFormat,GetKeyIdFromInvalidKidIsNotPresent)287 TEST(JwtFormat, GetKeyIdFromInvalidKidIsNotPresent) {
288 EXPECT_THAT(GetKeyId(""), Eq(absl::nullopt));
289 EXPECT_THAT(GetKeyId("Gsap"), Eq(absl::nullopt));
290 EXPECT_THAT(GetKeyId("GsapRAAA"), Eq(absl::nullopt));
291 }
292
TEST(JwtFormat,DecodeFixedPayload)293 TEST(JwtFormat, DecodeFixedPayload) {
294 // Example from https://tools.ietf.org/html/rfc7519#section-3.1
295 std::string encoded_payload =
296 "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0"
297 "dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ";
298
299 std::string expected =
300 "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n "
301 "\"http://example.com/is_root\":true}";
302 std::string output;
303 ASSERT_TRUE(DecodePayload(encoded_payload, &output));
304 EXPECT_THAT(output, Eq(expected));
305 }
306
TEST(JwtFormat,DecodePayloadWithLineFeedFails)307 TEST(JwtFormat, DecodePayloadWithLineFeedFails) {
308 // A linefeed as part of the payload (as in test DecodeFixedPayload) is fine,
309 // but a linefeed in the encoded payload is not.
310 std::string encoded_header_with_line_feed =
311 "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0\n"
312 "dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ";
313 std::string output;
314 ASSERT_FALSE(
315 DecodePayload(encoded_header_with_line_feed, &output));
316 }
317
TEST(JwtFormat,EncodeFixedSignature)318 TEST(JwtFormat, EncodeFixedSignature) {
319 std::string encoded_signature = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
320 std::string signature;
321 ASSERT_TRUE(DecodeSignature(encoded_signature, &signature));
322 EXPECT_THAT(EncodeSignature(signature), Eq(encoded_signature));
323 }
324
TEST(JwtFormat,DecodeSignatureWithLineFeedFails)325 TEST(JwtFormat, DecodeSignatureWithLineFeedFails) {
326 std::string output;
327 ASSERT_FALSE(
328 DecodePayload("dBjftJeZ4CVP-mB92K2\n7uhbUJU1p1r_wW1gFWFOEjXk", &output));
329 }
330
TEST(RawJwt,FromJson)331 TEST(RawJwt, FromJson) {
332 util::StatusOr<RawJwt> jwt = RawJwtParser::FromJson(
333 absl::nullopt,
334 R"({"iss":"issuer", "sub":"subject", "exp":123, "aud":["a1", "a2"]})");
335 ASSERT_THAT(jwt, IsOk());
336
337 EXPECT_FALSE(jwt->HasTypeHeader());
338 EXPECT_THAT(jwt->GetIssuer(), IsOkAndHolds("issuer"));
339 EXPECT_THAT(jwt->GetSubject(), IsOkAndHolds("subject"));
340 EXPECT_THAT(jwt->GetExpiration(), IsOkAndHolds(absl::FromUnixSeconds(123)));
341 std::vector<std::string> expected_audiences = {"a1", "a2"};
342 EXPECT_THAT(jwt->GetAudiences(), IsOkAndHolds(expected_audiences));
343 }
344
TEST(RawJwt,FromJsonWithTypeHeader)345 TEST(RawJwt, FromJsonWithTypeHeader) {
346 util::StatusOr<RawJwt> jwt =
347 RawJwtParser::FromJson("typeHeader", R"({"iss":"issuer"})");
348 ASSERT_THAT(jwt, IsOk());
349
350 EXPECT_THAT(jwt->GetTypeHeader(), IsOkAndHolds("typeHeader"));
351 EXPECT_THAT(jwt->GetIssuer(), IsOkAndHolds("issuer"));
352 }
353
TEST(RawJwt,FromJsonExpExpiration)354 TEST(RawJwt, FromJsonExpExpiration) {
355 util::StatusOr<RawJwt> jwt =
356 RawJwtParser::FromJson(absl::nullopt, R"({"exp":1e10})");
357 ASSERT_THAT(jwt, IsOk());
358
359 EXPECT_THAT(jwt->GetExpiration(),
360 IsOkAndHolds(absl::FromUnixSeconds(10000000000)));
361 }
362
TEST(RawJwt,FromJsonExpirationTooLarge)363 TEST(RawJwt, FromJsonExpirationTooLarge) {
364 util::StatusOr<RawJwt> jwt =
365 RawJwtParser::FromJson(absl::nullopt, R"({"exp":1e30})");
366 EXPECT_FALSE(jwt.ok());
367 }
368
TEST(RawJwt,FromJsonNegativeExpirationAreInvalid)369 TEST(RawJwt, FromJsonNegativeExpirationAreInvalid) {
370 util::StatusOr<RawJwt> jwt =
371 RawJwtParser::FromJson(absl::nullopt, R"({"exp":-1})");
372 EXPECT_FALSE(jwt.ok());
373 }
374
TEST(RawJwt,FromJsonPreservesStringAud)375 TEST(RawJwt, FromJsonPreservesStringAud) {
376 util::StatusOr<RawJwt> jwt =
377 RawJwtParser::FromJson(absl::nullopt, R"({"aud":"audience"})");
378 ASSERT_THAT(jwt, IsOk());
379
380 std::vector<std::string> expected = {"audience"};
381 EXPECT_TRUE(jwt->HasAudiences());
382 EXPECT_THAT(jwt->GetAudiences(), IsOkAndHolds(expected));
383
384 EXPECT_THAT(jwt->GetJsonPayload(), IsOkAndHolds(R"({"aud":"audience"})"));
385 }
386
TEST(RawJwt,FromJsonPreservesListAud)387 TEST(RawJwt, FromJsonPreservesListAud) {
388 util::StatusOr<RawJwt> jwt =
389 RawJwtParser::FromJson(absl::nullopt, R"({"aud":["audience"]})");
390 ASSERT_THAT(jwt, IsOk());
391
392 std::vector<std::string> expected = {"audience"};
393 EXPECT_TRUE(jwt->HasAudiences());
394 EXPECT_THAT(jwt->GetAudiences(), IsOkAndHolds(expected));
395
396 EXPECT_THAT(jwt->GetJsonPayload(), IsOkAndHolds(R"({"aud":["audience"]})"));
397 }
398
TEST(RawJwt,FromJsonWithBadRegisteredTypes)399 TEST(RawJwt, FromJsonWithBadRegisteredTypes) {
400 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"iss":123})").ok());
401 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"sub":123})").ok());
402 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"aud":123})").ok());
403 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"aud":[]})").ok());
404 EXPECT_FALSE(
405 RawJwtParser::FromJson(absl::nullopt, R"({"aud":["abc",123]})").ok());
406 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"exp":"abc"})").ok());
407 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"nbf":"abc"})").ok());
408 EXPECT_FALSE(RawJwtParser::FromJson(absl::nullopt, R"({"iat":"abc"})").ok());
409 }
410
411 } // namespace jwt_internal
412 } // namespace tink
413 } // namespace crypto
414