Add (fake) CompOS key generation.
Note: the CompOS work here is all still behind an if (false).
Added a new class, FakeCompOs, to allow prototyping of the interface
and implementation of the key management work that will be in CompOS.
Extensive refactoring of the certificate generation code to support
both a self-signed cert and our certificate for the CompOS key.
Bug: 190166662
Test: presubmits
Test: manual - certificate gets generated on first boot
Test: manual - certificate verifies ok on second boot
Test: manual inspection of the generated certs' text form
Change-Id: I2f2f043427774c0805e963dfe582feb8d3eac3a4
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index 9867f62..ce2b0fd 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -26,39 +26,55 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
-#include <fcntl.h>
+#include <optional>
#include <vector>
#include "KeyConstants.h"
-const char kBasicConstraints[] = "CA:TRUE";
-const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
-const char kSubjectKeyIdentifier[] = "hash";
-const char kAuthorityKeyIdentifier[] = "keyid:always";
+const char kRootCommonName[] = "ODS";
constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
-using android::base::Result;
-// using android::base::ErrnoError;
+using android::base::ErrnoError;
using android::base::Error;
+using android::base::Result;
-static bool add_ext(X509* cert, int nid, const char* value) {
- size_t len = strlen(value) + 1;
- std::vector<char> mutableValue(value, value + len);
- X509V3_CTX context;
+static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
+ X509* rawCert;
+ auto f = fopen(path.c_str(), "re");
+ if (f == nullptr) {
+ return Error() << "Failed to open " << path;
+ }
+ if (!d2i_X509_fp(f, &rawCert)) {
+ fclose(f);
+ return Error() << "Unable to decode x509 cert at " << path;
+ }
+ bssl::UniquePtr<X509> cert(rawCert);
- X509V3_set_ctx_nodb(&context);
+ fclose(f);
+ return cert;
+}
- X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
- X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+static X509V3_CTX makeContext(X509* issuer, X509* subject) {
+ X509V3_CTX context = {};
+ X509V3_set_ctx(&context, issuer, subject, nullptr, nullptr, 0);
+ return context;
+}
+
+static bool add_ext(X509V3_CTX* context, X509* cert, int nid, const char* value) {
+ bssl::UniquePtr<X509_EXTENSION> ex(X509V3_EXT_nconf_nid(nullptr, context, nid, value));
if (!ex) {
return false;
}
- X509_add_ext(cert, ex, -1);
- X509_EXTENSION_free(ex);
+ X509_add_ext(cert, ex.get(), -1);
return true;
}
+static void addNameEntry(X509_NAME* name, const char* field, const char* value) {
+ X509_NAME_add_entry_by_txt(name, field, MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(value), -1, -1, 0);
+}
+
Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
bssl::UniquePtr<BIGNUM> n(BN_new());
bssl::UniquePtr<BIGNUM> e(BN_new());
@@ -109,19 +125,31 @@
return public_key;
}
-Result<void> createSelfSignedCertificate(
- const std::vector<uint8_t>& publicKey,
- const std::function<Result<std::string>(const std::string&)>& signFunction,
- const std::string& path) {
+static Result<void> createCertificate(
+ const char* commonName, const std::vector<uint8_t>& publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::optional<std::string>& issuerCertPath, const std::string& path) {
+
+ // If an issuer cert is specified, we are signing someone else's key.
+ // Otherwise we are signing our key - a self-signed certificate.
+ bool selfSigned = !issuerCertPath;
+
bssl::UniquePtr<X509> x509(X509_new());
if (!x509) {
return Error() << "Unable to allocate x509 container";
}
X509_set_version(x509.get(), 2);
-
- ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), selfSigned ? 1 : 2);
+
+ 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";
+ }
auto public_key = toRsaPkey(publicKey);
if (!public_key.ok()) {
@@ -132,33 +160,53 @@
return Error() << "Unable to set x509 public key";
}
- X509_NAME* name = X509_get_subject_name(x509.get());
- if (!name) {
+ X509_NAME* subjectName = X509_get_subject_name(x509.get());
+ if (!subjectName) {
return Error() << "Unable to get x509 subject name";
}
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
- if (!X509_set_issuer_name(x509.get(), name)) {
- return Error() << "Unable to set x509 issuer name";
+ addNameEntry(subjectName, "C", "US");
+ addNameEntry(subjectName, "O", "Android");
+ addNameEntry(subjectName, "CN", commonName);
+
+ if (selfSigned) {
+ if (!X509_set_issuer_name(x509.get(), subjectName)) {
+ return Error() << "Unable to set x509 issuer name";
+ }
+ } else {
+ X509_NAME* issuerName = X509_get_issuer_name(x509.get());
+ if (!issuerName) {
+ return Error() << "Unable to get x509 issuer name";
+ }
+ addNameEntry(issuerName, "C", "US");
+ addNameEntry(issuerName, "O", "Android");
+ addNameEntry(issuerName, "CN", kRootCommonName);
}
- add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
- add_ext(x509.get(), NID_key_usage, kKeyUsage);
- add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
- add_ext(x509.get(), NID_authority_key_identifier, kAuthorityKeyIdentifier);
+ // Beware: context contains a pointer to issuerCert, so we need to keep it alive.
+ bssl::UniquePtr<X509> issuerCert;
+ X509V3_CTX context;
- 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";
+ if (selfSigned) {
+ context = makeContext(x509.get(), x509.get());
+ } else {
+ auto certStatus = loadX509(*issuerCertPath);
+ if (!certStatus.ok()) {
+ return Error() << "Unable to load issuer cert: " << certStatus.error();
+ }
+ issuerCert = std::move(certStatus.value());
+ context = makeContext(issuerCert.get(), x509.get());
}
+ // If it's a self-signed cert we use it for signing certs, otherwise only for signing data.
+ const char* basicConstraints = selfSigned ? "CA:TRUE" : "CA:FALSE";
+ const char* keyUsage =
+ selfSigned ? "critical,keyCertSign,cRLSign,digitalSignature" : "critical,digitalSignature";
+
+ add_ext(&context, x509.get(), NID_basic_constraints, basicConstraints);
+ add_ext(&context, x509.get(), NID_key_usage, keyUsage);
+ add_ext(&context, x509.get(), NID_subject_key_identifier, "hash");
+ add_ext(&context, x509.get(), NID_authority_key_identifier, "keyid:always");
+
// Get the data to be signed
unsigned char* to_be_signed_buf(nullptr);
size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), &to_be_signed_buf);
@@ -177,14 +225,30 @@
auto f = fopen(path.c_str(), "wbe");
if (f == nullptr) {
- return Error() << "Failed to open " << path;
+ return ErrnoError() << "Failed to open " << path;
}
i2d_X509_fp(f, x509.get());
- fclose(f);
+ if (fclose(f) != 0) {
+ return ErrnoError() << "Failed to close " << path;
+ }
return {};
}
+Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path) {
+ return createCertificate(kRootCommonName, publicKey, signFunction, {}, path);
+}
+
+android::base::Result<void> createLeafCertificate(
+ const char* commonName, const std::vector<uint8_t>& publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::string& issuerCertPath, const std::string& path) {
+ return createCertificate(commonName, publicKey, signFunction, issuerCertPath, path);
+}
+
Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
if (pkey == nullptr) {
return Error() << "Failed to extract public key from x509 cert";
@@ -225,22 +289,6 @@
return extractPublicKey(decoded_pkey.get());
}
-static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
- X509* rawCert;
- auto f = fopen(path.c_str(), "re");
- if (f == nullptr) {
- return Error() << "Failed to open " << path;
- }
- if (!d2i_X509_fp(f, &rawCert)) {
- fclose(f);
- return Error() << "Unable to decode x509 cert at " << path;
- }
- bssl::UniquePtr<X509> cert(rawCert);
-
- fclose(f);
- return cert;
-}
-
Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
auto cert = loadX509(path);
if (!cert.ok()) {