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, ¶ms, &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;
}