xref: /openssh-portable/digest-openssl.c (revision 1a14c131)
1*1a14c131Sdjm@openbsd.org /* $OpenBSD: digest-openssl.c,v 1.9 2020/10/29 02:52:43 djm Exp $ */
2ec93d151SDamien Miller /*
3ec93d151SDamien Miller  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
4ec93d151SDamien Miller  *
5ec93d151SDamien Miller  * Permission to use, copy, modify, and distribute this software for any
6ec93d151SDamien Miller  * purpose with or without fee is hereby granted, provided that the above
7ec93d151SDamien Miller  * copyright notice and this permission notice appear in all copies.
8ec93d151SDamien Miller  *
9ec93d151SDamien Miller  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ec93d151SDamien Miller  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ec93d151SDamien Miller  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ec93d151SDamien Miller  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ec93d151SDamien Miller  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ec93d151SDamien Miller  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ec93d151SDamien Miller  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ec93d151SDamien Miller  */
17ec93d151SDamien Miller 
18ec93d151SDamien Miller #include "includes.h"
19ec93d151SDamien Miller 
2072ef7c14SDamien Miller #ifdef WITH_OPENSSL
2172ef7c14SDamien Miller 
22ec93d151SDamien Miller #include <sys/types.h>
23ec93d151SDamien Miller #include <limits.h>
24ec93d151SDamien Miller #include <stdlib.h>
25ec93d151SDamien Miller #include <string.h>
26ec93d151SDamien Miller 
27ec93d151SDamien Miller #include <openssl/evp.h>
28ec93d151SDamien Miller 
29ec93d151SDamien Miller #include "openbsd-compat/openssl-compat.h"
30ec93d151SDamien Miller 
318668706dSDamien Miller #include "sshbuf.h"
32ec93d151SDamien Miller #include "digest.h"
338668706dSDamien Miller #include "ssherr.h"
34ec93d151SDamien Miller 
35f6293a0bSDamien Miller #ifndef HAVE_EVP_SHA256
36f6293a0bSDamien Miller # define EVP_sha256 NULL
3711cba2a4SDarren Tucker #endif
3811cba2a4SDarren Tucker #ifndef HAVE_EVP_SHA384
39f6293a0bSDamien Miller # define EVP_sha384 NULL
4011cba2a4SDarren Tucker #endif
4111cba2a4SDarren Tucker #ifndef HAVE_EVP_SHA512
42f6293a0bSDamien Miller # define EVP_sha512 NULL
4311cba2a4SDarren Tucker #endif
44f6293a0bSDamien Miller 
45ec93d151SDamien Miller struct ssh_digest_ctx {
46ec93d151SDamien Miller 	int alg;
47482d23bcSdjm@openbsd.org 	EVP_MD_CTX *mdctx;
48ec93d151SDamien Miller };
49ec93d151SDamien Miller 
50ec93d151SDamien Miller struct ssh_digest {
51ec93d151SDamien Miller 	int id;
52ec93d151SDamien Miller 	const char *name;
53ec93d151SDamien Miller 	size_t digest_len;
54ec93d151SDamien Miller 	const EVP_MD *(*mdfunc)(void);
55ec93d151SDamien Miller };
56ec93d151SDamien Miller 
57ec93d151SDamien Miller /* NB. Indexed directly by algorithm number */
58ec93d151SDamien Miller const struct ssh_digest digests[] = {
59ec93d151SDamien Miller 	{ SSH_DIGEST_MD5,	"MD5",		16,	EVP_md5 },
60ec93d151SDamien Miller 	{ SSH_DIGEST_SHA1,	"SHA1",		20,	EVP_sha1 },
61ec93d151SDamien Miller 	{ SSH_DIGEST_SHA256,	"SHA256",	32,	EVP_sha256 },
62ec93d151SDamien Miller 	{ SSH_DIGEST_SHA384,	"SHA384",	48,	EVP_sha384 },
63ec93d151SDamien Miller 	{ SSH_DIGEST_SHA512,	"SHA512",	64,	EVP_sha512 },
64ec93d151SDamien Miller 	{ -1,			NULL,		0,	NULL },
65ec93d151SDamien Miller };
66ec93d151SDamien Miller 
67ec93d151SDamien Miller static const struct ssh_digest *
ssh_digest_by_alg(int alg)68ec93d151SDamien Miller ssh_digest_by_alg(int alg)
69ec93d151SDamien Miller {
70ec93d151SDamien Miller 	if (alg < 0 || alg >= SSH_DIGEST_MAX)
71ec93d151SDamien Miller 		return NULL;
72ec93d151SDamien Miller 	if (digests[alg].id != alg) /* sanity */
73ec93d151SDamien Miller 		return NULL;
74f6293a0bSDamien Miller 	if (digests[alg].mdfunc == NULL)
75f6293a0bSDamien Miller 		return NULL;
76ec93d151SDamien Miller 	return &(digests[alg]);
77ec93d151SDamien Miller }
78ec93d151SDamien Miller 
7956d1c83cSdjm@openbsd.org int
ssh_digest_alg_by_name(const char * name)8056d1c83cSdjm@openbsd.org ssh_digest_alg_by_name(const char *name)
8156d1c83cSdjm@openbsd.org {
8256d1c83cSdjm@openbsd.org 	int alg;
8356d1c83cSdjm@openbsd.org 
8456d1c83cSdjm@openbsd.org 	for (alg = 0; digests[alg].id != -1; alg++) {
8556d1c83cSdjm@openbsd.org 		if (strcasecmp(name, digests[alg].name) == 0)
8656d1c83cSdjm@openbsd.org 			return digests[alg].id;
8756d1c83cSdjm@openbsd.org 	}
8856d1c83cSdjm@openbsd.org 	return -1;
8956d1c83cSdjm@openbsd.org }
9056d1c83cSdjm@openbsd.org 
9156d1c83cSdjm@openbsd.org const char *
ssh_digest_alg_name(int alg)9256d1c83cSdjm@openbsd.org ssh_digest_alg_name(int alg)
9356d1c83cSdjm@openbsd.org {
9456d1c83cSdjm@openbsd.org 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
9556d1c83cSdjm@openbsd.org 
9656d1c83cSdjm@openbsd.org 	return digest == NULL ? NULL : digest->name;
9756d1c83cSdjm@openbsd.org }
9856d1c83cSdjm@openbsd.org 
99ec93d151SDamien Miller size_t
ssh_digest_bytes(int alg)100ec93d151SDamien Miller ssh_digest_bytes(int alg)
101ec93d151SDamien Miller {
102ec93d151SDamien Miller 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
103ec93d151SDamien Miller 
104ec93d151SDamien Miller 	return digest == NULL ? 0 : digest->digest_len;
105ec93d151SDamien Miller }
106ec93d151SDamien Miller 
107ec93d151SDamien Miller size_t
ssh_digest_blocksize(struct ssh_digest_ctx * ctx)108ec93d151SDamien Miller ssh_digest_blocksize(struct ssh_digest_ctx *ctx)
109ec93d151SDamien Miller {
110482d23bcSdjm@openbsd.org 	return EVP_MD_CTX_block_size(ctx->mdctx);
111ec93d151SDamien Miller }
112ec93d151SDamien Miller 
113ec93d151SDamien Miller struct ssh_digest_ctx *
ssh_digest_start(int alg)114ec93d151SDamien Miller ssh_digest_start(int alg)
115ec93d151SDamien Miller {
116ec93d151SDamien Miller 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
117ec93d151SDamien Miller 	struct ssh_digest_ctx *ret;
118ec93d151SDamien Miller 
119ec93d151SDamien Miller 	if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL))
120ec93d151SDamien Miller 		return NULL;
121ec93d151SDamien Miller 	ret->alg = alg;
122482d23bcSdjm@openbsd.org 	if ((ret->mdctx = EVP_MD_CTX_new()) == NULL) {
123ec93d151SDamien Miller 		free(ret);
124ec93d151SDamien Miller 		return NULL;
125ec93d151SDamien Miller 	}
126482d23bcSdjm@openbsd.org 	if (EVP_DigestInit_ex(ret->mdctx, digest->mdfunc(), NULL) != 1) {
127482d23bcSdjm@openbsd.org 		ssh_digest_free(ret);
128482d23bcSdjm@openbsd.org 		return NULL;
129482d23bcSdjm@openbsd.org 	}
130ec93d151SDamien Miller 	return ret;
131ec93d151SDamien Miller }
132ec93d151SDamien Miller 
133ec93d151SDamien Miller int
ssh_digest_copy_state(struct ssh_digest_ctx * from,struct ssh_digest_ctx * to)134ec93d151SDamien Miller ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
135ec93d151SDamien Miller {
1368668706dSDamien Miller 	if (from->alg != to->alg)
1378668706dSDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
138ec93d151SDamien Miller 	/* we have bcopy-style order while openssl has memcpy-style */
139482d23bcSdjm@openbsd.org 	if (!EVP_MD_CTX_copy_ex(to->mdctx, from->mdctx))
1408668706dSDamien Miller 		return SSH_ERR_LIBCRYPTO_ERROR;
141ec93d151SDamien Miller 	return 0;
142ec93d151SDamien Miller }
143ec93d151SDamien Miller 
144ec93d151SDamien Miller int
ssh_digest_update(struct ssh_digest_ctx * ctx,const void * m,size_t mlen)145ec93d151SDamien Miller ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
146ec93d151SDamien Miller {
147482d23bcSdjm@openbsd.org 	if (EVP_DigestUpdate(ctx->mdctx, m, mlen) != 1)
1488668706dSDamien Miller 		return SSH_ERR_LIBCRYPTO_ERROR;
149ec93d151SDamien Miller 	return 0;
150ec93d151SDamien Miller }
151ec93d151SDamien Miller 
152ec93d151SDamien Miller int
ssh_digest_update_buffer(struct ssh_digest_ctx * ctx,const struct sshbuf * b)1538668706dSDamien Miller ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
154ec93d151SDamien Miller {
1558668706dSDamien Miller 	return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
156ec93d151SDamien Miller }
157ec93d151SDamien Miller 
158ec93d151SDamien Miller int
ssh_digest_final(struct ssh_digest_ctx * ctx,u_char * d,size_t dlen)159ec93d151SDamien Miller ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
160ec93d151SDamien Miller {
161ec93d151SDamien Miller 	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
162ec93d151SDamien Miller 	u_int l = dlen;
163ec93d151SDamien Miller 
1644a4b75adSdtucker@openbsd.org 	if (digest == NULL || dlen > UINT_MAX)
1658668706dSDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
166ec93d151SDamien Miller 	if (dlen < digest->digest_len) /* No truncation allowed */
1678668706dSDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
168482d23bcSdjm@openbsd.org 	if (EVP_DigestFinal_ex(ctx->mdctx, d, &l) != 1)
1698668706dSDamien Miller 		return SSH_ERR_LIBCRYPTO_ERROR;
170ec93d151SDamien Miller 	if (l != digest->digest_len) /* sanity */
1718668706dSDamien Miller 		return SSH_ERR_INTERNAL_ERROR;
172ec93d151SDamien Miller 	return 0;
173ec93d151SDamien Miller }
174ec93d151SDamien Miller 
175ec93d151SDamien Miller void
ssh_digest_free(struct ssh_digest_ctx * ctx)176ec93d151SDamien Miller ssh_digest_free(struct ssh_digest_ctx *ctx)
177ec93d151SDamien Miller {
178482d23bcSdjm@openbsd.org 	if (ctx == NULL)
179482d23bcSdjm@openbsd.org 		return;
180482d23bcSdjm@openbsd.org 	EVP_MD_CTX_free(ctx->mdctx);
181482d23bcSdjm@openbsd.org 	freezero(ctx, sizeof(*ctx));
182ec93d151SDamien Miller }
183ec93d151SDamien Miller 
184ec93d151SDamien Miller int
ssh_digest_memory(int alg,const void * m,size_t mlen,u_char * d,size_t dlen)185ec93d151SDamien Miller ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
186ec93d151SDamien Miller {
187c174a3b7SDamien Miller 	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
188c174a3b7SDamien Miller 	u_int mdlen;
189ec93d151SDamien Miller 
190c174a3b7SDamien Miller 	if (digest == NULL)
1918668706dSDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
192c174a3b7SDamien Miller 	if (dlen > UINT_MAX)
193c174a3b7SDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
194c174a3b7SDamien Miller 	if (dlen < digest->digest_len)
195c174a3b7SDamien Miller 		return SSH_ERR_INVALID_ARGUMENT;
196c174a3b7SDamien Miller 	mdlen = dlen;
197c174a3b7SDamien Miller 	if (!EVP_Digest(m, mlen, d, &mdlen, digest->mdfunc(), NULL))
198c174a3b7SDamien Miller 		return SSH_ERR_LIBCRYPTO_ERROR;
199ec93d151SDamien Miller 	return 0;
200ec93d151SDamien Miller }
201ec93d151SDamien Miller 
202ec93d151SDamien Miller int
ssh_digest_buffer(int alg,const struct sshbuf * b,u_char * d,size_t dlen)2038668706dSDamien Miller ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
204ec93d151SDamien Miller {
2058668706dSDamien Miller 	return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
206ec93d151SDamien Miller }
20772ef7c14SDamien Miller #endif /* WITH_OPENSSL */
208