blob: f3dda66928668cc2df3544cd1b9f8d5915cc88b9 [file] [log] [blame]
djm@openbsd.orgbe02d7c2019-09-06 04:53:27 +00001/* $OpenBSD: mac.c,v 1.35 2019/09/06 04:53:27 djm Exp $ */
Ben Lindstrom06b33aa2001-02-15 03:01:59 +00002/*
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"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000027
Damien Millerd7834352006-08-05 12:39:39 +100028#include <sys/types.h>
29
Darren Tucker2ea60312019-07-23 22:11:50 +100030#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100031#include <string.h>
markus@openbsd.org128343b2015-01-13 19:31:40 +000032#include <stdio.h>
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000033
Damien Miller4e8d9372014-02-04 11:02:42 +110034#include "digest.h"
35#include "hmac.h"
Damien Millere45796f2007-06-11 14:01:42 +100036#include "umac.h"
markus@openbsd.org128343b2015-01-13 19:31:40 +000037#include "mac.h"
38#include "misc.h"
39#include "ssherr.h"
40#include "sshbuf.h"
Damien Millere45796f2007-06-11 14:01:42 +100041
Darren Tuckere9b3ad72012-01-17 14:03:34 +110042#include "openbsd-compat/openssl-compat.h"
43
Damien Miller4e8d9372014-02-04 11:02:42 +110044#define SSH_DIGEST 1 /* SSH_DIGEST_XXX */
Damien Millere45796f2007-06-11 14:01:42 +100045#define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */
Darren Tucker427e4092012-10-05 11:02:39 +100046#define SSH_UMAC128 3
Damien Millere45796f2007-06-11 14:01:42 +100047
Damien Millerea111192013-04-23 19:24:32 +100048struct macalg {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000049 char *name;
Damien Millere45796f2007-06-11 14:01:42 +100050 int type;
Damien Miller4e8d9372014-02-04 11:02:42 +110051 int alg;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000052 int truncatebits; /* truncate digest if != 0 */
Damien Millere45796f2007-06-11 14:01:42 +100053 int key_len; /* just for UMAC */
54 int len; /* just for UMAC */
Damien Miller37834af2012-12-12 11:00:37 +110055 int etm; /* Encrypt-then-MAC */
Damien Millerea111192013-04-23 19:24:32 +100056};
57
58static const struct macalg macs[] = {
Damien Milleraf43a7a2012-12-12 10:46:31 +110059 /* Encrypt-and-MAC (encrypt-and-authenticate) variants */
Damien Miller4e8d9372014-02-04 11:02:42 +110060 { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
61 { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
Damien Miller4e8d9372014-02-04 11:02:42 +110062 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110064 { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 },
65 { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 },
Damien Miller4e8d9372014-02-04 11:02:42 +110066 { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 },
67 { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 },
Damien Milleraf43a7a2012-12-12 10:46:31 +110068
69 /* Encrypt-then-MAC variants */
Damien Miller4e8d9372014-02-04 11:02:42 +110070 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110072 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110074 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110076 { "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 },
Damien Milleraf43a7a2012-12-12 10:46:31 +110078
Damien Miller4e8d9372014-02-04 11:02:42 +110079 { NULL, 0, 0, 0, 0, 0, 0 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000080};
81
Damien Miller690d9892013-11-08 12:16:49 +110082/* Returns a list of supported MACs separated by the specified char. */
Damien Millerea111192013-04-23 19:24:32 +100083char *
Damien Miller690d9892013-11-08 12:16:49 +110084mac_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +100085{
markus@openbsd.org128343b2015-01-13 19:31:40 +000086 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +100087 size_t nlen, rlen = 0;
88 const struct macalg *m;
89
90 for (m = macs; m->name != NULL; m++) {
91 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +110092 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +100093 nlen = strlen(m->name);
markus@openbsd.org128343b2015-01-13 19:31:40 +000094 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
95 free(ret);
96 return NULL;
97 }
98 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +100099 memcpy(ret + rlen, m->name, nlen + 1);
100 rlen += nlen;
101 }
102 return ret;
103}
104
markus@openbsd.org128343b2015-01-13 19:31:40 +0000105static int
106mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg)
Damien Millere45796f2007-06-11 14:01:42 +1000107{
Damien Millerea111192013-04-23 19:24:32 +1000108 mac->type = macalg->type;
Damien Miller4e8d9372014-02-04 11:02:42 +1100109 if (mac->type == SSH_DIGEST) {
110 if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000111 return SSH_ERR_ALLOC_FAIL;
Damien Miller4e8d9372014-02-04 11:02:42 +1100112 mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg);
Damien Millere45796f2007-06-11 14:01:42 +1000113 } else {
Damien Millerea111192013-04-23 19:24:32 +1000114 mac->mac_len = macalg->len / 8;
115 mac->key_len = macalg->key_len / 8;
Damien Millere45796f2007-06-11 14:01:42 +1000116 mac->umac_ctx = NULL;
117 }
Damien Millerea111192013-04-23 19:24:32 +1000118 if (macalg->truncatebits != 0)
119 mac->mac_len = macalg->truncatebits / 8;
120 mac->etm = macalg->etm;
markus@openbsd.org128343b2015-01-13 19:31:40 +0000121 return 0;
Damien Millere45796f2007-06-11 14:01:42 +1000122}
123
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000124int
markus@openbsd.org128343b2015-01-13 19:31:40 +0000125mac_setup(struct sshmac *mac, char *name)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000126{
Damien Millerea111192013-04-23 19:24:32 +1000127 const struct macalg *m;
Damien Millereccb9de2005-06-17 12:59:34 +1000128
Damien Millerea111192013-04-23 19:24:32 +1000129 for (m = macs; m->name != NULL; m++) {
130 if (strcmp(name, m->name) != 0)
131 continue;
markus@openbsd.org128343b2015-01-13 19:31:40 +0000132 if (mac != NULL)
133 return mac_setup_by_alg(mac, m);
134 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000135 }
markus@openbsd.org128343b2015-01-13 19:31:40 +0000136 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000137}
138
Damien Millere45796f2007-06-11 14:01:42 +1000139int
markus@openbsd.org128343b2015-01-13 19:31:40 +0000140mac_init(struct sshmac *mac)
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000141{
142 if (mac->key == NULL)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000143 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000144 switch (mac->type) {
Damien Miller4e8d9372014-02-04 11:02:42 +1100145 case SSH_DIGEST:
146 if (mac->hmac_ctx == NULL ||
147 ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000148 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000149 return 0;
150 case SSH_UMAC:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000151 if ((mac->umac_ctx = umac_new(mac->key)) == NULL)
152 return SSH_ERR_ALLOC_FAIL;
Damien Millere45796f2007-06-11 14:01:42 +1000153 return 0;
Darren Tucker427e4092012-10-05 11:02:39 +1000154 case SSH_UMAC128:
naddy@openbsd.orgddef9992015-01-15 18:32:54 +0000155 if ((mac->umac_ctx = umac128_new(mac->key)) == NULL)
156 return SSH_ERR_ALLOC_FAIL;
Darren Tucker427e4092012-10-05 11:02:39 +1000157 return 0;
Damien Millere45796f2007-06-11 14:01:42 +1000158 default:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000159 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000160 }
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000161}
162
markus@openbsd.org128343b2015-01-13 19:31:40 +0000163int
djm@openbsd.org6d311932016-07-08 03:44:42 +0000164mac_compute(struct sshmac *mac, u_int32_t seqno,
165 const u_char *data, int datalen,
markus@openbsd.org128343b2015-01-13 19:31:40 +0000166 u_char *digest, size_t dlen)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000167{
Darren Tucker4ac66af2013-06-06 08:12:37 +1000168 static union {
markus@openbsd.org128343b2015-01-13 19:31:40 +0000169 u_char m[SSH_DIGEST_MAX_LENGTH];
Darren Tucker4ac66af2013-06-06 08:12:37 +1000170 u_int64_t for_align;
171 } u;
Damien Miller1f0311c2014-05-15 14:24:09 +1000172 u_char b[4];
Damien Miller1f0311c2014-05-15 14:24:09 +1000173 u_char nonce[8];
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000174
Darren Tucker4ac66af2013-06-06 08:12:37 +1000175 if (mac->mac_len > sizeof(u))
markus@openbsd.org128343b2015-01-13 19:31:40 +0000176 return SSH_ERR_INTERNAL_ERROR;
Damien Millere45796f2007-06-11 14:01:42 +1000177
178 switch (mac->type) {
Damien Miller4e8d9372014-02-04 11:02:42 +1100179 case SSH_DIGEST:
Damien Millere45796f2007-06-11 14:01:42 +1000180 put_u32(b, seqno);
181 /* reset HMAC context */
Damien Miller4e8d9372014-02-04 11:02:42 +1100182 if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 ||
183 ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 ||
184 ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 ||
185 ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000186 return SSH_ERR_LIBCRYPTO_ERROR;
Damien Millere45796f2007-06-11 14:01:42 +1000187 break;
188 case SSH_UMAC:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000189 POKE_U64(nonce, seqno);
Damien Millere45796f2007-06-11 14:01:42 +1000190 umac_update(mac->umac_ctx, data, datalen);
Darren Tucker4ac66af2013-06-06 08:12:37 +1000191 umac_final(mac->umac_ctx, u.m, nonce);
Damien Millere45796f2007-06-11 14:01:42 +1000192 break;
Darren Tucker427e4092012-10-05 11:02:39 +1000193 case SSH_UMAC128:
194 put_u64(nonce, seqno);
195 umac128_update(mac->umac_ctx, data, datalen);
Darren Tucker4ac66af2013-06-06 08:12:37 +1000196 umac128_final(mac->umac_ctx, u.m, nonce);
Darren Tucker427e4092012-10-05 11:02:39 +1000197 break;
Damien Millere45796f2007-06-11 14:01:42 +1000198 default:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000199 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000200 }
markus@openbsd.org128343b2015-01-13 19:31:40 +0000201 if (digest != NULL) {
202 if (dlen > mac->mac_len)
203 dlen = mac->mac_len;
204 memcpy(digest, u.m, dlen);
205 }
206 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000207}
208
djm@openbsd.org6d311932016-07-08 03:44:42 +0000209int
210mac_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{
214 u_char ourmac[SSH_DIGEST_MAX_LENGTH];
215 int r;
216
217 if (mac->mac_len > mlen)
218 return SSH_ERR_INVALID_ARGUMENT;
219 if ((r = mac_compute(mac, seqno, data, dlen,
220 ourmac, sizeof(ourmac))) != 0)
221 return r;
222 if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0)
223 return SSH_ERR_MAC_INVALID;
224 return 0;
225}
226
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000227void
markus@openbsd.org128343b2015-01-13 19:31:40 +0000228mac_clear(struct sshmac *mac)
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000229{
Damien Millere45796f2007-06-11 14:01:42 +1000230 if (mac->type == SSH_UMAC) {
231 if (mac->umac_ctx != NULL)
232 umac_delete(mac->umac_ctx);
Darren Tucker427e4092012-10-05 11:02:39 +1000233 } else if (mac->type == SSH_UMAC128) {
234 if (mac->umac_ctx != NULL)
235 umac128_delete(mac->umac_ctx);
Damien Miller4e8d9372014-02-04 11:02:42 +1100236 } else if (mac->hmac_ctx != NULL)
237 ssh_hmac_free(mac->hmac_ctx);
238 mac->hmac_ctx = NULL;
Damien Millere45796f2007-06-11 14:01:42 +1000239 mac->umac_ctx = NULL;
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000240}
241
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000242/* XXX copied from ciphers_valid */
243#define MAC_SEP ","
244int
245mac_valid(const char *names)
246{
247 char *maclist, *cp, *p;
248
249 if (names == NULL || strcmp(names, "") == 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000250 return 0;
251 if ((maclist = cp = strdup(names)) == NULL)
252 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000253 for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
Damien Miller9f0f5c62001-12-21 14:45:46 +1100254 (p = strsep(&cp, MAC_SEP))) {
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000255 if (mac_setup(NULL, p) < 0) {
Darren Tuckera627d422013-06-02 07:31:17 +1000256 free(maclist);
markus@openbsd.org128343b2015-01-13 19:31:40 +0000257 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000258 }
259 }
Darren Tuckera627d422013-06-02 07:31:17 +1000260 free(maclist);
markus@openbsd.org128343b2015-01-13 19:31:40 +0000261 return 1;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000262}