summaryrefslogtreecommitdiff
path: root/ondevice-signing/VerityUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ondevice-signing/VerityUtils.cpp')
-rw-r--r--ondevice-signing/VerityUtils.cpp221
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