Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD: mac.c,v 1.35 2019/09/06 04:53:27 djm Exp $ */ |
2 | | /* |
3 | | * Copyright (c) 2001 Markus Friedl. All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * 1. Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * |
14 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
15 | | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
16 | | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
17 | | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
18 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
19 | | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
20 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
21 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
23 | | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | | */ |
25 | | |
26 | | #include "includes.h" |
27 | | |
28 | | #include <sys/types.h> |
29 | | |
30 | | #include <stdlib.h> |
31 | | #include <string.h> |
32 | | #include <stdio.h> |
33 | | |
34 | | #include "digest.h" |
35 | | #include "hmac.h" |
36 | | #include "umac.h" |
37 | | #include "mac.h" |
38 | | #include "misc.h" |
39 | | #include "ssherr.h" |
40 | | #include "sshbuf.h" |
41 | | |
42 | | #include "openbsd-compat/openssl-compat.h" |
43 | | |
44 | 80 | #define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ |
45 | 116 | #define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ |
46 | 98 | #define SSH_UMAC128 3 |
47 | | |
48 | | struct macalg { |
49 | | char *name; |
50 | | int type; |
51 | | int alg; |
52 | | int truncatebits; /* truncate digest if != 0 */ |
53 | | int key_len; /* just for UMAC */ |
54 | | int len; /* just for UMAC */ |
55 | | int etm; /* Encrypt-then-MAC */ |
56 | | }; |
57 | | |
58 | | static const struct macalg macs[] = { |
59 | | /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ |
60 | | { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, |
61 | | { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, |
62 | | { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, |
63 | | { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, |
64 | | { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 }, |
65 | | { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 }, |
66 | | { "none", SSH_DIGEST, SSH_DIGEST_NULL, 0, 0, 0, 0 }, |
67 | | { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 }, |
68 | | { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 }, |
69 | | |
70 | | /* Encrypt-then-MAC variants */ |
71 | | { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, |
72 | | { "hmac-sha1-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 }, |
73 | | { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, |
74 | | { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, |
75 | | { "hmac-md5-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 1 }, |
76 | | { "hmac-md5-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 }, |
77 | | { "umac-64-etm@openssh.com", SSH_UMAC, 0, 0, 128, 64, 1 }, |
78 | | { "umac-128-etm@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 1 }, |
79 | | |
80 | | { NULL, 0, 0, 0, 0, 0, 0 } |
81 | | }; |
82 | | |
83 | | /* Returns a list of supported MACs separated by the specified char. */ |
84 | | char * |
85 | | mac_alg_list(char sep) |
86 | 0 | { |
87 | 0 | char *ret = NULL, *tmp; |
88 | 0 | size_t nlen, rlen = 0; |
89 | 0 | const struct macalg *m; |
90 | |
|
91 | 0 | for (m = macs; m->name != NULL; m++) { |
92 | 0 | if (ret != NULL) |
93 | 0 | ret[rlen++] = sep; |
94 | 0 | nlen = strlen(m->name); |
95 | 0 | if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { |
96 | 0 | free(ret); |
97 | 0 | return NULL; |
98 | 0 | } |
99 | 0 | ret = tmp; |
100 | 0 | memcpy(ret + rlen, m->name, nlen + 1); |
101 | 0 | rlen += nlen; |
102 | 0 | } |
103 | 0 | return ret; |
104 | 0 | } |
105 | | |
106 | | static int |
107 | | mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg) |
108 | 80 | { |
109 | 80 | mac->type = macalg->type; |
110 | 80 | if (mac->type == SSH_DIGEST) { |
111 | 39 | if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL) |
112 | 0 | return SSH_ERR_ALLOC_FAIL; |
113 | 39 | mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg); |
114 | 41 | } else { |
115 | 41 | mac->mac_len = macalg->len / 8; |
116 | 41 | mac->key_len = macalg->key_len / 8; |
117 | 41 | mac->umac_ctx = NULL; |
118 | 41 | } |
119 | 80 | if (macalg->truncatebits != 0) |
120 | 0 | mac->mac_len = macalg->truncatebits / 8; |
121 | 80 | mac->etm = macalg->etm; |
122 | 80 | return 0; |
123 | 80 | } |
124 | | |
125 | | int |
126 | | mac_setup(struct sshmac *mac, char *name) |
127 | 80 | { |
128 | 80 | const struct macalg *m; |
129 | | |
130 | 703 | for (m = macs; m->name != NULL; m++) { |
131 | 703 | if (strcmp(name, m->name) != 0) |
132 | 623 | continue; |
133 | 80 | if (mac != NULL) |
134 | 80 | return mac_setup_by_alg(mac, m); |
135 | 0 | return 0; |
136 | 80 | } |
137 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
138 | 80 | } |
139 | | |
140 | | int |
141 | | mac_init(struct sshmac *mac) |
142 | 0 | { |
143 | 0 | if (mac->key == NULL) |
144 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
145 | 0 | switch (mac->type) { |
146 | 0 | case SSH_DIGEST: |
147 | 0 | if (mac->hmac_ctx == NULL || |
148 | 0 | ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0) |
149 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
150 | 0 | return 0; |
151 | 0 | case SSH_UMAC: |
152 | 0 | if ((mac->umac_ctx = umac_new(mac->key)) == NULL) |
153 | 0 | return SSH_ERR_ALLOC_FAIL; |
154 | 0 | return 0; |
155 | 0 | case SSH_UMAC128: |
156 | 0 | if ((mac->umac_ctx = umac128_new(mac->key)) == NULL) |
157 | 0 | return SSH_ERR_ALLOC_FAIL; |
158 | 0 | return 0; |
159 | 0 | default: |
160 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | int |
165 | | mac_compute(struct sshmac *mac, u_int32_t seqno, |
166 | | const u_char *data, int datalen, |
167 | | u_char *digest, size_t dlen) |
168 | 0 | { |
169 | 0 | static union { |
170 | 0 | u_char m[SSH_DIGEST_MAX_LENGTH]; |
171 | 0 | u_int64_t for_align; |
172 | 0 | } u; |
173 | 0 | u_char b[4]; |
174 | 0 | u_char nonce[8]; |
175 | |
|
176 | 0 | if (mac->mac_len > sizeof(u)) |
177 | 0 | return SSH_ERR_INTERNAL_ERROR; |
178 | | |
179 | 0 | switch (mac->type) { |
180 | 0 | case SSH_DIGEST: |
181 | 0 | put_u32(b, seqno); |
182 | | /* reset HMAC context */ |
183 | 0 | if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 || |
184 | 0 | ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 || |
185 | 0 | ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 || |
186 | 0 | ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0) |
187 | 0 | return SSH_ERR_LIBCRYPTO_ERROR; |
188 | 0 | break; |
189 | 0 | case SSH_UMAC: |
190 | 0 | POKE_U64(nonce, seqno); |
191 | 0 | umac_update(mac->umac_ctx, data, datalen); |
192 | 0 | umac_final(mac->umac_ctx, u.m, nonce); |
193 | 0 | break; |
194 | 0 | case SSH_UMAC128: |
195 | 0 | put_u64(nonce, seqno); |
196 | 0 | umac128_update(mac->umac_ctx, data, datalen); |
197 | 0 | umac128_final(mac->umac_ctx, u.m, nonce); |
198 | 0 | break; |
199 | 0 | default: |
200 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
201 | 0 | } |
202 | 0 | if (digest != NULL) { |
203 | 0 | if (dlen > mac->mac_len) |
204 | 0 | dlen = mac->mac_len; |
205 | 0 | memcpy(digest, u.m, dlen); |
206 | 0 | } |
207 | 0 | return 0; |
208 | 0 | } |
209 | | |
210 | | int |
211 | | mac_check(struct sshmac *mac, u_int32_t seqno, |
212 | | const u_char *data, size_t dlen, |
213 | | const u_char *theirmac, size_t mlen) |
214 | 0 | { |
215 | 0 | u_char ourmac[SSH_DIGEST_MAX_LENGTH]; |
216 | 0 | int r; |
217 | |
|
218 | 0 | if (mac->mac_len > mlen) |
219 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
220 | 0 | if ((r = mac_compute(mac, seqno, data, dlen, |
221 | 0 | ourmac, sizeof(ourmac))) != 0) |
222 | 0 | return r; |
223 | 0 | if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0) |
224 | 0 | return SSH_ERR_MAC_INVALID; |
225 | 0 | return 0; |
226 | 0 | } |
227 | | |
228 | | void |
229 | | mac_clear(struct sshmac *mac) |
230 | 116 | { |
231 | 116 | if (mac->type == SSH_UMAC) { |
232 | 18 | if (mac->umac_ctx != NULL) |
233 | 0 | umac_delete(mac->umac_ctx); |
234 | 98 | } else if (mac->type == SSH_UMAC128) { |
235 | 23 | if (mac->umac_ctx != NULL) |
236 | 0 | umac128_delete(mac->umac_ctx); |
237 | 75 | } else if (mac->hmac_ctx != NULL) |
238 | 39 | ssh_hmac_free(mac->hmac_ctx); |
239 | 116 | mac->hmac_ctx = NULL; |
240 | 116 | mac->umac_ctx = NULL; |
241 | 116 | } |
242 | | |
243 | | /* XXX copied from ciphers_valid */ |
244 | 0 | #define MAC_SEP "," |
245 | | int |
246 | | mac_valid(const char *names) |
247 | 0 | { |
248 | 0 | char *maclist, *cp, *p; |
249 | |
|
250 | 0 | if (names == NULL || strcmp(names, "") == 0) |
251 | 0 | return 0; |
252 | 0 | if ((maclist = cp = strdup(names)) == NULL) |
253 | 0 | return 0; |
254 | 0 | for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0'; |
255 | 0 | (p = strsep(&cp, MAC_SEP))) { |
256 | 0 | if (mac_setup(NULL, p) < 0) { |
257 | 0 | free(maclist); |
258 | 0 | return 0; |
259 | 0 | } |
260 | 0 | } |
261 | 0 | free(maclist); |
262 | 0 | return 1; |
263 | 0 | } |