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 // Implementation of a JWT Service.
18 #include "jwt_impl.h"
19
20 #include <memory>
21 #include <ostream>
22 #include <sstream>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "absl/time/time.h"
28 #include "tink/binary_keyset_reader.h"
29 #include "tink/binary_keyset_writer.h"
30 #include "tink/cleartext_keyset_handle.h"
31 #include "tink/jwt/jwk_set_converter.h"
32 #include "tink/jwt/jwt_mac.h"
33 #include "tink/jwt/jwt_public_key_sign.h"
34 #include "tink/jwt/jwt_public_key_verify.h"
35 #include "tink/jwt/raw_jwt.h"
36 #include "tink/util/status.h"
37 #include "create.h"
38
39 namespace tink_testing_api {
40
41 using ::crypto::tink::BinaryKeysetReader;
42 using ::crypto::tink::BinaryKeysetWriter;
43 using ::crypto::tink::CleartextKeysetHandle;
44 using ::crypto::tink::JwtMac;
45 using ::crypto::tink::JwtPublicKeySign;
46 using ::crypto::tink::JwtPublicKeyVerify;
47 using ::crypto::tink::KeysetReader;
48 using ::crypto::tink::RawJwt;
49 using ::crypto::tink::VerifiedJwt;
50 using ::crypto::tink::util::StatusOr;
51
52 using ::crypto::tink::JwkSetToPublicKeysetHandle;
53
TimestampToTime(google::protobuf::Timestamp t)54 absl::Time TimestampToTime(google::protobuf::Timestamp t) {
55 return absl::FromUnixMillis(t.seconds() * 1000 + t.nanos() / 1000000);
56 }
57
TimeToTimestamp(absl::Time time)58 google::protobuf::Timestamp TimeToTimestamp(absl::Time time) {
59 int64_t millis = absl::ToUnixMillis(time);
60 int64_t seconds = millis / 1000;
61 int32_t nanos = (millis - seconds * 1000) * 1000000;
62 google::protobuf::Timestamp timestamp;
63 timestamp.set_seconds(seconds);
64 timestamp.set_nanos(nanos);
65 return timestamp;
66 }
67
RawJwtFromProto(const JwtToken & raw_jwt_proto)68 crypto::tink::util::StatusOr<crypto::tink::RawJwt> RawJwtFromProto(
69 const JwtToken& raw_jwt_proto) {
70 auto builder = crypto::tink::RawJwtBuilder();
71 if (raw_jwt_proto.has_type_header()) {
72 builder.SetTypeHeader(raw_jwt_proto.type_header().value());
73 }
74 if (raw_jwt_proto.has_issuer()) {
75 builder.SetIssuer(raw_jwt_proto.issuer().value());
76 }
77 if (raw_jwt_proto.has_subject()) {
78 builder.SetSubject(raw_jwt_proto.subject().value());
79 }
80 for (const std::string& audience : raw_jwt_proto.audiences()) {
81 builder.AddAudience(audience);
82 }
83 if (raw_jwt_proto.has_jwt_id()) {
84 builder.SetJwtId(raw_jwt_proto.jwt_id().value());
85 }
86 if (raw_jwt_proto.has_expiration()) {
87 builder.SetExpiration(TimestampToTime(raw_jwt_proto.expiration()));
88 } else {
89 builder.WithoutExpiration();
90 }
91 if (raw_jwt_proto.has_issued_at()) {
92 builder.SetIssuedAt(TimestampToTime(raw_jwt_proto.issued_at()));
93 }
94 if (raw_jwt_proto.has_not_before()) {
95 builder.SetNotBefore(TimestampToTime(raw_jwt_proto.not_before()));
96 }
97 auto claims = raw_jwt_proto.custom_claims();
98 for (auto it = claims.begin(); it != claims.end(); it++) {
99 const auto& name = it->first;
100 const auto& value = it->second;
101 if (value.kind_case() == JwtClaimValue::kNullValue) {
102 builder.AddNullClaim(name);
103 } else if (value.kind_case() == JwtClaimValue::kBoolValue) {
104 builder.AddBooleanClaim(name, value.bool_value());
105 } else if (value.kind_case() == JwtClaimValue::kNumberValue) {
106 builder.AddNumberClaim(name, value.number_value());
107 } else if (value.kind_case() == JwtClaimValue::kStringValue) {
108 builder.AddStringClaim(name, value.string_value());
109 } else if (value.kind_case() == JwtClaimValue::kJsonObjectValue) {
110 builder.AddJsonObjectClaim(name, value.json_object_value());
111 } else if (value.kind_case() == JwtClaimValue::kJsonArrayValue) {
112 builder.AddJsonArrayClaim(name, value.json_array_value());
113 }
114 }
115 return builder.Build();
116 }
117
VerifiedJwtToProto(const crypto::tink::VerifiedJwt & verified_jwt)118 JwtToken VerifiedJwtToProto(const crypto::tink::VerifiedJwt& verified_jwt) {
119 JwtToken token;
120 if (verified_jwt.HasTypeHeader()) {
121 token.mutable_type_header()->set_value(
122 verified_jwt.GetTypeHeader().value());
123 }
124 if (verified_jwt.HasIssuer()) {
125 token.mutable_issuer()->set_value(verified_jwt.GetIssuer().value());
126 }
127 if (verified_jwt.HasSubject()) {
128 token.mutable_subject()->set_value(verified_jwt.GetSubject().value());
129 }
130 if (verified_jwt.HasAudiences()) {
131 std::vector<std::string> audiences = verified_jwt.GetAudiences().value();
132 for (const std::string& audience : audiences) {
133 token.add_audiences(audience);
134 }
135 }
136 if (verified_jwt.HasJwtId()) {
137 token.mutable_jwt_id()->set_value(verified_jwt.GetJwtId().value());
138 }
139 if (verified_jwt.HasExpiration()) {
140 *token.mutable_expiration() =
141 TimeToTimestamp(verified_jwt.GetExpiration().value());
142 }
143 if (verified_jwt.HasIssuedAt()) {
144 *token.mutable_issued_at() =
145 TimeToTimestamp(verified_jwt.GetIssuedAt().value());
146 }
147 if (verified_jwt.HasNotBefore()) {
148 *token.mutable_not_before() =
149 TimeToTimestamp(verified_jwt.GetNotBefore().value());
150 }
151 std::vector<std::string> names = verified_jwt.CustomClaimNames();
152 for (const std::string& name : names) {
153 if (verified_jwt.IsNullClaim(name)) {
154 (*token.mutable_custom_claims())[name].set_null_value(
155 NullValue::NULL_VALUE);
156 } else if (verified_jwt.HasBooleanClaim(name)) {
157 (*token.mutable_custom_claims())[name].set_bool_value(
158 verified_jwt.GetBooleanClaim(name).value());
159 } else if (verified_jwt.HasNumberClaim(name)) {
160 (*token.mutable_custom_claims())[name].set_number_value(
161 verified_jwt.GetNumberClaim(name).value());
162 } else if (verified_jwt.HasStringClaim(name)) {
163 (*token.mutable_custom_claims())[name].set_string_value(
164 verified_jwt.GetStringClaim(name).value());
165 } else if (verified_jwt.HasJsonObjectClaim(name)) {
166 (*token.mutable_custom_claims())[name].set_json_object_value(
167 verified_jwt.GetJsonObjectClaim(name).value());
168 } else if (verified_jwt.HasJsonArrayClaim(name)) {
169 (*token.mutable_custom_claims())[name].set_json_array_value(
170 verified_jwt.GetJsonArrayClaim(name).value());
171 }
172 }
173 return token;
174 }
175
JwtValidatorFromProto(const JwtValidator & validator_proto)176 crypto::tink::util::StatusOr<crypto::tink::JwtValidator> JwtValidatorFromProto(
177 const JwtValidator& validator_proto) {
178 auto builder = crypto::tink::JwtValidatorBuilder();
179 if (validator_proto.has_expected_type_header()) {
180 builder.ExpectTypeHeader(validator_proto.expected_type_header().value());
181 }
182 if (validator_proto.has_expected_issuer()) {
183 builder.ExpectIssuer(validator_proto.expected_issuer().value());
184 }
185 if (validator_proto.has_expected_audience()) {
186 builder.ExpectAudience(validator_proto.expected_audience().value());
187 }
188 if (validator_proto.ignore_type_header()) {
189 builder.IgnoreTypeHeader();
190 }
191 if (validator_proto.ignore_issuer()) {
192 builder.IgnoreIssuer();
193 }
194 if (validator_proto.ignore_audience()) {
195 builder.IgnoreAudiences();
196 }
197 if (validator_proto.allow_missing_expiration()) {
198 builder.AllowMissingExpiration();
199 }
200 if (validator_proto.expect_issued_in_the_past()) {
201 builder.ExpectIssuedInThePast();
202 }
203 if (validator_proto.has_now()) {
204 builder.SetFixedNow(TimestampToTime(validator_proto.now()));
205 }
206 if (validator_proto.has_clock_skew()) {
207 builder.SetClockSkew(
208 absl::Seconds(validator_proto.clock_skew().seconds()));
209 }
210 return builder.Build();
211 }
212
CreateJwtMac(grpc::ServerContext * context,const CreationRequest * request,CreationResponse * response)213 ::grpc::Status JwtImpl::CreateJwtMac(grpc::ServerContext* context,
214 const CreationRequest* request,
215 CreationResponse* response) {
216 return CreatePrimitiveForRpc<JwtMac>(request, response);
217 }
218
CreateJwtPublicKeySign(grpc::ServerContext * context,const CreationRequest * request,CreationResponse * response)219 ::grpc::Status JwtImpl::CreateJwtPublicKeySign(grpc::ServerContext* context,
220 const CreationRequest* request,
221 CreationResponse* response) {
222 return CreatePrimitiveForRpc<JwtPublicKeySign>(request, response);
223 }
224
CreateJwtPublicKeyVerify(grpc::ServerContext * context,const CreationRequest * request,CreationResponse * response)225 ::grpc::Status JwtImpl::CreateJwtPublicKeyVerify(grpc::ServerContext* context,
226 const CreationRequest* request,
227 CreationResponse* response) {
228 return CreatePrimitiveForRpc<JwtPublicKeyVerify>(request, response);
229 }
230
231 // Computes a MAC and generates a signed compact JWT
ComputeMacAndEncode(grpc::ServerContext * context,const JwtSignRequest * request,JwtSignResponse * response)232 grpc::Status JwtImpl::ComputeMacAndEncode(grpc::ServerContext* context,
233 const JwtSignRequest* request,
234 JwtSignResponse* response) {
235 StatusOr<std::unique_ptr<JwtMac>> jwt_mac =
236 PrimitiveFromSerializedBinaryProtoKeyset<JwtMac>(
237 request->annotated_keyset());
238 if (!jwt_mac.ok()) {
239 response->set_err(std::string(jwt_mac.status().message()));
240 return grpc::Status::OK;
241 }
242 StatusOr<RawJwt> raw_jwt = RawJwtFromProto(request->raw_jwt());
243 if (!raw_jwt.ok()) {
244 response->set_err(std::string(raw_jwt.status().message()));
245 return grpc::Status::OK;
246 }
247 StatusOr<std::string> compact =
248 (*jwt_mac)->ComputeMacAndEncode(*raw_jwt);
249 if (!compact.ok()) {
250 response->set_err(std::string(compact.status().message()));
251 return grpc::Status::OK;
252 }
253 response->set_signed_compact_jwt(*compact);
254 return grpc::Status::OK;
255 }
256
257 // Verifies a signed compact JWT
VerifyMacAndDecode(grpc::ServerContext * context,const JwtVerifyRequest * request,JwtVerifyResponse * response)258 grpc::Status JwtImpl::VerifyMacAndDecode(grpc::ServerContext* context,
259 const JwtVerifyRequest* request,
260 JwtVerifyResponse* response) {
261 StatusOr<std::unique_ptr<JwtMac>> jwt_mac =
262 PrimitiveFromSerializedBinaryProtoKeyset<JwtMac>(
263 request->annotated_keyset());
264 if (!jwt_mac.ok()) {
265 response->set_err(std::string(jwt_mac.status().message()));
266 return grpc::Status::OK;
267 }
268 StatusOr<crypto::tink::JwtValidator> validator =
269 JwtValidatorFromProto(request->validator());
270 StatusOr<VerifiedJwt> verified_jwt =
271 (*jwt_mac)->VerifyMacAndDecode(request->signed_compact_jwt(), *validator);
272 if (!verified_jwt.ok()) {
273 response->set_err(std::string(verified_jwt.status().message()));
274 return grpc::Status::OK;
275 }
276 *response->mutable_verified_jwt() = VerifiedJwtToProto(*verified_jwt);
277 return grpc::Status::OK;
278 }
279
PublicKeySignAndEncode(grpc::ServerContext * context,const JwtSignRequest * request,JwtSignResponse * response)280 grpc::Status JwtImpl::PublicKeySignAndEncode(grpc::ServerContext* context,
281 const JwtSignRequest* request,
282 JwtSignResponse* response) {
283 StatusOr<std::unique_ptr<JwtPublicKeySign>> jwt_sign =
284 PrimitiveFromSerializedBinaryProtoKeyset<JwtPublicKeySign>(
285 request->annotated_keyset());
286 if (!jwt_sign.ok()) {
287 response->set_err(std::string(jwt_sign.status().message()));
288 return grpc::Status::OK;
289 }
290 StatusOr<RawJwt> raw_jwt = RawJwtFromProto(request->raw_jwt());
291 if (!raw_jwt.ok()) {
292 response->set_err(std::string(raw_jwt.status().message()));
293 return grpc::Status::OK;
294 }
295 StatusOr<std::string> compact = (*jwt_sign)->SignAndEncode(*raw_jwt);
296 if (!compact.ok()) {
297 response->set_err(std::string(compact.status().message()));
298 return grpc::Status::OK;
299 }
300 response->set_signed_compact_jwt(*compact);
301 return grpc::Status::OK;
302 }
303
PublicKeyVerifyAndDecode(grpc::ServerContext * context,const JwtVerifyRequest * request,JwtVerifyResponse * response)304 grpc::Status JwtImpl::PublicKeyVerifyAndDecode(grpc::ServerContext* context,
305 const JwtVerifyRequest* request,
306 JwtVerifyResponse* response) {
307 StatusOr<std::unique_ptr<JwtPublicKeyVerify>> jwt_verify =
308 PrimitiveFromSerializedBinaryProtoKeyset<JwtPublicKeyVerify>(
309 request->annotated_keyset());
310 if (!jwt_verify.ok()) {
311 response->set_err(std::string(jwt_verify.status().message()));
312 return grpc::Status::OK;
313 }
314 StatusOr<crypto::tink::JwtValidator> validator =
315 JwtValidatorFromProto(request->validator());
316 StatusOr<VerifiedJwt> verified_jwt =
317 (*jwt_verify)->VerifyAndDecode(request->signed_compact_jwt(), *validator);
318 if (!verified_jwt.ok()) {
319 response->set_err(std::string(verified_jwt.status().message()));
320 return grpc::Status::OK;
321 }
322 *response->mutable_verified_jwt() = VerifiedJwtToProto(*verified_jwt);
323 return grpc::Status::OK;
324 }
325
ToJwkSet(grpc::ServerContext * context,const JwtToJwkSetRequest * request,JwtToJwkSetResponse * response)326 ::grpc::Status JwtImpl::ToJwkSet(grpc::ServerContext* context,
327 const JwtToJwkSetRequest* request,
328 JwtToJwkSetResponse* response) {
329 StatusOr<std::unique_ptr<KeysetReader>> reader =
330 BinaryKeysetReader::New(request->keyset());
331 if (!reader.ok()) {
332 response->set_err(std::string(reader.status().message()));
333 return ::grpc::Status::OK;
334 }
335 StatusOr<std::unique_ptr<::crypto::tink::KeysetHandle>> handle =
336 CleartextKeysetHandle::Read(*std::move(reader));
337 if (!handle.ok()) {
338 response->set_err(std::string(handle.status().message()));
339 return ::grpc::Status::OK;
340 }
341 StatusOr<std::string> jwk_set = JwkSetFromPublicKeysetHandle(**handle);
342 if (!jwk_set.ok()) {
343 response->set_err(std::string(jwk_set.status().message()));
344 return ::grpc::Status::OK;
345 }
346 response->set_jwk_set(*jwk_set);
347 return ::grpc::Status::OK;
348 }
349
FromJwkSet(grpc::ServerContext * context,const JwtFromJwkSetRequest * request,JwtFromJwkSetResponse * response)350 ::grpc::Status JwtImpl::FromJwkSet(grpc::ServerContext* context,
351 const JwtFromJwkSetRequest* request,
352 JwtFromJwkSetResponse* response) {
353 StatusOr<std::unique_ptr<::crypto::tink::KeysetHandle>> keyset_handle =
354 JwkSetToPublicKeysetHandle(request->jwk_set());
355 if (!keyset_handle.ok()) {
356 response->set_err(std::string(keyset_handle.status().message()));
357 return ::grpc::Status::OK;
358 }
359 std::stringbuf keyset;
360 StatusOr<std::unique_ptr<crypto::tink::BinaryKeysetWriter>> writer =
361 BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
362 if (!writer.ok()) {
363 response->set_err(std::string(writer.status().message()));
364 return ::grpc::Status::OK;
365 }
366 crypto::tink::util::Status status =
367 CleartextKeysetHandle::Write(writer->get(), **keyset_handle);
368 if (!status.ok()) {
369 response->set_err(std::string(status.message()));
370 return ::grpc::Status::OK;
371 }
372 response->set_keyset(keyset.str());
373 return ::grpc::Status::OK;
374 }
375
376 } // namespace tink_testing_api
377