blob: 7a77583d9379a540631d544f9fb94b87c5b99d65 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* fs-verity: read-only file-based integrity/authenticity
*
* hash_algs.c: hash algorithm management
*
* Copyright (C) 2018, Google, Inc.
*
* Written by Eric Biggers.
*/
#include "fsverity_private.h"
#include <asm/unaligned.h>
#include <crypto/hash.h>
/* The list of hash algorithms supported by fs-verity */
static struct fsverity_hash_alg fsverity_hash_algs[] = {
[FS_VERITY_ALG_SHA256] = {
.friendly_name = "SHA-256",
.algname = "sha256",
.digest_size = 32,
},
#ifdef CONFIG_FS_VERITY_INTEGRITY_ONLY
[FS_VERITY_ALG_CRC32] = {
.friendly_name = "CRC-32",
.algname = "crc32",
.digest_size = 4,
},
#endif
};
/*
* Translate the given fs-verity hash algorithm number into a struct describing
* the algorithm, and ensure it has a hash transform ready to go. The hash
* transforms are allocated on-demand firstly to not waste resources when they
* aren't needed, and secondly because the fs-verity module may be loaded
* earlier than the needed crypto modules.
*/
const struct fsverity_hash_alg *fsverity_get_hash_alg(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].digest_size) {
pr_warn("Unknown hash algorithm: %u\n", num);
return ERR_PTR(-EINVAL);
}
alg = &fsverity_hash_algs[num];
retry:
/* pairs with cmpxchg_release() below */
tfm = smp_load_acquire(&alg->tfm);
if (tfm)
return alg;
tfm = crypto_alloc_shash(alg->algname, 0, 0);
if (IS_ERR(tfm)) {
if (PTR_ERR(tfm) == -ENOENT)
pr_warn("Algorithm %u (%s) is unavailable\n",
num, alg->friendly_name);
else
pr_warn("Error allocating algorithm %u (%s): %ld\n",
num, alg->friendly_name, PTR_ERR(tfm));
return ERR_CAST(tfm);
}
err = -EINVAL;
if (WARN_ON(alg->digest_size != crypto_shash_digestsize(tfm)))
goto err_free_tfm;
pr_info("%s using implementation \"%s\"\n", alg->friendly_name,
crypto_shash_alg(tfm)->base.cra_driver_name);
/* Set the initial value if required by the algorithm */
if (num == FS_VERITY_ALG_CRC32) {
__le32 initial_value = cpu_to_le32(0xFFFFFFFF);
err = crypto_shash_setkey(tfm, (u8 *)&initial_value, 4);
if (err) {
pr_warn("Error setting CRC-32 initial value: %d\n",
err);
goto err_free_tfm;
}
}
/* pairs with smp_load_acquire() above */
if (cmpxchg_release(&alg->tfm, NULL, tfm) != NULL) {
crypto_free_shash(tfm);
goto retry;
}
return alg;
err_free_tfm:
crypto_free_shash(tfm);
return ERR_PTR(err);
}
int fsverity_finalize_hash(struct fsverity_info *vi, struct shash_desc *desc,
u8 *out)
{
int err;
err = crypto_shash_final(desc, out);
if (err)
return err;
/*
* For CRC-32, invert at the end and convert to big-endian format. This
* is done for compatibility with the 'veritysetup' tool, which uses the
* crc32 algorithm from libgcrypt, which uses this convention.
*/
if (vi->hash_alg == &fsverity_hash_algs[FS_VERITY_ALG_CRC32])
put_unaligned_be32(~get_unaligned_le32(out), out);
return 0;
}
void __init fsverity_check_hash_algs(void)
{
int i;
/*
* Sanity check the digest sizes (could be a build-time check, but
* they're in an array)
*/
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
struct fsverity_hash_alg *alg = &fsverity_hash_algs[i];
if (!alg->digest_size)
continue;
BUG_ON(alg->digest_size > FS_VERITY_MAX_DIGEST_SIZE);
BUG_ON(!is_power_of_2(alg->digest_size));
}
}
void __exit fsverity_exit_hash_algs(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++)
crypto_free_shash(fsverity_hash_algs[i].tfm);
}