diff options
Diffstat (limited to 'ondevice-signing/VerityUtils.cpp')
-rw-r--r-- | ondevice-signing/VerityUtils.cpp | 221 |
1 files changed, 53 insertions, 168 deletions
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp index cd9a1ea6..0b631daa 100644 --- a/ondevice-signing/VerityUtils.cpp +++ b/ondevice-signing/VerityUtils.cpp @@ -26,16 +26,15 @@ #include <sys/types.h> #include <sys/wait.h> +#include "android-base/errors.h" #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/result.h> #include <android-base/unique_fd.h> #include <asm/byteorder.h> #include <libfsverity.h> #include <linux/fsverity.h> -#include "CertUtils.h" -#include "SigningKey.h" - #define FS_VERITY_MAX_DIGEST_SIZE 64 using android::base::ErrnoError; @@ -58,23 +57,6 @@ static std::string toHex(std::span<const uint8_t> data) { return ss.str(); } -static std::vector<uint8_t> fromHex(std::string_view hex) { - if (hex.size() % 2 != 0) { - return {}; - } - std::vector<uint8_t> result; - result.reserve(hex.size() / 2); - for (size_t i = 0; i < hex.size(); i += 2) { - uint8_t byte; - auto conversion_result = std::from_chars(&hex[i], &hex[i + 2], byte, 16); - if (conversion_result.ptr != &hex[i + 2] || conversion_result.ec != std::errc()) { - return {}; - } - result.push_back(byte); - } - return result; -} - 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; @@ -127,20 +109,6 @@ template <typename T> struct DeleteAsPODArray { } }; -static Result<void> measureFsVerity(int fd, const fsverity_digest* digest) { - if (ioctl(fd, FS_IOC_MEASURE_VERITY, digest) != 0) { - if (errno == ENODATA) { - return Error() << "File is not in fs-verity"; - } else { - return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY"; - } - } - - return {}; -} - -} // namespace - template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>; template <typename T> @@ -150,28 +118,35 @@ static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_si return trailing_unique_ptr<T>{ptr}; } -static Result<std::vector<uint8_t>> signDigest(const SigningKey& key, - const std::vector<uint8_t>& digest) { - auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size()); +static Result<std::string> measureFsVerity(int fd) { + auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE); + d->digest_size = FS_VERITY_MAX_DIGEST_SIZE; + + if (ioctl(fd, FS_IOC_MEASURE_VERITY, d.get()) != 0) { + if (errno == ENODATA) { + return Error() << "File is not in fs-verity"; + } else { + return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY"; + } + } - 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()); + return toHex({&d->digest[0], &d->digest[d->digest_size]}); +} - auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size())); - if (!signed_digest.ok()) { - return signed_digest.error(); +static Result<std::string> measureFsVerity(const std::string& path) { + unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + if (!fd.ok()) { + return ErrnoError() << "Failed to open " << path; } - return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end()); + return measureFsVerity(fd.get()); } -static Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) { +} // namespace + +static Result<void> enableFsVerity(int fd) { struct fsverity_enable_arg arg = {.version = 1}; - arg.sig_ptr = reinterpret_cast<uint64_t>(pkcs7.data()); - arg.sig_size = pkcs7.size(); arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; arg.block_size = 4096; @@ -184,59 +159,24 @@ static Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) { return {}; } -Result<std::string> enableFsVerity(int fd, const SigningKey& key) { - auto digest = createDigest(fd); - if (!digest.ok()) { - return Error() << digest.error(); - } - - auto signed_digest = signDigest(key, digest.value()); - if (!signed_digest.ok()) { - return signed_digest.error(); - } - - auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject); - if (!pkcs7_data.ok()) { - return pkcs7_data.error(); - } - - auto enabled = enableFsVerity(fd, pkcs7_data.value()); - if (!enabled.ok()) { - return Error() << enabled.error(); - } - - // Return the root hash as a hex string - return toHex(digest.value()); -} - -static Result<std::string> isFileInVerity(int fd) { - auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE); - d->digest_size = FS_VERITY_MAX_DIGEST_SIZE; - - const auto& status = measureFsVerity(fd, d.get()); - if (!status.ok()) { - return status.error(); - } - - return toHex({&d->digest[0], &d->digest[d->digest_size]}); -} - -static Result<std::string> isFileInVerity(const std::string& path) { +Result<void> enableFsVerity(const std::string& path) { unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); if (!fd.ok()) { - return ErrnoError() << "Failed to open " << path; + return Error() << "Can't open " << path; } - auto digest = isFileInVerity(fd.get()); - if (!digest.ok()) { - return Error() << digest.error() << ": " << path; - } + return enableFsVerity(fd.get()); +} - return digest; +static Result<bool> isFileInVerity(int fd) { + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) { + return ErrnoError() << "ioctl(FS_IOC_GETFLAGS) failed"; + } + return (flags & FS_VERITY_FL) != 0; } -Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path, - const SigningKey& key) { +Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path) { std::map<std::string, std::string> digests; std::error_code ec; @@ -247,18 +187,15 @@ Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std:: if (!fd.ok()) { return ErrnoError() << "Failed to open " << path; } - auto digest = isFileInVerity(fd); - if (!digest.ok()) { + auto enabled = OR_RETURN(isFileInVerity(fd)); + if (!enabled) { LOG(INFO) << "Adding " << it->path() << " to fs-verity..."; - auto result = enableFsVerity(fd, key); - if (!result.ok()) { - return result.error(); - } - digests[it->path()] = *result; + OR_RETURN(enableFsVerity(fd)); } else { LOG(INFO) << it->path() << " was already in fs-verity."; - digests[it->path()] = *digest; } + auto digest = OR_RETURN(measureFsVerity(fd)); + digests[it->path()] = digest; } } if (ec) { @@ -268,31 +205,6 @@ Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std:: return digests; } -Result<void> enableFsVerity(const std::string& path, const std::string& signature_path) { - unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); - if (!fd.ok()) { - return Error() << "Can't open " << path; - } - - std::string signature; - android::base::ReadFileToString(signature_path, &signature); - std::vector<uint8_t> span = std::vector<uint8_t>(signature.begin(), signature.end()); - - const auto& enable = enableFsVerity(fd.get(), span); - if (!enable.ok()) { - return enable.error(); - } - - auto digest = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE); - digest->digest_size = FS_VERITY_MAX_DIGEST_SIZE; - const auto& measure = measureFsVerity(fd.get(), digest.get()); - if (!measure.ok()) { - return measure.error(); - } - - return {}; -} - Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) { std::map<std::string, std::string> digests; std::error_code ec; @@ -303,11 +215,8 @@ Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::str while (!ec && it != end) { if (it->is_regular_file()) { // Verify the file is in fs-verity - auto result = isFileInVerity(it->path()); - if (!result.ok()) { - return result.error(); - } - digests[it->path()] = *result; + auto result = OR_RETURN(measureFsVerity(it->path())); + digests[it->path()] = result; } else if (it->is_directory()) { // These are fine to ignore } else if (it->is_symlink()) { @@ -325,8 +234,7 @@ Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::str } Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path, - const std::map<std::string, std::string>& digests, - const SigningKey& signing_key) { + const std::map<std::string, std::string>& digests) { std::error_code ec; size_t verified_count = 0; auto it = std::filesystem::recursive_directory_iterator(directory_path, ec); @@ -344,41 +252,18 @@ Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path, return ErrnoError() << "Can't open " << path; } - auto verity_digest = isFileInVerity(fd); - if (verity_digest.ok()) { - // The file is already in fs-verity. We need to make sure it was signed - // by CompOS, so we just check that it has the digest we expect. - if (verity_digest.value() == compos_digest) { - ++verified_count; - } else { - return Error() << "fs-verity digest does not match CompOS digest: " << path; - } - } else { - // Not in fs-verity yet. We know the digest CompOS provided; If - // it's not the correct digest for the file then enabling - // fs-verity will fail, so we don't need to check it explicitly - // ourselves. Otherwise we should be good. - LOG(INFO) << "Adding " << path << " to fs-verity..."; - - auto digest_bytes = fromHex(compos_digest); - if (digest_bytes.empty()) { - return Error() << "Invalid digest " << compos_digest; - } - auto signed_digest = signDigest(signing_key, digest_bytes); - if (!signed_digest.ok()) { - return signed_digest.error(); - } - - auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject); - if (!pkcs7_data.ok()) { - return pkcs7_data.error(); - } - - auto enabled = enableFsVerity(fd, pkcs7_data.value()); - if (!enabled.ok()) { - return Error() << enabled.error(); - } + bool enabled = OR_RETURN(isFileInVerity(fd)); + if (!enabled) { + LOG(INFO) << "Enabling fs-verity for " << path; + OR_RETURN(enableFsVerity(fd)); + } + + auto actual_digest = OR_RETURN(measureFsVerity(fd)); + // Make sure the file's fs-verity digest matches the known value. + if (actual_digest == compos_digest) { ++verified_count; + } else { + return Error() << "fs-verity digest does not match CompOS digest: " << path; } } else if (it->is_directory()) { // These are fine to ignore |