#linux-kernel #merkle-tree #digest #hash #read-file #filesystem #file-hashing

bin+lib fs-verity

Produce fs-verity measurements in pure Rust, or access the Linux kernel APIs to freeze a file with fs-verity and get its digest

2 unstable releases

0.2.0 Jun 9, 2023
0.1.0 Dec 20, 2020

#453 in Unix APIs


Used in 2 crates (via puzzlefs-lib)

MIT/Apache

40KB
410 lines

fs-verity

This crate implements utilities for working with fs-verity functionality of the Linux kernel.

What is fs-verity?

It allows you to make a file permanently immutable and have the kernel calculate and store a Merkle tree-based hash for the file that will be instantly retrievable from the file from that point on.

The kernel will also verify all data read from the file agains the Merkle tree data, and refure to read it if there is a mismatch. This means (as long as you trust the running kernel) you can be absolutely sure that this instantly retrievable hash will match the data you read from the file.

You are expected to determine whether the hash is the expected one through other means, such as checking whether it is in a list of accepted hashes which has been signed by some key.

The kernel's fs-verity code does implement a basic (and optional) in-kernel signature verification scheme. However, because you still have to manually check whether an opened file has fs-verity enabled, it does not add much value and is probably more of a proof-of-concept. This crate currently has no support for this scheme.

Currently fs-verity is only supported on f2fs and ext4 filesystems, and you may need to turn on the fs-verity option on your filesystem. See the Linux documentation for details.

What is this crate?

You can think of it as a pure Rust replacement for the main parts of fsverity-utils / libfsverity.

It consists of two parts:

  • An implementation of sha2::digest::Digest which can calculate an fs-verity measurement in userland Rust code. This is useful for e.g. servers and build systems which may not want to (or be able to) enable fs-verity for files, but still need to know the digest values so they can be used to create e.g. a signed manifest file containing all the digests.
  • On Linux systems, two functions which allow you to enable fs-verity for a file, and to fetch the digest for the file from the kernel once it's enabled. This uses the ioctl interface directly.

This was mostly produced as a coding exercise. It works and is pretty clean (well, as much as is possible under the API constraints I set myself) and thoroughly commented, but it is currently a bit slower than the C implementation because that uses libcrypto's assembly code for SHA256, and the SHA256 implementation in RustCrypto is not quite as fast.

One neat aspect of this implementation is that it works in a completely streaming manner, accepting any amount of input in chunks of whatever size.

Rust version

This crate should work with latest stable Rust. In case it does not work with older versions of Rust and you need this, please file an issue.

Safety

This crate calls the unsafe libc::ioctl function in the two function in the linux module. No other unsafe is used.

The underlying SHA256 and SHA512 hash functions from Rust Crypto are used. The asm feature of Rust Crypto is not enabled by default.

Development notes

To run unit tests, first generate the test files using python3 make_testfiles.py.

The "known good" hashes were generated using the official fsverity tool from the fsverity-utils or 'fsverity` package:

sudo apt install fsverity
for f in testfiles/*; do fsverity enable $f; done
fsverity measure testfiles/*

If that doesn't work your filesystem may not support fs-verity or you may need to turn on the feature in the fs superblock, see the excellent fs-verity documentation for details.

Very recent versions (you may have to compile from source) of the fsverity tool also support the digest subcommmand, which allows you to hash the files without kernel or filesystem support:

fsverity digest testfiles/*

License

This crate is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~5MB
~100K SLoC