Use accessors for certificates and RSA keys.

The upstream RSA APIs are annoyingly tedious, but ah well. Note
X509_set1_signature_algo sets both copies of the signature algorithm.
This also fixes an EVP_PKEY leak in some error paths.

Test: mm
Change-Id: Ifa6f130e9d7dce328c649aa241057dbe5c0e5e66
This commit is contained in:
David Benjamin 2021-05-06 13:36:46 -04:00
parent 6c72732a9b
commit 891b9540ed
3 changed files with 56 additions and 41 deletions

View file

@ -19,6 +19,7 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509v3.h>
#include <functional>
@ -512,10 +513,7 @@ std::variant<CertUtilsError, ASN1_STRING_Ptr> buildRsaPssParameter(Digest digest
return ASN1_STRING_Ptr(algo_str);
}
CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
if (algo_field == nullptr) {
return CertUtilsError::UnexpectedNullPointer;
}
std::variant<CertUtilsError, X509_ALGOR_Ptr> makeAlgo(Algo algo, Padding padding, Digest digest) {
ASN1_STRING_Ptr param;
int param_type = V_ASN1_UNDEF;
int nid = 0;
@ -584,23 +582,29 @@ CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding
return CertUtilsError::InvalidArgument;
}
if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
X509_ALGOR_Ptr result(X509_ALGOR_new());
if (!result) {
return CertUtilsError::MemoryAllocation;
}
if (!X509_ALGOR_set0(result.get(), OBJ_nid2obj(nid), param_type, param.get())) {
return CertUtilsError::Encoding;
}
// The X509 struct took ownership.
param.release();
return CertUtilsError::Ok;
return result;
}
// This function allows for signing a
CertUtilsError signCertWith(X509* certificate,
std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
Algo algo, Padding padding, Digest digest) {
if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
return error;
auto algo_objV = makeAlgo(algo, padding, digest);
if (auto error = std::get_if<CertUtilsError>(&algo_objV)) {
return *error;
}
if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
return error;
auto& algo_obj = std::get<X509_ALGOR_Ptr>(algo_objV);
if (!X509_set1_signature_algo(certificate, algo_obj.get())) {
return CertUtilsError::BoringSsl;
}
uint8_t* cert_buf = nullptr;
@ -615,13 +619,10 @@ CertUtilsError signCertWith(X509* certificate,
return CertUtilsError::SignatureFailed;
}
if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
if (!X509_set1_signature_value(certificate, signature.data(), signature.size())) {
return CertUtilsError::BoringSsl;
}
certificate->signature->flags &= ~(0x07);
certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
return CertUtilsError::Ok;
}

View file

@ -39,6 +39,7 @@ DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
DEFINE_OPENSSL_OBJECT_POINTER(X509);
DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);

View file

@ -21,6 +21,7 @@
#include <openssl/crypto.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <fcntl.h>
@ -56,12 +57,17 @@ static bool add_ext(X509* cert, int nid, const char* value) {
}
Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
bssl::UniquePtr<BIGNUM> n(BN_new());
bssl::UniquePtr<BIGNUM> e(BN_new());
bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
rsaPubkey->n = BN_new();
rsaPubkey->e = BN_new();
BN_bin2bn(publicKey.data(), publicKey.size(), rsaPubkey->n);
BN_set_word(rsaPubkey->e, kRsaKeyExponent);
if (!n || !e || !rsaPubkey || !BN_bin2bn(publicKey.data(), publicKey.size(), n.get()) ||
!BN_set_word(e.get(), kRsaKeyExponent) ||
!RSA_set0_key(rsaPubkey.get(), n.get(), e.get(), /*d=*/nullptr)) {
return Error() << "Failed to create RSA key";
}
// RSA_set0_key takes ownership of |n| and |e| on success.
(void)n.release();
(void)e.release();
return rsaPubkey;
}
@ -69,6 +75,9 @@ Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
Result<void> verifySignature(const std::string& message, const std::string& signature,
const std::vector<uint8_t>& publicKey) {
auto rsaKey = getRsa(publicKey);
if (!rsaKey.ok()) {
return rsaKey.error();
}
uint8_t hashBuf[SHA256_DIGEST_LENGTH];
SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
message.length(), hashBuf);
@ -99,11 +108,14 @@ Result<void> createSelfSignedCertificate(
// "publicKey" corresponds to the raw public key bytes - need to create
// a new RSA key with the correct exponent.
auto rsaPubkey = getRsa(publicKey);
if (!rsaPubkey.ok()) {
return rsaPubkey.error();
}
EVP_PKEY* public_key = EVP_PKEY_new();
EVP_PKEY_assign_RSA(public_key, rsaPubkey->release());
bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
EVP_PKEY_assign_RSA(public_key.get(), rsaPubkey->release());
if (!X509_set_pubkey(x509.get(), public_key)) {
if (!X509_set_pubkey(x509.get(), public_key.get())) {
return Error() << "Unable to set x509 public key";
}
@ -126,27 +138,30 @@ Result<void> createSelfSignedCertificate(
add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
V_ASN1_NULL, NULL);
X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
if (!algor ||
!X509_ALGOR_set0(algor.get(), OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL,
NULL) ||
!X509_set1_signature_algo(x509.get(), algor.get())) {
return Error() << "Unable to set x509 signature algorithm";
}
// Get the data to be signed
char* to_be_signed_buf(nullptr);
size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
unsigned char* to_be_signed_buf(nullptr);
size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), &to_be_signed_buf);
auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
auto signed_data = signFunction(
std::string(reinterpret_cast<const char*>(to_be_signed_buf), to_be_signed_length));
if (!signed_data.ok()) {
return signed_data.error();
}
// This is the only part that doesn't use boringssl default functions - we manually copy in the
// signature that was provided to us.
x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
x509->signature->length = signed_data->size();
if (!X509_set1_signature_value(x509.get(),
reinterpret_cast<const uint8_t*>(signed_data->data()),
signed_data->size())) {
return Error() << "Unable to set x509 signature";
}
x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
auto f = fopen(path.c_str(), "wbe");
if (f == nullptr) {
return Error() << "Failed to open " << path;
@ -154,7 +169,6 @@ Result<void> createSelfSignedCertificate(
i2d_X509_fp(f, x509.get());
fclose(f);
EVP_PKEY_free(public_key);
return {};
}
@ -163,15 +177,14 @@ Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
return Error() << "Failed to extract public key from x509 cert";
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
return Error() << "The public key is not an RSA key";
}
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
auto num_bytes = BN_num_bytes(rsa->n);
RSA* rsa = EVP_PKEY_get0_RSA(pkey);
auto num_bytes = BN_num_bytes(RSA_get0_n(rsa));
std::vector<uint8_t> pubKey(num_bytes);
int res = BN_bn2bin(rsa->n, pubKey.data());
RSA_free(rsa);
int res = BN_bn2bin(RSA_get0_n(rsa), pubKey.data());
if (!res) {
return Error() << "Failed to convert public key to bytes";