]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fsverity: Switch from crypto_shash to SHA-2 library
authorEric Biggers <ebiggers@kernel.org>
Mon, 30 Jun 2025 17:22:24 +0000 (10:22 -0700)
committerEric Biggers <ebiggers@kernel.org>
Mon, 14 Jul 2025 18:29:32 +0000 (11:29 -0700)
fsverity supports two hash algorithms: SHA-256 and SHA-512.  Since both
of these have a library API now, just use the library API instead of
crypto_shash.  Even with multiple algorithms, the library-based code
still ends up being quite a bit simpler, due to how clumsy the
old-school crypto API is.  The library-based code is also more
efficient, since it avoids overheads such as indirect calls.

Acked-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20250630172224.46909-3-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Documentation/filesystems/fsverity.rst
fs/verity/Kconfig
fs/verity/enable.c
fs/verity/fsverity_private.h
fs/verity/hash_algs.c
fs/verity/open.c
fs/verity/verify.c

index dacdbc1149e671537da34d9fb53df8b5d3b1a055..412cf11e329852a97ab946b5fa4647b7b120706b 100644 (file)
@@ -185,8 +185,7 @@ FS_IOC_ENABLE_VERITY can fail with the following errors:
 - ``ENOKEY``: the ".fs-verity" keyring doesn't contain the certificate
   needed to verify the builtin signature
 - ``ENOPKG``: fs-verity recognizes the hash algorithm, but it's not
-  available in the kernel's crypto API as currently configured (e.g.
-  for SHA-512, missing CONFIG_CRYPTO_SHA512).
+  available in the kernel as currently configured
 - ``ENOTTY``: this type of filesystem does not implement fs-verity
 - ``EOPNOTSUPP``: the kernel was not configured with fs-verity
   support; or the filesystem superblock has not had the 'verity'
index 40569d3527a71eb7805bd6986aee81b1d5eb1133..76d1c5971b82611b39a68132437944a4f42a44d6 100644 (file)
@@ -2,11 +2,9 @@
 
 config FS_VERITY
        bool "FS Verity (read-only file-based authenticity protection)"
-       select CRYPTO
        select CRYPTO_HASH_INFO
-       # SHA-256 is selected as it's intended to be the default hash algorithm.
-       # To avoid bloat, other wanted algorithms must be selected explicitly.
-       select CRYPTO_SHA256
+       select CRYPTO_LIB_SHA256
+       select CRYPTO_LIB_SHA512
        help
          This option enables fs-verity.  fs-verity is the dm-verity
          mechanism implemented at the file level.  On supported
index 9b335bee88a5a5d118c8ca1fe5088ffd01f1e9e5..503268cf429627fdd3fd111d49de2e86629ba5b4 100644 (file)
@@ -7,7 +7,6 @@
 
 #include "fsverity_private.h"
 
-#include <crypto/hash.h>
 #include <linux/export.h>
 #include <linux/mount.h>
 #include <linux/sched/signal.h>
@@ -25,7 +24,6 @@ static int hash_one_block(struct inode *inode,
                          struct block_buffer *cur)
 {
        struct block_buffer *next = cur + 1;
-       int err;
 
        /*
         * Safety check to prevent a buffer overflow in case of a filesystem bug
@@ -38,10 +36,8 @@ static int hash_one_block(struct inode *inode,
        /* Zero-pad the block if it's shorter than the block size. */
        memset(&cur->data[cur->filled], 0, params->block_size - cur->filled);
 
-       err = fsverity_hash_block(params, inode, cur->data,
-                                 &next->data[next->filled]);
-       if (err)
-               return err;
+       fsverity_hash_block(params, inode, cur->data,
+                           &next->data[next->filled]);
        next->filled += params->digest_size;
        cur->filled = 0;
        return 0;
index b3506f56e180b5005708b9abad30b31ac142a73c..5fe854a5b9ad3d1da8d9b761b0894002cd6b8725 100644 (file)
@@ -20,7 +20,6 @@
 
 /* A hash algorithm supported by fs-verity */
 struct fsverity_hash_alg {
-       struct crypto_shash *tfm; /* hash tfm, allocated on demand */
        const char *name;         /* crypto API name, e.g. sha256 */
        unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */
        unsigned int block_size;  /* block size in bytes, e.g. 64 for SHA-256 */
@@ -31,10 +30,16 @@ struct fsverity_hash_alg {
        enum hash_algo algo_id;
 };
 
+union fsverity_hash_ctx {
+       struct sha256_ctx sha256;
+       struct sha512_ctx sha512;
+};
+
 /* Merkle tree parameters: hash algorithm, initial hash state, and topology */
 struct merkle_tree_params {
        const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
-       const u8 *hashstate;            /* initial hash state or NULL */
+       /* initial hash state if salted, NULL if unsalted */
+       const union fsverity_hash_ctx *hashstate;
        unsigned int digest_size;       /* same as hash_alg->digest_size */
        unsigned int block_size;        /* size of data and tree blocks */
        unsigned int hashes_per_block;  /* number of hashes per tree block */
@@ -76,16 +81,17 @@ struct fsverity_info {
 
 /* hash_algs.c */
 
-extern struct fsverity_hash_alg fsverity_hash_algs[];
+extern const struct fsverity_hash_alg fsverity_hash_algs[];
 
 const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
                                                      unsigned int num);
-const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
-                                     const u8 *salt, size_t salt_size);
-int fsverity_hash_block(const struct merkle_tree_params *params,
-                       const struct inode *inode, const void *data, u8 *out);
-int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
-                        const void *data, size_t size, u8 *out);
+union fsverity_hash_ctx *
+fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
+                           const u8 *salt, size_t salt_size);
+void fsverity_hash_block(const struct merkle_tree_params *params,
+                        const struct inode *inode, const void *data, u8 *out);
+void fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
+                         const void *data, size_t size, u8 *out);
 void __init fsverity_check_hash_algs(void);
 
 /* init.c */
