xref: /aosp_15_r20/external/tink/cc/jwt/internal/jwt_mac_impl.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_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