blob: 4e9cea1b4002707b7261ce69c27e7181ebc07b58 [file] [log] [blame]
// Copyright 2023 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.
//
////////////////////////////////////////////////////////////////////////////////
#include "tink/signature/rsa_ssa_pss_proto_serialization.h"
#include <string>
#include "absl/base/attributes.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "tink/big_integer.h"
#include "tink/insecure_secret_key_access.h"
#include "tink/internal/key_parser.h"
#include "tink/internal/key_serializer.h"
#include "tink/internal/mutable_serialization_registry.h"
#include "tink/internal/parameters_parser.h"
#include "tink/internal/parameters_serializer.h"
#include "tink/internal/proto_key_serialization.h"
#include "tink/internal/proto_parameters_serialization.h"
#include "tink/partial_key_access.h"
#include "tink/restricted_big_integer.h"
#include "tink/restricted_data.h"
#include "tink/secret_key_access_token.h"
#include "tink/signature/rsa_ssa_pss_parameters.h"
#include "tink/signature/rsa_ssa_pss_private_key.h"
#include "tink/signature/rsa_ssa_pss_public_key.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "proto/common.pb.h"
#include "proto/rsa_ssa_pss.pb.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
namespace {
using ::google::crypto::tink::HashType;
using ::google::crypto::tink::KeyData;
using ::google::crypto::tink::OutputPrefixType;
using ::google::crypto::tink::RsaSsaPssKeyFormat;
using ::google::crypto::tink::RsaSsaPssParams;
using RsaSsaPssProtoParametersParserImpl =
internal::ParametersParserImpl<internal::ProtoParametersSerialization,
RsaSsaPssParameters>;
using RsaSsaPssProtoParametersSerializerImpl =
internal::ParametersSerializerImpl<RsaSsaPssParameters,
internal::ProtoParametersSerialization>;
using RsaSsaPssProtoPublicKeyParserImpl =
internal::KeyParserImpl<internal::ProtoKeySerialization,
RsaSsaPssPublicKey>;
using RsaSsaPssProtoPublicKeySerializerImpl =
internal::KeySerializerImpl<RsaSsaPssPublicKey,
internal::ProtoKeySerialization>;
using RsaSsaPssProtoPrivateKeyParserImpl =
internal::KeyParserImpl<internal::ProtoKeySerialization,
RsaSsaPssPrivateKey>;
using RsaSsaPssProtoPrivateKeySerializerImpl =
internal::KeySerializerImpl<RsaSsaPssPrivateKey,
internal::ProtoKeySerialization>;
const absl::string_view kPrivateTypeUrl =
"type.googleapis.com/google.crypto.tink.RsaSsaPssPrivateKey";
const absl::string_view kPublicTypeUrl =
"type.googleapis.com/google.crypto.tink.RsaSsaPssPublicKey";
util::StatusOr<RsaSsaPssParameters::Variant> ToVariant(
OutputPrefixType output_prefix_type) {
switch (output_prefix_type) {
case OutputPrefixType::LEGACY:
return RsaSsaPssParameters::Variant::kLegacy;
case OutputPrefixType::CRUNCHY:
return RsaSsaPssParameters::Variant::kCrunchy;
case OutputPrefixType::RAW:
return RsaSsaPssParameters::Variant::kNoPrefix;
case OutputPrefixType::TINK:
return RsaSsaPssParameters::Variant::kTink;
default:
return util::Status(absl::StatusCode::kInvalidArgument,
"Could not determine RsaSsaPssParameters::Variant");
}
}
util::StatusOr<OutputPrefixType> ToOutputPrefixType(
RsaSsaPssParameters::Variant variant) {
switch (variant) {
case RsaSsaPssParameters::Variant::kLegacy:
return OutputPrefixType::LEGACY;
case RsaSsaPssParameters::Variant::kCrunchy:
return OutputPrefixType::CRUNCHY;
case RsaSsaPssParameters::Variant::kNoPrefix:
return OutputPrefixType::RAW;
case RsaSsaPssParameters::Variant::kTink:
return OutputPrefixType::TINK;
default:
return util::Status(absl::StatusCode::kInvalidArgument,
"Could not determine output prefix type.");
}
}
util::StatusOr<RsaSsaPssParameters::HashType> ToEnumHashType(
HashType hash_type) {
switch (hash_type) {
case HashType::SHA256:
return RsaSsaPssParameters::HashType::kSha256;
case HashType::SHA384:
return RsaSsaPssParameters::HashType::kSha384;
case HashType::SHA512:
return RsaSsaPssParameters::HashType::kSha512;
default:
return util::Status(absl::StatusCode::kInvalidArgument,
"Could not determine HashType");
}
}
util::StatusOr<HashType> ToProtoHashType(
RsaSsaPssParameters::HashType hash_type) {
switch (hash_type) {
case RsaSsaPssParameters::HashType::kSha256:
return HashType::SHA256;
case RsaSsaPssParameters::HashType::kSha384:
return HashType::SHA384;
case RsaSsaPssParameters::HashType::kSha512:
return HashType::SHA512;
default:
return util::Status(absl::StatusCode::kInvalidArgument,
"Could not determine RsaSsaPssParameters::HashType");
}
}
util::StatusOr<RsaSsaPssParameters> ToParameters(
OutputPrefixType output_prefix_type, const RsaSsaPssParams& params,
int modulus_size_in_bits, const BigInteger& public_exponent) {
util::StatusOr<RsaSsaPssParameters::Variant> variant =
ToVariant(output_prefix_type);
if (!variant.ok()) {
return variant.status();
}
util::StatusOr<RsaSsaPssParameters::HashType> sig_hash_type =
ToEnumHashType(params.sig_hash());
if (!sig_hash_type.ok()) {
return sig_hash_type.status();
}
util::StatusOr<RsaSsaPssParameters::HashType> mgf1_hash_type =
ToEnumHashType(params.sig_hash());
if (!mgf1_hash_type.ok()) {
return mgf1_hash_type.status();
}
return RsaSsaPssParameters::Builder()
.SetVariant(*variant)
.SetSigHashType(*sig_hash_type)
.SetMgf1HashType(*mgf1_hash_type)
.SetModulusSizeInBits(modulus_size_in_bits)
.SetPublicExponent(public_exponent)
.SetSaltLengthInBytes(params.salt_length())
.Build();
}
util::StatusOr<RsaSsaPssParams> FromParameters(RsaSsaPssParameters parameters) {
util::StatusOr<HashType> sig_hash_type =
ToProtoHashType(parameters.GetSigHashType());
if (!sig_hash_type.ok()) {
return sig_hash_type.status();
}
util::StatusOr<HashType> mgf1_hash_type =
ToProtoHashType(parameters.GetMgf1HashType());
if (!mgf1_hash_type.ok()) {
return mgf1_hash_type.status();
}
RsaSsaPssParams params;
params.set_sig_hash(*sig_hash_type);
params.set_mgf1_hash(*mgf1_hash_type);
params.set_salt_length(parameters.GetSaltLengthInBytes());
return params;
}
util::StatusOr<RsaSsaPssParameters> ParseParameters(
const internal::ProtoParametersSerialization& serialization) {
if (serialization.GetKeyTemplate().type_url() != kPrivateTypeUrl) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Wrong type URL when parsing RsaSsaPssParameters.");
}
RsaSsaPssKeyFormat proto_key_format;
if (!proto_key_format.ParseFromString(
serialization.GetKeyTemplate().value())) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Failed to parse RsaSsaPssKeyFormat proto");
}
if (!proto_key_format.has_params()) {
return util::Status(absl::StatusCode::kInvalidArgument,
"RsaSsaPssKeyFormat proto is missing params field.");
}
return ToParameters(serialization.GetKeyTemplate().output_prefix_type(),
proto_key_format.params(),
proto_key_format.modulus_size_in_bits(),
BigInteger(proto_key_format.public_exponent()));
}
util::StatusOr<RsaSsaPssPublicKey> ParsePublicKey(
const internal::ProtoKeySerialization& serialization,
absl::optional<SecretKeyAccessToken> token) {
if (serialization.TypeUrl() != kPublicTypeUrl) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Wrong type URL when parsing RsaSsaPssPublicKey.");
}
google::crypto::tink::RsaSsaPssPublicKey proto_key;
const RestrictedData& restricted_data = serialization.SerializedKeyProto();
if (!proto_key.ParseFromString(
restricted_data.GetSecret(InsecureSecretKeyAccess::Get()))) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Failed to parse RsaSsaPssPublicKey proto");
}
if (proto_key.version() != 0) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Only version 0 keys are accepted.");
}
BigInteger modulus(proto_key.n());
int modulus_size_in_bits = modulus.SizeInBytes() * 8;
util::StatusOr<RsaSsaPssParameters> parameters =
ToParameters(serialization.GetOutputPrefixType(), proto_key.params(),
modulus_size_in_bits, BigInteger(proto_key.e()));
if (!parameters.ok()) {
return parameters.status();
}
return RsaSsaPssPublicKey::Create(*parameters, modulus,
serialization.IdRequirement(),
GetPartialKeyAccess());
}
util::StatusOr<RsaSsaPssPrivateKey> ParsePrivateKey(
const internal::ProtoKeySerialization& serialization,
absl::optional<SecretKeyAccessToken> token) {
if (serialization.TypeUrl() != kPrivateTypeUrl) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Wrong type URL when parsing RsaSsaPssPrivateKey.");
}
if (!token.has_value()) {
return util::Status(absl::StatusCode::kPermissionDenied,
"SecretKeyAccess is required");
}
google::crypto::tink::RsaSsaPssPrivateKey proto_key;
const RestrictedData& restricted_data = serialization.SerializedKeyProto();
if (!proto_key.ParseFromString(restricted_data.GetSecret(*token))) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Failed to parse RsaSsaPssPrivateKey proto");
}
if (proto_key.version() != 0) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Only version 0 keys are accepted.");
}
BigInteger modulus(proto_key.public_key().n());
int modulus_size_in_bits = modulus.SizeInBytes() * 8;
util::StatusOr<RsaSsaPssParameters> parameters = ToParameters(
serialization.GetOutputPrefixType(), proto_key.public_key().params(),
modulus_size_in_bits, BigInteger(proto_key.public_key().e()));
if (!parameters.ok()) {
return parameters.status();
}
util::StatusOr<RsaSsaPssPublicKey> public_key = RsaSsaPssPublicKey::Create(
*parameters, modulus, serialization.IdRequirement(),
GetPartialKeyAccess());
if (!public_key.ok()) {
return public_key.status();
}
return RsaSsaPssPrivateKey::Builder()
.SetPublicKey(*public_key)
.SetPrimeP(RestrictedBigInteger(proto_key.p(), *token))
.SetPrimeQ(RestrictedBigInteger(proto_key.q(), *token))
.SetPrimeExponentP(RestrictedBigInteger(proto_key.dp(), *token))
.SetPrimeExponentQ(RestrictedBigInteger(proto_key.dq(), *token))
.SetPrivateExponent(RestrictedBigInteger(proto_key.d(), *token))
.SetCrtCoefficient(RestrictedBigInteger(proto_key.crt(), *token))
.Build(GetPartialKeyAccess());
}
util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters(
const RsaSsaPssParameters& parameters) {
util::StatusOr<OutputPrefixType> output_prefix_type =
ToOutputPrefixType(parameters.GetVariant());
if (!output_prefix_type.ok()) {
return output_prefix_type.status();
}
util::StatusOr<RsaSsaPssParams> params = FromParameters(parameters);
if (!params.ok()) {
return params.status();
}
RsaSsaPssKeyFormat proto_key_format;
proto_key_format.set_modulus_size_in_bits(parameters.GetModulusSizeInBits());
// OSS proto library complains if input is not converted to a string.
proto_key_format.set_public_exponent(
std::string(parameters.GetPublicExponent().GetValue()));
*proto_key_format.mutable_params() = *params;
return internal::ProtoParametersSerialization::Create(
kPrivateTypeUrl, *output_prefix_type,
proto_key_format.SerializeAsString());
}
util::StatusOr<internal::ProtoKeySerialization> SerializePublicKey(
const RsaSsaPssPublicKey& key, absl::optional<SecretKeyAccessToken> token) {
util::StatusOr<RsaSsaPssParams> params = FromParameters(key.GetParameters());
if (!params.ok()) {
return params.status();
}
google::crypto::tink::RsaSsaPssPublicKey proto_key;
proto_key.set_version(0);
*proto_key.mutable_params() = *params;
// OSS proto library complains if input is not converted to a string.
proto_key.set_n(
std::string(key.GetModulus(GetPartialKeyAccess()).GetValue()));
proto_key.set_e(
std::string(key.GetParameters().GetPublicExponent().GetValue()));
util::StatusOr<OutputPrefixType> output_prefix_type =
ToOutputPrefixType(key.GetParameters().GetVariant());
if (!output_prefix_type.ok()) {
return output_prefix_type.status();
}
RestrictedData restricted_output = RestrictedData(
proto_key.SerializeAsString(), InsecureSecretKeyAccess::Get());
return internal::ProtoKeySerialization::Create(
kPublicTypeUrl, restricted_output, KeyData::ASYMMETRIC_PUBLIC,
*output_prefix_type, key.GetIdRequirement());
}
util::StatusOr<internal::ProtoKeySerialization> SerializePrivateKey(
const RsaSsaPssPrivateKey& key,
absl::optional<SecretKeyAccessToken> token) {
if (!token.has_value()) {
return util::Status(absl::StatusCode::kPermissionDenied,
"SecretKeyAccess is required");
}
util::StatusOr<RsaSsaPssParams> params =
FromParameters(key.GetPublicKey().GetParameters());
if (!params.ok()) {
return params.status();
}
google::crypto::tink::RsaSsaPssPublicKey proto_public_key;
proto_public_key.set_version(0);
*proto_public_key.mutable_params() = *params;
// OSS proto library complains if input is not converted to a string.
proto_public_key.set_n(std::string(
key.GetPublicKey().GetModulus(GetPartialKeyAccess()).GetValue()));
proto_public_key.set_e(std::string(
key.GetPublicKey().GetParameters().GetPublicExponent().GetValue()));
google::crypto::tink::RsaSsaPssPrivateKey proto_private_key;
proto_private_key.set_version(0);
*proto_private_key.mutable_public_key() = proto_public_key;
// OSS proto library complains if input is not converted to a string.
proto_private_key.set_p(
std::string(key.GetPrimeP(GetPartialKeyAccess()).GetSecret(*token)));
proto_private_key.set_q(
std::string(key.GetPrimeQ(GetPartialKeyAccess()).GetSecret(*token)));
proto_private_key.set_dp(
std::string(key.GetPrimeExponentP().GetSecret(*token)));
proto_private_key.set_dq(
std::string(key.GetPrimeExponentQ().GetSecret(*token)));
proto_private_key.set_d(
std::string(key.GetPrivateExponent().GetSecret(*token)));
proto_private_key.set_crt(
std::string(key.GetCrtCoefficient().GetSecret(*token)));
util::StatusOr<OutputPrefixType> output_prefix_type =
ToOutputPrefixType(key.GetPublicKey().GetParameters().GetVariant());
if (!output_prefix_type.ok()) {
return output_prefix_type.status();
}
RestrictedData restricted_output =
RestrictedData(proto_private_key.SerializeAsString(), *token);
return internal::ProtoKeySerialization::Create(
kPrivateTypeUrl, restricted_output, KeyData::ASYMMETRIC_PRIVATE,
*output_prefix_type, key.GetIdRequirement());
}
RsaSsaPssProtoParametersParserImpl* RsaSsaPssProtoParametersParser() {
static auto* parser =
new RsaSsaPssProtoParametersParserImpl(kPrivateTypeUrl, ParseParameters);
return parser;
}
RsaSsaPssProtoParametersSerializerImpl* RsaSsaPssProtoParametersSerializer() {
static auto* serializer = new RsaSsaPssProtoParametersSerializerImpl(
kPrivateTypeUrl, SerializeParameters);
return serializer;
}
RsaSsaPssProtoPublicKeyParserImpl* RsaSsaPssProtoPublicKeyParser() {
static auto* parser =
new RsaSsaPssProtoPublicKeyParserImpl(kPublicTypeUrl, ParsePublicKey);
return parser;
}
RsaSsaPssProtoPublicKeySerializerImpl* RsaSsaPssProtoPublicKeySerializer() {
static auto* serializer =
new RsaSsaPssProtoPublicKeySerializerImpl(SerializePublicKey);
return serializer;
}
RsaSsaPssProtoPrivateKeyParserImpl* RsaSsaPssProtoPrivateKeyParser() {
static auto* parser =
new RsaSsaPssProtoPrivateKeyParserImpl(kPrivateTypeUrl, ParsePrivateKey);
return parser;
}
RsaSsaPssProtoPrivateKeySerializerImpl* RsaSsaPssProtoPrivateKeySerializer() {
static auto* serializer =
new RsaSsaPssProtoPrivateKeySerializerImpl(SerializePrivateKey);
return serializer;
}
} // namespace
util::Status RegisterRsaSsaPssProtoSerialization() {
util::Status status =
internal::MutableSerializationRegistry::GlobalInstance()
.RegisterParametersParser(RsaSsaPssProtoParametersParser());
if (!status.ok()) {
return status;
}
status =
internal::MutableSerializationRegistry::GlobalInstance()
.RegisterParametersSerializer(RsaSsaPssProtoParametersSerializer());
if (!status.ok()) {
return status;
}
status = internal::MutableSerializationRegistry::GlobalInstance()
.RegisterKeyParser(RsaSsaPssProtoPublicKeyParser());
if (!status.ok()) {
return status;
}
status = internal::MutableSerializationRegistry::GlobalInstance()
.RegisterKeySerializer(RsaSsaPssProtoPublicKeySerializer());
if (!status.ok()) {
return status;
}
status = internal::MutableSerializationRegistry::GlobalInstance()
.RegisterKeyParser(RsaSsaPssProtoPrivateKeyParser());
if (!status.ok()) {
return status;
}
return internal::MutableSerializationRegistry::GlobalInstance()
.RegisterKeySerializer(RsaSsaPssProtoPrivateKeySerializer());
}
} // namespace tink
} // namespace crypto