index 6b08b1d9a7d7c49baf287a402ad1ac105fbb5614..9bb3c6344907e9549fe24f7d4b71d30f2da45997 100644 (file)
@@ -7,10 +7,8 @@
 
 #include "fsverity_private.h"
 
-#include <crypto/hash.h>
-
 /* The hash algorithms supported by fs-verity */
-struct fsverity_hash_alg fsverity_hash_algs[] = {
+const struct fsverity_hash_alg fsverity_hash_algs[] = {
        [FS_VERITY_HASH_ALG_SHA256] = {
                .name = "sha256",
                .digest_size = SHA256_DIGEST_SIZE,
@@ -25,106 +23,42 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
        },
 };
 
-static DEFINE_MUTEX(fsverity_hash_alg_init_mutex);
-
 /**
- * fsverity_get_hash_alg() - validate and prepare a hash algorithm
+ * fsverity_get_hash_alg() - get a hash algorithm by number
  * @inode: optional inode for logging purposes
  * @num: the hash algorithm number
  *
- * Get the struct fsverity_hash_alg for the given hash algorithm number, and
- * ensure it has a hash transform ready to go.  The hash transforms are
- * allocated on-demand so that we don't waste resources unnecessarily, and
- * because the crypto modules may be initialized later than fs/verity/.
+ * Get the struct fsverity_hash_alg for the given hash algorithm number.
  *
- * Return: pointer to the hash alg on success, else an ERR_PTR()
+ * Return: pointer to the hash alg if it's known, otherwise NULL.
  */
 const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
                                                      unsigned int num)
 {
-       struct fsverity_hash_alg *alg;
-       struct crypto_shash *tfm;
-       int err;
-
        if (num >= ARRAY_SIZE(fsverity_hash_algs) ||
            !fsverity_hash_algs[num].name) {
                fsverity_warn(inode, "Unknown hash algorithm number: %u", num);
-               return ERR_PTR(-EINVAL);
-       }
-       alg = &fsverity_hash_algs[num];
-
-       /* pairs with smp_store_release() below */
-       if (likely(smp_load_acquire(&alg->tfm) != NULL))
-               return alg;
-
-       mutex_lock(&fsverity_hash_alg_init_mutex);
-
-       if (alg->tfm != NULL)
-               goto out_unlock;
-
-       tfm = crypto_alloc_shash(alg->name, 0, 0);
-       if (IS_ERR(tfm)) {
-               if (PTR_ERR(tfm) == -ENOENT) {
-                       fsverity_warn(inode,
-                                     "Missing crypto API support for hash algorithm \"%s\"",
-                                     alg->name);
-                       alg = ERR_PTR(-ENOPKG);
-                       goto out_unlock;
-               }
-               fsverity_err(inode,
-                            "Error allocating hash algorithm \"%s\": %ld",
-                            alg->name, PTR_ERR(tfm));
-               alg = ERR_CAST(tfm);
-               goto out_unlock;
+               return NULL;
        }
-
-       err = -EINVAL;
-       if (WARN_ON_ONCE(alg->digest_size != crypto_shash_digestsize(tfm)))
-               goto err_free_tfm;
-       if (WARN_ON_ONCE(alg->block_size != crypto_shash_blocksize(tfm)))
-               goto err_free_tfm;
-
-       pr_info("%s using implementation \"%s\"\n",
-               alg->name, crypto_shash_driver_name(tfm));
-
-       /* pairs with smp_load_acquire() above */
-       smp_store_release(&alg->tfm, tfm);
-       goto out_unlock;
-
-err_free_tfm:
-       crypto_free_shash(tfm);
-       alg = ERR_PTR(err);
-out_unlock:
-       mutex_unlock(&fsverity_hash_alg_init_mutex);
-       return alg;
+       return &fsverity_hash_algs[num];
 }
 
 /**
  * fsverity_prepare_hash_state() - precompute the initial hash state
  * @alg: hash algorithm
  * @salt: a salt which is to be prepended to all data to be hashed
- * @salt_size: salt size in bytes, possibly 0
+ * @salt_size: salt size in bytes
  *
- * Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed
- *        initial hash state on success or an ERR_PTR() on failure.
+ * Return: the kmalloc()'ed initial hash state, or NULL if out of memory.
  */
