|  | /* | 
|  | * Copyright 2020, The Android Open Source Project | 
|  | * | 
|  | * 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 <certificate_utils.h> | 
|  |  | 
|  | #include <openssl/err.h> | 
|  | #include <openssl/evp.h> | 
|  | #include <openssl/mem.h> | 
|  | #include <openssl/x509v3.h> | 
|  |  | 
|  | #include <functional> | 
|  | #include <limits> | 
|  | #include <string> | 
|  | #include <variant> | 
|  | #include <vector> | 
|  |  | 
|  | namespace keystore { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr int kDigitalSignatureKeyUsageBit = 0; | 
|  | constexpr int kKeyEnciphermentKeyUsageBit = 2; | 
|  | constexpr int kDataEnciphermentKeyUsageBit = 3; | 
|  | constexpr int kKeyCertSignBit = 5; | 
|  | constexpr int kMaxKeyUsageBit = 8; | 
|  |  | 
|  | DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING); | 
|  | DEFINE_OPENSSL_OBJECT_POINTER(RSA_PSS_PARAMS); | 
|  | DEFINE_OPENSSL_OBJECT_POINTER(AUTHORITY_KEYID); | 
|  | DEFINE_OPENSSL_OBJECT_POINTER(BASIC_CONSTRAINTS); | 
|  | DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR); | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | std::variant<CertUtilsError, X509_NAME_Ptr> makeCommonName(const std::string& name) { | 
|  | X509_NAME_Ptr x509_name(X509_NAME_new()); | 
|  | if (!x509_name) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | if (!X509_NAME_add_entry_by_txt(x509_name.get(), "CN", MBSTRING_ASC, | 
|  | reinterpret_cast<const uint8_t*>(name.c_str()), name.length(), | 
|  | -1 /* loc */, 0 /* set */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | return x509_name; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, std::vector<uint8_t>> makeKeyId(const X509* cert) { | 
|  | std::vector<uint8_t> keyid(20); | 
|  | unsigned int len; | 
|  | if (!X509_pubkey_digest(cert, EVP_sha1(), keyid.data(), &len)) { | 
|  | return CertUtilsError::Encoding; | 
|  | } | 
|  | return keyid; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, AUTHORITY_KEYID_Ptr> | 
|  | makeAuthorityKeyExtension(const std::vector<uint8_t>& keyid) { | 
|  | AUTHORITY_KEYID_Ptr auth_key(AUTHORITY_KEYID_new()); | 
|  | if (!auth_key) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  |  | 
|  | auth_key->keyid = ASN1_OCTET_STRING_new(); | 
|  | if (auth_key->keyid == nullptr) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  |  | 
|  | if (!ASN1_OCTET_STRING_set(auth_key->keyid, keyid.data(), keyid.size())) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | return auth_key; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, ASN1_OCTET_STRING_Ptr> | 
|  | makeSubjectKeyExtension(const std::vector<uint8_t>& keyid) { | 
|  |  | 
|  | // Build OCTET_STRING | 
|  | ASN1_OCTET_STRING_Ptr keyid_str(ASN1_OCTET_STRING_new()); | 
|  | if (!keyid_str || !ASN1_OCTET_STRING_set(keyid_str.get(), keyid.data(), keyid.size())) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | return keyid_str; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, BASIC_CONSTRAINTS_Ptr> | 
|  | makeBasicConstraintsExtension(bool is_ca, std::optional<int> path_length) { | 
|  |  | 
|  | BASIC_CONSTRAINTS_Ptr bcons(BASIC_CONSTRAINTS_new()); | 
|  | if (!bcons) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  |  | 
|  | bcons->ca = is_ca; | 
|  | bcons->pathlen = nullptr; | 
|  | if (path_length) { | 
|  | bcons->pathlen = ASN1_INTEGER_new(); | 
|  | if (bcons->pathlen == nullptr || !ASN1_INTEGER_set(bcons->pathlen, *path_length)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | return bcons; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, ASN1_BIT_STRING_Ptr> | 
|  | makeKeyUsageExtension(bool is_signing_key, bool is_encryption_key, bool is_cert_key) { | 
|  | // Build BIT_STRING with correct contents. | 
|  | ASN1_BIT_STRING_Ptr key_usage(ASN1_BIT_STRING_new()); | 
|  | if (!key_usage) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i <= kMaxKeyUsageBit; ++i) { | 
|  | if (!ASN1_BIT_STRING_set_bit(key_usage.get(), i, 0)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (is_signing_key) { | 
|  | if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kDigitalSignatureKeyUsageBit, 1)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (is_encryption_key) { | 
|  | if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyEnciphermentKeyUsageBit, 1) || | 
|  | !ASN1_BIT_STRING_set_bit(key_usage.get(), kDataEnciphermentKeyUsageBit, 1)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (is_cert_key) { | 
|  | if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyCertSignBit, 1)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | return key_usage; | 
|  | } | 
|  |  | 
|  | // Creates a rump certificate structure with serial, subject and issuer names, as well as | 
|  | // activation and expiry date. | 
|  | // Callers should pass an empty X509_Ptr and check the return value for CertUtilsError::Ok (0) | 
|  | // before accessing the result. | 
|  | std::variant<CertUtilsError, X509_Ptr> | 
|  | makeCertRump(const uint32_t serial, const char subject[], const uint64_t activeDateTimeMilliSeconds, | 
|  | const uint64_t usageExpireDateTimeMilliSeconds) { | 
|  |  | 
|  | // Sanitize pointer arguments. | 
|  | if (!subject || strlen(subject) == 0) { | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  |  | 
|  | // Create certificate structure. | 
|  | X509_Ptr certificate(X509_new()); | 
|  | if (!certificate) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | // Set the X509 version. | 
|  | if (!X509_set_version(certificate.get(), 2 /* version 3, but zero-based */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | // Set the certificate serialNumber | 
|  | ASN1_INTEGER_Ptr serialNumber(ASN1_INTEGER_new()); | 
|  | if (!serialNumber || !ASN1_INTEGER_set(serialNumber.get(), serial) || | 
|  | !X509_set_serialNumber(certificate.get(), serialNumber.get() /* Don't release; copied */)) | 
|  | return CertUtilsError::BoringSsl; | 
|  |  | 
|  | // Set Subject Name | 
|  | auto subjectName = makeCommonName(subject); | 
|  | if (auto x509_subject = std::get_if<X509_NAME_Ptr>(&subjectName)) { | 
|  | if (!X509_set_subject_name(certificate.get(), x509_subject->get() /* copied */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } else { | 
|  | return std::get<CertUtilsError>(subjectName); | 
|  | } | 
|  |  | 
|  | // Set activation date. | 
|  | ASN1_TIME_Ptr notBefore(ASN1_TIME_new()); | 
|  | if (!notBefore || !ASN1_TIME_set(notBefore.get(), activeDateTimeMilliSeconds / 1000) || | 
|  | !X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */)) | 
|  | return CertUtilsError::BoringSsl; | 
|  |  | 
|  | // Set expiration date. | 
|  | time_t notAfterTime; | 
|  | notAfterTime = (time_t)std::min((uint64_t)std::numeric_limits<time_t>::max(), | 
|  | usageExpireDateTimeMilliSeconds / 1000); | 
|  |  | 
|  | ASN1_TIME_Ptr notAfter(ASN1_TIME_new()); | 
|  | if (!notAfter || !ASN1_TIME_set(notAfter.get(), notAfterTime) || | 
|  | !X509_set_notAfter(certificate.get(), notAfter.get() /* Don't release; copied */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | return certificate; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, X509_Ptr> | 
|  | makeCert(const EVP_PKEY* evp_pkey, const uint32_t serial, const char subject[], | 
|  | const uint64_t activeDateTimeMilliSeconds, const uint64_t usageExpireDateTimeMilliSeconds, | 
|  | bool addSubjectKeyIdEx, std::optional<KeyUsageExtension> keyUsageEx, | 
|  | std::optional<BasicConstraintsExtension> basicConstraints) { | 
|  |  | 
|  | // Make the rump certificate with serial, subject, not before and not after dates. | 
|  | auto certificateV = | 
|  | makeCertRump(serial, subject, activeDateTimeMilliSeconds, usageExpireDateTimeMilliSeconds); | 
|  | if (auto error = std::get_if<CertUtilsError>(&certificateV)) { | 
|  | return *error; | 
|  | } | 
|  | auto certificate = std::move(std::get<X509_Ptr>(certificateV)); | 
|  |  | 
|  | // Set the public key. | 
|  | if (!X509_set_pubkey(certificate.get(), const_cast<EVP_PKEY*>(evp_pkey))) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | if (keyUsageEx) { | 
|  | // Make and add the key usage extension. | 
|  | auto key_usage_extensionV = makeKeyUsageExtension( | 
|  | keyUsageEx->isSigningKey, keyUsageEx->isEncryptionKey, keyUsageEx->isCertificationKey); | 
|  | if (auto error = std::get_if<CertUtilsError>(&key_usage_extensionV)) { | 
|  | return *error; | 
|  | } | 
|  | auto key_usage_extension = std::move(std::get<ASN1_BIT_STRING_Ptr>(key_usage_extensionV)); | 
|  | if (!X509_add1_ext_i2d(certificate.get(), NID_key_usage, | 
|  | key_usage_extension.get() /* Don't release; copied */, | 
|  | true /* critical */, 0 /* flags */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (basicConstraints) { | 
|  | // Make and add basic constraints | 
|  | auto basic_constraints_extensionV = | 
|  | makeBasicConstraintsExtension(basicConstraints->isCa, basicConstraints->pathLength); | 
|  | if (auto error = std::get_if<CertUtilsError>(&basic_constraints_extensionV)) { | 
|  | return *error; | 
|  | } | 
|  | auto basic_constraints_extension = | 
|  | std::move(std::get<BASIC_CONSTRAINTS_Ptr>(basic_constraints_extensionV)); | 
|  | if (!X509_add1_ext_i2d(certificate.get(), NID_basic_constraints, | 
|  | basic_constraints_extension.get() /* Don't release; copied */, | 
|  | true /* critical */, 0 /* flags */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (addSubjectKeyIdEx) { | 
|  | // Make and add subject key id extension. | 
|  | auto keyidV = makeKeyId(certificate.get()); | 
|  | if (auto error = std::get_if<CertUtilsError>(&keyidV)) { | 
|  | return *error; | 
|  | } | 
|  | auto& keyid = std::get<std::vector<uint8_t>>(keyidV); | 
|  |  | 
|  | auto subject_key_extensionV = makeSubjectKeyExtension(keyid); | 
|  | if (auto error = std::get_if<CertUtilsError>(&subject_key_extensionV)) { | 
|  | return *error; | 
|  | } | 
|  | auto subject_key_extension = | 
|  | std::move(std::get<ASN1_OCTET_STRING_Ptr>(subject_key_extensionV)); | 
|  | if (!X509_add1_ext_i2d(certificate.get(), NID_subject_key_identifier, | 
|  | subject_key_extension.get() /* Don't release; copied */, | 
|  | false /* critical */, 0 /* flags */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  |  | 
|  | return certificate; | 
|  | } | 
|  |  | 
|  | CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt) { | 
|  |  | 
|  | X509_NAME* issuerName(X509_get_subject_name(signingCert)); | 
|  |  | 
|  | // Set Issuer Name | 
|  | if (issuerName) { | 
|  | if (!X509_set_issuer_name(cert, issuerName /* copied */)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } else { | 
|  | return CertUtilsError::Encoding; | 
|  | } | 
|  |  | 
|  | if (addAuthKeyExt) { | 
|  | // Make and add authority key extension - self signed. | 
|  | auto keyidV = makeKeyId(signingCert); | 
|  | if (auto error = std::get_if<CertUtilsError>(&keyidV)) { | 
|  | return *error; | 
|  | } | 
|  | auto& keyid = std::get<std::vector<uint8_t>>(keyidV); | 
|  |  | 
|  | auto auth_key_extensionV = makeAuthorityKeyExtension(keyid); | 
|  | if (auto error = std::get_if<CertUtilsError>(&auth_key_extensionV)) { | 
|  | return *error; | 
|  | } | 
|  | auto auth_key_extension = std::move(std::get<AUTHORITY_KEYID_Ptr>(auth_key_extensionV)); | 
|  | if (!X509_add1_ext_i2d(cert, NID_authority_key_identifier, auth_key_extension.get(), false, | 
|  | 0)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | } | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | // Takes a certificate a signing certificate and the raw private signing_key. And signs | 
|  | // the certificate with the latter. | 
|  | CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key) { | 
|  |  | 
|  | if (certificate == nullptr) { | 
|  | return CertUtilsError::UnexpectedNullPointer; | 
|  | } | 
|  |  | 
|  | if (!X509_sign(certificate, signing_key, EVP_sha256())) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate) { | 
|  | int len = i2d_X509(certificate, nullptr); | 
|  | if (len < 0) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | auto result = std::vector<uint8_t>(len); | 
|  | uint8_t* p = result.data(); | 
|  |  | 
|  | if (i2d_X509(certificate, &p) < 0) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | CertUtilsError setRsaDigestAlgorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) { | 
|  | if (alg_ptr == nullptr || digest == nullptr) { | 
|  | return CertUtilsError::UnexpectedNullPointer; | 
|  | } | 
|  | *alg_ptr = X509_ALGOR_new(); | 
|  | if (*alg_ptr == nullptr) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  | X509_ALGOR_set_md(*alg_ptr, digest); | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | CertUtilsError setPssMaskGeneratorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) { | 
|  | X509_ALGOR* mgf1_digest = nullptr; | 
|  | if (auto error = setRsaDigestAlgorField(&mgf1_digest, digest)) { | 
|  | return error; | 
|  | } | 
|  | X509_ALGOR_Ptr mgf1_digest_ptr(mgf1_digest); | 
|  |  | 
|  | ASN1_OCTET_STRING* mgf1_digest_algor_str = nullptr; | 
|  | if (!ASN1_item_pack(mgf1_digest, ASN1_ITEM_rptr(X509_ALGOR), &mgf1_digest_algor_str)) { | 
|  | return CertUtilsError::Encoding; | 
|  | } | 
|  | ASN1_OCTET_STRING_Ptr mgf1_digest_algor_str_ptr(mgf1_digest_algor_str); | 
|  |  | 
|  | *alg_ptr = X509_ALGOR_new(); | 
|  | if (*alg_ptr == nullptr) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  | X509_ALGOR_set0(*alg_ptr, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, mgf1_digest_algor_str); | 
|  | // *alg_ptr took ownership of the octet string | 
|  | mgf1_digest_algor_str_ptr.release(); | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | static CertUtilsError setSaltLength(RSA_PSS_PARAMS* pss_params, unsigned length) { | 
|  | pss_params->saltLength = ASN1_INTEGER_new(); | 
|  | if (pss_params->saltLength == nullptr) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  | if (!ASN1_INTEGER_set(pss_params->saltLength, length)) { | 
|  | return CertUtilsError::Encoding; | 
|  | }; | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | std::variant<CertUtilsError, ASN1_STRING_Ptr> buildRsaPssParameter(Digest digest) { | 
|  | RSA_PSS_PARAMS_Ptr pss(RSA_PSS_PARAMS_new()); | 
|  | if (!pss) { | 
|  | return CertUtilsError::MemoryAllocation; | 
|  | } | 
|  |  | 
|  | const EVP_MD* md = nullptr; | 
|  |  | 
|  | switch (digest) { | 
|  | case Digest::SHA1: | 
|  | break; | 
|  | case Digest::SHA224: | 
|  | md = EVP_sha224(); | 
|  | break; | 
|  | case Digest::SHA256: | 
|  | md = EVP_sha256(); | 
|  | break; | 
|  | case Digest::SHA384: | 
|  | md = EVP_sha384(); | 
|  | break; | 
|  | case Digest::SHA512: | 
|  | md = EVP_sha512(); | 
|  | break; | 
|  | default: | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  |  | 
|  | if (md != nullptr) { | 
|  | if (auto error = setSaltLength(pss.get(), EVP_MD_size(md))) { | 
|  | return error; | 
|  | } | 
|  | if (auto error = setRsaDigestAlgorField(&pss->hashAlgorithm, md)) { | 
|  | return error; | 
|  | } | 
|  | if (auto error = setPssMaskGeneratorField(&pss->maskGenAlgorithm, md)) { | 
|  | return error; | 
|  | } | 
|  | } | 
|  |  | 
|  | ASN1_STRING* algo_str = nullptr; | 
|  | if (!ASN1_item_pack(pss.get(), ASN1_ITEM_rptr(RSA_PSS_PARAMS), &algo_str)) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | ASN1_STRING_Ptr param; | 
|  | int param_type = V_ASN1_UNDEF; | 
|  | int nid = 0; | 
|  | switch (algo) { | 
|  | case Algo::ECDSA: | 
|  | switch (digest) { | 
|  | case Digest::SHA1: | 
|  | nid = NID_ecdsa_with_SHA1; | 
|  | break; | 
|  | case Digest::SHA224: | 
|  | nid = NID_ecdsa_with_SHA224; | 
|  | break; | 
|  | case Digest::SHA256: | 
|  | nid = NID_ecdsa_with_SHA256; | 
|  | break; | 
|  | case Digest::SHA384: | 
|  | nid = NID_ecdsa_with_SHA384; | 
|  | break; | 
|  | case Digest::SHA512: | 
|  | nid = NID_ecdsa_with_SHA512; | 
|  | break; | 
|  | default: | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  | break; | 
|  | case Algo::RSA: | 
|  | switch (padding) { | 
|  | case Padding::PKCS1_5: | 
|  | param_type = V_ASN1_NULL; | 
|  | switch (digest) { | 
|  | case Digest::SHA1: | 
|  | nid = NID_sha1WithRSAEncryption; | 
|  | break; | 
|  | case Digest::SHA224: | 
|  | nid = NID_sha224WithRSAEncryption; | 
|  | break; | 
|  | case Digest::SHA256: | 
|  | nid = NID_sha256WithRSAEncryption; | 
|  | break; | 
|  | case Digest::SHA384: | 
|  | nid = NID_sha384WithRSAEncryption; | 
|  | break; | 
|  | case Digest::SHA512: | 
|  | nid = NID_sha512WithRSAEncryption; | 
|  | break; | 
|  | default: | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  | break; | 
|  | case Padding::PSS: { | 
|  | auto v = buildRsaPssParameter(digest); | 
|  | if (auto param_str = std::get_if<ASN1_STRING_Ptr>(&v)) { | 
|  | param = std::move(*param_str); | 
|  | param_type = V_ASN1_SEQUENCE; | 
|  | nid = NID_rsassaPss; | 
|  | } else { | 
|  | return std::get<CertUtilsError>(v); | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | return CertUtilsError::InvalidArgument; | 
|  | } | 
|  |  | 
|  | if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) { | 
|  | return CertUtilsError::Encoding; | 
|  | } | 
|  | // The X509 struct took ownership. | 
|  | param.release(); | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | // 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; | 
|  | } | 
|  | if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) { | 
|  | return error; | 
|  | } | 
|  |  | 
|  | uint8_t* cert_buf = nullptr; | 
|  | int buf_len = i2d_re_X509_tbs(certificate, &cert_buf); | 
|  | if (buf_len < 0) { | 
|  | return CertUtilsError::Encoding; | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<uint8_t> free_cert_buf(cert_buf); | 
|  | auto signature = sign(cert_buf, buf_len); | 
|  |  | 
|  | if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) { | 
|  | return CertUtilsError::BoringSsl; | 
|  | } | 
|  |  | 
|  | certificate->signature->flags &= ~(0x07); | 
|  | certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT; | 
|  |  | 
|  | return CertUtilsError::Ok; | 
|  | } | 
|  |  | 
|  | }  // namespace keystore |