kmscng/operation/sign_utils.cc (298 lines of code) (raw):
// 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 "kmscng/operation/sign_utils.h"
#include "absl/strings/str_format.h"
#include "common/kms_client.h"
#include "common/status_macros.h"
#include "kmscng/algorithm_details.h"
#include "kmscng/cng_headers.h"
#include "kmscng/object.h"
#include "kmscng/util/errors.h"
#include "kmscng/util/status_utils.h"
#include "kmscng/util/string_utils.h"
#include "kmsp11/util/crypto_utils.h"
namespace cloud_kms::kmscng {
namespace {
// TODO(b/270419822): drop these once crypto_utils has been migrated to common.
using cloud_kms::kmsp11::EcdsaSigAsn1ToP1363;
using cloud_kms::kmsp11::EcdsaSigLengthP1363;
using cloud_kms::kmsp11::ParseX509PublicKeyDer;
using cloud_kms::kmsp11::SslErrorToString;
absl::Status CopyEcSignature(Object* object, std::string_view src,
absl::Span<uint8_t> dest) {
ASSIGN_OR_RETURN(int curve, CurveIdForAlgorithm(object->algorithm()));
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(curve));
absl::StatusOr<std::vector<uint8_t>> ec_sig =
EcdsaSigAsn1ToP1363(src, group.get());
if (!ec_sig.ok()) {
absl::Status result = ec_sig.status();
SetErrorSs(result, NTE_INTERNAL_ERROR);
return result;
}
if (ec_sig->size() != dest.size()) {
return NewInternalError(
absl::StrFormat("unexpected signature length (got %d, want %d)",
ec_sig->size(), dest.size()),
SOURCE_LOCATION);
}
std::copy(ec_sig->begin(), ec_sig->end(), dest.data());
return absl::OkStatus();
}
absl::Status CopyRsaSignature(Object* object, std::string_view src,
absl::Span<uint8_t> dest) {
if (src.size() != dest.size()) {
return NewInternalError(
absl::StrFormat("unexpected signature length (got %d, want %d)",
src.size(), dest.size()),
SOURCE_LOCATION);
}
const uint8_t* sig_data = reinterpret_cast<const uint8_t*>(src.data());
std::copy(sig_data, sig_data + src.size(), dest.data());
return absl::OkStatus();
}
absl::Status CopySignature(Object* object, std::string_view src,
absl::Span<uint8_t> dest) {
switch (object->algorithm()) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
return CopyEcSignature(object, src, dest);
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return CopyRsaSignature(object, src, dest);
default:
return NewInternalError(
absl::StrFormat("unsupported signature algorithm %s)",
object->algorithm()),
SOURCE_LOCATION);
}
}
absl::StatusOr<std::vector<uint8_t>> SerializePublicEcKey(Object* object) {
ASSIGN_OR_RETURN(int curve, CurveIdForAlgorithm(object->algorithm()));
size_t header_size = sizeof(BCRYPT_ECCKEY_BLOB);
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(curve));
size_t uncompressed_length =
2 * BN_num_bytes(EC_GROUP_get0_order(group.get())); // == 32 for P-256
std::vector<uint8_t> result(header_size + uncompressed_length);
// The uncompressed public key includes a 0x04 prefix byte to indicate that
// the key is in uncompressed format, but the BCRYPT_ECCPUBLIC_BLOB struct
// should only include the X and Y coordinates, so we write starting from
// header_size - 1 (since we override the other struct fields below anyways).
uint8_t* pub_minus_one = result.data() + header_size - 1;
size_t bytes_written = EC_POINT_point2oct(
group.get(), EC_KEY_get0_public_key(object->ec_public_key()),
POINT_CONVERSION_UNCOMPRESSED, pub_minus_one, uncompressed_length + 1,
nullptr);
if (bytes_written != uncompressed_length + 1) {
return NewInternalError(
absl::StrFormat(
"Error serializing public key: %d bytes written, wanted %d. %s",
bytes_written, uncompressed_length, SslErrorToString()),
SOURCE_LOCATION);
}
BCRYPT_ECCKEY_BLOB* header =
reinterpret_cast<BCRYPT_ECCKEY_BLOB*>(result.data());
ASSIGN_OR_RETURN(header->dwMagic, MagicIdForAlgorithm(object->algorithm()));
header->cbKey = uncompressed_length / 2;
return result;
}
absl::StatusOr<std::vector<uint8_t>> SerializePublicRsaKey(Object* object) {
RSA* rsa = object->rsa_public_key();
const BIGNUM* public_exponent = RSA_get0_e(rsa);
const BIGNUM* modulus = RSA_get0_n(rsa);
unsigned int public_exponent_size = BN_num_bytes(public_exponent);
unsigned int modulus_size = BN_num_bytes(modulus);
// Build the BCRYPT_RSAPUBLIC_BLOB structure, as specified in
// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_rsakey_blob#remarks
size_t header_size = sizeof(BCRYPT_RSAKEY_BLOB);
size_t payload_size = public_exponent_size + modulus_size;
std::vector<uint8_t> result(header_size + payload_size);
// Header
BCRYPT_RSAKEY_BLOB* header =
reinterpret_cast<BCRYPT_RSAKEY_BLOB*>(result.data());
ASSIGN_OR_RETURN(header->Magic, MagicIdForAlgorithm(object->algorithm()));
header->BitLength = RSA_bits(rsa);
header->cbPublicExp = public_exponent_size;
header->cbModulus = modulus_size;
header->cbPrime1 = 0L; // not used by public keys
header->cbPrime2 = 0L; // not used by public keys
// Public key data, Big Endian
size_t bytes_written;
uint8_t* exponent_data = result.data() + header_size;
bytes_written = BN_bn2bin(public_exponent, exponent_data);
if (bytes_written != public_exponent_size) {
return NewInternalError(
absl::StrFormat("Error serializing public key (RSA public exponent): "
"%d bytes written, wanted %d. %s",
bytes_written, public_exponent_size,
SslErrorToString()),
SOURCE_LOCATION);
}
uint8_t* modulus_data = exponent_data + public_exponent_size;
bytes_written = BN_bn2bin(modulus, modulus_data);
if (bytes_written != modulus_size) {
return NewInternalError(
absl::StrFormat("Error serializing public key (RSA modulus): %d bytes "
"written, wanted %d. %s",
bytes_written, modulus_size, SslErrorToString()),
SOURCE_LOCATION);
}
return result;
}
} // namespace
absl::Status IsValidSigningAlgorithm(
kms_v1::CryptoKeyVersion::CryptoKeyVersionAlgorithm algorithm) {
switch (algorithm) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return absl::OkStatus();
default:
return NewInternalError(
absl::StrFormat("invalid asymmetric signing algorithm: %d",
algorithm),
SOURCE_LOCATION);
}
}
absl::StatusOr<const EVP_MD*> DigestForAlgorithm(
kms_v1::CryptoKeyVersion::CryptoKeyVersionAlgorithm algorithm) {
switch (algorithm) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return EVP_sha256();
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
return EVP_sha384();
default:
return NewInternalError(
absl::StrFormat("cannot get digest type for algorithm: %d",
algorithm),
SOURCE_LOCATION);
}
}
absl::StatusOr<int> CurveIdForAlgorithm(
kms_v1::CryptoKeyVersion::CryptoKeyVersionAlgorithm algorithm) {
switch (algorithm) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
return NID_X9_62_prime256v1;
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
return NID_secp384r1;
default:
return NewInternalError(
absl::StrFormat("cannot get curve NID for algorithm: %d", algorithm),
SOURCE_LOCATION);
}
}
absl::StatusOr<uint32_t> MagicIdForAlgorithm(
kms_v1::CryptoKeyVersion::CryptoKeyVersionAlgorithm algorithm) {
switch (algorithm) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
return BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
return BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return BCRYPT_RSAPUBLIC_MAGIC;
default:
return NewInternalError(
absl::StrFormat("cannot get magic ID for algorithm: %d", algorithm),
SOURCE_LOCATION);
}
}
absl::Status ValidateKeyPreconditions(Object* object) {
RETURN_IF_ERROR(IsValidSigningAlgorithm(object->algorithm()));
ASSIGN_OR_RETURN(AlgorithmDetails details, GetDetails(object->algorithm()));
ASSIGN_OR_RETURN(auto algorithm_group,
object->GetProperty(NCRYPT_ALGORITHM_GROUP_PROPERTY));
if (algorithm_group != WideToBytes(details.algorithm_group)) {
return NewInternalError(
absl::StrFormat("invalid algorithm_group property, expected %s",
WideToString(details.algorithm_group)),
SOURCE_LOCATION);
}
ASSIGN_OR_RETURN(auto algorithm_property,
object->GetProperty(NCRYPT_ALGORITHM_PROPERTY));
if (algorithm_property != WideToBytes(details.algorithm_property)) {
return NewInternalError(
absl::StrFormat("invalid algorithm_property, expected %s",
WideToString(details.algorithm_property)),
SOURCE_LOCATION);
}
ASSIGN_OR_RETURN(auto key_usage,
object->GetProperty(NCRYPT_KEY_USAGE_PROPERTY));
if (key_usage != Uint32ToBytes(details.key_usage)) {
return NewInternalError(
absl::StrFormat("invalid key_usage property, expected %u",
details.key_usage),
SOURCE_LOCATION);
}
return absl::OkStatus();
}
absl::StatusOr<std::vector<uint8_t>> SerializePublicKey(Object* object) {
switch (object->algorithm()) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384:
return SerializePublicEcKey(object);
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return SerializePublicRsaKey(object);
default:
return NewInternalError(
absl::StrFormat("invalid asymmetric signing algorithm: %d",
object->algorithm()),
SOURCE_LOCATION);
}
}
absl::StatusOr<size_t> SignatureLength(Object* object) {
switch (object->algorithm()) {
case kms_v1::CryptoKeyVersion::EC_SIGN_P256_SHA256:
case kms_v1::CryptoKeyVersion::EC_SIGN_P384_SHA384: {
ASSIGN_OR_RETURN(int curve, CurveIdForAlgorithm(object->algorithm()));
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(curve));
return EcdsaSigLengthP1363(group.get());
}
// RSA signatures have the same size as the key bit size (in bytes here).
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_2048_SHA256:
return 2048 / 8;
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_3072_SHA256:
return 3072 / 8;
case kms_v1::CryptoKeyVersion::RSA_SIGN_PKCS1_4096_SHA256:
return 4096 / 8;
default:
return NewInternalError(
absl::StrFormat("cannot get signature length for algorithm: %d",
object->algorithm()),
SOURCE_LOCATION);
}
}
absl::Status SignDigest(Object* object, absl::Span<const uint8_t> digest,
absl::Span<uint8_t> signature) {
ASSIGN_OR_RETURN(const EVP_MD* md, DigestForAlgorithm(object->algorithm()));
if (digest.size() != EVP_MD_size(md)) {
return NewInvalidArgumentError(
absl::StrFormat("provided digest has incorrect size (got %d, want %d)",
digest.size(), EVP_MD_size(md)),
NTE_INVALID_PARAMETER, SOURCE_LOCATION);
}
ASSIGN_OR_RETURN(auto expected_sig_length, SignatureLength(object));
if (signature.size() != expected_sig_length) {
return NewInternalError(
absl::StrFormat(
"provided signature buffer has incorrect size (got %d, want %d)",
signature.size(), expected_sig_length),
SOURCE_LOCATION);
}
kms_v1::AsymmetricSignRequest req;
req.set_name(std::string(object->kms_key_name()));
int digest_nid = EVP_MD_type(md);
switch (digest_nid) {
case NID_sha256:
req.mutable_digest()->set_sha256(digest.data(), digest.size());
break;
case NID_sha384:
req.mutable_digest()->set_sha384(digest.data(), digest.size());
break;
default:
return NewInternalError(
absl::StrFormat("unhandled digest type: %d", digest_nid),
SOURCE_LOCATION);
}
ASSIGN_OR_RETURN(kms_v1::AsymmetricSignResponse resp,
object->kms_client()->AsymmetricSign(req));
RETURN_IF_ERROR(CopySignature(object, resp.signature(), signature));
return absl::OkStatus();
}
} // namespace cloud_kms::kmscng