Merge "Revert "Keystore 2.0: Move keymint spec to security namespace.""
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
new file mode 100644
index 0000000..8da28f2
--- /dev/null
+++ b/ondevice-signing/Android.bp
@@ -0,0 +1,102 @@
+// Copyright (C) 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.
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
+
+tidy_errors = [
+ "cert-err34-c",
+ "google-default-arguments",
+ "google-explicit-constructor",
+ "google-runtime-int",
+ "google-runtime-member-string-references",
+ "misc-move-const-arg",
+ "misc-move-forwarding-reference",
+ "misc-unused-parameters",
+ "misc-unused-using-decls",
+ "misc-use-after-move",
+ "modernize-pass-by-value",
+ "performance-faster-string-find",
+ "performance-for-range-copy",
+ "performance-implicit-conversion-in-loop",
+ "performance-inefficient-vector-operation",
+ "performance-move-const-arg",
+ "performance-move-constructor-init",
+ "performance-noexcept-move-constructor",
+ "performance-unnecessary-value-param",
+]
+
+cc_defaults {
+ cpp_std: "experimental",
+ name: "odsign_flags_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+
+ // Some extra flags.
+ "-fstrict-aliasing",
+ "-Wredundant-decls",
+ "-Wshadow",
+ "-Wstrict-aliasing",
+ "-Wthread-safety",
+ "-Wthread-safety-negative",
+ "-Wunreachable-code",
+ "-Wunreachable-code-break",
+ "-Wunreachable-code-return",
+ "-Wunused",
+ "-Wused-but-marked-unused",
+ ],
+ tidy: true,
+ tidy_checks: tidy_errors,
+ tidy_checks_as_errors: tidy_errors,
+ tidy_flags: [
+ "-format-style='file'",
+ ],
+}
+
+cc_binary {
+ name: "odsign",
+ defaults: [
+ "odsign_flags_defaults",
+ ],
+ cpp_std: "experimental",
+ init_rc: ["odsign.rc"],
+ srcs: [
+ "odsign_main.cpp",
+ "CertUtils.cpp",
+ "Keymaster.cpp",
+ "KeymasterSigningKey.cpp",
+ "VerityUtils.cpp",
+ ],
+
+ static_libs: [
+ "libmini_keyctl_static", // TODO need static?
+ "libc++fs",
+ ],
+
+ shared_libs: [
+ "android.hardware.keymaster@4.1",
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libfsverity",
+ "libhidlbase",
+ "liblogwrap",
+ "libkeymaster4support", // For authorization_set
+ "libkeymaster4_1support",
+ "libkeyutils",
+ "libutils",
+ ],
+}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
new file mode 100644
index 0000000..6b24391
--- /dev/null
+++ b/ondevice-signing/CertUtils.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 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 <android-base/logging.h>
+#include <android-base/result.h>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+
+#include <fcntl.h>
+#include <vector>
+const char kBasicConstraints[] = "CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+using android::base::Result;
+// using android::base::ErrnoError;
+using android::base::Error;
+
+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;
+
+ X509V3_set_ctx_nodb(&context);
+
+ X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+ X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+ if (!ex) {
+ return false;
+ }
+
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
+ return true;
+}
+
+Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path) {
+ 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);
+
+ auto pubKeyData = publicKey.data();
+ EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &pubKeyData, publicKey.size());
+ if (!X509_set_pubkey(x509.get(), public_key)) {
+ return Error() << "Unable to set x509 public key";
+ }
+
+ X509_NAME* name = X509_get_subject_name(x509.get());
+ if (!name) {
+ 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";
+ }
+
+ 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, "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);
+
+ // 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);
+
+ auto signed_data = signFunction(std::string(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();
+
+ x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ auto f = fopen(path.c_str(), "wb");
+ // TODO error checking
+ i2d_X509_fp(f, x509.get());
+ fclose(f);
+
+ return {};
+}
+
+Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
+ if (pkey == nullptr) {
+ return Error() << "Failed to extract public key from x509 cert";
+ }
+
+ if (EVP_PKEY_type(pkey->type) != 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);
+ std::vector<uint8_t> pubKey(num_bytes);
+ int res = BN_bn2bin(rsa->n, pubKey.data());
+ RSA_free(rsa);
+
+ if (!res) {
+ return Error() << "Failed to convert public key to bytes";
+ }
+
+ return pubKey;
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
+ auto keyDataBytes = keyData.data();
+ EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
+
+ return extractPublicKey(public_key);
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
+ X509* cert;
+ auto f = fopen(path.c_str(), "r");
+ if (!d2i_X509_fp(f, &cert)) {
+ return Error() << "Unable to decode x509 cert at " << path;
+ }
+
+ fclose(f);
+ return extractPublicKey(X509_get_pubkey(cert));
+}
+
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+ CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
+ CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
+ uint8_t *pkcs7_data, *name_der;
+ size_t pkcs7_data_len, name_der_len;
+ BIGNUM* serial = BN_new();
+ int sig_nid = NID_rsaEncryption;
+
+ X509_NAME* name = X509_NAME_new();
+ if (!name) {
+ 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);
+
+ BN_set_word(serial, 1);
+ name_der_len = i2d_X509_NAME(name, &name_der);
+ CBB_init(&out, 1024);
+
+ if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
+ !CBB_add_asn1(&outer_seq, &wrapped_seq,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ // See https://tools.ietf.org/html/rfc2315#section-9.1
+ !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
+ !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
+ !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
+ !CBB_add_asn1(&signer_info, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
+ !BN_marshal_asn1(&issuer_and_serial, serial) ||
+ !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&sign_algo, sig_nid) || !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&signature, signed_digest.data(), signed_digest.size()) ||
+ !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
+ return Error() << "Failed to create PKCS7 certificate.";
+ }
+
+ return std::vector<uint8_t>(&pkcs7_data[0], &pkcs7_data[pkcs7_data_len]);
+}
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
new file mode 100644
index 0000000..d9172d0
--- /dev/null
+++ b/ondevice-signing/CertUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+android::base::Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path);
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
+
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromX509(const std::vector<uint8_t>& path);
+android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
new file mode 100644
index 0000000..d43828a
--- /dev/null
+++ b/ondevice-signing/Keymaster.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 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 <string>
+
+#include <android-base/logging.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/authorization_set.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "Keymaster.h"
+
+using AuthorizationSet = ::android::hardware::keymaster::V4_0::AuthorizationSet;
+using AuthorizationSetBuilder = ::android::hardware::keymaster::V4_0::AuthorizationSetBuilder;
+using Digest = ::android::hardware::keymaster::V4_0::Digest;
+using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using HidlBuf = ::android::hardware::hidl_vec<uint8_t>;
+using KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
+using KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
+using KeyPurpose = ::android::hardware::keymaster::V4_0::KeyPurpose;
+using KmSupport = ::android::hardware::keymaster::V4_1::support::Keymaster;
+using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+using OperationHandle = ::android::hardware::keymaster::V4_0::OperationHandle;
+using PaddingMode = ::android::hardware::keymaster::V4_0::PaddingMode;
+using VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
+
+using android::sp;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+using android::hardware::hidl_vec;
+
+Keymaster::Keymaster() {}
+
+bool Keymaster::initialize() {
+ // TODO(b/165630556): Stop using Keymaster directly and migrate to keystore2
+ // (once available).
+ auto devices = KmSupport::enumerateAvailableDevices();
+ sp<KmDevice> devToUse = nullptr;
+ for (const auto& dev : devices) {
+ auto version = dev->halVersion();
+ if (version.majorVersion > 4 || (version.majorVersion == 4 && version.minorVersion >= 1)) {
+ // TODO we probably have a preference for the SE, hoping Keystore2 will provide this
+ LOG(INFO) << "Using keymaster " << version.keymasterName << " "
+ << (int)version.majorVersion << "." << (int)version.minorVersion;
+ devToUse = dev;
+ break;
+ }
+ }
+
+ mDevice = devToUse;
+
+ return true;
+}
+
+std::optional<Keymaster> Keymaster::getInstance() {
+ static Keymaster keymaster;
+
+ if (!keymaster.initialize()) {
+ return {};
+ } else {
+ return {keymaster};
+ }
+}
+
+Result<std::vector<uint8_t>> Keymaster::createKey() const {
+ ErrorCode error;
+ HidlBuf keyBlob;
+
+ auto params = AuthorizationSetBuilder()
+ .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+ // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+ // currently doesn't work on cuttlefish (b/173618442)
+ //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+ mDevice->generateKey(params.hidl_data(), [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
+ const KeyCharacteristics&
+ /* hidl_key_characteristics */) {
+ error = hidl_error;
+ keyBlob = hidl_key_blob;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error creating keymaster signing key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return keyBlob;
+}
+
+static ErrorCode Begin(const sp<KmDevice>& keymaster_, KeyPurpose purpose, const HidlBuf& key_blob,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params,
+ OperationHandle* op_handle) {
+ ErrorCode error;
+ OperationHandle saved_handle = *op_handle;
+ CHECK(keymaster_
+ ->begin(purpose, key_blob, in_params.hidl_data(), HardwareAuthToken(),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ uint64_t hidl_op_handle) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ *op_handle = hidl_op_handle;
+ })
+ .isOk());
+ if (error != ErrorCode::OK) {
+ // Some implementations may modify *op_handle on error.
+ *op_handle = saved_handle;
+ }
+ return error;
+}
+
+static ErrorCode Update(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+ const AuthorizationSet& in_params, const std::string& input,
+ AuthorizationSet* out_params, std::string* output, size_t* input_consumed) {
+ ErrorCode error;
+ HidlBuf inputData(input.size());
+ memcpy(inputData.data(), input.c_str(), input.size());
+ CHECK(keymaster_
+ ->update(op_handle, in_params.hidl_data(), inputData, HardwareAuthToken(),
+ VerificationToken(),
+ [&](ErrorCode hidl_error, uint32_t hidl_input_consumed,
+ const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ out_params->push_back(AuthorizationSet(hidl_out_params));
+ std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+ hidl_output.size());
+ output->append(retdata);
+ *input_consumed = hidl_input_consumed;
+ })
+ .isOk());
+ return error;
+}
+
+static ErrorCode Finish(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+ const AuthorizationSet& in_params, const std::string& input,
+ const std::string& signature, AuthorizationSet* out_params,
+ std::string* output) {
+ ErrorCode error;
+ HidlBuf inputData(input.size());
+ memcpy(inputData.data(), input.c_str(), input.size());
+ HidlBuf signatureData(signature.size());
+ memcpy(signatureData.data(), signature.c_str(), signature.size());
+ // TODO still need to handle error -62 - key requires upgrade
+ CHECK(keymaster_
+ ->finish(op_handle, in_params.hidl_data(), inputData, signatureData,
+ HardwareAuthToken(), VerificationToken(),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+ hidl_output.size());
+ output->append(retdata);
+ })
+ .isOk());
+ return error;
+}
+
+static std::string ProcessMessage(const sp<KmDevice>& keymaster_, const HidlBuf& key_blob,
+ KeyPurpose operation, const std::string& message,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params) {
+ AuthorizationSet begin_out_params;
+ OperationHandle op_handle_;
+ ErrorCode ec =
+ Begin(keymaster_, operation, key_blob, in_params, &begin_out_params, &op_handle_);
+
+ std::string output;
+ size_t consumed = 0;
+ AuthorizationSet update_params;
+ AuthorizationSet update_out_params;
+ ec = Update(keymaster_, op_handle_, update_params, message, &update_out_params, &output,
+ &consumed);
+
+ std::string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ ec = Finish(keymaster_, op_handle_, finish_params, message.substr(consumed), unused,
+ &finish_out_params, &output);
+
+ out_params->push_back(begin_out_params);
+ out_params->push_back(finish_out_params);
+ return output;
+}
+
+Result<std::vector<uint8_t>>
+Keymaster::extractPublicKey(const std::vector<uint8_t>& keyBlob) const {
+ std::vector<uint8_t> publicKey;
+ ErrorCode error;
+
+ mDevice->exportKey(KeyFormat::X509, keyBlob, {} /* clientId */, {} /* appData */,
+ [&](ErrorCode hidl_error, const HidlBuf& keyData) {
+ error = hidl_error;
+ publicKey = keyData;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error extracting public key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return publicKey;
+}
+
+Result<KeymasterVerifyResult> Keymaster::verifyKey(const std::vector<uint8_t>& keyBlob) const {
+ ErrorCode error;
+ KeyCharacteristics characteristics;
+
+ mDevice->getKeyCharacteristics(
+ keyBlob, {} /* clientId */, {} /* appData */,
+ [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_characteristics) {
+ error = hidl_error;
+ characteristics = hidl_characteristics;
+ });
+
+ if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
+ return KeymasterVerifyResult::UPGRADE;
+ }
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error getting key characteristics: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ // TODO(b/165630556)
+ // Verify this is an early boot key and the other key parameters
+ return KeymasterVerifyResult::OK;
+}
+
+Result<std::vector<uint8_t>> Keymaster::upgradeKey(const std::vector<uint8_t>& keyBlob) const {
+ ErrorCode error;
+ HidlBuf newKeyBlob;
+
+ // TODO deduplicate
+ auto params = AuthorizationSetBuilder()
+ .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+ // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+ // currently doesn't work on cuttlefish (b/173618442)
+ //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+ mDevice->upgradeKey(keyBlob, params.hidl_data(),
+ [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob) {
+ error = hidl_error;
+ newKeyBlob = hidl_key_blob;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error upgrading keymaster signing key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return newKeyBlob;
+}
+
+Result<std::string> Keymaster::sign(const std::vector<uint8_t>& keyBlob,
+ const std::string& message) const {
+ AuthorizationSet out_params;
+ auto params = AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+ std::string signature =
+ ProcessMessage(mDevice, keyBlob, KeyPurpose::SIGN, message, params, &out_params);
+ if (!out_params.empty()) {
+ return Error() << "Error signing key: expected empty out params.";
+ }
+ return signature;
+}
diff --git a/ondevice-signing/Keymaster.h b/ondevice-signing/Keymaster.h
new file mode 100644
index 0000000..455289f
--- /dev/null
+++ b/ondevice-signing/Keymaster.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <keymasterV4_1/Keymaster.h>
+
+#include <utils/StrongPointer.h>
+
+enum class KeymasterVerifyResult {
+ OK = 0,
+ UPGRADE = -1,
+};
+
+class Keymaster {
+ using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+ public:
+ static std::optional<Keymaster> getInstance();
+
+ android::base::Result<std::vector<uint8_t>> createKey() const;
+
+ android::base::Result<std::vector<uint8_t>>
+ extractPublicKey(const std::vector<uint8_t>& keyBlob) const;
+
+ android::base::Result<KeymasterVerifyResult>
+ verifyKey(const std::vector<uint8_t>& keyBlob) const;
+
+ android::base::Result<std::vector<uint8_t>>
+ upgradeKey(const std::vector<uint8_t>& keyBlob) const;
+
+ /* Sign a message with an initialized signing key */
+ android::base::Result<std::string> sign(const std::vector<uint8_t>& keyBlob,
+ const std::string& message) const;
+
+ private:
+ Keymaster();
+ bool initialize();
+
+ android::sp<KmDevice> mDevice;
+};
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
new file mode 100644
index 0000000..f35f92b
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 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 <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "Keymaster.h"
+#include "KeymasterSigningKey.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::ReadFileToString;
+using android::base::Result;
+using android::base::unique_fd;
+
+KeymasterSigningKey::KeymasterSigningKey() {}
+
+Result<KeymasterSigningKey> KeymasterSigningKey::loadFromBlobAndVerify(const std::string& path) {
+ KeymasterSigningKey signingKey;
+
+ auto status = signingKey.initializeFromKeyblob(path);
+
+ if (!status.ok()) {
+ return status.error();
+ }
+
+ return std::move(signingKey);
+}
+
+Result<KeymasterSigningKey> KeymasterSigningKey::createNewKey() {
+ KeymasterSigningKey signingKey;
+
+ auto status = signingKey.createSigningKey();
+
+ if (!status.ok()) {
+ return status.error();
+ }
+
+ return std::move(signingKey);
+}
+
+Result<void> KeymasterSigningKey::createSigningKey() {
+ KeymasterSigningKey signingKey;
+ mKeymaster = Keymaster::getInstance();
+
+ auto keyBlob = mKeymaster->createKey();
+
+ if (!keyBlob.ok()) {
+ return keyBlob.error();
+ }
+
+ mVerifiedKeyBlob.assign(keyBlob->begin(), keyBlob->end());
+
+ return {};
+}
+
+Result<void> KeymasterSigningKey::saveKeyblob(const std::string& path) const {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0600)));
+ if (fd == -1) {
+ return ErrnoError() << "Error creating key blob file " << path;
+ }
+
+ if (!android::base::WriteFully(fd, mVerifiedKeyBlob.data(), mVerifiedKeyBlob.size())) {
+ return ErrnoError() << "Error writing key blob file " << path;
+ } else {
+ return {};
+ }
+}
+
+Result<std::vector<uint8_t>> KeymasterSigningKey::getPublicKey() const {
+ auto publicKeyX509 = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
+ if (!publicKeyX509.ok()) {
+ return publicKeyX509.error();
+ }
+ return extractPublicKeyFromX509(publicKeyX509.value());
+}
+
+Result<void> KeymasterSigningKey::createX509Cert(const std::string& outPath) const {
+ auto publicKey = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
+
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+
+ auto keymasterSignFunction = [&](const std::string& to_be_signed) {
+ return this->sign(to_be_signed);
+ };
+ createSelfSignedCertificate(*publicKey, keymasterSignFunction, outPath);
+ return {};
+}
+
+Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
+ mKeymaster = Keymaster::getInstance();
+ std::string keyBlobData;
+
+ bool result = ReadFileToString(path, &keyBlobData);
+ if (!result) {
+ return ErrnoError() << "Failed to read " << path;
+ }
+
+ std::vector<uint8_t> keyBlob = {keyBlobData.begin(), keyBlobData.end()};
+
+ auto verifyResult = mKeymaster->verifyKey(keyBlob);
+ if (!verifyResult.ok()) {
+ return Error() << "Failed to verify key: " << verifyResult.error().message();
+ }
+
+ if (*verifyResult == KeymasterVerifyResult::UPGRADE) {
+ auto upgradeResult = mKeymaster->upgradeKey(keyBlob);
+ if (!upgradeResult.ok()) {
+ return Error() << "Failed to upgrade key: " << upgradeResult.error().message();
+ }
+ mVerifiedKeyBlob = *upgradeResult;
+ // Make sure we persist the new blob
+ auto saveResult = saveKeyblob(path);
+ if (!saveResult.ok()) {
+ return Error() << "Failed to store upgraded key";
+ }
+ } else {
+ mVerifiedKeyBlob = keyBlob;
+ }
+
+ return {};
+}
+
+Result<std::string> KeymasterSigningKey::sign(const std::string& message) const {
+ return mKeymaster->sign(mVerifiedKeyBlob, message);
+}
diff --git a/ondevice-signing/KeymasterSigningKey.h b/ondevice-signing/KeymasterSigningKey.h
new file mode 100644
index 0000000..7631059
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <utils/StrongPointer.h>
+
+#include "Keymaster.h"
+
+class KeymasterSigningKey {
+ using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+ public:
+ // Allow the key to be moved around
+ KeymasterSigningKey& operator=(KeymasterSigningKey&& other) = default;
+ KeymasterSigningKey(KeymasterSigningKey&& other) = default;
+
+ static android::base::Result<KeymasterSigningKey>
+ loadFromBlobAndVerify(const std::string& path);
+ static android::base::Result<KeymasterSigningKey> createNewKey();
+
+ /* Sign a message with an initialized signing key */
+ android::base::Result<std::string> sign(const std::string& message) const;
+ android::base::Result<void> saveKeyblob(const std::string& path) const;
+ android::base::Result<std::vector<uint8_t>> getPublicKey() const;
+ android::base::Result<void> createX509Cert(const std::string& path) const;
+
+ private:
+ KeymasterSigningKey();
+
+ android::base::Result<void> createSigningKey();
+ android::base::Result<void> initializeFromKeyblob(const std::string& path);
+
+ std::optional<Keymaster> mKeymaster;
+ std::vector<uint8_t> mVerifiedKeyBlob;
+
+ DISALLOW_COPY_AND_ASSIGN(KeymasterSigningKey);
+};
diff --git a/ondevice-signing/OWNERS b/ondevice-signing/OWNERS
new file mode 100644
index 0000000..72a8eb5
--- /dev/null
+++ b/ondevice-signing/OWNERS
@@ -0,0 +1,3 @@
+maco@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/ondevice-signing/PREUPLOAD.cfg b/ondevice-signing/PREUPLOAD.cfg
new file mode 100644
index 0000000..4c6fbd6
--- /dev/null
+++ b/ondevice-signing/PREUPLOAD.cfg
@@ -0,0 +1,11 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+
+[Builtin Hooks]
+clang_format = true
+commit_msg_changeid_field = true
+commit_msg_test_field = true
+gofmt = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
\ No newline at end of file
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
new file mode 100644
index 0000000..579d3d8
--- /dev/null
+++ b/ondevice-signing/VerityUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 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 <filesystem>
+#include <string>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <libfsverity.h>
+#include <linux/fsverity.h>
+
+#include "CertUtils.h"
+#include "KeymasterSigningKey.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
+#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
+#else
+#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
+#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
+#endif
+
+struct fsverity_signed_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
+
+static int read_callback(void* file, void* buf, size_t count) {
+ int* fd = (int*)file;
+ if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
+ return 0;
+}
+
+static Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+ struct stat filestat;
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+
+ stat(path.c_str(), &filestat);
+ struct libfsverity_merkle_tree_params params = {
+ .version = 1,
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = static_cast<uint64_t>(filestat.st_size),
+ .block_size = 4096,
+ };
+
+ struct libfsverity_digest* digest;
+ libfsverity_compute_digest(&fd, &read_callback, ¶ms, &digest);
+
+ return std::vector<uint8_t>(&digest->digest[0], &digest->digest[32]);
+}
+
+static Result<std::vector<uint8_t>> signDigest(const KeymasterSigningKey& key,
+ const std::vector<uint8_t>& digest) {
+ struct fsverity_signed_digest* d = NULL;
+ size_t signed_digest_size = sizeof(*d) + digest.size();
+ d = (struct fsverity_signed_digest*)malloc(signed_digest_size);
+
+ memcpy(d->magic, "FSVerity", 8);
+ d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = cpu_to_le16(digest.size());
+ memcpy(d->digest, digest.data(), digest.size());
+
+ auto signed_digest = key.sign(std::string((char*)d, signed_digest_size));
+ if (!signed_digest.ok()) {
+ return signed_digest.error();
+ }
+
+ return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
+}
+
+Result<void> enableFsVerity(const std::string& path, const KeymasterSigningKey& key) {
+ auto digest = createDigest(path);
+ if (!digest.ok()) {
+ return digest.error();
+ }
+
+ auto signed_digest = signDigest(key, digest.value());
+ if (!signed_digest.ok()) {
+ return signed_digest.error();
+ }
+
+ auto pkcs7_data = createPkcs7(signed_digest.value());
+
+ struct fsverity_enable_arg arg = {.version = 1};
+
+ arg.sig_ptr = (uint64_t)pkcs7_data->data();
+ arg.sig_size = pkcs7_data->size();
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+ if (ret != 0) {
+ return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+ }
+
+ return {};
+}
+
+Result<void> addFilesToVerityRecursive(const std::string& path, const KeymasterSigningKey& key) {
+ std::error_code ec;
+
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
+ if (it->is_regular_file()) {
+ LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+ auto result = enableFsVerity(it->path(), key);
+ if (!result.ok()) {
+ return result.error();
+ }
+ }
+ ++it;
+ }
+
+ return {};
+}
+
+Result<bool> isFileInVerity(const std::string& path) {
+ unsigned int flags;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+
+ int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+ if (ret < 0) {
+ return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
+ }
+
+ return (flags & FS_VERITY_FL);
+}
+
+Result<void> verifyAllFilesInVerity(const std::string& path) {
+ std::error_code ec;
+
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
+ if (it->is_regular_file()) {
+ // Verify
+ auto result = isFileInVerity(it->path());
+ if (!result.ok()) {
+ return result.error();
+ }
+ if (!*result) {
+ return Error() << "File " << it->path() << " not in fs-verity";
+ }
+ } // TODO reject other types besides dirs?
+ ++it;
+ }
+
+ return {};
+}
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/VerityUtils.h
new file mode 100644
index 0000000..1eca5a6
--- /dev/null
+++ b/ondevice-signing/VerityUtils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+#include "KeymasterSigningKey.h"
+
+android::base::Result<void> verifyAllFilesInVerity(const std::string& path);
+android::base::Result<void> addFilesToVerityRecursive(const std::string& path,
+ const KeymasterSigningKey& key);
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
new file mode 100644
index 0000000..044bae7
--- /dev/null
+++ b/ondevice-signing/odsign.rc
@@ -0,0 +1,6 @@
+service odsign /system/bin/odsign
+ class core
+ user root
+ group system
+ oneshot
+ disabled # does not start with the core class
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
new file mode 100644
index 0000000..efe7d35
--- /dev/null
+++ b/ondevice-signing/odsign_main.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 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 <fcntl.h>
+#include <filesystem>
+#include <iomanip>
+#include <iostream>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <logwrap/logwrap.h>
+
+#include "CertUtils.h"
+#include "KeymasterSigningKey.h"
+#include "VerityUtils.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
+
+const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/system";
+
+static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+
+static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+
+static const bool kForceCompilation = false;
+
+Result<void> addCertToFsVerityKeyring(const std::string& path) {
+ const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
+
+ int fd = open(path.c_str(), O_RDONLY);
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ int argc = arraysize(argv);
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char*));
+ argv_child[argc] = nullptr;
+ execvp(argv_child[0], const_cast<char**>(argv_child));
+ PLOG(ERROR) << "exec in ForkExecvp";
+ _exit(EXIT_FAILURE);
+ } else {
+ close(fd);
+ }
+ if (pid == -1) {
+ return ErrnoError() << "Failed to fork.";
+ }
+ int status;
+ if (waitpid(pid, &status, 0) == -1) {
+ return ErrnoError() << "waitpid() failed.";
+ }
+ if (!WIFEXITED(status)) {
+ return Error() << kFsVerityInitPath << ": abnormal process exit";
+ }
+ if (WEXITSTATUS(status)) {
+ if (status != 0) {
+ return Error() << kFsVerityInitPath << " exited with " << status;
+ }
+ }
+
+ return {};
+}
+
+Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
+ if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
+ return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
+ }
+ return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
+}
+
+Result<void> verifyAndLoadExistingCert(const KeymasterSigningKey& key) {
+ if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
+ return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
+ }
+ auto trustedPublicKey = key.getPublicKey();
+ if (!trustedPublicKey.ok()) {
+ return Error() << "Failed to retrieve signing public key.";
+ }
+
+ auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
+ if (!publicKeyFromExistingCert.ok()) {
+ return publicKeyFromExistingCert.error();
+ }
+ if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
+ return Error() << "Public key of existing certificate at " << kSigningKeyCert
+ << " does not match signing public key.";
+ }
+
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+ if (!cert_add_result.ok()) {
+ return cert_add_result.error();
+ }
+
+ // At this point, we know the cert matches
+ return {};
+}
+
+Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
+ auto key = KeymasterSigningKey::createNewKey();
+
+ if (!key.ok()) {
+ return key.error();
+ }
+
+ auto result = key->saveKeyblob(path);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ return key;
+}
+
+bool compileArtifacts(bool force) {
+ const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+
+ return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
+ 0;
+}
+
+bool validateArtifacts() {
+ const char* const argv[] = {kOdrefreshPath, "--check"};
+
+ return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
+ 0;
+}
+
+int main(int /* argc */, char** /* argv */) {
+ auto removeArtifacts = []() {
+ std::error_code ec;
+ auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
+ if (ec) {
+ // TODO can't remove artifacts, signal Zygote shouldn't use them
+ LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
+ } else {
+ LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
+ }
+ };
+ // Make sure we delete the artifacts in all early (error) exit paths
+ auto scope_guard = android::base::make_scope_guard(removeArtifacts);
+
+ auto key = loadAndVerifyExistingKey();
+ if (!key.ok()) {
+ LOG(WARNING) << key.error().message();
+
+ key = createAndPersistKey(kSigningKeyBlob);
+ if (!key.ok()) {
+ LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
+ return -1;
+ }
+ } else {
+ LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
+ }
+
+ auto existing_cert = verifyAndLoadExistingCert(key.value());
+ if (!existing_cert.ok()) {
+ LOG(WARNING) << existing_cert.error().message();
+
+ // Try to create a new cert
+ auto new_cert = key->createX509Cert(kSigningKeyCert);
+ if (!new_cert.ok()) {
+ LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
+ // TODO apparently the key become invalid - delete the blob / cert
+ return -1;
+ }
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+ if (!cert_add_result.ok()) {
+ LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
+ << cert_add_result.error().message();
+ return -1;
+ }
+ } else {
+ LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
+ }
+
+ auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
+ if (!verityStatus.ok()) {
+ LOG(WARNING) << verityStatus.error().message() << ", removing " << kArtArtifactsDir;
+ removeArtifacts();
+ }
+
+ bool artifactsValid = validateArtifacts();
+
+ if (!artifactsValid || kForceCompilation) {
+ removeArtifacts();
+
+ LOG(INFO) << "Starting compilation... ";
+ bool ret = compileArtifacts(kForceCompilation);
+ LOG(INFO) << "Compilation done, returned " << ret;
+
+ verityStatus = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
+
+ if (!verityStatus.ok()) {
+ LOG(ERROR) << "Failed to add " << verityStatus.error().message();
+ return -1;
+ }
+ }
+
+ // TODO we want to make sure Zygote only picks up the artifacts if we deemed
+ // everything was ok here. We could use a sysprop, or some other mechanism?
+ LOG(INFO) << "On-device signing done.";
+
+ scope_guard.Disable();
+ return 0;
+}