kmsp11/operation/rsaes_oaep.cc (121 lines of code) (raw):
// 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.
#include "kmsp11/operation/rsaes_oaep.h"
#include "absl/cleanup/cleanup.h"
#include "common/status_macros.h"
#include "kmsp11/object.h"
#include "kmsp11/operation/preconditions.h"
#include "kmsp11/util/crypto_utils.h"
#include "kmsp11/util/errors.h"
#include "kmsp11/util/string_utils.h"
namespace cloud_kms::kmsp11 {
namespace {
// An implementation of EncrypterInterface that encrypts RSAES-OAEP ciphertexts
// using BoringSSL.
class RsaOaepEncrypter : public EncrypterInterface {
public:
RsaOaepEncrypter(std::shared_ptr<Object> object,
bssl::UniquePtr<EVP_PKEY> key)
: object_(object),
key_(std::move(key)),
ciphertext_(object->algorithm().key_bit_length / 8) {}
// Encrypt returns a span whose underlying bytes are bound to the lifetime of
// this encrypter.
absl::StatusOr<absl::Span<const uint8_t>> Encrypt(
KmsClient* client, absl::Span<const uint8_t> ciphertext) override;
virtual ~RsaOaepEncrypter() {}
private:
std::shared_ptr<Object> object_;
bssl::UniquePtr<EVP_PKEY> key_;
std::vector<uint8_t> ciphertext_;
};
absl::StatusOr<absl::Span<const uint8_t>> RsaOaepEncrypter::Encrypt(
KmsClient* client, absl::Span<const uint8_t> plaintext) {
ASSIGN_OR_RETURN(const EVP_MD* digest,
DigestForMechanism(*object_->algorithm().digest_mechanism));
RETURN_IF_ERROR(EncryptRsaOaep(key_.get(), digest, plaintext,
absl::MakeSpan(ciphertext_)));
return absl::MakeConstSpan(ciphertext_);
}
// An implementation of DecrypterInterface that decrypts RSAES-OAEP ciphertexts
// using Cloud KMS.
class RsaOaepDecrypter : public DecrypterInterface {
public:
RsaOaepDecrypter(std::shared_ptr<Object> key) : key_(key) {}
// Decrypt returns a span whose underlying bytes are bound to the lifetime of
// this decrypter.
absl::StatusOr<absl::Span<const uint8_t>> Decrypt(
KmsClient* client, absl::Span<const uint8_t> ciphertext) override;
virtual ~RsaOaepDecrypter() {}
private:
std::shared_ptr<Object> key_;
std::unique_ptr<std::string, ZeroDelete<std::string>> plaintext_;
};
absl::StatusOr<absl::Span<const uint8_t>> RsaOaepDecrypter::Decrypt(
KmsClient* client, absl::Span<const uint8_t> ciphertext) {
size_t expected_size = key_->algorithm().key_bit_length / 8;
if (ciphertext.size() != expected_size) {
return NewInvalidArgumentError(
absl::StrFormat("ciphertext size mismatch (got %d, want %d)",
ciphertext.size(), expected_size),
CKR_ENCRYPTED_DATA_LEN_RANGE, SOURCE_LOCATION);
}
kms_v1::AsymmetricDecryptRequest req;
req.set_name(std::string(key_->kms_key_name()));
req.set_ciphertext(ciphertext.data(), ciphertext.size());
absl::StatusOr<kms_v1::AsymmetricDecryptResponse> resp =
client->AsymmetricDecrypt(req);
if (!resp.ok()) {
switch (resp.status().code()) {
case absl::StatusCode::kInvalidArgument:
// A nice enhancement would be to consider if there is a clearer way for
// KMS to specify that it's the ciphertext that's invalid (and not
// something else).
return NewInvalidArgumentError(resp.status().message(),
CKR_ENCRYPTED_DATA_INVALID,
SOURCE_LOCATION);
default:
return NewError(resp.status().code(), resp.status().message(),
CKR_DEVICE_ERROR, SOURCE_LOCATION);
}
}
plaintext_.reset(resp->release_plaintext());
return absl::MakeConstSpan(reinterpret_cast<uint8_t*>(plaintext_->data()),
plaintext_->size());
}
absl::Status ValidateRsaOaepParameters(Object* key, void* parameters,
CK_ULONG parameters_size) {
if (parameters_size != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) {
return InvalidMechanismParamError(
"mechanism parameters must be of type CK_RSA_PKCS_OAEP_PARAMS",
SOURCE_LOCATION);
}
CK_RSA_PKCS_OAEP_PARAMS* params =
static_cast<CK_RSA_PKCS_OAEP_PARAMS*>(parameters);
ASSIGN_OR_RETURN(const EVP_MD* digest,
DigestForMechanism(*key->algorithm().digest_mechanism));
RETURN_IF_ERROR(EnsureHashMatches(params->hashAlg, digest));
RETURN_IF_ERROR(EnsureMgf1HashMatches(params->mgf, digest));
switch (params->source) {
case 0: // For compatibility. See b/217419373.
case CKZ_DATA_SPECIFIED:
break;
default:
return InvalidMechanismParamError(
"source for OAEP must be 0 or CKZ_DATA_SPECIFIED", SOURCE_LOCATION);
}
if (params->pSourceData != nullptr || params->ulSourceDataLen != 0) {
return InvalidMechanismParamError("OAEP labels are not supported",
SOURCE_LOCATION);
}
return absl::OkStatus();
}
} // namespace
absl::StatusOr<std::unique_ptr<EncrypterInterface>> NewRsaOaepEncrypter(
std::shared_ptr<Object> key, const CK_MECHANISM* mechanism) {
RETURN_IF_ERROR(CheckKeyPreconditions(CKK_RSA, CKO_PUBLIC_KEY,
CKM_RSA_PKCS_OAEP, key.get()));
RETURN_IF_ERROR(ValidateRsaOaepParameters(key.get(), mechanism->pParameter,
mechanism->ulParameterLen));
ASSIGN_OR_RETURN(std::string_view key_der,
key->attributes().Value(CKA_PUBLIC_KEY_INFO));
ASSIGN_OR_RETURN(bssl::UniquePtr<EVP_PKEY> parsed_key,
ParseX509PublicKeyDer(key_der));
return std::make_unique<RsaOaepEncrypter>(key, std::move(parsed_key));
}
absl::StatusOr<std::unique_ptr<DecrypterInterface>> NewRsaOaepDecrypter(
std::shared_ptr<Object> key, const CK_MECHANISM* mechanism) {
RETURN_IF_ERROR(CheckKeyPreconditions(CKK_RSA, CKO_PRIVATE_KEY,
CKM_RSA_PKCS_OAEP, key.get()));
RETURN_IF_ERROR(ValidateRsaOaepParameters(key.get(), mechanism->pParameter,
mechanism->ulParameterLen));
return std::make_unique<RsaOaepDecrypter>(key);
}
} // namespace cloud_kms::kmsp11