1 |
/* $OpenBSD: digest-libc.c,v 1.6 2017/05/08 22:57:38 djm Exp $ */ |
2 |
/* |
3 |
* Copyright (c) 2013 Damien Miller <djm@mindrot.org> |
4 |
* Copyright (c) 2014 Markus Friedl. All rights reserved. |
5 |
* |
6 |
* Permission to use, copy, modify, and distribute this software for any |
7 |
* purpose with or without fee is hereby granted, provided that the above |
8 |
* copyright notice and this permission notice appear in all copies. |
9 |
* |
10 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
*/ |
18 |
|
19 |
#include "includes.h" |
20 |
|
21 |
#ifndef WITH_OPENSSL |
22 |
|
23 |
#include <sys/types.h> |
24 |
#include <limits.h> |
25 |
#include <stdlib.h> |
26 |
#include <string.h> |
27 |
|
28 |
#if 0 |
29 |
#include <md5.h> |
30 |
#include <rmd160.h> |
31 |
#include <sha1.h> |
32 |
#include <sha2.h> |
33 |
#endif |
34 |
|
35 |
#include "ssherr.h" |
36 |
#include "sshbuf.h" |
37 |
#include "digest.h" |
38 |
|
39 |
typedef void md_init_fn(void *mdctx); |
40 |
typedef void md_update_fn(void *mdctx, const u_int8_t *m, size_t mlen); |
41 |
typedef void md_final_fn(u_int8_t[], void *mdctx); |
42 |
|
43 |
struct ssh_digest_ctx { |
44 |
int alg; |
45 |
void *mdctx; |
46 |
}; |
47 |
|
48 |
struct ssh_digest { |
49 |
int id; |
50 |
const char *name; |
51 |
size_t block_len; |
52 |
size_t digest_len; |
53 |
size_t ctx_len; |
54 |
md_init_fn *md_init; |
55 |
md_update_fn *md_update; |
56 |
md_final_fn *md_final; |
57 |
}; |
58 |
|
59 |
/* NB. Indexed directly by algorithm number */ |
60 |
const struct ssh_digest digests[SSH_DIGEST_MAX] = { |
61 |
{ |
62 |
SSH_DIGEST_MD5, |
63 |
"MD5", |
64 |
MD5_BLOCK_LENGTH, |
65 |
MD5_DIGEST_LENGTH, |
66 |
sizeof(MD5_CTX), |
67 |
(md_init_fn *) MD5Init, |
68 |
(md_update_fn *) MD5Update, |
69 |
(md_final_fn *) MD5Final |
70 |
}, |
71 |
{ |
72 |
SSH_DIGEST_SHA1, |
73 |
"SHA1", |
74 |
SHA1_BLOCK_LENGTH, |
75 |
SHA1_DIGEST_LENGTH, |
76 |
sizeof(SHA1_CTX), |
77 |
(md_init_fn *) SHA1Init, |
78 |
(md_update_fn *) SHA1Update, |
79 |
(md_final_fn *) SHA1Final |
80 |
}, |
81 |
{ |
82 |
SSH_DIGEST_SHA256, |
83 |
"SHA256", |
84 |
SHA256_BLOCK_LENGTH, |
85 |
SHA256_DIGEST_LENGTH, |
86 |
sizeof(SHA256_CTX), |
87 |
(md_init_fn *) SHA256_Init, |
88 |
(md_update_fn *) SHA256_Update, |
89 |
(md_final_fn *) SHA256_Final |
90 |
}, |
91 |
{ |
92 |
SSH_DIGEST_SHA384, |
93 |
"SHA384", |
94 |
SHA384_BLOCK_LENGTH, |
95 |
SHA384_DIGEST_LENGTH, |
96 |
sizeof(SHA384_CTX), |
97 |
(md_init_fn *) SHA384_Init, |
98 |
(md_update_fn *) SHA384_Update, |
99 |
(md_final_fn *) SHA384_Final |
100 |
}, |
101 |
{ |
102 |
SSH_DIGEST_SHA512, |
103 |
"SHA512", |
104 |
SHA512_BLOCK_LENGTH, |
105 |
SHA512_DIGEST_LENGTH, |
106 |
sizeof(SHA512_CTX), |
107 |
(md_init_fn *) SHA512_Init, |
108 |
(md_update_fn *) SHA512_Update, |
109 |
(md_final_fn *) SHA512_Final |
110 |
} |
111 |
}; |
112 |
|
113 |
static const struct ssh_digest * |
114 |
ssh_digest_by_alg(int alg) |
115 |
{ |
116 |
if (alg < 0 || alg >= SSH_DIGEST_MAX) |
117 |
return NULL; |
118 |
if (digests[alg].id != alg) /* sanity */ |
119 |
return NULL; |
120 |
return &(digests[alg]); |
121 |
} |
122 |
|
123 |
int |
124 |
ssh_digest_alg_by_name(const char *name) |
125 |
{ |
126 |
int alg; |
127 |
|
128 |
for (alg = 0; alg < SSH_DIGEST_MAX; alg++) { |
129 |
if (strcasecmp(name, digests[alg].name) == 0) |
130 |
return digests[alg].id; |
131 |
} |
132 |
return -1; |
133 |
} |
134 |
|
135 |
const char * |
136 |
ssh_digest_alg_name(int alg) |
137 |
{ |
138 |
const struct ssh_digest *digest = ssh_digest_by_alg(alg); |
139 |
|
140 |
return digest == NULL ? NULL : digest->name; |
141 |
} |
142 |
|
143 |
size_t |
144 |
ssh_digest_bytes(int alg) |
145 |
{ |
146 |
const struct ssh_digest *digest = ssh_digest_by_alg(alg); |
147 |
|
148 |
return digest == NULL ? 0 : digest->digest_len; |
149 |
} |
150 |
|
151 |
size_t |
152 |
ssh_digest_blocksize(struct ssh_digest_ctx *ctx) |
153 |
{ |
154 |
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); |
155 |
|
156 |
return digest == NULL ? 0 : digest->block_len; |
157 |
} |
158 |
|
159 |
struct ssh_digest_ctx * |
160 |
ssh_digest_start(int alg) |
161 |
{ |
162 |
const struct ssh_digest *digest = ssh_digest_by_alg(alg); |
163 |
struct ssh_digest_ctx *ret; |
164 |
|
165 |
if (digest == NULL || (ret = calloc(1, sizeof(*ret))) == NULL) |
166 |
return NULL; |
167 |
if ((ret->mdctx = calloc(1, digest->ctx_len)) == NULL) { |
168 |
free(ret); |
169 |
return NULL; |
170 |
} |
171 |
ret->alg = alg; |
172 |
digest->md_init(ret->mdctx); |
173 |
return ret; |
174 |
} |
175 |
|
176 |
int |
177 |
ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) |
178 |
{ |
179 |
const struct ssh_digest *digest = ssh_digest_by_alg(from->alg); |
180 |
|
181 |
if (digest == NULL || from->alg != to->alg) |
182 |
return SSH_ERR_INVALID_ARGUMENT; |
183 |
memcpy(to->mdctx, from->mdctx, digest->ctx_len); |
184 |
return 0; |
185 |
} |
186 |
|
187 |
int |
188 |
ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) |
189 |
{ |
190 |
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); |
191 |
|
192 |
if (digest == NULL) |
193 |
return SSH_ERR_INVALID_ARGUMENT; |
194 |
digest->md_update(ctx->mdctx, m, mlen); |
195 |
return 0; |
196 |
} |
197 |
|
198 |
int |
199 |
ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) |
200 |
{ |
201 |
return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); |
202 |
} |
203 |
|
204 |
int |
205 |
ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) |
206 |
{ |
207 |
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); |
208 |
|
209 |
if (digest == NULL) |
210 |
return SSH_ERR_INVALID_ARGUMENT; |
211 |
if (dlen > UINT_MAX) |
212 |
return SSH_ERR_INVALID_ARGUMENT; |
213 |
if (dlen < digest->digest_len) /* No truncation allowed */ |
214 |
return SSH_ERR_INVALID_ARGUMENT; |
215 |
digest->md_final(d, ctx->mdctx); |
216 |
return 0; |
217 |
} |
218 |
|
219 |
void |
220 |
ssh_digest_free(struct ssh_digest_ctx *ctx) |
221 |
{ |
222 |
const struct ssh_digest *digest; |
223 |
|
224 |
if (ctx != NULL) { |
225 |
digest = ssh_digest_by_alg(ctx->alg); |
226 |
if (digest) { |
227 |
explicit_bzero(ctx->mdctx, digest->ctx_len); |
228 |
free(ctx->mdctx); |
229 |
explicit_bzero(ctx, sizeof(*ctx)); |
230 |
free(ctx); |
231 |
} |
232 |
} |
233 |
} |
234 |
|
235 |
int |
236 |
ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) |
237 |
{ |
238 |
struct ssh_digest_ctx *ctx = ssh_digest_start(alg); |
239 |
|
240 |
if (ctx == NULL) |
241 |
return SSH_ERR_INVALID_ARGUMENT; |
242 |
if (ssh_digest_update(ctx, m, mlen) != 0 || |
243 |
ssh_digest_final(ctx, d, dlen) != 0) |
244 |
return SSH_ERR_INVALID_ARGUMENT; |
245 |
ssh_digest_free(ctx); |
246 |
return 0; |
247 |
} |
248 |
|
249 |
int |
250 |
ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) |
251 |
{ |
252 |
return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); |
253 |
} |
254 |
#endif /* !WITH_OPENSSL */ |