1295367Sdes/* $OpenBSD: digest-openssl.c,v 1.5 2014/12/21 22:27:56 djm Exp $ */
2263635Sdes/*
3263635Sdes * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
4263635Sdes *
5263635Sdes * Permission to use, copy, modify, and distribute this software for any
6263635Sdes * purpose with or without fee is hereby granted, provided that the above
7263635Sdes * copyright notice and this permission notice appear in all copies.
8263635Sdes *
9263635Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10263635Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11263635Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12263635Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13263635Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14263635Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15263635Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16263635Sdes */
17263635Sdes
18263635Sdes#include "includes.h"
19263635Sdes
20295367Sdes#ifdef WITH_OPENSSL
21295367Sdes
22263635Sdes#include <sys/types.h>
23263635Sdes#include <limits.h>
24263635Sdes#include <stdlib.h>
25263635Sdes#include <string.h>
26263635Sdes
27263635Sdes#include <openssl/evp.h>
28263635Sdes
29263635Sdes#include "openbsd-compat/openssl-compat.h"
30263635Sdes
31295367Sdes#include "sshbuf.h"
32263635Sdes#include "digest.h"
33295367Sdes#include "ssherr.h"
34263635Sdes
35295367Sdes#ifndef HAVE_EVP_RIPEMD160
36295367Sdes# define EVP_ripemd160 NULL
37295367Sdes#endif /* HAVE_EVP_RIPEMD160 */
38295367Sdes#ifndef HAVE_EVP_SHA256
39295367Sdes# define EVP_sha256 NULL
40295367Sdes# define EVP_sha384 NULL
41295367Sdes# define EVP_sha512 NULL
42295367Sdes#endif /* HAVE_EVP_SHA256 */
43295367Sdes
44263635Sdesstruct ssh_digest_ctx {
45263635Sdes	int alg;
46263635Sdes	EVP_MD_CTX mdctx;
47263635Sdes};
48263635Sdes
49263635Sdesstruct ssh_digest {
50263635Sdes	int id;
51263635Sdes	const char *name;
52263635Sdes	size_t digest_len;
53263635Sdes	const EVP_MD *(*mdfunc)(void);
54263635Sdes};
55263635Sdes
56263635Sdes/* NB. Indexed directly by algorithm number */
57263635Sdesconst struct ssh_digest digests[] = {
58263635Sdes	{ SSH_DIGEST_MD5,	"MD5",	 	16,	EVP_md5 },
59263635Sdes	{ SSH_DIGEST_RIPEMD160,	"RIPEMD160",	20,	EVP_ripemd160 },
60263635Sdes	{ SSH_DIGEST_SHA1,	"SHA1",	 	20,	EVP_sha1 },
61263635Sdes	{ SSH_DIGEST_SHA256,	"SHA256", 	32,	EVP_sha256 },
62263635Sdes	{ SSH_DIGEST_SHA384,	"SHA384",	48,	EVP_sha384 },
63263635Sdes	{ SSH_DIGEST_SHA512,	"SHA512", 	64,	EVP_sha512 },
64263635Sdes	{ -1,			NULL,		0,	NULL },
65263635Sdes};
66263635Sdes
67263635Sdesstatic const struct ssh_digest *
68263635Sdesssh_digest_by_alg(int alg)
69263635Sdes{
70263635Sdes	if (alg < 0 || alg >= SSH_DIGEST_MAX)
71263635Sdes		return NULL;
72263635Sdes	if (digests[alg].id != alg) /* sanity */
73263635Sdes		return NULL;
74295367Sdes	if (digests[alg].mdfunc == NULL)
75295367Sdes		return NULL;
76263635Sdes	return &(digests[alg]);
77263635Sdes}
78263635Sdes
79295367Sdesint
80295367Sdesssh_digest_alg_by_name(const char *name)
81295367Sdes{
82295367Sdes	int alg;
83295367Sdes
84295367Sdes	for (alg = 0; digests[alg].id != -1; alg++) {
85295367Sdes		if (strcasecmp(name, digests[alg].name) == 0)
86295367Sdes			return digests[alg].id;
87295367Sdes	}
88295367Sdes	return -1;
89295367Sdes}
90295367Sdes
91295367Sdesconst char *
92295367Sdesssh_digest_alg_name(int alg)
93295367Sdes{
94295367Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
95295367Sdes
96295367Sdes	return digest == NULL ? NULL : digest->name;
97295367Sdes}
98295367Sdes
99263635Sdessize_t
100263635Sdesssh_digest_bytes(int alg)
101263635Sdes{
102263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
103263635Sdes
104263635Sdes	return digest == NULL ? 0 : digest->digest_len;
105263635Sdes}
106263635Sdes
107263635Sdessize_t
108263635Sdesssh_digest_blocksize(struct ssh_digest_ctx *ctx)
109263635Sdes{
110263635Sdes	return EVP_MD_CTX_block_size(&ctx->mdctx);
111263635Sdes}
112263635Sdes
113263635Sdesstruct ssh_digest_ctx *
114263635Sdesssh_digest_start(int alg)
115263635Sdes{
116263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
117263635Sdes	struct ssh_digest_ctx *ret;
118263635Sdes
119263635Sdes	if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL))
120263635Sdes		return NULL;
121263635Sdes	ret->alg = alg;
122263635Sdes	EVP_MD_CTX_init(&ret->mdctx);
123263635Sdes	if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) {
124263635Sdes		free(ret);
125263635Sdes		return NULL;
126263635Sdes	}
127263635Sdes	return ret;
128263635Sdes}
129263635Sdes
130263635Sdesint
131263635Sdesssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
132263635Sdes{
133295367Sdes	if (from->alg != to->alg)
134295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
135263635Sdes	/* we have bcopy-style order while openssl has memcpy-style */
136263635Sdes	if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx))
137295367Sdes		return SSH_ERR_LIBCRYPTO_ERROR;
138263635Sdes	return 0;
139263635Sdes}
140263635Sdes
141263635Sdesint
142263635Sdesssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
143263635Sdes{
144263635Sdes	if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1)
145295367Sdes		return SSH_ERR_LIBCRYPTO_ERROR;
146263635Sdes	return 0;
147263635Sdes}
148263635Sdes
149263635Sdesint
150295367Sdesssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
151263635Sdes{
152295367Sdes	return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
153263635Sdes}
154263635Sdes
155263635Sdesint
156263635Sdesssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
157263635Sdes{
158263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
159263635Sdes	u_int l = dlen;
160263635Sdes
161263635Sdes	if (dlen > UINT_MAX)
162295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
163263635Sdes	if (dlen < digest->digest_len) /* No truncation allowed */
164295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
165263635Sdes	if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1)
166295367Sdes		return SSH_ERR_LIBCRYPTO_ERROR;
167263635Sdes	if (l != digest->digest_len) /* sanity */
168295367Sdes		return SSH_ERR_INTERNAL_ERROR;
169263635Sdes	return 0;
170263635Sdes}
171263635Sdes
172263635Sdesvoid
173263635Sdesssh_digest_free(struct ssh_digest_ctx *ctx)
174263635Sdes{
175263635Sdes	if (ctx != NULL) {
176263635Sdes		EVP_MD_CTX_cleanup(&ctx->mdctx);
177263635Sdes		explicit_bzero(ctx, sizeof(*ctx));
178263635Sdes		free(ctx);
179263635Sdes	}
180263635Sdes}
181263635Sdes
182263635Sdesint
183263635Sdesssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
184263635Sdes{
185295367Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
186295367Sdes	u_int mdlen;
187263635Sdes
188295367Sdes	if (digest == NULL)
189295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
190295367Sdes	if (dlen > UINT_MAX)
191295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
192295367Sdes	if (dlen < digest->digest_len)
193295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
194295367Sdes	mdlen = dlen;
195295367Sdes	if (!EVP_Digest(m, mlen, d, &mdlen, digest->mdfunc(), NULL))
196295367Sdes		return SSH_ERR_LIBCRYPTO_ERROR;
197263635Sdes	return 0;
198263635Sdes}
199263635Sdes
200263635Sdesint
201295367Sdesssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
202263635Sdes{
203295367Sdes	return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
204263635Sdes}
205295367Sdes#endif /* WITH_OPENSSL */
206