Add signing command for testing.

Extend the AIDL to allow for signing. This will need to be backed out
before shipping, but it's needed for testing right now.

Add a command to sign a set of files to compos_key_cmd & write the
signature files.

Move compos_key_cmd from virt to compos APEX. (We're definitely going
to need this code in CompOS in some form, so might as well make sure
all the dependencies are available.)

Bug: 190166662
Test: Manual: Sign artifacts, check odsign accepts them.
Change-Id: I25361ed0bee52a9ff13924c77d9378efe8bfd314
diff --git a/compos/compos_key_cmd/Android.bp b/compos/compos_key_cmd/Android.bp
index e03dfdf..460b96f 100644
--- a/compos/compos_key_cmd/Android.bp
+++ b/compos/compos_key_cmd/Android.bp
@@ -5,12 +5,18 @@
 cc_binary {
     name: "compos_key_cmd",
     srcs: ["compos_key_cmd.cpp"],
-    apex_available: ["com.android.virt"],
+    apex_available: ["com.android.compos"],
+
+    static_libs: [
+        "lib_compos_proto",
+    ],
 
     shared_libs: [
         "compos_aidl_interface-ndk_platform",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
+        "libfsverity",
+        "libprotobuf-cpp-lite",
     ],
 }
diff --git a/compos/compos_key_cmd/compos_key_cmd.cpp b/compos/compos_key_cmd/compos_key_cmd.cpp
index d98dac5..5047a48 100644
--- a/compos/compos_key_cmd/compos_key_cmd.cpp
+++ b/compos/compos_key_cmd/compos_key_cmd.cpp
@@ -17,21 +17,33 @@
 #include <aidl/com/android/compos/ICompOsKeyService.h>
 #include <android-base/file.h>
 #include <android-base/result.h>
+#include <android-base/unique_fd.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_manager.h>
+#include <asm/byteorder.h>
+#include <libfsverity.h>
+#include <linux/fsverity.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
-#include <openssl/rsa.h>
+#include <openssl/sha.h>
 #include <openssl/x509.h>
 
+#include <filesystem>
 #include <iostream>
 #include <string>
+#include <string_view>
 
-using android::base::Error;
-using android::base::Result;
+#include "compos_signature.pb.h"
+
+using namespace std::literals;
 
 using aidl::com::android::compos::CompOsKeyData;
 using aidl::com::android::compos::ICompOsKeyService;
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+using compos::proto::Signature;
 
 static bool writeBytesToFile(const std::vector<uint8_t>& bytes, const std::string& path) {
     std::string str(bytes.begin(), bytes.end());
@@ -131,15 +143,103 @@
     return result;
 }
 
+static Result<void> signFile(ICompOsKeyService* service, const std::vector<uint8_t>& key_blob,
+                             const std::string& file) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (!fd.ok()) {
+        return ErrnoError() << "Failed to open";
+    }
+
+    std::filesystem::path signature_path{file};
+    signature_path += ".signature";
+    unique_fd out_fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(),
+                                             O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                             S_IRUSR | S_IWUSR | S_IRGRP)));
+    if (!out_fd.ok()) {
+        return ErrnoError() << "Unable to create signature file";
+    }
+
+    struct stat filestat;
+    if (fstat(fd, &filestat) != 0) {
+        return ErrnoError() << "Failed to fstat";
+    }
+
+    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,
+    };
+
+    auto read_callback = [](void* file, void* buf, size_t count) {
+        int* fd = static_cast<int*>(file);
+        if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return -errno;
+        return 0;
+    };
+
+    struct libfsverity_digest* digest;
+    int ret = libfsverity_compute_digest(&fd, read_callback, &params, &digest);
+    if (ret < 0) {
+        return Error(-ret) << "Failed to compute fs-verity digest";
+    }
+    std::unique_ptr<libfsverity_digest, decltype(&std::free)> digestOwner{digest, std::free};
+
+    std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + digest->digest_size);
+    auto to_be_signed = new (buffer.data()) fsverity_formatted_digest;
+    memcpy(to_be_signed->magic, "FSVerity", sizeof(to_be_signed->magic));
+    to_be_signed->digest_algorithm = __cpu_to_le16(digest->digest_algorithm);
+    to_be_signed->digest_size = __cpu_to_le16(digest->digest_size);
+    memcpy(to_be_signed->digest, digest->digest, digest->digest_size);
+
+    std::vector<uint8_t> signature;
+    auto status = service->sign(key_blob, buffer, &signature);
+    if (!status.isOk()) {
+        return Error() << "Failed to sign: " << status.getDescription();
+    }
+
+    Signature compos_signature;
+    compos_signature.set_digest(digest->digest, digest->digest_size);
+    compos_signature.set_signature(signature.data(), signature.size());
+    if (!compos_signature.SerializeToFileDescriptor(out_fd.get())) {
+        return Error() << "Failed to write signature";
+    }
+    if (close(out_fd.release()) != 0) {
+        return ErrnoError() << "Failed to close signature file";
+    }
+
+    return {};
+}
+
+static Result<void> sign(const std::string& blob_file, const std::vector<std::string>& files) {
+    ndk::SpAIBinder binder(AServiceManager_getService("android.system.composkeyservice"));
+    auto service = ICompOsKeyService::fromBinder(binder);
+    if (!service) {
+        return Error() << "No service";
+    }
+
+    auto blob = readBytesFromFile(blob_file);
+    if (!blob.ok()) {
+        return blob.error();
+    }
+
+    for (auto& file : files) {
+        auto result = signFile(service.get(), blob.value(), file);
+        if (!result.ok()) {
+            return Error() << result.error() << ": " << file;
+        }
+    }
+    return {};
+}
+
 int main(int argc, char** argv) {
-    if (argc == 4 && std::string(argv[1]) == "--generate") {
+    if (argc == 4 && argv[1] == "--generate"sv) {
         auto result = generate(argv[2], argv[3]);
         if (result.ok()) {
             return 0;
         } else {
             std::cerr << result.error() << '\n';
         }
-    } else if (argc == 4 && std::string(argv[1]) == "--verify") {
+    } else if (argc == 4 && argv[1] == "--verify"sv) {
         auto result = verify(argv[2], argv[3]);
         if (result.ok()) {
             if (result.value()) {
@@ -151,13 +251,24 @@
         } else {
             std::cerr << result.error() << '\n';
         }
+    } else if (argc >= 4 && argv[1] == "--sign"sv) {
+        const std::vector<std::string> files{&argv[3], &argv[argc]};
+        auto result = sign(argv[2], files);
+        if (result.ok()) {
+            std::cerr << "All signatures generated.\n";
+            return 0;
+        } else {
+            std::cerr << result.error() << '\n';
+        }
     } else {
         std::cerr << "Usage: \n"
                   << "  --generate <blob file> <public key file> Generate new key pair and "
                      "write\n"
                   << "    the private key blob and public key to the specified files.\n "
                   << "  --verify <blob file> <public key file> Verify that the content of the\n"
-                  << "    specified private key blob and public key files are valid.\n ";
+                  << "    specified private key blob and public key files are valid.\n "
+                  << "  --sign <blob file> <files to be signed> Generate signatures for one or\n"
+                  << "    more files using the supplied private key blob.\n";
     }
     return 1;
 }