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 | 28.7k | #define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ |
45 | 28.5k | #define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ |
46 | 106k | #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 | | { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 }, |
67 | | { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 }, |
68 | | |
69 | | /* Encrypt-then-MAC variants */ |
70 | | { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, |
71 | | { "hmac-sha1-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 }, |
72 | | { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, |
73 | | { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, |
74 | | { "hmac-md5-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 1 }, |
75 | | { "hmac-md5-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 }, |
76 | | { "umac-64-etm@openssh.com", SSH_UMAC, 0, 0, 128, 64, 1 }, |
77 | | { "umac-128-etm@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 1 }, |
78 | | |
79 | | { NULL, 0, 0, 0, 0, 0, 0 } |
80 | | }; |
81 | | |
82 | | /* Returns a list of supported MACs separated by the specified char. */ |
83 | | char * |
84 | | mac_alg_list(char sep) |
85 | 0 | { |
86 | 0 | char *ret = NULL, *tmp; |
87 | 0 | size_t nlen, rlen = 0; |
88 | 0 | const struct macalg *m; |
89 | |
|
90 | 0 | for (m = macs; m->name != NULL; m++) { |
91 | 0 | if (ret != NULL) |
92 | 0 | ret[rlen++] = sep; |
93 | 0 | nlen = strlen(m->name); |
94 | 0 | if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { |
95 | 0 | free(ret); |
96 | 0 | return NULL; |
97 | 0 | } |
98 | 0 | ret = tmp; |
99 | 0 | memcpy(ret + rlen, m->name, nlen + 1); |
100 | 0 | rlen += nlen; |
101 | 0 | } |
102 | 0 | return ret; |
103 | 0 | } |
104 | | |
105 | | static int |
106 | | mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg) |
107 | 24.2k | { |
108 | 24.2k | mac->type = macalg->type; |
109 | 24.2k | if (mac->type == SSH_DIGEST) { |
110 | 3.97k | if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL) |
111 | 0 | return SSH_ERR_ALLOC_FAIL; |
112 | 3.97k | mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg); |
113 | 20.2k | } else { |
114 | 20.2k | mac->mac_len = macalg->len / 8; |
115 | 20.2k | mac->key_len = macalg->key_len / 8; |
116 | 20.2k | mac->umac_ctx = NULL; |
117 | 20.2k | } |
118 | 24.2k | if (macalg->truncatebits != 0) |
119 | 0 | mac->mac_len = macalg->truncatebits / 8; |
120 | 24.2k | mac->etm = macalg->etm; |
121 | 24.2k | return 0; |
122 | 24.2k | } |
123 | | |
124 | | int |
125 | | mac_setup(struct sshmac *mac, char *name) |
126 | 24.2k | { |
127 | 24.2k | const struct macalg *m; |
128 | | |
129 | 314k | for (m = macs; m->name != NULL; m++) { |
130 | 314k | if (strcmp(name, m->name) != 0) |
131 | 290k | continue; |
132 | 24.2k | if (mac != NULL) |
133 | 24.2k | return mac_setup_by_alg(mac, m); |
134 | 0 | return 0; |
135 | 24.2k | } |
136 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
137 | 24.2k | } |
138 | | |
139 | | int |
140 | | mac_init(struct sshmac *mac) |
141 | 925 | { |
142 | 925 | if (mac->key == NULL) |
143 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
144 | 925 | switch (mac->type) { |
145 | 168 | case SSH_DIGEST: |
146 | 168 | if (mac->hmac_ctx == NULL || |
147 | 168 | ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0) |
148 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
149 | 168 | return 0; |
150 | 321 | case SSH_UMAC: |
151 | 321 | if ((mac->umac_ctx = umac_new(mac->key)) == NULL) |
152 | 0 | return SSH_ERR_ALLOC_FAIL; |
153 | 321 | return 0; |
154 | 436 | case SSH_UMAC128: |
155 | 436 | if ((mac->umac_ctx = umac128_new(mac->key)) == NULL) |
156 | 0 | return SSH_ERR_ALLOC_FAIL; |
157 | 436 | return 0; |
158 | 0 | default: |
159 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
160 | 925 | } |
161 | 925 | } |
162 | | |
163 | | int |
164 | | mac_compute(struct sshmac *mac, u_int32_t seqno, |
165 | | const u_char *data, int datalen, |
166 | | u_char *digest, size_t dlen) |
167 | 108k | { |
168 | 108k | static union { |
169 | 108k | u_char m[SSH_DIGEST_MAX_LENGTH]; |
170 | 108k | u_int64_t for_align; |
171 | 108k | } u; |
172 | 108k | u_char b[4]; |
173 | 108k | u_char nonce[8]; |
174 | | |
175 | 108k | if (mac->mac_len > sizeof(u)) |
176 | 0 | return SSH_ERR_INTERNAL_ERROR; |
177 | | |
178 | 108k | switch (mac->type) { |
179 | 4.34k | case SSH_DIGEST: |
180 | 4.34k | put_u32(b, seqno); |
181 | | /* reset HMAC context */ |
182 | 4.34k | if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 || |
183 | 4.34k | ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 || |
184 | 4.34k | ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 || |
185 | 4.34k | ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0) |
186 | 0 | return SSH_ERR_LIBCRYPTO_ERROR; |
187 | 4.34k | break; |
188 | 4.34k | case SSH_UMAC: |
189 | 3.99k | POKE_U64(nonce, seqno); |
190 | 3.99k | umac_update(mac->umac_ctx, data, datalen); |
191 | 3.99k | umac_final(mac->umac_ctx, u.m, nonce); |
192 | 3.99k | break; |
193 | 100k | case SSH_UMAC128: |
194 | 100k | put_u64(nonce, seqno); |
195 | 100k | umac128_update(mac->umac_ctx, data, datalen); |
196 | 100k | umac128_final(mac->umac_ctx, u.m, nonce); |
197 | 100k | break; |
198 | 0 | default: |
199 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
200 | 108k | } |
201 | 108k | if (digest != NULL) { |
202 | 108k | if (dlen > mac->mac_len) |
203 | 107k | dlen = mac->mac_len; |
204 | 108k | memcpy(digest, u.m, dlen); |
205 | 108k | } |
206 | 108k | return 0; |
207 | 108k | } |
208 | | |
209 | | int |
210 | | mac_check(struct sshmac *mac, u_int32_t seqno, |
211 | | const u_char *data, size_t dlen, |
212 | | const u_char *theirmac, size_t mlen) |
213 | 202 | { |
214 | 202 | u_char ourmac[SSH_DIGEST_MAX_LENGTH]; |
215 | 202 | int r; |
216 | | |
217 | 202 | if (mac->mac_len > mlen) |
218 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
219 | 202 | if ((r = mac_compute(mac, seqno, data, dlen, |
220 | 202 | ourmac, sizeof(ourmac))) != 0) |
221 | 0 | return r; |
222 | 202 | if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0) |
223 | 202 | return SSH_ERR_MAC_INVALID; |
224 | 0 | return 0; |
225 | 202 | } |
226 | | |
227 | | void |
228 | | mac_clear(struct sshmac *mac) |
229 | 24.2k | { |
230 | 24.2k | if (mac->type == SSH_UMAC) { |
231 | 18.1k | if (mac->umac_ctx != NULL) |
232 | 321 | umac_delete(mac->umac_ctx); |
233 | 18.1k | } else if (mac->type == SSH_UMAC128) { |
234 | 2.07k | if (mac->umac_ctx != NULL) |
235 | 436 | umac128_delete(mac->umac_ctx); |
236 | 4.00k | } else if (mac->hmac_ctx != NULL) |
237 | 3.97k | ssh_hmac_free(mac->hmac_ctx); |
238 | 24.2k | mac->hmac_ctx = NULL; |
239 | 24.2k | mac->umac_ctx = NULL; |
240 | 24.2k | } |
241 | | |
242 | | /* XXX copied from ciphers_valid */ |
243 | 0 | #define MAC_SEP "," |
244 | | int |
245 | | mac_valid(const char *names) |
246 | 0 | { |
247 | 0 | char *maclist, *cp, *p; |
248 | |
|
249 | 0 | if (names == NULL || strcmp(names, "") == 0) |
250 | 0 | return 0; |
251 | 0 | if ((maclist = cp = strdup(names)) == NULL) |
252 | 0 | return 0; |
253 | 0 | for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0'; |
254 | 0 | (p = strsep(&cp, MAC_SEP))) { |
255 | 0 | if (mac_setup(NULL, p) < 0) { |
256 | 0 | free(maclist); |
257 | 0 | return 0; |
258 | 0 | } |
259 | 0 | } |
260 | 0 | free(maclist); |
261 | 0 | return 1; |
262 | 0 | } |