diff options
author | Eric Biggers <ebiggers@google.com> | 2018-07-27 10:47:02 -0700 |
---|---|---|
committer | Eric Biggers <ebiggers@google.com> | 2018-07-27 10:47:02 -0700 |
commit | 25b594522ad0b002272f9b00dcc278b766456d22 (patch) | |
tree | 90684ba52bfc81865579c084bc1d6b014351d995 | |
parent | 75488a25486e442b701ec77862d9c6faf823fd96 (diff) | |
download | fsverity-utils-25b594522ad0b002272f9b00dcc278b766456d22.tar.gz |
Update to match latest kernel changes
- Root hash is now stored in authenticated extensions
- Magic number is now at the very end of the file too
- 'set_measurement' is replaced with 'measure'
- UAPI header now declares on-disk format
- log_tree_blocksize is stored instead of log_arity
- CRC-32 is changed to CRC-32C
- SHA-512 is now supported too
Signed-off-by: Eric Biggers <ebiggers@google.com>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 27 | ||||
-rw-r--r-- | cmd_enable.c | 2 | ||||
-rw-r--r-- | cmd_measure.c | 72 | ||||
-rw-r--r-- | cmd_set_measurement.c | 87 | ||||
-rw-r--r-- | cmd_setup.c | 145 | ||||
-rw-r--r-- | commands.h | 4 | ||||
-rw-r--r-- | crc32c_table.h | 72 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rw-r--r-- | elide_patch.c | 2 | ||||
-rw-r--r-- | fsverity.c | 16 | ||||
-rw-r--r-- | fsverity_sys_decls.h | 91 | ||||
-rw-r--r-- | fsverity_uapi.h | 112 | ||||
-rw-r--r-- | hash_algs.c | 79 | ||||
-rw-r--r-- | hash_algs.h | 5 | ||||
-rw-r--r-- | scripts/gen_crc32c_table.c | 62 | ||||
-rw-r--r-- | sign.c | 50 |
17 files changed, 505 insertions, 325 deletions
@@ -1,7 +1,7 @@ EXE := fsverity CFLAGS := -O2 -Wall CPPFLAGS := -D_FILE_OFFSET_BITS=64 -LDLIBS := -lcrypto -lz +LDLIBS := -lcrypto DESTDIR := /usr/local SRC := $(wildcard *.c) OBJ := $(SRC:.c=.o) @@ -1,6 +1,6 @@ # Introduction -This is `fsverity`, the userspace utility for fs-verity. fs-verity is +This is `fsverity`, a userspace utility for fs-verity. fs-verity is a Linux kernel feature that does transparent on-demand integrity/authenticity verification of the contents of read-only files, using a Merkle tree (hash tree) hidden after the end of the @@ -8,16 +8,17 @@ file. The mechanism is similar to dm-verity, but implemented at the file level rather than at the block device level. The `fsverity` utility allows you to set up fs-verity protected files. -Currently, fs-verity is supported by the ext4 and f2fs filesystems. +So far, fs-verity is planned to be supported by the ext4 and f2fs +filesystems. # Building and installing -The `fsverity` utility uses the OpenSSL and zlib libraries, so you -first must install the needed development files. For example, on -Debian-based systems, run: +The `fsverity` utility uses the OpenSSL library, so you first must +install the needed development files. For example, on Debian-based +systems, run: ```bash - sudo apt-get install libssl-dev libz-dev + sudo apt-get install libssl-dev ``` OpenSSL must be version 1.0.0 or later. @@ -41,26 +42,20 @@ Then, to build and install: md5sum file # Append the Merkle tree and other metadata to the file, and - # sign the file with the kernel build-time generated key: + # (optional) sign the file with the kernel build-time generated key: fsverity setup file --signing-key ~/linux/certs/signing_key.pem # Enable fs-verity on the file fsverity enable file + # Should show the same hash that 'fsverity setup' printed + fsverity measure file + # Contents are now transparently verified and should match the # original file contents, i.e. the metadata is hidden. md5sum file ``` -Usage without signing the file (deprecated, requires that the kernel -was built with `CONFIG_FS_VERITY_USERSPACE_SIG_VERIFY=y`): -```bash - m=$(fsverity setup file | awk '/measurement/{print $3}') - fsverity enable file - fsverity set_measurement file $m - md5sum file -``` - # Notices Copyright (C) 2018 Google, Inc. diff --git a/cmd_enable.c b/cmd_enable.c index 6d28297..c7d7abd 100644 --- a/cmd_enable.c +++ b/cmd_enable.c @@ -11,7 +11,7 @@ #include <sys/ioctl.h> #include "commands.h" -#include "fsverity_sys_decls.h" +#include "fsverity_uapi.h" int fsverity_cmd_enable(const struct fsverity_command *cmd, int argc, char *argv[]) diff --git a/cmd_measure.c b/cmd_measure.c new file mode 100644 index 0000000..547e5e7 --- /dev/null +++ b/cmd_measure.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'fsverity measure' command + * + * Copyright (C) 2018 Google, Inc. + * + * Written by Eric Biggers, 2018. + */ + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/ioctl.h> + +#include "commands.h" +#include "fsverity_uapi.h" +#include "hash_algs.h" + +int fsverity_cmd_measure(const struct fsverity_command *cmd, + int argc, char *argv[]) +{ + struct fsverity_digest *d = NULL; + struct filedes file; + char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1]; + const struct fsverity_hash_alg *hash_alg; + char _hash_alg_name[32]; + const char *hash_alg_name; + int status; + int i; + + if (argc < 2) + goto out_usage; + + d = xzalloc(sizeof(*d) + FS_VERITY_MAX_DIGEST_SIZE); + + for (i = 1; i < argc; i++) { + d->digest_size = FS_VERITY_MAX_DIGEST_SIZE; + + if (!open_file(&file, argv[i], O_RDONLY, 0)) + goto out_err; + if (ioctl(file.fd, FS_IOC_MEASURE_VERITY, d) != 0) { + error_msg_errno("FS_IOC_MEASURE_VERITY failed on '%s'", + file.name); + filedes_close(&file); + goto out_err; + } + filedes_close(&file); + + ASSERT(d->digest_size <= FS_VERITY_MAX_DIGEST_SIZE); + bin2hex(d->digest, d->digest_size, digest_hex); + hash_alg = find_hash_alg_by_num(d->digest_algorithm); + if (hash_alg) { + hash_alg_name = hash_alg->name; + } else { + sprintf(_hash_alg_name, "ALG_%u", d->digest_algorithm); + hash_alg_name = _hash_alg_name; + } + printf("%s:%s %s\n", hash_alg_name, digest_hex, argv[i]); + } + status = 0; +out: + free(d); + return status; + +out_err: + status = 1; + goto out; + +out_usage: + usage(cmd, stderr); + status = 2; + goto out; +} diff --git a/cmd_set_measurement.c b/cmd_set_measurement.c deleted file mode 100644 index db237b8..0000000 --- a/cmd_set_measurement.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * The 'fsverity set_measurement' command - * - * Copyright (C) 2018 Google, Inc. - * - * Written by Eric Biggers, 2018. - */ - -#include <fcntl.h> -#include <getopt.h> -#include <stdlib.h> -#include <sys/ioctl.h> - -#include "commands.h" -#include "fsverity_sys_decls.h" -#include "hash_algs.h" - -enum { - OPT_HASH, -}; - -static const struct option longopts[] = { - {"hash", required_argument, NULL, OPT_HASH}, - {NULL, 0, NULL, 0} -}; - -int fsverity_cmd_set_measurement(const struct fsverity_command *cmd, - int argc, char *argv[]) -{ - const struct fsverity_hash_alg *alg = DEFAULT_HASH_ALG; - struct fsverity_measurement *measurement = NULL; - struct filedes file; - int c; - int status; - - while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { - switch (c) { - case OPT_HASH: - alg = find_hash_alg(optarg); - if (!alg) - goto out_usage; - break; - default: - goto out_usage; - } - } - argv += optind; - argc -= optind; - - if (argc != 2) - goto out_usage; - - measurement = xzalloc(sizeof(*measurement) + alg->digest_size); - measurement->digest_algorithm = alg - fsverity_hash_algs; - measurement->digest_size = alg->digest_size; - if (!hex2bin(argv[1], measurement->digest, alg->digest_size)) { - error_msg("Invalid EXPECTED_MEASUREMENT hex string.\n" - " Expected %u-character hex string for hash algorithm '%s'.", - alg->digest_size * 2, alg->name); - goto out_usage; - } - - if (!open_file(&file, argv[0], O_RDONLY, 0)) - goto out_err; - if (ioctl(file.fd, FS_IOC_SET_VERITY_MEASUREMENT, measurement) != 0) { - error_msg_errno("FS_IOC_SET_VERITY_MEASUREMENT failed on '%s'", - file.name); - filedes_close(&file); - goto out_err; - } - if (!filedes_close(&file)) - goto out_err; - status = 0; -out: - free(measurement); - return status; - -out_err: - status = 1; - goto out; - -out_usage: - usage(cmd, stderr); - status = 2; - goto out; -} diff --git a/cmd_setup.c b/cmd_setup.c index 871d19b..f2a6524 100644 --- a/cmd_setup.c +++ b/cmd_setup.c @@ -14,7 +14,7 @@ #include <unistd.h> #include "commands.h" -#include "fsverity_sys_decls.h" +#include "fsverity_uapi.h" #include "fsveritysetup.h" #include "hash_algs.h" @@ -62,7 +62,7 @@ static bool parse_blocksize_option(const char *opt, int *blocksize_ret) * block offset at which that level's hash blocks start. Level 'depth - 1' is * the root and is stored first in the file, in the first block following the * original data. Level 0 is the "leaf" level: it's directly "above" the data - * blocks and is stored last in the file, just before the fs-verity footer. + * blocks and is stored last in the file. */ static void compute_tree_layout(u64 data_size, u64 tree_offset, int blockbits, unsigned int hashes_per_block, @@ -229,61 +229,58 @@ void fsverity_append_extension(void **buf_p, int type, } /* - * Append the authenticated portion of the fs-verity footer to 'out', in the + * Append the authenticated portion of the fs-verity descriptor to 'out', in the * process updating 'hash' with the data written. */ -static int append_auth_footer(const struct fsveritysetup_params *params, - u64 filesize, struct filedes *out, - struct hash_ctx *hash) +static int append_fsverity_descriptor(const struct fsveritysetup_params *params, + u64 filesize, const u8 *root_hash, + struct filedes *out, + struct hash_ctx *hash) { - size_t ftr_auth_len; + size_t desc_auth_len; void *buf; - struct fsverity_footer *ftr; + struct fsverity_descriptor *desc; + u16 auth_ext_count; int status; - ftr_auth_len = sizeof(*ftr); + desc_auth_len = sizeof(*desc); + desc_auth_len += FSVERITY_EXTLEN(params->hash_alg->digest_size); if (params->saltlen) - ftr_auth_len += FSVERITY_EXTLEN(params->saltlen); - ftr_auth_len += total_elide_patch_ext_length(params); - ftr = buf = xzalloc(ftr_auth_len); - - memcpy(ftr->magic, FS_VERITY_MAGIC, sizeof(ftr->magic)); - ftr->major_version = FS_VERITY_MAJOR; - ftr->minor_version = FS_VERITY_MINOR; - ftr->log_blocksize = params->blockbits; - /* TODO: should we be storing 'log_hash_blocksize' instead? */ - if (!is_power_of_2(params->hashes_per_block)) { - error_msg("Unsupported hashes_per_block (%u); must be a power of 2", - params->hashes_per_block); - goto out_err; - } - ftr->log_arity = ilog2(params->hashes_per_block); - ftr->meta_algorithm = cpu_to_le16(params->hash_alg - - fsverity_hash_algs); - ftr->data_algorithm = ftr->meta_algorithm; - ftr->size = cpu_to_le64(filesize); - - ftr->authenticated_ext_count = params->num_elisions_and_patches; + desc_auth_len += FSVERITY_EXTLEN(params->saltlen); + desc_auth_len += total_elide_patch_ext_length(params); + desc = buf = xzalloc(desc_auth_len); + + memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic)); + desc->major_version = 1; + desc->minor_version = 0; + desc->log_data_blocksize = params->blockbits; + desc->log_tree_blocksize = params->blockbits; + desc->data_algorithm = cpu_to_le16(params->hash_alg - + fsverity_hash_algs); + desc->tree_algorithm = desc->data_algorithm; + desc->orig_file_size = cpu_to_le64(filesize); + + auth_ext_count = 1; /* root hash */ if (params->saltlen) - ftr->authenticated_ext_count++; + auth_ext_count++; + auth_ext_count += params->num_elisions_and_patches; + desc->auth_ext_count = cpu_to_le16(auth_ext_count); - ftr->unauthenticated_ext_count = 0; - if (params->signing_key_file || params->signature_file) - ftr->unauthenticated_ext_count++; - - buf += sizeof(*ftr); + buf += sizeof(*desc); + fsverity_append_extension(&buf, FS_VERITY_EXT_ROOT_HASH, + root_hash, params->hash_alg->digest_size); if (params->saltlen) fsverity_append_extension(&buf, FS_VERITY_EXT_SALT, params->salt, params->saltlen); append_elide_patch_exts(&buf, params); - ASSERT(buf - (void *)ftr == ftr_auth_len); + ASSERT(buf - (void *)desc == desc_auth_len); - hash_update(hash, ftr, ftr_auth_len); - if (!full_write(out, ftr, ftr_auth_len)) + hash_update(hash, desc, desc_auth_len); + if (!full_write(out, desc, desc_auth_len)) goto out_err; status = 0; out: - free(ftr); + free(desc); return status; out_err: @@ -291,12 +288,47 @@ out_err: goto out; } -static int append_ftr_reverse_offset(struct filedes *out, u64 ftr_offset) +/* + * Append any needed unauthenticated extension items: currently, just possibly a + * PKCS7_SIGNATURE item containing the signed file measurement. + */ +static int +append_unauthenticated_extensions(struct filedes *out, + const struct fsveritysetup_params *params, + const u8 *measurement) +{ + u16 unauth_ext_count = 0; + struct { + __le16 unauth_ext_count; + __le16 pad[3]; + } hdr; + bool have_sig = params->signing_key_file || params->signature_file; + + if (have_sig) + unauth_ext_count++; + + ASSERT(sizeof(hdr) % 8 == 0); + memset(&hdr, 0, sizeof(hdr)); + hdr.unauth_ext_count = cpu_to_le16(unauth_ext_count); + + if (!full_write(out, &hdr, sizeof(hdr))) + return 1; + + if (have_sig) + return append_signed_measurement(out, params, measurement); + + return 0; +} + +static int append_footer(struct filedes *out, u64 desc_offset) { - __le32 offs; + struct fsverity_footer ftr; + u32 offset = (out->pos + sizeof(ftr)) - desc_offset; + + ftr.desc_reverse_offset = cpu_to_le32(offset); + memcpy(ftr.magic, FS_VERITY_MAGIC, sizeof(ftr.magic)); - offs = cpu_to_le32(out->pos + sizeof(offs) - ftr_offset); - if (!full_write(out, &offs, sizeof(offs))) + if (!full_write(out, &ftr, sizeof(ftr))) return 1; return 0; } @@ -373,35 +405,28 @@ static int fsveritysetup(const char *infile, const char *outfile, &tree_end_offset, root_hash); if (status) goto out; - - /* - * Append the fixed-size portion of the footer and any authenticated - * extensions, then calculate the file measurement: the hash of the - * authenticated footer portion and the Merkle tree root hash. - */ if (!filedes_seek(out, tree_end_offset, SEEK_SET)) goto out_err; + + /* Append the additional needed metadata */ + hash_init(hash); - status = append_auth_footer(params, filesize, out, hash); + status = append_fsverity_descriptor(params, filesize, root_hash, + out, hash); if (status) goto out; - hash_update(hash, root_hash, params->hash_alg->digest_size); hash_final(hash, measurement); - /* If requested, append the signed file measurement */ - status = append_signed_measurement(out, params, measurement); + status = append_unauthenticated_extensions(out, params, measurement); if (status) goto out; - /* Finish by appending the 'ftr_reverse_offset' field */ - status = append_ftr_reverse_offset(out, tree_end_offset); + status = append_footer(out, tree_end_offset); if (status) goto out; - bin2hex(root_hash, params->hash_alg->digest_size, hash_hex); - printf("Merkle root hash: %s\n", hash_hex); bin2hex(measurement, params->hash_alg->digest_size, hash_hex); - printf("fs-verity measurement: %s\n", hash_hex); + printf("File measurement: %s:%s\n", params->hash_alg->name, hash_hex); status = 0; out: hash_free(hash); @@ -437,7 +462,7 @@ int fsverity_cmd_setup(const struct fsverity_command *cmd, while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch (c) { case OPT_HASH: - params.hash_alg = find_hash_alg(optarg); + params.hash_alg = find_hash_alg_by_name(optarg); if (!params.hash_alg) goto out_usage; break; @@ -14,7 +14,7 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd, int argc, char *argv[]); int fsverity_cmd_setup(const struct fsverity_command *cmd, int argc, char *argv[]); -int fsverity_cmd_set_measurement(const struct fsverity_command *cmd, - int argc, char *argv[]); +int fsverity_cmd_measure(const struct fsverity_command *cmd, + int argc, char *argv[]); #endif /* COMMANDS_H */ diff --git a/crc32c_table.h b/crc32c_table.h new file mode 100644 index 0000000..4c3dafb --- /dev/null +++ b/crc32c_table.h @@ -0,0 +1,72 @@ +/* + * crc32c_table.h - data table to accelerate CRC-32C computation + * + * This file was automatically generated by scripts/gen_crc32c_table.c + */ + +static const u32 crc32c_table[] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351, +}; diff --git a/debian/control b/debian/control index d93f4f3..aaa4ac3 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,7 @@ Source: fsverity-utils Priority: optional Maintainer: Eric Biggers <ebiggers@google.com> -Build-Depends: debhelper (>= 10), libssl-dev (>= 1.0.0), libz-dev +Build-Depends: debhelper (>= 10), libssl-dev (>= 1.0.0) Standards-Version: 4.0.0 Vcs-Git: git://git.kernel.org/pub/scm/linux/kernel/git/mhalcrow/fsverity diff --git a/elide_patch.c b/elide_patch.c index 5198d36..374227a 100644 --- a/elide_patch.c +++ b/elide_patch.c @@ -13,7 +13,7 @@ #include <stdlib.h> #include <string.h> -#include "fsverity_sys_decls.h" +#include "fsverity_uapi.h" #include "fsveritysetup.h" /* An elision or a patch */ @@ -27,6 +27,13 @@ static const struct fsverity_command { .usage_str = " fsverity enable FILE\n" }, { + .name = "measure", + .func = fsverity_cmd_measure, + .short_desc = +"Display the measurement of the given fs-verity file(s)", + .usage_str = +" fsverity measure FILE...\n" + }, { .name = "setup", .func = fsverity_cmd_setup, .short_desc = "Create the verity metadata for a file", @@ -35,14 +42,7 @@ static const struct fsverity_command { " [--hash=HASH_ALG] [--salt=SALT] [--signing-key=KEYFILE]\n" " [--signing-cert=CERTFILE] [--signature=SIGFILE]\n" " [--patch=OFFSET,PATCHFILE] [--elide=OFFSET,LENGTH]\n" - }, { - .name = "set_measurement", - .func = fsverity_cmd_set_measurement, - .short_desc = -"Set the trusted file measurement for the given fs-verity file", - .usage_str = -" fsverity set_measurement FILE EXPECTED_MEASUREMENT [--hash=HASH_ALG]\n" - }, + } }; static void usage_all(FILE *fp) diff --git a/fsverity_sys_decls.h b/fsverity_sys_decls.h deleted file mode 100644 index 122f4a2..0000000 --- a/fsverity_sys_decls.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -#ifndef FSVERITY_SYS_DECLS_H -#define FSVERITY_SYS_DECLS_H - -#include <linux/limits.h> -#include <linux/ioctl.h> -#include <linux/types.h> - -/* ========== Ioctls ========== */ - -#define FS_VERITY_ALG_SHA256 1 -#define FS_VERITY_ALG_CRC32 2 - -/* Same as 'struct fsverity_signed_measurement', but with native endianness */ -struct fsverity_measurement { - __u16 digest_algorithm; - __u16 digest_size; - __u32 reserved1; - __u64 reserved2[3]; - __u8 digest[]; -}; - -#define FS_IOC_ENABLE_VERITY _IO('f', 133) -#define FS_IOC_SET_VERITY_MEASUREMENT _IOW('f', 134, struct fsverity_measurement) - -/* ========== On-disk footer format ========== */ - -#define FS_VERITY_MAGIC "TrueBrew" -#define FS_VERITY_MAJOR 1 -#define FS_VERITY_MINOR 0 - -/* Fixed-length portion of footer (begins after Merkle tree) */ -struct fsverity_footer { - __u8 magic[8]; /* must be FS_VERITY_MAGIC */ - __u8 major_version; /* must be FS_VERITY_MAJOR */ - __u8 minor_version; /* must be FS_VERITY_MINOR */ - __u8 log_blocksize; /* log2(data-bytes-per-hash), e.g. 12 for 4KB */ - __u8 log_arity; /* log2(leaves-per-node), e.g. 7 for SHA-256 */ - __le16 meta_algorithm; /* hash algorithm for tree blocks */ - __le16 data_algorithm; /* hash algorithm for data blocks */ - __le32 flags; /* flags */ - __le32 reserved1; /* must be 0 */ - __le64 size; /* size of the original, unpadded data */ - __u8 authenticated_ext_count; /* number of authenticated extensions */ - __u8 unauthenticated_ext_count; /* number of unauthenticated extensions */ - __u8 reserved2[30]; /* must be 0 */ - /* This structure is 64 bytes long */ -}; /* followed by zero or more extensions (struct fsverity_extension) */ - -#define FS_VERITY_FLAG_INTEGRITY_ONLY 0x00000001 - -/* extension types */ -#define FS_VERITY_EXT_ELIDE 1 -#define FS_VERITY_EXT_PATCH 2 -#define FS_VERITY_EXT_SALT 3 -#define FS_VERITY_EXT_PKCS7_SIGNATURE 4 - -/* Header of each variable-length metadata item following the fsverity_footer */ -struct fsverity_extension { - /* - * Length of this extension in bytes, including this header. Must be - * rounded up to an 8-byte boundary when advancing to the next - * extension. - */ - __le32 length; - __le16 type; /* Type of this extension (see codes above) */ - __le16 reserved; /* Reserved, must be 0 */ -}; - -struct fsverity_extension_elide { - __le64 offset; - __le64 length; -}; - -struct fsverity_extension_patch { - __le64 offset; -}; /* followed by variable-length replacement data */ - -/* - * Same as 'struct fsverity_measurement', but with fixed endianness, so it can - * be stored on-disk in the file footer. - */ -struct fsverity_signed_measurement { - __le16 digest_algorithm; - __le16 digest_size; - __le32 reserved1; - __le64 reserved2[3]; - __u8 digest[]; -}; - -#endif /* FSVERITY_SYS_DECLS_H */ diff --git a/fsverity_uapi.h b/fsverity_uapi.h new file mode 100644 index 0000000..8881c54 --- /dev/null +++ b/fsverity_uapi.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * fs-verity (file-based verity) support + * + * Copyright (C) 2018 Google, Inc. + */ +#ifndef _UAPI_LINUX_FSVERITY_H +#define _UAPI_LINUX_FSVERITY_H + +#include <linux/limits.h> +#include <linux/ioctl.h> +#include <linux/types.h> + +/* ========== Ioctls ========== */ + +struct fsverity_digest { + __u16 digest_algorithm; + __u16 digest_size; /* input/output */ + __u8 digest[]; +}; + +#define FS_IOC_ENABLE_VERITY _IO('f', 133) +#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) + +/* ========== On-disk format ========== */ + +#define FS_VERITY_MAGIC "FSVerity" + +/* Supported hash algorithms */ +#define FS_VERITY_ALG_SHA256 1 +#define FS_VERITY_ALG_SHA512 2 +#define FS_VERITY_ALG_CRC32C 3 /* for integrity only */ + +/* Metadata stored near the end of fs-verity files, after the Merkle tree */ +/* This structure is 64 bytes long */ +struct fsverity_descriptor { + __u8 magic[8]; /* must be FS_VERITY_MAGIC */ + __u8 major_version; /* must be 1 */ + __u8 minor_version; /* must be 0 */ + __u8 log_data_blocksize;/* log2(data-bytes-per-hash), e.g. 12 for 4KB */ + __u8 log_tree_blocksize;/* log2(tree-bytes-per-hash), e.g. 12 for 4KB */ + __le16 data_algorithm; /* hash algorithm for data blocks */ + __le16 tree_algorithm; /* hash algorithm for tree blocks */ + __le32 flags; /* flags */ + __le32 reserved1; /* must be 0 */ + __le64 orig_file_size; /* size of the original, unpadded data */ + __le16 auth_ext_count; /* number of authenticated extensions */ + __u8 reserved2[30]; /* must be 0 */ +}; +/* followed by list of 'auth_ext_count' authenticated extensions */ +/* + * then followed by '__le16 unauth_ext_count' padded to next 8-byte boundary, + * then a list of 'unauth_ext_count' (may be 0) unauthenticated extensions + */ + +/* Extension types */ +#define FS_VERITY_EXT_ROOT_HASH 1 +#define FS_VERITY_EXT_SALT 2 +#define FS_VERITY_EXT_PKCS7_SIGNATURE 3 +#define FS_VERITY_EXT_ELIDE 4 +#define FS_VERITY_EXT_PATCH 5 + +/* Header of each extension (variable-length metadata item) */ +struct fsverity_extension { + /* + * Length in bytes, including this header but excluding padding to next + * 8-byte boundary that is applied when advancing to the next extension. + */ + __le32 length; + __le16 type; /* Type of this extension (see codes above) */ + __le16 reserved; /* Reserved, must be 0 */ +}; +/* followed by the payload of 'length - 8' bytes */ + +/* Extension payload formats */ + +/* + * FS_VERITY_EXT_ROOT_HASH payload is just a byte array, with size equal to the + * digest size of the hash algorithm given in the fsverity_descriptor + */ + +/* FS_VERITY_EXT_SALT payload is just a byte array, any size */ + +/* + * FS_VERITY_EXT_PKCS7_SIGNATURE payload is a DER-encoded PKCS#7 message + * containing the signed file measurement in the following format: + */ +struct fsverity_digest_disk { + __le16 digest_algorithm; + __le16 digest_size; + __u8 digest[]; +}; + +/* FS_VERITY_EXT_ELIDE payload */ +struct fsverity_extension_elide { + __le64 offset; + __le64 length; +}; + +/* FS_VERITY_EXT_PATCH payload */ +struct fsverity_extension_patch { + __le64 offset; + /* followed by variable-length patch data */ +}; + +/* Fields stored at the very end of the file */ +struct fsverity_footer { + __le32 desc_reverse_offset; /* distance to fsverity_descriptor */ + __u8 magic[8]; /* FS_VERITY_MAGIC */ +} __packed; + +#endif /* _UAPI_LINUX_FSVERITY_H */ diff --git a/hash_algs.c b/hash_algs.c index 566a75c..00acf26 100644 --- a/hash_algs.c +++ b/hash_algs.c @@ -10,9 +10,8 @@ #include <openssl/evp.h> #include <stdlib.h> #include <string.h> -#include <zlib.h> /* for crc32() */ -#include "fsverity_sys_decls.h" +#include "fsverity_uapi.h" #include "hash_algs.h" static void free_hash_ctx(struct hash_ctx *ctx) @@ -100,47 +99,60 @@ static struct hash_ctx *create_sha256_ctx(const struct fsverity_hash_alg *alg) return openssl_digest_ctx_create(alg, EVP_sha256()); } -/* ========== zlib wrapper for CRC-32 ========== */ +static struct hash_ctx *create_sha512_ctx(const struct fsverity_hash_alg *alg) +{ + return openssl_digest_ctx_create(alg, EVP_sha512()); +} + +/* ========== CRC-32C ========== */ -struct crc32_hash_ctx { +/* + * There are faster ways to calculate CRC's, but for now we just use the + * 256-entry table method as it's portable and not too complex. + */ + +#include "crc32c_table.h" + +struct crc32c_hash_ctx { struct hash_ctx base; /* must be first */ u32 remainder; }; -static void crc32_init(struct hash_ctx *_ctx) +static void crc32c_init(struct hash_ctx *_ctx) { - struct crc32_hash_ctx *ctx = (void *)_ctx; + struct crc32c_hash_ctx *ctx = (void *)_ctx; - ctx->remainder = 0; + ctx->remainder = ~0; } -static void crc32_update(struct hash_ctx *_ctx, const void *data, size_t size) +static void crc32c_update(struct hash_ctx *_ctx, const void *data, size_t size) { - struct crc32_hash_ctx *ctx = (void *)_ctx; + struct crc32c_hash_ctx *ctx = (void *)_ctx; + const u8 *p = data; + u32 r = ctx->remainder; - ctx->remainder = crc32(ctx->remainder, data, size); + while (size--) + r = (r >> 8) ^ crc32c_table[(u8)r ^ *p++]; + + ctx->remainder = r; } -/* - * Big endian, to be compatible with `veritysetup --hash=crc32`, which uses - * libgcrypt, which uses big endian CRC-32. - */ -static void crc32_final(struct hash_ctx *_ctx, u8 *digest) +static void crc32c_final(struct hash_ctx *_ctx, u8 *digest) { - struct crc32_hash_ctx *ctx = (void *)_ctx; - __be32 remainder = cpu_to_be32(ctx->remainder); + struct crc32c_hash_ctx *ctx = (void *)_ctx; + __le32 remainder = cpu_to_le32(~ctx->remainder); memcpy(digest, &remainder, sizeof(remainder)); } -static struct hash_ctx *create_crc32_ctx(const struct fsverity_hash_alg *alg) +static struct hash_ctx *create_crc32c_ctx(const struct fsverity_hash_alg *alg) { - struct crc32_hash_ctx *ctx = xzalloc(sizeof(*ctx)); + struct crc32c_hash_ctx *ctx = xzalloc(sizeof(*ctx)); ctx->base.alg = alg; - ctx->base.init = crc32_init; - ctx->base.update = crc32_update; - ctx->base.final = crc32_final; + ctx->base.init = crc32c_init; + ctx->base.update = crc32c_update; + ctx->base.final = crc32c_final; ctx->base.free = free_hash_ctx; return &ctx->base; } @@ -154,14 +166,20 @@ const struct fsverity_hash_alg fsverity_hash_algs[] = { .cryptographic = true, .create_ctx = create_sha256_ctx, }, - [FS_VERITY_ALG_CRC32] = { - .name = "crc32", + [FS_VERITY_ALG_SHA512] = { + .name = "sha512", + .digest_size = 64, + .cryptographic = true, + .create_ctx = create_sha512_ctx, + }, + [FS_VERITY_ALG_CRC32C] = { + .name = "crc32c", .digest_size = 4, - .create_ctx = create_crc32_ctx, + .create_ctx = create_crc32c_ctx, }, }; -const struct fsverity_hash_alg *find_hash_alg(const char *name) +const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name) { int i; @@ -177,6 +195,15 @@ const struct fsverity_hash_alg *find_hash_alg(const char *name) return NULL; } +const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num) +{ + if (num < ARRAY_SIZE(fsverity_hash_algs) && + fsverity_hash_algs[num].name) + return &fsverity_hash_algs[num]; + + return NULL; +} + void show_all_hash_algs(FILE *fp) { int i; diff --git a/hash_algs.h b/hash_algs.h index 1f16f7a..3cb0a98 100644 --- a/hash_algs.h +++ b/hash_algs.h @@ -23,7 +23,8 @@ struct hash_ctx { void (*free)(struct hash_ctx *ctx); }; -const struct fsverity_hash_alg *find_hash_alg(const char *name); +const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name); +const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num); void show_all_hash_algs(FILE *fp); #define DEFAULT_HASH_ALG (&fsverity_hash_algs[FS_VERITY_ALG_SHA256]) @@ -31,7 +32,7 @@ void show_all_hash_algs(FILE *fp); * Largest digest size among all hash algorithms supported by fs-verity. * This can be increased if needed. */ -#define FS_VERITY_MAX_DIGEST_SIZE 32 +#define FS_VERITY_MAX_DIGEST_SIZE 64 static inline struct hash_ctx *hash_create(const struct fsverity_hash_alg *alg) { diff --git a/scripts/gen_crc32c_table.c b/scripts/gen_crc32c_table.c new file mode 100644 index 0000000..fb045ff --- /dev/null +++ b/scripts/gen_crc32c_table.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generate a table for CRC-32C calculation. + * + * Copyright (C) 2018 Google, Inc. + * + * Written by Eric Biggers, 2018. + */ + +#include <stdint.h> +#include <stdio.h> + +/* + * This is the CRC-32C (Castagnoli) polynomial: x^32+x^28+x^27+x^26+x^25+x^23+ + * x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+x^8+x^6+x^0, with the polynomial + * coefficients mapped to bits using the "little endian" convention. + */ +#define CRC32C_POLY_LE 0x82F63B78 + +static uint32_t crc32c_update_bit(uint32_t remainder, uint8_t bit) +{ + return (remainder >> 1) ^ + (((remainder ^ bit) & 1) ? CRC32C_POLY_LE : 0); +} + +static uint32_t crc32c_update_byte(uint32_t remainder, uint8_t byte) +{ + int bit; + + for (bit = 0; bit < 8; bit++, byte >>= 1) + remainder = crc32c_update_bit(remainder, byte & 1); + return remainder; +} + +static uint32_t crc32c_table[256]; + +int main(void) +{ + int i, j; + + for (i = 0; i < 256; i++) + crc32c_table[i] = crc32c_update_byte(0, i); + + printf("/*\n"); + printf(" * crc32c_table.h - data table to accelerate CRC-32C computation\n"); + printf(" *\n"); + printf(" * This file was automatically generated by scripts/gen_crc32c_table.c\n"); + printf(" */\n"); + printf("\n"); + printf("static const u32 crc32c_table[] = {\n"); + for (i = 0; i < 64; i++) { + printf("\t"); + for (j = 0; j < 4; j++) { + printf("0x%08x,", crc32c_table[i * 4 + j]); + if (j != 3) + printf(" "); + } + printf("\n"); + } + printf("};\n"); + return 0; +} @@ -16,7 +16,7 @@ #include <stdlib.h> #include <string.h> -#include "fsverity_sys_decls.h" +#include "fsverity_uapi.h" #include "fsveritysetup.h" #include "hash_algs.h" @@ -99,33 +99,29 @@ static X509 *read_certificate(const char *certfile) } /* - * Check that the given data is a valid 'struct fsverity_signed_measurement' - * that matches the given @expected_measurement and @hash_alg. + * Check that the given data is a valid 'struct fsverity_digest_disk' that + * matches the given @expected_digest and @hash_alg. * - * Return: NULL if valid, else a string describing why the data is invalid. + * Return: NULL if the digests match, else a string describing the difference. */ static const char * -sanity_check_fsverity_measurement(const void *signed_data, size_t size, - const u8 *expected_measurement, - const struct fsverity_hash_alg *hash_alg) +compare_fsverity_digest(const void *data, size_t size, + const u8 *expected_digest, + const struct fsverity_hash_alg *hash_alg) { - const struct fsverity_signed_measurement *m = signed_data; + const struct fsverity_digest_disk *d = data; - if (size != sizeof(*m) + hash_alg->digest_size) + if (size != sizeof(*d) + hash_alg->digest_size) return "unexpected length"; - if (le16_to_cpu(m->digest_algorithm) != hash_alg - fsverity_hash_algs) + if (le16_to_cpu(d->digest_algorithm) != hash_alg - fsverity_hash_algs) return "unexpected hash algorithm"; - if (le16_to_cpu(m->digest_size) != hash_alg->digest_size) + if (le16_to_cpu(d->digest_size) != hash_alg->digest_size) return "wrong digest size for hash algorithm"; - if (m->reserved1 || - m->reserved2[0] || m->reserved2[1] || m->reserved2[2]) - return "reserved bits set"; - - if (memcmp(expected_measurement, m->digest, hash_alg->digest_size)) - return "wrong hash"; + if (memcmp(expected_digest, d->digest, hash_alg->digest_size)) + return "wrong digest"; return NULL; } @@ -287,9 +283,9 @@ static bool read_signature(const char *signature_file, } else { const ASN1_OCTET_STRING *o = p7->d.sign->contents->d.data; - reason = sanity_check_fsverity_measurement(o->data, o->length, - expected_measurement, - hash_alg); + reason = compare_fsverity_digest(o->data, o->length, + expected_measurement, + hash_alg); } if (reason) { error_msg("signed file measurement from '%s' is invalid (%s)", @@ -328,8 +324,8 @@ static bool write_signature(const char *signature_file, } /* - * If requested, append the signed file measurement to the fs-verity footer as a - * PKCS7_SIGNATURE extension item. + * Append the signed file measurement to the output file as a PKCS7_SIGNATURE + * extension item. * * Return: exit status code (0 on success, nonzero on failure) */ @@ -337,7 +333,7 @@ int append_signed_measurement(struct filedes *out, const struct fsveritysetup_params *params, const u8 *measurement) { - struct fsverity_signed_measurement *data_to_sign = NULL; + struct fsverity_digest_disk *data_to_sign = NULL; void *sig = NULL; void *extbuf = NULL; void *tmp; @@ -358,8 +354,8 @@ int append_signed_measurement(struct filedes *out, memcpy(data_to_sign->digest, measurement, params->hash_alg->digest_size); - ASSERT(sanity_check_fsverity_measurement(data_to_sign, - data_size, measurement, params->hash_alg) == NULL); + ASSERT(compare_fsverity_digest(data_to_sign, data_size, + measurement, params->hash_alg) == NULL); if (!sign_data(data_to_sign, data_size, params->signing_key_file, @@ -373,16 +369,12 @@ int append_signed_measurement(struct filedes *out, !write_signature(params->signature_file, sig, sig_size)) goto out_err; } else { - if (!params->signature_file) - return 0; - /* Using a signature that was already created */ if (!read_signature(params->signature_file, measurement, params->hash_alg, &sig, &sig_size)) goto out_err; } - /* Append the signature to the fs-verity footer as an extension item */ tmp = extbuf = xzalloc(FSVERITY_EXTLEN(sig_size)); fsverity_append_extension(&tmp, FS_VERITY_EXT_PKCS7_SIGNATURE, sig, sig_size); |