1 |
/* $OpenBSD: digest-openssl.c,v 1.2 2014/02/02 03:44:31 djm Exp $ */ |
2 |
/* |
3 |
* Copyright (c) 2013 Damien Miller <djm@mindrot.org> |
4 |
* |
5 |
* Permission to use, copy, modify, and distribute this software for any |
6 |
* purpose with or without fee is hereby granted, provided that the above |
7 |
* copyright notice and this permission notice appear in all copies. |
8 |
* |
9 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 |
*/ |
17 |
|
18 |
#include "includes.h" |
19 |
|
20 |
#include <sys/types.h> |
21 |
#include <limits.h> |
22 |
#include <stdlib.h> |
23 |
#include <string.h> |
24 |
|
25 |
#include <openssl/evp.h> |
26 |
|
27 |
#include "openbsd-compat/openssl-compat.h" |
28 |
|
29 |
#include "buffer.h" |
30 |
#include "digest.h" |
31 |
|
32 |
struct ssh_digest_ctx { |
33 |
int alg; |
34 |
EVP_MD_CTX mdctx; |
35 |
}; |
36 |
|
37 |
struct ssh_digest { |
38 |
int id; |
39 |
const char *name; |
40 |
size_t digest_len; |
41 |
const EVP_MD *(*mdfunc)(void); |
42 |
}; |
43 |
|
44 |
/* NB. Indexed directly by algorithm number */ |
45 |
const struct ssh_digest digests[] = { |
46 |
{ SSH_DIGEST_MD5, "MD5", 16, EVP_md5 }, |
47 |
{ SSH_DIGEST_RIPEMD160, "RIPEMD160", 20, EVP_ripemd160 }, |
48 |
{ SSH_DIGEST_SHA1, "SHA1", 20, EVP_sha1 }, |
49 |
#ifdef HAVE_EVP_SHA256 /* XXX replace with local if missing */ |
50 |
{ SSH_DIGEST_SHA256, "SHA256", 32, EVP_sha256 }, |
51 |
{ SSH_DIGEST_SHA384, "SHA384", 48, EVP_sha384 }, |
52 |
{ SSH_DIGEST_SHA512, "SHA512", 64, EVP_sha512 }, |
53 |
#endif |
54 |
{ -1, NULL, 0, NULL }, |
55 |
}; |
56 |
|
57 |
static const struct ssh_digest * |
58 |
ssh_digest_by_alg(int alg) |
59 |
{ |
60 |
if (alg < 0 || alg >= SSH_DIGEST_MAX) |
61 |
return NULL; |
62 |
if (digests[alg].id != alg) /* sanity */ |
63 |
return NULL; |
64 |
return &(digests[alg]); |
65 |
} |
66 |
|
67 |
size_t |
68 |
ssh_digest_bytes(int alg) |
69 |
{ |
70 |
const struct ssh_digest *digest = ssh_digest_by_alg(alg); |
71 |
|
72 |
return digest == NULL ? 0 : digest->digest_len; |
73 |
} |
74 |
|
75 |
size_t |
76 |
ssh_digest_blocksize(struct ssh_digest_ctx *ctx) |
77 |
{ |
78 |
return EVP_MD_CTX_block_size(&ctx->mdctx); |
79 |
} |
80 |
|
81 |
struct ssh_digest_ctx * |
82 |
ssh_digest_start(int alg) |
83 |
{ |
84 |
const struct ssh_digest *digest = ssh_digest_by_alg(alg); |
85 |
struct ssh_digest_ctx *ret; |
86 |
|
87 |
if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) |
88 |
return NULL; |
89 |
ret->alg = alg; |
90 |
EVP_MD_CTX_init(&ret->mdctx); |
91 |
if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) { |
92 |
free(ret); |
93 |
return NULL; |
94 |
} |
95 |
return ret; |
96 |
} |
97 |
|
98 |
int |
99 |
ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) |
100 |
{ |
101 |
/* we have bcopy-style order while openssl has memcpy-style */ |
102 |
if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) |
103 |
return -1; |
104 |
return 0; |
105 |
} |
106 |
|
107 |
int |
108 |
ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) |
109 |
{ |
110 |
if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) |
111 |
return -1; |
112 |
return 0; |
113 |
} |
114 |
|
115 |
int |
116 |
ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b) |
117 |
{ |
118 |
return ssh_digest_update(ctx, buffer_ptr(b), buffer_len(b)); |
119 |
} |
120 |
|
121 |
int |
122 |
ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) |
123 |
{ |
124 |
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); |
125 |
u_int l = dlen; |
126 |
|
127 |
if (dlen > UINT_MAX) |
128 |
return -1; |
129 |
if (dlen < digest->digest_len) /* No truncation allowed */ |
130 |
return -1; |
131 |
if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) |
132 |
return -1; |
133 |
if (l != digest->digest_len) /* sanity */ |
134 |
return -1; |
135 |
return 0; |
136 |
} |
137 |
|
138 |
void |
139 |
ssh_digest_free(struct ssh_digest_ctx *ctx) |
140 |
{ |
141 |
if (ctx != NULL) { |
142 |
EVP_MD_CTX_cleanup(&ctx->mdctx); |
143 |
explicit_bzero(ctx, sizeof(*ctx)); |
144 |
free(ctx); |
145 |
} |
146 |
} |
147 |
|
148 |
int |
149 |
ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) |
150 |
{ |
151 |
struct ssh_digest_ctx *ctx = ssh_digest_start(alg); |
152 |
|
153 |
if (ctx == NULL) |
154 |
return -1; |
155 |
if (ssh_digest_update(ctx, m, mlen) != 0 || |
156 |
ssh_digest_final(ctx, d, dlen) != 0) |
157 |
return -1; |
158 |
ssh_digest_free(ctx); |
159 |
return 0; |
160 |
} |
161 |
|
162 |
int |
163 |
ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen) |
164 |
{ |
165 |
return ssh_digest_memory(alg, buffer_ptr(b), buffer_len(b), d, dlen); |
166 |
} |