// SPDX-License-Identifier: GPL-2.0+
|
/*
|
* fs-verity hash algorithms
|
*
|
* Copyright (C) 2018 Google LLC
|
*
|
* Written by Eric Biggers.
|
*/
|
|
#include <openssl/evp.h>
|
#include <stdlib.h>
|
#include <string.h>
|
|
#include "fsverity_uapi.h"
|
#include "hash_algs.h"
|
|
static void free_hash_ctx(struct hash_ctx *ctx)
|
{
|
free(ctx);
|
}
|
|
/* ========== libcrypto (OpenSSL) wrappers ========== */
|
|
struct openssl_hash_ctx {
|
struct hash_ctx base; /* must be first */
|
EVP_MD_CTX *md_ctx;
|
const EVP_MD *md;
|
};
|
|
static void openssl_digest_init(struct hash_ctx *_ctx)
|
{
|
struct openssl_hash_ctx *ctx = (void *)_ctx;
|
|
if (EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL) != 1)
|
fatal_error("EVP_DigestInit_ex() failed for algorithm '%s'",
|
ctx->base.alg->name);
|
}
|
|
static void openssl_digest_update(struct hash_ctx *_ctx,
|
const void *data, size_t size)
|
{
|
struct openssl_hash_ctx *ctx = (void *)_ctx;
|
|
if (EVP_DigestUpdate(ctx->md_ctx, data, size) != 1)
|
fatal_error("EVP_DigestUpdate() failed for algorithm '%s'",
|
ctx->base.alg->name);
|
}
|
|
static void openssl_digest_final(struct hash_ctx *_ctx, u8 *digest)
|
{
|
struct openssl_hash_ctx *ctx = (void *)_ctx;
|
|
if (EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL) != 1)
|
fatal_error("EVP_DigestFinal_ex() failed for algorithm '%s'",
|
ctx->base.alg->name);
|
}
|
|
static void openssl_digest_ctx_free(struct hash_ctx *_ctx)
|
{
|
struct openssl_hash_ctx *ctx = (void *)_ctx;
|
|
/*
|
* OpenSSL 1.1.0 renamed EVP_MD_CTX_destroy() to EVP_MD_CTX_free() but
|
* kept the old name as a macro. Use the old name for compatibility
|
* with older OpenSSL versions.
|
*/
|
EVP_MD_CTX_destroy(ctx->md_ctx);
|
free(ctx);
|
}
|
|
static struct hash_ctx *
|
openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
|
{
|
struct openssl_hash_ctx *ctx;
|
|
ctx = xzalloc(sizeof(*ctx));
|
ctx->base.alg = alg;
|
ctx->base.init = openssl_digest_init;
|
ctx->base.update = openssl_digest_update;
|
ctx->base.final = openssl_digest_final;
|
ctx->base.free = openssl_digest_ctx_free;
|
/*
|
* OpenSSL 1.1.0 renamed EVP_MD_CTX_create() to EVP_MD_CTX_new() but
|
* kept the old name as a macro. Use the old name for compatibility
|
* with older OpenSSL versions.
|
*/
|
ctx->md_ctx = EVP_MD_CTX_create();
|
if (!ctx->md_ctx)
|
fatal_error("out of memory");
|
|
ctx->md = md;
|
ASSERT(EVP_MD_size(md) == alg->digest_size);
|
|
return &ctx->base;
|
}
|
|
static struct hash_ctx *create_sha256_ctx(const struct fsverity_hash_alg *alg)
|
{
|
return openssl_digest_ctx_create(alg, EVP_sha256());
|
}
|
|
static struct hash_ctx *create_sha512_ctx(const struct fsverity_hash_alg *alg)
|
{
|
return openssl_digest_ctx_create(alg, EVP_sha512());
|
}
|
|
/* ========== CRC-32C ========== */
|
|
/*
|
* 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 crc32c_init(struct hash_ctx *_ctx)
|
{
|
struct crc32c_hash_ctx *ctx = (void *)_ctx;
|
|
ctx->remainder = ~0;
|
}
|
|
static void crc32c_update(struct hash_ctx *_ctx, const void *data, size_t size)
|
{
|
struct crc32c_hash_ctx *ctx = (void *)_ctx;
|
const u8 *p = data;
|
u32 r = ctx->remainder;
|
|
while (size--)
|
r = (r >> 8) ^ crc32c_table[(u8)r ^ *p++];
|
|
ctx->remainder = r;
|
}
|
|
static void crc32c_final(struct hash_ctx *_ctx, u8 *digest)
|
{
|
struct crc32c_hash_ctx *ctx = (void *)_ctx;
|
__le32 remainder = cpu_to_le32(~ctx->remainder);
|
|
memcpy(digest, &remainder, sizeof(remainder));
|
}
|
|
static struct hash_ctx *create_crc32c_ctx(const struct fsverity_hash_alg *alg)
|
{
|
struct crc32c_hash_ctx *ctx = xzalloc(sizeof(*ctx));
|
|
ctx->base.alg = alg;
|
ctx->base.init = crc32c_init;
|
ctx->base.update = crc32c_update;
|
ctx->base.final = crc32c_final;
|
ctx->base.free = free_hash_ctx;
|
return &ctx->base;
|
}
|
|
/* ========== Hash algorithm definitions ========== */
|
|
const struct fsverity_hash_alg fsverity_hash_algs[] = {
|
[FS_VERITY_ALG_SHA256] = {
|
.name = "sha256",
|
.digest_size = 32,
|
.cryptographic = true,
|
.create_ctx = create_sha256_ctx,
|
},
|
[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_crc32c_ctx,
|
},
|
};
|
|
const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name)
|
{
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
|
if (fsverity_hash_algs[i].name &&
|
!strcmp(name, fsverity_hash_algs[i].name))
|
return &fsverity_hash_algs[i];
|
}
|
error_msg("unknown hash algorithm: '%s'", name);
|
fputs("Available hash algorithms: ", stderr);
|
show_all_hash_algs(stderr);
|
putc('\n', stderr);
|
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;
|
const char *sep = "";
|
|
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
|
if (fsverity_hash_algs[i].name) {
|
fprintf(fp, "%s%s", sep, fsverity_hash_algs[i].name);
|
sep = ", ";
|
}
|
}
|
}
|