1d059297112922cabb0c674840589be8db821fd9aAdam Langley/* $OpenBSD: digest-openssl.c,v 1.5 2014/12/21 22:27:56 djm Exp $ */
2d059297112922cabb0c674840589be8db821fd9aAdam Langley/*
3d059297112922cabb0c674840589be8db821fd9aAdam Langley * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
4d059297112922cabb0c674840589be8db821fd9aAdam Langley *
5d059297112922cabb0c674840589be8db821fd9aAdam Langley * Permission to use, copy, modify, and distribute this software for any
6d059297112922cabb0c674840589be8db821fd9aAdam Langley * purpose with or without fee is hereby granted, provided that the above
7d059297112922cabb0c674840589be8db821fd9aAdam Langley * copyright notice and this permission notice appear in all copies.
8d059297112922cabb0c674840589be8db821fd9aAdam Langley *
9d059297112922cabb0c674840589be8db821fd9aAdam Langley * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d059297112922cabb0c674840589be8db821fd9aAdam Langley * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d059297112922cabb0c674840589be8db821fd9aAdam Langley * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d059297112922cabb0c674840589be8db821fd9aAdam Langley * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d059297112922cabb0c674840589be8db821fd9aAdam Langley * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d059297112922cabb0c674840589be8db821fd9aAdam Langley * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d059297112922cabb0c674840589be8db821fd9aAdam Langley * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d059297112922cabb0c674840589be8db821fd9aAdam Langley */
17d059297112922cabb0c674840589be8db821fd9aAdam Langley
18d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "includes.h"
19d059297112922cabb0c674840589be8db821fd9aAdam Langley
20d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_OPENSSL
21d059297112922cabb0c674840589be8db821fd9aAdam Langley
22d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/types.h>
23d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <limits.h>
24d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <stdlib.h>
25d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <string.h>
26d059297112922cabb0c674840589be8db821fd9aAdam Langley
27d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <openssl/evp.h>
28d059297112922cabb0c674840589be8db821fd9aAdam Langley
29d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "openbsd-compat/openssl-compat.h"
30d059297112922cabb0c674840589be8db821fd9aAdam Langley
31d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshbuf.h"
32d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "digest.h"
33d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "ssherr.h"
34d059297112922cabb0c674840589be8db821fd9aAdam Langley
35d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifndef HAVE_EVP_RIPEMD160
36d059297112922cabb0c674840589be8db821fd9aAdam Langley# define EVP_ripemd160 NULL
37d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* HAVE_EVP_RIPEMD160 */
38d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifndef HAVE_EVP_SHA256
39d059297112922cabb0c674840589be8db821fd9aAdam Langley# define EVP_sha256 NULL
40d059297112922cabb0c674840589be8db821fd9aAdam Langley# define EVP_sha384 NULL
41d059297112922cabb0c674840589be8db821fd9aAdam Langley# define EVP_sha512 NULL
42d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* HAVE_EVP_SHA256 */
43d059297112922cabb0c674840589be8db821fd9aAdam Langley
44d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct ssh_digest_ctx {
45d059297112922cabb0c674840589be8db821fd9aAdam Langley	int alg;
46d059297112922cabb0c674840589be8db821fd9aAdam Langley	EVP_MD_CTX mdctx;
47d059297112922cabb0c674840589be8db821fd9aAdam Langley};
48d059297112922cabb0c674840589be8db821fd9aAdam Langley
49d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct ssh_digest {
50d059297112922cabb0c674840589be8db821fd9aAdam Langley	int id;
51d059297112922cabb0c674840589be8db821fd9aAdam Langley	const char *name;
52d059297112922cabb0c674840589be8db821fd9aAdam Langley	size_t digest_len;
53d059297112922cabb0c674840589be8db821fd9aAdam Langley	const EVP_MD *(*mdfunc)(void);
54d059297112922cabb0c674840589be8db821fd9aAdam Langley};
55d059297112922cabb0c674840589be8db821fd9aAdam Langley
56d059297112922cabb0c674840589be8db821fd9aAdam Langley/* NB. Indexed directly by algorithm number */
57d059297112922cabb0c674840589be8db821fd9aAdam Langleyconst struct ssh_digest digests[] = {
58d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_MD5,	"MD5",	 	16,	EVP_md5 },
59d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_RIPEMD160,	"RIPEMD160",	20,	EVP_ripemd160 },
60d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_SHA1,	"SHA1",	 	20,	EVP_sha1 },
61d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_SHA256,	"SHA256", 	32,	EVP_sha256 },
62d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_SHA384,	"SHA384",	48,	EVP_sha384 },
63d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ SSH_DIGEST_SHA512,	"SHA512", 	64,	EVP_sha512 },
64d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ -1,			NULL,		0,	NULL },
65d059297112922cabb0c674840589be8db821fd9aAdam Langley};
66d059297112922cabb0c674840589be8db821fd9aAdam Langley
67d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic const struct ssh_digest *
68d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_by_alg(int alg)
69d059297112922cabb0c674840589be8db821fd9aAdam Langley{
70d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (alg < 0 || alg >= SSH_DIGEST_MAX)
71d059297112922cabb0c674840589be8db821fd9aAdam Langley		return NULL;
72d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (digests[alg].id != alg) /* sanity */
73d059297112922cabb0c674840589be8db821fd9aAdam Langley		return NULL;
74d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (digests[alg].mdfunc == NULL)
75d059297112922cabb0c674840589be8db821fd9aAdam Langley		return NULL;
76d059297112922cabb0c674840589be8db821fd9aAdam Langley	return &(digests[alg]);
77d059297112922cabb0c674840589be8db821fd9aAdam Langley}
78d059297112922cabb0c674840589be8db821fd9aAdam Langley
79d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
80d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_alg_by_name(const char *name)
81d059297112922cabb0c674840589be8db821fd9aAdam Langley{
82d059297112922cabb0c674840589be8db821fd9aAdam Langley	int alg;
83d059297112922cabb0c674840589be8db821fd9aAdam Langley
84d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (alg = 0; digests[alg].id != -1; alg++) {
85d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (strcasecmp(name, digests[alg].name) == 0)
86d059297112922cabb0c674840589be8db821fd9aAdam Langley			return digests[alg].id;
87d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
88d059297112922cabb0c674840589be8db821fd9aAdam Langley	return -1;
89d059297112922cabb0c674840589be8db821fd9aAdam Langley}
90d059297112922cabb0c674840589be8db821fd9aAdam Langley
91d059297112922cabb0c674840589be8db821fd9aAdam Langleyconst char *
92d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_alg_name(int alg)
93d059297112922cabb0c674840589be8db821fd9aAdam Langley{
94d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
95d059297112922cabb0c674840589be8db821fd9aAdam Langley
96d059297112922cabb0c674840589be8db821fd9aAdam Langley	return digest == NULL ? NULL : digest->name;
97d059297112922cabb0c674840589be8db821fd9aAdam Langley}
98d059297112922cabb0c674840589be8db821fd9aAdam Langley
99d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t
100d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_bytes(int alg)
101d059297112922cabb0c674840589be8db821fd9aAdam Langley{
102d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
103d059297112922cabb0c674840589be8db821fd9aAdam Langley
104d059297112922cabb0c674840589be8db821fd9aAdam Langley	return digest == NULL ? 0 : digest->digest_len;
105d059297112922cabb0c674840589be8db821fd9aAdam Langley}
106d059297112922cabb0c674840589be8db821fd9aAdam Langley
107d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t
108d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_blocksize(struct ssh_digest_ctx *ctx)
109d059297112922cabb0c674840589be8db821fd9aAdam Langley{
110d059297112922cabb0c674840589be8db821fd9aAdam Langley	return EVP_MD_CTX_block_size(&ctx->mdctx);
111d059297112922cabb0c674840589be8db821fd9aAdam Langley}
112d059297112922cabb0c674840589be8db821fd9aAdam Langley
113d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct ssh_digest_ctx *
114d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_start(int alg)
115d059297112922cabb0c674840589be8db821fd9aAdam Langley{
116d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
117d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct ssh_digest_ctx *ret;
118d059297112922cabb0c674840589be8db821fd9aAdam Langley
119d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL))
120d059297112922cabb0c674840589be8db821fd9aAdam Langley		return NULL;
121d059297112922cabb0c674840589be8db821fd9aAdam Langley	ret->alg = alg;
122d059297112922cabb0c674840589be8db821fd9aAdam Langley	EVP_MD_CTX_init(&ret->mdctx);
123d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) {
124d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(ret);
125d059297112922cabb0c674840589be8db821fd9aAdam Langley		return NULL;
126d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
127d059297112922cabb0c674840589be8db821fd9aAdam Langley	return ret;
128d059297112922cabb0c674840589be8db821fd9aAdam Langley}
129d059297112922cabb0c674840589be8db821fd9aAdam Langley
130d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
131d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
132d059297112922cabb0c674840589be8db821fd9aAdam Langley{
133d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (from->alg != to->alg)
134d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
135d059297112922cabb0c674840589be8db821fd9aAdam Langley	/* we have bcopy-style order while openssl has memcpy-style */
136d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx))
137d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_LIBCRYPTO_ERROR;
138d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
139d059297112922cabb0c674840589be8db821fd9aAdam Langley}
140d059297112922cabb0c674840589be8db821fd9aAdam Langley
141d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
142d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
143d059297112922cabb0c674840589be8db821fd9aAdam Langley{
144d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1)
145d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_LIBCRYPTO_ERROR;
146d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
147d059297112922cabb0c674840589be8db821fd9aAdam Langley}
148d059297112922cabb0c674840589be8db821fd9aAdam Langley
149d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
150d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
151d059297112922cabb0c674840589be8db821fd9aAdam Langley{
152d059297112922cabb0c674840589be8db821fd9aAdam Langley	return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
153d059297112922cabb0c674840589be8db821fd9aAdam Langley}
154d059297112922cabb0c674840589be8db821fd9aAdam Langley
155d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
156d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
157d059297112922cabb0c674840589be8db821fd9aAdam Langley{
158d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
159d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int l = dlen;
160d059297112922cabb0c674840589be8db821fd9aAdam Langley
161d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (dlen > UINT_MAX)
162d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
163d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (dlen < digest->digest_len) /* No truncation allowed */
164d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
165d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1)
166d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_LIBCRYPTO_ERROR;
167d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (l != digest->digest_len) /* sanity */
168d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
169d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
170d059297112922cabb0c674840589be8db821fd9aAdam Langley}
171d059297112922cabb0c674840589be8db821fd9aAdam Langley
172d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
173d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_free(struct ssh_digest_ctx *ctx)
174d059297112922cabb0c674840589be8db821fd9aAdam Langley{
175d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (ctx != NULL) {
176d059297112922cabb0c674840589be8db821fd9aAdam Langley		EVP_MD_CTX_cleanup(&ctx->mdctx);
177d059297112922cabb0c674840589be8db821fd9aAdam Langley		explicit_bzero(ctx, sizeof(*ctx));
178d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(ctx);
179d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
180d059297112922cabb0c674840589be8db821fd9aAdam Langley}
181d059297112922cabb0c674840589be8db821fd9aAdam Langley
182d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
183d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
184d059297112922cabb0c674840589be8db821fd9aAdam Langley{
185d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
186d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int mdlen;
187d059297112922cabb0c674840589be8db821fd9aAdam Langley
188d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (digest == NULL)
189d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
190d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (dlen > UINT_MAX)
191d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
192d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (dlen < digest->digest_len)
193d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
194d059297112922cabb0c674840589be8db821fd9aAdam Langley	mdlen = dlen;
195d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (!EVP_Digest(m, mlen, d, &mdlen, digest->mdfunc(), NULL))
196d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_LIBCRYPTO_ERROR;
197d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
198d059297112922cabb0c674840589be8db821fd9aAdam Langley}
199d059297112922cabb0c674840589be8db821fd9aAdam Langley
200d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
201d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
202d059297112922cabb0c674840589be8db821fd9aAdam Langley{
203d059297112922cabb0c674840589be8db821fd9aAdam Langley	return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
204d059297112922cabb0c674840589be8db821fd9aAdam Langley}
205d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* WITH_OPENSSL */
206