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 <string>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/status/status.h"
24 #include "absl/strings/escaping.h"
25 #include "absl/strings/str_split.h"
26 #include "tink/jwt/internal/json_util.h"
27 #include "tink/jwt/internal/jwt_format.h"
28
29 namespace crypto {
30 namespace tink {
31 namespace jwt_internal {
32
ComputeMacAndEncodeWithKid(const RawJwt & token,absl::optional<absl::string_view> kid) const33 util::StatusOr<std::string> JwtMacImpl::ComputeMacAndEncodeWithKid(
34 const RawJwt& token, absl::optional<absl::string_view> kid) const {
35 absl::optional<std::string> type_header;
36 if (token.HasTypeHeader()) {
37 util::StatusOr<std::string> type = token.GetTypeHeader();
38 if (!type.ok()) {
39 return type.status();
40 }
41 type_header = *type;
42 }
43 if (custom_kid_.has_value()) {
44 if (kid.has_value()) {
45 return util::Status(absl::StatusCode::kInvalidArgument,
46 "TINK keys are not allowed to have a kid value set.");
47 }
48 kid = *custom_kid_;
49 }
50 util::StatusOr<std::string> encoded_header =
51 CreateHeader(algorithm_, type_header, kid);
52 if (!encoded_header.ok()) {
53 return encoded_header.status();
54 }
55 util::StatusOr<std::string> payload = token.GetJsonPayload();
56 if (!payload.ok()) {
57 return payload.status();
58 }
59 std::string encoded_payload = EncodePayload(*payload);
60 std::string unsigned_token =
61 absl::StrCat(*encoded_header, ".", encoded_payload);
62 util::StatusOr<std::string> tag = mac_->ComputeMac(unsigned_token);
63 if (!tag.ok()) {
64 return tag.status();
65 }
66 std::string encoded_tag = EncodeSignature(*tag);
67 return absl::StrCat(unsigned_token, ".", encoded_tag);
68 }
69
VerifyMacAndDecodeWithKid(absl::string_view compact,const JwtValidator & validator,absl::optional<absl::string_view> kid) const70 util::StatusOr<VerifiedJwt> JwtMacImpl::VerifyMacAndDecodeWithKid(
71 absl::string_view compact, const JwtValidator& validator,
72 absl::optional<absl::string_view> kid) const {
73 std::size_t mac_pos = compact.find_last_of('.');
74 if (mac_pos == absl::string_view::npos) {
75 return util::Status(absl::StatusCode::kInvalidArgument, "invalid token");
76 }
77 absl::string_view unsigned_token = compact.substr(0, mac_pos);
78 std::string mac_value;
79 if (!DecodeSignature(compact.substr(mac_pos + 1), &mac_value)) {
80 return util::Status(absl::StatusCode::kInvalidArgument, "invalid JWT MAC");
81 }
82 util::Status verify_result = mac_->VerifyMac(mac_value, unsigned_token);
83 if (!verify_result.ok()) {
84 // Use a different error code so that we can distinguish it.
85 return util::Status(absl::StatusCode::kUnauthenticated,
86 verify_result.message());
87 }
88 std::vector<absl::string_view> parts = absl::StrSplit(unsigned_token, '.');
89 if (parts.size() != 2) {
90 return util::Status(
91 absl::StatusCode::kInvalidArgument,
92 "only tokens in JWS compact serialization format are supported");
93 }
94 std::string json_header;
95 if (!DecodeHeader(parts[0], &json_header)) {
96 return util::Status(absl::StatusCode::kInvalidArgument, "invalid header");
97 }
98 util::StatusOr<google::protobuf::Struct> header =
99 JsonStringToProtoStruct(json_header);
100 if (!header.ok()) {
101 return header.status();
102 }
103 util::Status validate_header_result =
104 ValidateHeader(*header, algorithm_, kid, custom_kid_);
105 if (!validate_header_result.ok()) {
106 return validate_header_result;
107 }
108 std::string json_payload;
109 if (!DecodePayload(parts[1], &json_payload)) {
110 return util::Status(absl::StatusCode::kInvalidArgument,
111 "invalid JWT payload");
112 }
113 util::StatusOr<RawJwt> raw_jwt =
114 RawJwtParser::FromJson(GetTypeHeader(*header), json_payload);
115 if (!raw_jwt.ok()) {
116 return raw_jwt.status();
117 }
118 util::Status validate_result = validator.Validate(*raw_jwt);
119 if (!validate_result.ok()) {
120 return validate_result;
121 }
122 return VerifiedJwt(*std::move(raw_jwt));
123 }
124
125 } // namespace jwt_internal
126 } // namespace tink
127 } // namespace crypto
128