xref: /aosp_15_r20/external/tink/cc/jwt/internal/jwt_format_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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