On-device signing binary.

This is a first version of the on-device signing binary, that will be
used to sign ART compilation artifacts on an ART mainline module update.

The following basic functionality is implemented:
1) Creating a signing key (though not an early-boot key yet, because
those are broken on cuttlefish)
2) Creating an X.509 certificate from that key
3) Adding said certificate to the fs-verity keychain
4) Verify existing artifacts are in fs-verity (and delete them if
they're not)
5) Call odrefresh --check to have ART verify the artifacts are fresh and
correct
6) Call odrefresh --compile if they're not
7) Add generated output files to fs-verity

Important things left to do (not an exhaustive list):
1) Verify the key characteristics (eg, early-boot key)
2) Add a "manifest" file that records the signed files and their root
hashes; this will prevent us from accepting any random fs-verity file
3) Add a property or other signal to tell Zygote "we think these
artifacts are safe to use"
4) Migrate to keystore2 (once available)

Bug: 165630556
Test: Run odsign as root
Change-Id: I8aa09914d76f20f30c2093961271202fe7add711
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, &params, &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;
+}