1// SPDX-License-Identifier: MIT
2/*
3 * fs-verity hash algorithms
4 *
5 * Copyright 2018 Google LLC
6 *
7 * Use of this source code is governed by an MIT-style
8 * license that can be found in the LICENSE file or at
9 * https://opensource.org/licenses/MIT.
10 */
11
12#include "lib_private.h"
13
14#include <openssl/evp.h>
15#include <stdlib.h>
16#include <string.h>
17
18/* ========== libcrypto (OpenSSL) wrappers ========== */
19
20struct openssl_hash_ctx {
21	struct hash_ctx base;	/* must be first */
22	EVP_MD_CTX *md_ctx;
23	const EVP_MD *md;
24};
25
26static void openssl_digest_init(struct hash_ctx *_ctx)
27{
28	struct openssl_hash_ctx *ctx = (void *)_ctx;
29	int ret;
30
31	ret = EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL);
32	BUG_ON(ret != 1);
33}
34
35static void openssl_digest_update(struct hash_ctx *_ctx,
36				  const void *data, size_t size)
37{
38	struct openssl_hash_ctx *ctx = (void *)_ctx;
39	int ret;
40
41	ret = EVP_DigestUpdate(ctx->md_ctx, data, size);
42	BUG_ON(ret != 1);
43}
44
45static void openssl_digest_final(struct hash_ctx *_ctx, u8 *digest)
46{
47	struct openssl_hash_ctx *ctx = (void *)_ctx;
48	int ret;
49
50	ret = EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL);
51	BUG_ON(ret != 1);
52}
53
54static void openssl_digest_ctx_free(struct hash_ctx *_ctx)
55{
56	struct openssl_hash_ctx *ctx = (void *)_ctx;
57
58	/*
59	 * OpenSSL 1.1.0 renamed EVP_MD_CTX_destroy() to EVP_MD_CTX_free() but
60	 * kept the old name as a macro.  Use the old name for compatibility
61	 * with older OpenSSL versions.
62	 */
63	EVP_MD_CTX_destroy(ctx->md_ctx);
64	free(ctx);
65}
66
67static struct hash_ctx *
68openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
69{
70	struct openssl_hash_ctx *ctx;
71
72	ctx = libfsverity_zalloc(sizeof(*ctx));
73	if (!ctx)
74		return NULL;
75
76	ctx->base.alg = alg;
77	ctx->base.init = openssl_digest_init;
78	ctx->base.update = openssl_digest_update;
79	ctx->base.final = openssl_digest_final;
80	ctx->base.free = openssl_digest_ctx_free;
81	/*
82	 * OpenSSL 1.1.0 renamed EVP_MD_CTX_create() to EVP_MD_CTX_new() but
83	 * kept the old name as a macro.  Use the old name for compatibility
84	 * with older OpenSSL versions.
85	 */
86	ctx->md_ctx = EVP_MD_CTX_create();
87	if (!ctx->md_ctx) {
88		libfsverity_error_msg("failed to allocate EVP_MD_CTX");
89		goto err1;
90	}
91
92	ctx->md = md;
93	if (WARN_ON(EVP_MD_size(md) != alg->digest_size))
94		goto err2;
95
96	return &ctx->base;
97
98err2:
99	EVP_MD_CTX_destroy(ctx->md_ctx);
100err1:
101	free(ctx);
102	return NULL;
103}
104
105static struct hash_ctx *create_sha256_ctx(const struct fsverity_hash_alg *alg)
106{
107	return openssl_digest_ctx_create(alg, EVP_sha256());
108}
109
110static struct hash_ctx *create_sha512_ctx(const struct fsverity_hash_alg *alg)
111{
112	return openssl_digest_ctx_create(alg, EVP_sha512());
113}
114
115/* ========== Hash utilities ========== */
116
117void libfsverity_hash_init(struct hash_ctx *ctx)
118{
119	ctx->init(ctx);
120}
121
122void libfsverity_hash_update(struct hash_ctx *ctx, const void *data,
123			     size_t size)
124{
125	ctx->update(ctx, data, size);
126}
127
128void libfsverity_hash_final(struct hash_ctx *ctx, u8 *digest)
129{
130	ctx->final(ctx, digest);
131}
132
133/* ->init(), ->update(), and ->final() all in one step */
134void libfsverity_hash_full(struct hash_ctx *ctx, const void *data, size_t size,
135			   u8 *digest)
136{
137	libfsverity_hash_init(ctx);
138	libfsverity_hash_update(ctx, data, size);
139	libfsverity_hash_final(ctx, digest);
140}
141
142void libfsverity_free_hash_ctx(struct hash_ctx *ctx)
143{
144	if (ctx)
145		ctx->free(ctx);
146}
147
148/* ========== Hash algorithm definitions ========== */
149
150static const struct fsverity_hash_alg fsverity_hash_algs[] = {
151	[FS_VERITY_HASH_ALG_SHA256] = {
152		.name = "sha256",
153		.digest_size = 32,
154		.block_size = 64,
155		.create_ctx = create_sha256_ctx,
156	},
157	[FS_VERITY_HASH_ALG_SHA512] = {
158		.name = "sha512",
159		.digest_size = 64,
160		.block_size = 128,
161		.create_ctx = create_sha512_ctx,
162	},
163};
164
165LIBEXPORT u32
166libfsverity_find_hash_alg_by_name(const char *name)
167{
168	int i;
169
170	if (!name)
171		return 0;
172
173	for (i = 1; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
174		if (fsverity_hash_algs[i].name &&
175		    !strcmp(name, fsverity_hash_algs[i].name))
176			return i;
177	}
178	return 0;
179}
180
181const struct fsverity_hash_alg *libfsverity_find_hash_alg_by_num(u32 alg_num)
182{
183	if (alg_num < ARRAY_SIZE(fsverity_hash_algs) &&
184	    fsverity_hash_algs[alg_num].name)
185		return &fsverity_hash_algs[alg_num];
186
187	return NULL;
188}
189
190LIBEXPORT int
191libfsverity_get_digest_size(u32 alg_num)
192{
193	const struct fsverity_hash_alg *alg =
194		libfsverity_find_hash_alg_by_num(alg_num);
195
196	return alg ? alg->digest_size : -1;
197}
198
199LIBEXPORT const char *
200libfsverity_get_hash_name(u32 alg_num)
201{
202	const struct fsverity_hash_alg *alg =
203		libfsverity_find_hash_alg_by_num(alg_num);
204
205	return alg ? alg->name : NULL;
206}
207