xref: /openbsd-src/usr.bin/ssh/digest-libc.c (revision c9831b39c7f05cf54db0775dea423b6be448db6e)
1*c9831b39Sjsg /* $OpenBSD: digest-libc.c,v 1.7 2020/02/26 13:40:09 jsg Exp $ */
230042712Smarkus /*
330042712Smarkus  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
430042712Smarkus  * Copyright (c) 2014 Markus Friedl.  All rights reserved.
530042712Smarkus  *
630042712Smarkus  * Permission to use, copy, modify, and distribute this software for any
730042712Smarkus  * purpose with or without fee is hereby granted, provided that the above
830042712Smarkus  * copyright notice and this permission notice appear in all copies.
930042712Smarkus  *
1030042712Smarkus  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1130042712Smarkus  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1230042712Smarkus  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1330042712Smarkus  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1430042712Smarkus  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1530042712Smarkus  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1630042712Smarkus  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1730042712Smarkus  */
1830042712Smarkus 
1930042712Smarkus #include <sys/types.h>
2030042712Smarkus #include <limits.h>
2130042712Smarkus #include <stdlib.h>
2230042712Smarkus #include <string.h>
2330042712Smarkus 
2430042712Smarkus #include <md5.h>
2530042712Smarkus #include <rmd160.h>
2630042712Smarkus #include <sha1.h>
2730042712Smarkus #include <sha2.h>
2830042712Smarkus 
29ea2d8289Sdjm #include "ssherr.h"
30ea2d8289Sdjm #include "sshbuf.h"
3130042712Smarkus #include "digest.h"
3230042712Smarkus 
3330042712Smarkus typedef void md_init_fn(void *mdctx);
3430042712Smarkus typedef void md_update_fn(void *mdctx, const u_int8_t *m, size_t mlen);
3530042712Smarkus typedef void md_final_fn(u_int8_t[], void *mdctx);
3630042712Smarkus 
3730042712Smarkus struct ssh_digest_ctx {
3830042712Smarkus 	int alg;
3930042712Smarkus 	void *mdctx;
4030042712Smarkus };
4130042712Smarkus 
4230042712Smarkus struct ssh_digest {
4330042712Smarkus 	int id;
4430042712Smarkus 	const char *name;
4530042712Smarkus 	size_t block_len;
4630042712Smarkus 	size_t digest_len;
4730042712Smarkus 	size_t ctx_len;
4830042712Smarkus 	md_init_fn *md_init;
4930042712Smarkus 	md_update_fn *md_update;
5030042712Smarkus 	md_final_fn *md_final;
5130042712Smarkus };
5230042712Smarkus 
5330042712Smarkus /* NB. Indexed directly by algorithm number */
5430042712Smarkus const struct ssh_digest digests[SSH_DIGEST_MAX] = {
5530042712Smarkus 	{
5630042712Smarkus 		SSH_DIGEST_MD5,
5730042712Smarkus 		"MD5",
5830042712Smarkus 		MD5_BLOCK_LENGTH,
5930042712Smarkus 		MD5_DIGEST_LENGTH,
6030042712Smarkus 		sizeof(MD5_CTX),
6130042712Smarkus 		(md_init_fn *) MD5Init,
6230042712Smarkus 		(md_update_fn *) MD5Update,
6330042712Smarkus 		(md_final_fn *) MD5Final
6430042712Smarkus 	},
6530042712Smarkus 	{
6630042712Smarkus 		SSH_DIGEST_SHA1,
6730042712Smarkus 		"SHA1",
6830042712Smarkus 		SHA1_BLOCK_LENGTH,
6930042712Smarkus 		SHA1_DIGEST_LENGTH,
7030042712Smarkus 		sizeof(SHA1_CTX),
7130042712Smarkus 		(md_init_fn *) SHA1Init,
7230042712Smarkus 		(md_update_fn *) SHA1Update,
7330042712Smarkus 		(md_final_fn *) SHA1Final
7430042712Smarkus 	},
7530042712Smarkus 	{
7630042712Smarkus 		SSH_DIGEST_SHA256,
7730042712Smarkus 		"SHA256",
7830042712Smarkus 		SHA256_BLOCK_LENGTH,
7930042712Smarkus 		SHA256_DIGEST_LENGTH,
8030042712Smarkus 		sizeof(SHA2_CTX),
8130042712Smarkus 		(md_init_fn *) SHA256Init,
8230042712Smarkus 		(md_update_fn *) SHA256Update,
8330042712Smarkus 		(md_final_fn *) SHA256Final
8430042712Smarkus 	},
8530042712Smarkus 	{
8630042712Smarkus 		SSH_DIGEST_SHA384,
8730042712Smarkus 		"SHA384",
8830042712Smarkus 		SHA384_BLOCK_LENGTH,
8930042712Smarkus 		SHA384_DIGEST_LENGTH,
9030042712Smarkus 		sizeof(SHA2_CTX),
9130042712Smarkus 		(md_init_fn *) SHA384Init,
9230042712Smarkus 		(md_update_fn *) SHA384Update,
9330042712Smarkus 		(md_final_fn *) SHA384Final
9430042712Smarkus 	},
9530042712Smarkus 	{
9630042712Smarkus 		SSH_DIGEST_SHA512,
9730042712Smarkus 		"SHA512",
9830042712Smarkus 		SHA512_BLOCK_LENGTH,
9930042712Smarkus 		SHA512_DIGEST_LENGTH,
10030042712Smarkus 		sizeof(SHA2_CTX),
10130042712Smarkus 		(md_init_fn *) SHA512Init,
10230042712Smarkus 		(md_update_fn *) SHA512Update,
10330042712Smarkus 		(md_final_fn *) SHA512Final
10430042712Smarkus 	}
10530042712Smarkus };
10630042712Smarkus 
10730042712Smarkus static const struct ssh_digest *
ssh_digest_by_alg(int alg)10830042712Smarkus ssh_digest_by_alg(int alg)
10930042712Smarkus {
11030042712Smarkus 	if (alg < 0 || alg >= SSH_DIGEST_MAX)
11130042712Smarkus 		return NULL;
11230042712Smarkus 	if (digests[alg].id != alg) /* sanity */
11330042712Smarkus 		return NULL;
11430042712Smarkus 	return &(digests[alg]);
11530042712Smarkus }
11630042712Smarkus 
1173dbedef4Sdjm int
ssh_digest_alg_by_name(const char * name)1183dbedef4Sdjm ssh_digest_alg_by_name(const char *name)
1193dbedef4Sdjm {
1203dbedef4Sdjm 	int alg;
1213dbedef4Sdjm 
1223dbedef4Sdjm 	for (alg = 0; alg < SSH_DIGEST_MAX; alg++) {
1233dbedef4Sdjm 		if (strcasecmp(name, digests[alg].name) == 0)
1243dbedef4Sdjm 			return digests[alg].id;
1253dbedef4Sdjm 	}
1263dbedef4Sdjm 	return -1;
1273dbedef4Sdjm }
1283dbedef4Sdjm 
1293dbedef4Sdjm const char *
ssh_digest_alg_name(int alg)1303dbedef4Sdjm ssh_digest_alg_name(int alg)
1313dbedef4Sdjm {
1323dbedef4Sdjm 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
1333dbedef4Sdjm 
1343dbedef4Sdjm 	return digest == NULL ? NULL : digest->name;
1353dbedef4Sdjm }
1363dbedef4Sdjm 
13730042712Smarkus size_t
ssh_digest_bytes(int alg)13830042712Smarkus ssh_digest_bytes(int alg)
13930042712Smarkus {
14030042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
14130042712Smarkus 
14230042712Smarkus 	return digest == NULL ? 0 : digest->digest_len;
14330042712Smarkus }
14430042712Smarkus 
14530042712Smarkus size_t
ssh_digest_blocksize(struct ssh_digest_ctx * ctx)14630042712Smarkus ssh_digest_blocksize(struct ssh_digest_ctx *ctx)
14730042712Smarkus {
14830042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
14930042712Smarkus 
15030042712Smarkus 	return digest == NULL ? 0 : digest->block_len;
15130042712Smarkus }
15230042712Smarkus 
15330042712Smarkus struct ssh_digest_ctx *
ssh_digest_start(int alg)15430042712Smarkus ssh_digest_start(int alg)
15530042712Smarkus {
15630042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
15730042712Smarkus 	struct ssh_digest_ctx *ret;
15830042712Smarkus 
15928ead74dSjsg 	if (digest == NULL || (ret = calloc(1, sizeof(*ret))) == NULL)
16030042712Smarkus 		return NULL;
16130042712Smarkus 	if ((ret->mdctx = calloc(1, digest->ctx_len)) == NULL) {
16230042712Smarkus 		free(ret);
16330042712Smarkus 		return NULL;
16430042712Smarkus 	}
16530042712Smarkus 	ret->alg = alg;
16630042712Smarkus 	digest->md_init(ret->mdctx);
16730042712Smarkus 	return ret;
16830042712Smarkus }
16930042712Smarkus 
17030042712Smarkus int
ssh_digest_copy_state(struct ssh_digest_ctx * from,struct ssh_digest_ctx * to)17130042712Smarkus ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
17230042712Smarkus {
17330042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(from->alg);
17430042712Smarkus 
17530042712Smarkus 	if (digest == NULL || from->alg != to->alg)
176ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
17730042712Smarkus 	memcpy(to->mdctx, from->mdctx, digest->ctx_len);
17830042712Smarkus 	return 0;
17930042712Smarkus }
18030042712Smarkus 
18130042712Smarkus int
ssh_digest_update(struct ssh_digest_ctx * ctx,const void * m,size_t mlen)18230042712Smarkus ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
18330042712Smarkus {
18430042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
18530042712Smarkus 
18630042712Smarkus 	if (digest == NULL)
187ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
18830042712Smarkus 	digest->md_update(ctx->mdctx, m, mlen);
18930042712Smarkus 	return 0;
19030042712Smarkus }
19130042712Smarkus 
19230042712Smarkus int
ssh_digest_update_buffer(struct ssh_digest_ctx * ctx,const struct sshbuf * b)193ea2d8289Sdjm ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
19430042712Smarkus {
195ea2d8289Sdjm 	return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
19630042712Smarkus }
19730042712Smarkus 
19830042712Smarkus int
ssh_digest_final(struct ssh_digest_ctx * ctx,u_char * d,size_t dlen)19930042712Smarkus ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
20030042712Smarkus {
20130042712Smarkus 	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
20230042712Smarkus 
20330042712Smarkus 	if (digest == NULL)
204ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
20530042712Smarkus 	if (dlen > UINT_MAX)
206ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
20730042712Smarkus 	if (dlen < digest->digest_len) /* No truncation allowed */
208ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
20930042712Smarkus 	digest->md_final(d, ctx->mdctx);
21030042712Smarkus 	return 0;
21130042712Smarkus }
21230042712Smarkus 
21330042712Smarkus void
ssh_digest_free(struct ssh_digest_ctx * ctx)21430042712Smarkus ssh_digest_free(struct ssh_digest_ctx *ctx)
21530042712Smarkus {
21630042712Smarkus 	const struct ssh_digest *digest;
21730042712Smarkus 
21830042712Smarkus 	if (ctx != NULL) {
21930042712Smarkus 		digest = ssh_digest_by_alg(ctx->alg);
22030042712Smarkus 		if (digest) {
221c671dcf1Sdjm 			explicit_bzero(ctx->mdctx, digest->ctx_len);
22230042712Smarkus 			free(ctx->mdctx);
223*c9831b39Sjsg 			freezero(ctx, sizeof(*ctx));
22430042712Smarkus 		}
22530042712Smarkus 	}
22630042712Smarkus }
22730042712Smarkus 
22830042712Smarkus int
ssh_digest_memory(int alg,const void * m,size_t mlen,u_char * d,size_t dlen)22930042712Smarkus ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
23030042712Smarkus {
23130042712Smarkus 	struct ssh_digest_ctx *ctx = ssh_digest_start(alg);
23230042712Smarkus 
23330042712Smarkus 	if (ctx == NULL)
234ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
23530042712Smarkus 	if (ssh_digest_update(ctx, m, mlen) != 0 ||
23630042712Smarkus 	    ssh_digest_final(ctx, d, dlen) != 0)
237ea2d8289Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
23830042712Smarkus 	ssh_digest_free(ctx);
23930042712Smarkus 	return 0;
24030042712Smarkus }
24130042712Smarkus 
24230042712Smarkus int
ssh_digest_buffer(int alg,const struct sshbuf * b,u_char * d,size_t dlen)243ea2d8289Sdjm ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
24430042712Smarkus {
245ea2d8289Sdjm 	return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
24630042712Smarkus }
247