-const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
-                                     const u8 *salt, size_t salt_size)
+union fsverity_hash_ctx *
+fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
+                           const u8 *salt, size_t salt_size)
 {
-       u8 *hashstate = NULL;
-       SHASH_DESC_ON_STACK(desc, alg->tfm);
        u8 *padded_salt = NULL;
        size_t padded_salt_size;
-       int err;
-
-       desc->tfm = alg->tfm;
-
-       if (salt_size == 0)
-               return NULL;
-
-       hashstate = kmalloc(crypto_shash_statesize(alg->tfm), GFP_KERNEL);
-       if (!hashstate)
-               return ERR_PTR(-ENOMEM);
+       union fsverity_hash_ctx ctx;
+       void *res = NULL;
 
        /*
         * Zero-pad the salt to the next multiple of the input size of the hash
@@ -135,30 +69,26 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
         */
        padded_salt_size = round_up(salt_size, alg->block_size);
        padded_salt = kzalloc(padded_salt_size, GFP_KERNEL);
-       if (!padded_salt) {
-               err = -ENOMEM;
-               goto err_free;
-       }
+       if (!padded_salt)
+               return NULL;
        memcpy(padded_salt, salt, salt_size);
-       err = crypto_shash_init(desc);
-       if (err)
-               goto err_free;
-
-       err = crypto_shash_update(desc, padded_salt, padded_salt_size);
-       if (err)
-               goto err_free;
-
-       err = crypto_shash_export(desc, hashstate);
-       if (err)
-               goto err_free;
-out:
-       kfree(padded_salt);
-       return hashstate;
 
-err_free:
-       kfree(hashstate);
-       hashstate = ERR_PTR(err);
-       goto out;
+       switch (alg->algo_id) {
+       case HASH_ALGO_SHA256:
+               sha256_init(&ctx.sha256);
+               sha256_update(&ctx.sha256, padded_salt, padded_salt_size);
+               res = kmemdup(&ctx.sha256, sizeof(ctx.sha256), GFP_KERNEL);
+               break;
+       case HASH_ALGO_SHA512:
+               sha512_init(&ctx.sha512);
+               sha512_update(&ctx.sha512, padded_salt, padded_salt_size);
+               res = kmemdup(&ctx.sha512, sizeof(ctx.sha512), GFP_KERNEL);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       kfree(padded_salt);
+       return res;
 }
 
 /**
@@ -170,31 +100,32 @@ err_free:
  *
  * Hash a single data or hash block.  The hash is salted if a salt is specified
  * in the Merkle tree parameters.
- *
- * Return: 0 on success, -errno on failure
  */
-int fsverity_hash_block(const struct merkle_tree_params *params,
-                       const struct inode *inode, const void *data, u8 *out)
+void fsverity_hash_block(const struct merkle_tree_params *params,
+                        const struct inode *inode, const void *data, u8 *out)
 {
-       SHASH_DESC_ON_STACK(desc, params->hash_alg->tfm);
-       int err;
-
-       desc->tfm = params->hash_alg->tfm;
-
-       if (params->hashstate) {
-               err = crypto_shash_import(desc, params->hashstate);
-               if (err) {
-                       fsverity_err(inode,
-                                    "Error %d importing hash state", err);
-                       return err;
-               }
-               err = crypto_shash_finup(desc, data, params->block_size, out);
-       } else {
-               err = crypto_shash_digest(desc, data, params->block_size, out);
+       union fsverity_hash_ctx ctx;
+
+       if (!params->hashstate) {
+               fsverity_hash_buffer(params->hash_alg, data, params->block_size,
+                                    out);
+               return;
+       }
+
+       switch (params->hash_alg->algo_id) {
+       case HASH_ALGO_SHA256:
+               ctx.sha256 = params->hashstate->sha256;
+               sha256_update(&ctx.sha256, data, params->block_size);
+               sha256_final(&ctx.sha256, out);
+               return;
+       case HASH_ALGO_SHA512:
+               ctx.sha512 = params->hashstate->sha512;
+               sha512_update(&ctx.sha512, data, params->block_size);
+               sha512_final(&ctx.sha512, out);
+               return;
+       default:
+               BUG();
        }
-       if (err)
-               fsverity_err(inode, "Error %d computing block hash", err);
-       return err;
 }
 
 /**
@@ -203,13 +134,20 @@ int fsverity_hash_block(const struct merkle_tree_params *params,
  * @data: the data to hash
  * @size: size of data to hash, in bytes
  * @out: output digest, size 'alg->digest_size' bytes
- *
- * Return: 0 on success, -errno on failure
  */
-int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
-                        const void *data, size_t size, u8 *out)
+void fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
+                         const void *data, size_t size, u8 *out)
 {
-       return crypto_shash_tfm_digest(alg->tfm, data, size, out);
+       switch (alg->algo_id) {
+       case HASH_ALGO_SHA256:
+               sha256(data, size, out);
+               return;
+       case HASH_ALGO_SHA512:
+               sha512(data, size, out);
+               return;
+       default:
+               BUG();
+       }
 }
 
 void __init fsverity_check_hash_algs(void)
