1295367Sdes/* $OpenBSD: digest-libc.c,v 1.5 2015/05/05 02:48:17 jsg Exp $ */
2263635Sdes/*
3263635Sdes * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
4263635Sdes * Copyright (c) 2014 Markus Friedl.  All rights reserved.
5263635Sdes *
6263635Sdes * Permission to use, copy, modify, and distribute this software for any
7263635Sdes * purpose with or without fee is hereby granted, provided that the above
8263635Sdes * copyright notice and this permission notice appear in all copies.
9263635Sdes *
10263635Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11263635Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12263635Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13263635Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14263635Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15263635Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16263635Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17263635Sdes */
18263635Sdes
19263635Sdes#include "includes.h"
20263635Sdes
21295367Sdes#ifndef WITH_OPENSSL
22295367Sdes
23263635Sdes#include <sys/types.h>
24263635Sdes#include <limits.h>
25263635Sdes#include <stdlib.h>
26263635Sdes#include <string.h>
27263635Sdes
28295367Sdes#if 0
29263635Sdes#include <md5.h>
30263635Sdes#include <rmd160.h>
31263635Sdes#include <sha1.h>
32263635Sdes#include <sha2.h>
33295367Sdes#endif
34263635Sdes
35295367Sdes#include "ssherr.h"
36295367Sdes#include "sshbuf.h"
37263635Sdes#include "digest.h"
38263635Sdes
39263635Sdestypedef void md_init_fn(void *mdctx);
40263635Sdestypedef void md_update_fn(void *mdctx, const u_int8_t *m, size_t mlen);
41263635Sdestypedef void md_final_fn(u_int8_t[], void *mdctx);
42263635Sdes
43263635Sdesstruct ssh_digest_ctx {
44263635Sdes	int alg;
45263635Sdes	void *mdctx;
46263635Sdes};
47263635Sdes
48263635Sdesstruct ssh_digest {
49263635Sdes	int id;
50263635Sdes	const char *name;
51263635Sdes	size_t block_len;
52263635Sdes	size_t digest_len;
53263635Sdes	size_t ctx_len;
54263635Sdes	md_init_fn *md_init;
55263635Sdes	md_update_fn *md_update;
56263635Sdes	md_final_fn *md_final;
57263635Sdes};
58263635Sdes
59263635Sdes/* NB. Indexed directly by algorithm number */
60263635Sdesconst struct ssh_digest digests[SSH_DIGEST_MAX] = {
61263635Sdes	{
62263635Sdes		SSH_DIGEST_MD5,
63263635Sdes		"MD5",
64263635Sdes		MD5_BLOCK_LENGTH,
65263635Sdes		MD5_DIGEST_LENGTH,
66263635Sdes		sizeof(MD5_CTX),
67263635Sdes		(md_init_fn *) MD5Init,
68263635Sdes		(md_update_fn *) MD5Update,
69263635Sdes		(md_final_fn *) MD5Final
70263635Sdes	},
71263635Sdes	{
72263635Sdes		SSH_DIGEST_RIPEMD160,
73263635Sdes		"RIPEMD160",
74263635Sdes		RMD160_BLOCK_LENGTH,
75263635Sdes		RMD160_DIGEST_LENGTH,
76263635Sdes		sizeof(RMD160_CTX),
77263635Sdes		(md_init_fn *) RMD160Init,
78263635Sdes		(md_update_fn *) RMD160Update,
79263635Sdes		(md_final_fn *) RMD160Final
80263635Sdes	},
81263635Sdes	{
82263635Sdes		SSH_DIGEST_SHA1,
83263635Sdes		"SHA1",
84263635Sdes		SHA1_BLOCK_LENGTH,
85263635Sdes		SHA1_DIGEST_LENGTH,
86263635Sdes		sizeof(SHA1_CTX),
87263635Sdes		(md_init_fn *) SHA1Init,
88263635Sdes		(md_update_fn *) SHA1Update,
89263635Sdes		(md_final_fn *) SHA1Final
90263635Sdes	},
91263635Sdes	{
92263635Sdes		SSH_DIGEST_SHA256,
93263635Sdes		"SHA256",
94263635Sdes		SHA256_BLOCK_LENGTH,
95263635Sdes		SHA256_DIGEST_LENGTH,
96295367Sdes		sizeof(SHA256_CTX),
97295367Sdes		(md_init_fn *) SHA256_Init,
98295367Sdes		(md_update_fn *) SHA256_Update,
99295367Sdes		(md_final_fn *) SHA256_Final
100263635Sdes	},
101263635Sdes	{
102263635Sdes		SSH_DIGEST_SHA384,
103263635Sdes		"SHA384",
104263635Sdes		SHA384_BLOCK_LENGTH,
105263635Sdes		SHA384_DIGEST_LENGTH,
106295367Sdes		sizeof(SHA384_CTX),
107295367Sdes		(md_init_fn *) SHA384_Init,
108295367Sdes		(md_update_fn *) SHA384_Update,
109295367Sdes		(md_final_fn *) SHA384_Final
110263635Sdes	},
111263635Sdes	{
112263635Sdes		SSH_DIGEST_SHA512,
113263635Sdes		"SHA512",
114263635Sdes		SHA512_BLOCK_LENGTH,
115263635Sdes		SHA512_DIGEST_LENGTH,
116295367Sdes		sizeof(SHA512_CTX),
117295367Sdes		(md_init_fn *) SHA512_Init,
118295367Sdes		(md_update_fn *) SHA512_Update,
119295367Sdes		(md_final_fn *) SHA512_Final
120263635Sdes	}
121263635Sdes};
122263635Sdes
123263635Sdesstatic const struct ssh_digest *
124263635Sdesssh_digest_by_alg(int alg)
125263635Sdes{
126263635Sdes	if (alg < 0 || alg >= SSH_DIGEST_MAX)
127263635Sdes		return NULL;
128263635Sdes	if (digests[alg].id != alg) /* sanity */
129263635Sdes		return NULL;
130263635Sdes	return &(digests[alg]);
131263635Sdes}
132263635Sdes
133295367Sdesint
134295367Sdesssh_digest_alg_by_name(const char *name)
135295367Sdes{
136295367Sdes	int alg;
137295367Sdes
138295367Sdes	for (alg = 0; alg < SSH_DIGEST_MAX; alg++) {
139295367Sdes		if (strcasecmp(name, digests[alg].name) == 0)
140295367Sdes			return digests[alg].id;
141295367Sdes	}
142295367Sdes	return -1;
143295367Sdes}
144295367Sdes
145295367Sdesconst char *
146295367Sdesssh_digest_alg_name(int alg)
147295367Sdes{
148295367Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
149295367Sdes
150295367Sdes	return digest == NULL ? NULL : digest->name;
151295367Sdes}
152295367Sdes
153263635Sdessize_t
154263635Sdesssh_digest_bytes(int alg)
155263635Sdes{
156263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
157263635Sdes
158263635Sdes	return digest == NULL ? 0 : digest->digest_len;
159263635Sdes}
160263635Sdes
161263635Sdessize_t
162263635Sdesssh_digest_blocksize(struct ssh_digest_ctx *ctx)
163263635Sdes{
164263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
165263635Sdes
166263635Sdes	return digest == NULL ? 0 : digest->block_len;
167263635Sdes}
168263635Sdes
169263635Sdesstruct ssh_digest_ctx *
170263635Sdesssh_digest_start(int alg)
171263635Sdes{
172263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
173263635Sdes	struct ssh_digest_ctx *ret;
174263635Sdes
175294193Sdes	if (digest == NULL || (ret = calloc(1, sizeof(*ret))) == NULL)
176263635Sdes		return NULL;
177263635Sdes	if ((ret->mdctx = calloc(1, digest->ctx_len)) == NULL) {
178263635Sdes		free(ret);
179263635Sdes		return NULL;
180263635Sdes	}
181263635Sdes	ret->alg = alg;
182263635Sdes	digest->md_init(ret->mdctx);
183263635Sdes	return ret;
184263635Sdes}
185263635Sdes
186263635Sdesint
187263635Sdesssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
188263635Sdes{
189263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(from->alg);
190263635Sdes
191263635Sdes	if (digest == NULL || from->alg != to->alg)
192295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
193263635Sdes	memcpy(to->mdctx, from->mdctx, digest->ctx_len);
194263635Sdes	return 0;
195263635Sdes}
196263635Sdes
197263635Sdesint
198263635Sdesssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
199263635Sdes{
200263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
201263635Sdes
202263635Sdes	if (digest == NULL)
203295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
204263635Sdes	digest->md_update(ctx->mdctx, m, mlen);
205263635Sdes	return 0;
206263635Sdes}
207263635Sdes
208263635Sdesint
209295367Sdesssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
210263635Sdes{
211295367Sdes	return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
212263635Sdes}
213263635Sdes
214263635Sdesint
215263635Sdesssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
216263635Sdes{
217263635Sdes	const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
218263635Sdes
219263635Sdes	if (digest == NULL)
220295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
221263635Sdes	if (dlen > UINT_MAX)
222295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
223263635Sdes	if (dlen < digest->digest_len) /* No truncation allowed */
224295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
225263635Sdes	digest->md_final(d, ctx->mdctx);
226263635Sdes	return 0;
227263635Sdes}
228263635Sdes
229263635Sdesvoid
230263635Sdesssh_digest_free(struct ssh_digest_ctx *ctx)
231263635Sdes{
232263635Sdes	const struct ssh_digest *digest;
233263635Sdes
234263635Sdes	if (ctx != NULL) {
235263635Sdes		digest = ssh_digest_by_alg(ctx->alg);
236263635Sdes		if (digest) {
237263635Sdes			explicit_bzero(ctx->mdctx, digest->ctx_len);
238263635Sdes			free(ctx->mdctx);
239263635Sdes			explicit_bzero(ctx, sizeof(*ctx));
240263635Sdes			free(ctx);
241263635Sdes		}
242263635Sdes	}
243263635Sdes}
244263635Sdes
245263635Sdesint
246263635Sdesssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
247263635Sdes{
248263635Sdes	struct ssh_digest_ctx *ctx = ssh_digest_start(alg);
249263635Sdes
250263635Sdes	if (ctx == NULL)
251295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
252263635Sdes	if (ssh_digest_update(ctx, m, mlen) != 0 ||
253263635Sdes	    ssh_digest_final(ctx, d, dlen) != 0)
254295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
255263635Sdes	ssh_digest_free(ctx);
256263635Sdes	return 0;
257263635Sdes}
258263635Sdes
259263635Sdesint
260295367Sdesssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
261263635Sdes{
262295367Sdes	return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
263263635Sdes}
264295367Sdes#endif /* !WITH_OPENSSL */
265