blob: 63f55a9cb566ec1ef76a9af82450357f21b0e49c [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
// Implementation of a JWT Service.
#include "jwt_impl.h"
#include <string>
#include "absl/time/time.h"
#include "tink/binary_keyset_reader.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/jwt/jwt_mac.h"
#include "tink/jwt/jwt_public_key_sign.h"
#include "tink/jwt/jwt_public_key_verify.h"
#include "proto/testing/testing_api.grpc.pb.h"
#include "tink/jwt/raw_jwt.h"
#include "tink/util/status.h"
namespace tink_testing_api {
using ::crypto::tink::BinaryKeysetReader;
using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::util::StatusOr;
using ::crypto::tink::JwtPublicKeySign;
using ::crypto::tink::JwtPublicKeyVerify;
using ::crypto::tink::JwtMac;
using ::crypto::tink::RawJwt;
using ::crypto::tink::VerifiedJwt;
using ::crypto::tink::KeysetReader;
using ::grpc::ServerContext;
absl::Time TimestampToTime(tink_testing_api::Timestamp t) {
return absl::FromUnixMillis(t.seconds() * 1000 + t.nanos() / 1000000);
}
Timestamp TimeToTimestamp(absl::Time time) {
int64_t millis = absl::ToUnixMillis(time);
int64_t seconds = millis / 1000;
int32_t nanos = (millis - seconds * 1000) * 1000000;
Timestamp timestamp;
timestamp.set_seconds(seconds);
timestamp.set_nanos(nanos);
return timestamp;
}
crypto::tink::util::StatusOr<crypto::tink::RawJwt> RawJwtFromProto(
const JwtToken& raw_jwt_proto) {
auto builder = crypto::tink::RawJwtBuilder();
if (raw_jwt_proto.has_issuer()) {
builder.SetIssuer(raw_jwt_proto.issuer().value());
}
if (raw_jwt_proto.has_subject()) {
builder.SetSubject(raw_jwt_proto.subject().value());
}
for (const std::string& audience : raw_jwt_proto.audiences()) {
builder.AddAudience(audience);
}
if (raw_jwt_proto.has_jwt_id()) {
builder.SetJwtId(raw_jwt_proto.jwt_id().value());
}
if (raw_jwt_proto.has_expiration()) {
auto status = builder.SetExpiration(
TimestampToTime(raw_jwt_proto.expiration()));
if (!status.ok()) {
return status;
}
}
if (raw_jwt_proto.has_issued_at()) {
auto status = builder.SetIssuedAt(
TimestampToTime(raw_jwt_proto.issued_at()));
if (!status.ok()) {
return status;
}
}
if (raw_jwt_proto.has_not_before()) {
auto status = builder.SetNotBefore(
TimestampToTime(raw_jwt_proto.not_before()));
if (!status.ok()) {
return status;
}
}
auto claims = raw_jwt_proto.custom_claims();
for (auto it = claims.begin(); it != claims.end(); it++) {
const auto& name = it->first;
const auto& value = it->second;
if (value.kind_case() == JwtClaimValue::kNullValue) {
auto status = builder.AddNullClaim(name);
if (!status.ok()) {
return status;
}
} else if (value.kind_case() == JwtClaimValue::kBoolValue) {
auto status = builder.AddBooleanClaim(name, value.bool_value());
if (!status.ok()) {
return status;
}
} else if (value.kind_case() == JwtClaimValue::kNumberValue) {
auto status = builder.AddNumberClaim(name, value.number_value());
if (!status.ok()) {
return status;
}
} else if (value.kind_case() == JwtClaimValue::kStringValue) {
auto status = builder.AddStringClaim(name, value.string_value());
if (!status.ok()) {
return status;
}
} else if (value.kind_case() == JwtClaimValue::kJsonObjectValue) {
auto status = builder.AddJsonObjectClaim(name, value.json_object_value());
if (!status.ok()) {
return status;
}
} else if (value.kind_case() == JwtClaimValue::kJsonArrayValue) {
auto status = builder.AddJsonArrayClaim(name, value.json_array_value());
if (!status.ok()) {
return status;
}
}
}
return builder.Build();
}
JwtToken VerifiedJwtToProto(const crypto::tink::VerifiedJwt& verified_jwt) {
JwtToken token;
if (verified_jwt.HasIssuer()) {
token.mutable_issuer()->set_value(verified_jwt.GetIssuer().ValueOrDie());
}
if (verified_jwt.HasSubject()) {
token.mutable_subject()->set_value(verified_jwt.GetSubject().ValueOrDie());
}
if (verified_jwt.HasAudiences()) {
std::vector<std::string> audiences =
verified_jwt.GetAudiences().ValueOrDie();
for (const std::string& audience : audiences) {
token.add_audiences(audience);
}
}
if (verified_jwt.HasJwtId()) {
token.mutable_jwt_id()->set_value(verified_jwt.GetJwtId().ValueOrDie());
}
if (verified_jwt.HasExpiration()) {
*token.mutable_expiration() =
TimeToTimestamp(verified_jwt.GetExpiration().ValueOrDie());
}
if (verified_jwt.HasIssuedAt()) {
*token.mutable_issued_at() =
TimeToTimestamp(verified_jwt.GetIssuedAt().ValueOrDie());
}
if (verified_jwt.HasNotBefore()) {
*token.mutable_not_before() =
TimeToTimestamp(verified_jwt.GetNotBefore().ValueOrDie());
}
std::vector<std::string> names = verified_jwt.CustomClaimNames();
for (const std::string& name : names) {
if (verified_jwt.IsNullClaim(name)) {
(*token.mutable_custom_claims())[name].set_null_value(
NullValue::NULL_VALUE);
} else if (verified_jwt.HasBooleanClaim(name)) {
(*token.mutable_custom_claims())[name].set_bool_value(
verified_jwt.GetBooleanClaim(name).ValueOrDie());
} else if (verified_jwt.HasNumberClaim(name)) {
(*token.mutable_custom_claims())[name].set_number_value(
verified_jwt.GetNumberClaim(name).ValueOrDie());
} else if (verified_jwt.HasStringClaim(name)) {
(*token.mutable_custom_claims())[name].set_string_value(
verified_jwt.GetStringClaim(name).ValueOrDie());
} else if (verified_jwt.HasJsonObjectClaim(name)) {
(*token.mutable_custom_claims())[name].set_json_object_value(
verified_jwt.GetJsonObjectClaim(name).ValueOrDie());
} else if (verified_jwt.HasJsonArrayClaim(name)) {
(*token.mutable_custom_claims())[name].set_json_array_value(
verified_jwt.GetJsonArrayClaim(name).ValueOrDie());
}
}
return token;
}
crypto::tink::util::StatusOr<crypto::tink::JwtValidator> JwtValidatorFromProto(
const JwtValidator& validator_proto) {
auto builder = crypto::tink::JwtValidatorBuilder();
if (validator_proto.has_issuer()) {
builder.SetIssuer(validator_proto.issuer().value());
}
if (validator_proto.has_subject()) {
builder.SetSubject(validator_proto.subject().value());
}
if (validator_proto.has_audience()) {
builder.SetAudience(validator_proto.audience().value());
}
if (validator_proto.has_now()) {
builder.SetFixedNow(TimestampToTime(validator_proto.now()));
}
if (validator_proto.has_clock_skew()) {
auto skew_status = builder.SetClockSkew(
absl::Seconds(validator_proto.clock_skew().seconds()));
if (!skew_status.ok()) {
return skew_status;
}
}
return builder.Build();
}
// Computes a MAC and generates a signed compact JWT
::grpc::Status JwtImpl::ComputeMacAndEncode(grpc::ServerContext* context,
const JwtSignRequest* request,
JwtSignResponse* response) {
StatusOr<std::unique_ptr<KeysetReader>> reader_or =
BinaryKeysetReader::New(request->keyset());
if (!reader_or.ok()) {
response->set_err(reader_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<KeysetHandle>> handle_or =
CleartextKeysetHandle::Read(std::move(reader_or.ValueOrDie()));
if (!handle_or.ok()) {
response->set_err(handle_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<JwtMac>> jwt_mac_or =
handle_or.ValueOrDie()->GetPrimitive<JwtMac>();
if (!jwt_mac_or.ok()) {
response->set_err(jwt_mac_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<RawJwt> raw_jwt_or = RawJwtFromProto(request->raw_jwt());
if (!raw_jwt_or.ok()) {
response->set_err(raw_jwt_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::string> compact_or =
jwt_mac_or.ValueOrDie()->ComputeMacAndEncode(raw_jwt_or.ValueOrDie());
if (!compact_or.ok()) {
response->set_err(compact_or.status().error_message());
return ::grpc::Status::OK;
}
response->set_signed_compact_jwt(compact_or.ValueOrDie());
return ::grpc::Status::OK;
}
// Verifies a signed compact JWT
::grpc::Status JwtImpl::VerifyMacAndDecode(grpc::ServerContext* context,
const JwtVerifyRequest* request,
JwtVerifyResponse* response) {
StatusOr<std::unique_ptr<KeysetReader>> reader_or =
BinaryKeysetReader::New(request->keyset());
if (!reader_or.ok()) {
response->set_err(reader_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<KeysetHandle>> handle_or =
CleartextKeysetHandle::Read(std::move(reader_or.ValueOrDie()));
if (!handle_or.ok()) {
response->set_err(handle_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<JwtMac>> jwt_mac_or =
handle_or.ValueOrDie()->GetPrimitive<JwtMac>();
if (!jwt_mac_or.ok()) {
response->set_err(jwt_mac_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<crypto::tink::JwtValidator> validator_or =
JwtValidatorFromProto(request->validator());
StatusOr<VerifiedJwt> verified_jwt_or =
jwt_mac_or.ValueOrDie()->VerifyMacAndDecode(request->signed_compact_jwt(),
validator_or.ValueOrDie());
if (!verified_jwt_or.ok()) {
response->set_err(verified_jwt_or.status().error_message());
return ::grpc::Status::OK;
}
*response->mutable_verified_jwt() =
VerifiedJwtToProto(verified_jwt_or.ValueOrDie());
return ::grpc::Status::OK;
}
::grpc::Status JwtImpl::PublicKeySignAndEncode(grpc::ServerContext* context,
const JwtSignRequest* request,
JwtSignResponse* response) {
StatusOr<std::unique_ptr<KeysetReader>> reader_or =
BinaryKeysetReader::New(request->keyset());
if (!reader_or.ok()) {
response->set_err(reader_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<KeysetHandle>> handle_or =
CleartextKeysetHandle::Read(std::move(reader_or.ValueOrDie()));
if (!handle_or.ok()) {
response->set_err(handle_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<JwtPublicKeySign>> jwt_sign_or =
handle_or.ValueOrDie()->GetPrimitive<JwtPublicKeySign>();
if (!jwt_sign_or.ok()) {
response->set_err(jwt_sign_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<RawJwt> raw_jwt_or = RawJwtFromProto(request->raw_jwt());
if (!raw_jwt_or.ok()) {
response->set_err(raw_jwt_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::string> compact_or =
jwt_sign_or.ValueOrDie()->SignAndEncode(raw_jwt_or.ValueOrDie());
if (!compact_or.ok()) {
response->set_err(compact_or.status().error_message());
return ::grpc::Status::OK;
}
response->set_signed_compact_jwt(compact_or.ValueOrDie());
return ::grpc::Status::OK;
}
::grpc::Status JwtImpl::PublicKeyVerifyAndDecode(grpc::ServerContext* context,
const JwtVerifyRequest* request,
JwtVerifyResponse* response) {
StatusOr<std::unique_ptr<KeysetReader>> reader_or =
BinaryKeysetReader::New(request->keyset());
if (!reader_or.ok()) {
response->set_err(reader_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<KeysetHandle>> handle_or =
CleartextKeysetHandle::Read(std::move(reader_or.ValueOrDie()));
if (!handle_or.ok()) {
response->set_err(handle_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<std::unique_ptr<JwtPublicKeyVerify>> jwt_verify_or =
handle_or.ValueOrDie()->GetPrimitive<JwtPublicKeyVerify>();
if (!jwt_verify_or.ok()) {
response->set_err(jwt_verify_or.status().error_message());
return ::grpc::Status::OK;
}
StatusOr<crypto::tink::JwtValidator> validator_or =
JwtValidatorFromProto(request->validator());
StatusOr<VerifiedJwt> verified_jwt_or =
jwt_verify_or.ValueOrDie()->VerifyAndDecode(request->signed_compact_jwt(),
validator_or.ValueOrDie());
if (!verified_jwt_or.ok()) {
response->set_err(verified_jwt_or.status().error_message());
return ::grpc::Status::OK;
}
*response->mutable_verified_jwt() =
VerifiedJwtToProto(verified_jwt_or.ValueOrDie());
return ::grpc::Status::OK;
}
} // namespace tink_testing_api