index ac2c99fbf37b934a279c13ac36d6b79f64eb79de..c561e130cd0c61c405f964ef24837745b1222987 100644 (file)
@@ -43,18 +43,18 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
        memset(params, 0, sizeof(*params));
 
        hash_alg = fsverity_get_hash_alg(inode, hash_algorithm);
-       if (IS_ERR(hash_alg))
-               return PTR_ERR(hash_alg);
+       if (!hash_alg)
+               return -EINVAL;
        params->hash_alg = hash_alg;
        params->digest_size = hash_alg->digest_size;
 
-       params->hashstate = fsverity_prepare_hash_state(hash_alg, salt,
-                                                       salt_size);
-       if (IS_ERR(params->hashstate)) {
-               err = PTR_ERR(params->hashstate);
-               params->hashstate = NULL;
-               fsverity_err(inode, "Error %d preparing hash state", err);
-               goto out_err;
+       if (salt_size) {
+               params->hashstate =
+                       fsverity_prepare_hash_state(hash_alg, salt, salt_size);
+               if (!params->hashstate) {
+                       err = -ENOMEM;
+                       goto out_err;
+               }
        }
 
        /*
@@ -159,18 +159,15 @@ out_err:
  * Compute the file digest by hashing the fsverity_descriptor excluding the
  * builtin signature and with the sig_size field set to 0.
  */
-static int compute_file_digest(const struct fsverity_hash_alg *hash_alg,
-                              struct fsverity_descriptor *desc,
-                              u8 *file_digest)
+static void compute_file_digest(const struct fsverity_hash_alg *hash_alg,
+                               struct fsverity_descriptor *desc,
+                               u8 *file_digest)
 {
        __le32 sig_size = desc->sig_size;
-       int err;
 
        desc->sig_size = 0;
-       err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), file_digest);
+       fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), file_digest);
        desc->sig_size = sig_size;
-
-       return err;
 }
 
 /*
@@ -202,12 +199,7 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
 
        memcpy(vi->root_hash, desc->root_hash, vi->tree_params.digest_size);
 
-       err = compute_file_digest(vi->tree_params.hash_alg, desc,
-                                 vi->file_digest);
-       if (err) {
-               fsverity_err(inode, "Error %d computing file digest", err);
-               goto fail;
-       }
+       compute_file_digest(vi->tree_params.hash_alg, desc, vi->file_digest);
 
        err = fsverity_verify_signature(vi, desc->signature,
                                        le32_to_cpu(desc->sig_size));
index 14520289093cab2cc3e6c88cbf9083e55dc56688..a1f00c3fd3b2765eca730bf10ff5ef6db9c411e0 100644 (file)
@@ -7,7 +7,6 @@
 
 #include "fsverity_private.h"
 
-#include <crypto/hash.h>
 #include <linux/bio.h>
 #include <linux/export.h>
 
@@ -203,8 +202,7 @@ descend:
                unsigned long hblock_idx = hblocks[level - 1].index;
                unsigned int hoffset = hblocks[level - 1].hoffset;
 
-               if (fsverity_hash_block(params, inode, haddr, real_hash) != 0)
-                       goto error;
+               fsverity_hash_block(params, inode, haddr, real_hash);
                if (memcmp(want_hash, real_hash, hsize) != 0)
                        goto corrupted;
                /*
@@ -223,8 +221,7 @@ descend:
        }
 
        /* Finally, verify the data block. */
-       if (fsverity_hash_block(params, inode, data, real_hash) != 0)
-               goto error;
+       fsverity_hash_block(params, inode, data, real_hash);
        if (memcmp(want_hash, real_hash, hsize) != 0)
                goto corrupted;
        return true;