blob: de346ed203ea74e53da97097c1cb8906fca38530 [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>
djm@openbsd.orgbe02d7c2019-09-06 04:53:27 +000033#include <stdlib.h>
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000034
Damien Miller4e8d9372014-02-04 11:02:42 +110035#include "digest.h"
36#include "hmac.h"
Damien Millere45796f2007-06-11 14:01:42 +100037#include "umac.h"
markus@openbsd.org128343b2015-01-13 19:31:40 +000038#include "mac.h"
39#include "misc.h"
40#include "ssherr.h"
41#include "sshbuf.h"
Damien Millere45796f2007-06-11 14:01:42 +100042
Darren Tuckere9b3ad72012-01-17 14:03:34 +110043#include "openbsd-compat/openssl-compat.h"
44
Damien Miller4e8d9372014-02-04 11:02:42 +110045#define SSH_DIGEST 1 /* SSH_DIGEST_XXX */
Damien Millere45796f2007-06-11 14:01:42 +100046#define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */
Darren Tucker427e4092012-10-05 11:02:39 +100047#define SSH_UMAC128 3
Damien Millere45796f2007-06-11 14:01:42 +100048
Damien Millerea111192013-04-23 19:24:32 +100049struct macalg {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000050 char *name;
Damien Millere45796f2007-06-11 14:01:42 +100051 int type;
Damien Miller4e8d9372014-02-04 11:02:42 +110052 int alg;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000053 int truncatebits; /* truncate digest if != 0 */
Damien Millere45796f2007-06-11 14:01:42 +100054 int key_len; /* just for UMAC */
55 int len; /* just for UMAC */
Damien Miller37834af2012-12-12 11:00:37 +110056 int etm; /* Encrypt-then-MAC */
Damien Millerea111192013-04-23 19:24:32 +100057};
58
59static const struct macalg macs[] = {
Damien Milleraf43a7a2012-12-12 10:46:31 +110060 /* Encrypt-and-MAC (encrypt-and-authenticate) variants */
Damien Miller4e8d9372014-02-04 11:02:42 +110061 { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
62 { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
Damien Miller4e8d9372014-02-04 11:02:42 +110063 { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 },
64 { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 },
Damien Miller4e8d9372014-02-04 11:02:42 +110065 { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 },
66 { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 },
Damien Miller4e8d9372014-02-04 11:02:42 +110067 { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 },
68 { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 },
Damien Milleraf43a7a2012-12-12 10:46:31 +110069
70 /* Encrypt-then-MAC variants */
Damien Miller4e8d9372014-02-04 11:02:42 +110071 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110073 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110075 { "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 },
Damien Miller4e8d9372014-02-04 11:02:42 +110077 { "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 },
Damien Milleraf43a7a2012-12-12 10:46:31 +110079
Damien Miller4e8d9372014-02-04 11:02:42 +110080 { NULL, 0, 0, 0, 0, 0, 0 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000081};
82
Damien Miller690d9892013-11-08 12:16:49 +110083/* Returns a list of supported MACs separated by the specified char. */
Damien Millerea111192013-04-23 19:24:32 +100084char *
Damien Miller690d9892013-11-08 12:16:49 +110085mac_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +100086{
markus@openbsd.org128343b2015-01-13 19:31:40 +000087 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +100088 size_t nlen, rlen = 0;
89 const struct macalg *m;
90
91 for (m = macs; m->name != NULL; m++) {
92 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +110093 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +100094 nlen = strlen(m->name);
markus@openbsd.org128343b2015-01-13 19:31:40 +000095 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
96 free(ret);
97 return NULL;
98 }
99 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000100 memcpy(ret + rlen, m->name, nlen + 1);
101 rlen += nlen;
102 }
103 return ret;
104}
105
markus@openbsd.org128343b2015-01-13 19:31:40 +0000106static int
107mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg)
Damien Millere45796f2007-06-11 14:01:42 +1000108{
Damien Millerea111192013-04-23 19:24:32 +1000109 mac->type = macalg->type;
Damien Miller4e8d9372014-02-04 11:02:42 +1100110 if (mac->type == SSH_DIGEST) {
111 if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000112 return SSH_ERR_ALLOC_FAIL;
Damien Miller4e8d9372014-02-04 11:02:42 +1100113 mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg);
Damien Millere45796f2007-06-11 14:01:42 +1000114 } else {
Damien Millerea111192013-04-23 19:24:32 +1000115 mac->mac_len = macalg->len / 8;
116 mac->key_len = macalg->key_len / 8;
Damien Millere45796f2007-06-11 14:01:42 +1000117 mac->umac_ctx = NULL;
118 }
Damien Millerea111192013-04-23 19:24:32 +1000119 if (macalg->truncatebits != 0)
120 mac->mac_len = macalg->truncatebits / 8;
121 mac->etm = macalg->etm;
markus@openbsd.org128343b2015-01-13 19:31:40 +0000122 return 0;
Damien Millere45796f2007-06-11 14:01:42 +1000123}
124
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000125int
markus@openbsd.org128343b2015-01-13 19:31:40 +0000126mac_setup(struct sshmac *mac, char *name)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000127{
Damien Millerea111192013-04-23 19:24:32 +1000128 const struct macalg *m;
Damien Millereccb9de2005-06-17 12:59:34 +1000129
Damien Millerea111192013-04-23 19:24:32 +1000130 for (m = macs; m->name != NULL; m++) {
131 if (strcmp(name, m->name) != 0)
132 continue;
markus@openbsd.org128343b2015-01-13 19:31:40 +0000133 if (mac != NULL)
134 return mac_setup_by_alg(mac, m);
135 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000136 }
markus@openbsd.org128343b2015-01-13 19:31:40 +0000137 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000138}
139
Damien Millere45796f2007-06-11 14:01:42 +1000140int
markus@openbsd.org128343b2015-01-13 19:31:40 +0000141mac_init(struct sshmac *mac)
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000142{
143 if (mac->key == NULL)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000144 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000145 switch (mac->type) {
Damien Miller4e8d9372014-02-04 11:02:42 +1100146 case SSH_DIGEST:
147 if (mac->hmac_ctx == NULL ||
148 ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000149 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000150 return 0;
151 case SSH_UMAC:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000152 if ((mac->umac_ctx = umac_new(mac->key)) == NULL)
153 return SSH_ERR_ALLOC_FAIL;
Damien Millere45796f2007-06-11 14:01:42 +1000154 return 0;
Darren Tucker427e4092012-10-05 11:02:39 +1000155 case SSH_UMAC128:
naddy@openbsd.orgddef9992015-01-15 18:32:54 +0000156 if ((mac->umac_ctx = umac128_new(mac->key)) == NULL)
157 return SSH_ERR_ALLOC_FAIL;
Darren Tucker427e4092012-10-05 11:02:39 +1000158 return 0;
Damien Millere45796f2007-06-11 14:01:42 +1000159 default:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000160 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000161 }
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000162}
163
markus@openbsd.org128343b2015-01-13 19:31:40 +0000164int
djm@openbsd.org6d311932016-07-08 03:44:42 +0000165mac_compute(struct sshmac *mac, u_int32_t seqno,
166 const u_char *data, int datalen,
markus@openbsd.org128343b2015-01-13 19:31:40 +0000167 u_char *digest, size_t dlen)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000168{
Darren Tucker4ac66af2013-06-06 08:12:37 +1000169 static union {
markus@openbsd.org128343b2015-01-13 19:31:40 +0000170 u_char m[SSH_DIGEST_MAX_LENGTH];
Darren Tucker4ac66af2013-06-06 08:12:37 +1000171 u_int64_t for_align;
172 } u;
Damien Miller1f0311c2014-05-15 14:24:09 +1000173 u_char b[4];
Damien Miller1f0311c2014-05-15 14:24:09 +1000174 u_char nonce[8];
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000175
Darren Tucker4ac66af2013-06-06 08:12:37 +1000176 if (mac->mac_len > sizeof(u))
markus@openbsd.org128343b2015-01-13 19:31:40 +0000177 return SSH_ERR_INTERNAL_ERROR;
Damien Millere45796f2007-06-11 14:01:42 +1000178
179 switch (mac->type) {
Damien Miller4e8d9372014-02-04 11:02:42 +1100180 case SSH_DIGEST:
Damien Millere45796f2007-06-11 14:01:42 +1000181 put_u32(b, seqno);
182 /* reset HMAC context */
Damien Miller4e8d9372014-02-04 11:02:42 +1100183 if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 ||
184 ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 ||
185 ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 ||
186 ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000187 return SSH_ERR_LIBCRYPTO_ERROR;
Damien Millere45796f2007-06-11 14:01:42 +1000188 break;
189 case SSH_UMAC:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000190 POKE_U64(nonce, seqno);
Damien Millere45796f2007-06-11 14:01:42 +1000191 umac_update(mac->umac_ctx, data, datalen);
Darren Tucker4ac66af2013-06-06 08:12:37 +1000192 umac_final(mac->umac_ctx, u.m, nonce);
Damien Millere45796f2007-06-11 14:01:42 +1000193 break;
Darren Tucker427e4092012-10-05 11:02:39 +1000194 case SSH_UMAC128:
195 put_u64(nonce, seqno);
196 umac128_update(mac->umac_ctx, data, datalen);
Darren Tucker4ac66af2013-06-06 08:12:37 +1000197 umac128_final(mac->umac_ctx, u.m, nonce);
Darren Tucker427e4092012-10-05 11:02:39 +1000198 break;
Damien Millere45796f2007-06-11 14:01:42 +1000199 default:
markus@openbsd.org128343b2015-01-13 19:31:40 +0000200 return SSH_ERR_INVALID_ARGUMENT;
Damien Millere45796f2007-06-11 14:01:42 +1000201 }
markus@openbsd.org128343b2015-01-13 19:31:40 +0000202 if (digest != NULL) {
203 if (dlen > mac->mac_len)
204 dlen = mac->mac_len;
205 memcpy(digest, u.m, dlen);
206 }
207 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000208}
209
djm@openbsd.org6d311932016-07-08 03:44:42 +0000210int
211mac_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{
215 u_char ourmac[SSH_DIGEST_MAX_LENGTH];
216 int r;
217
218 if (mac->mac_len > mlen)
219 return SSH_ERR_INVALID_ARGUMENT;
220 if ((r = mac_compute(mac, seqno, data, dlen,
221 ourmac, sizeof(ourmac))) != 0)
222 return r;
223 if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0)
224 return SSH_ERR_MAC_INVALID;
225 return 0;
226}
227
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000228void
markus@openbsd.org128343b2015-01-13 19:31:40 +0000229mac_clear(struct sshmac *mac)
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000230{
Damien Millere45796f2007-06-11 14:01:42 +1000231 if (mac->type == SSH_UMAC) {
232 if (mac->umac_ctx != NULL)
233 umac_delete(mac->umac_ctx);
Darren Tucker427e4092012-10-05 11:02:39 +1000234 } else if (mac->type == SSH_UMAC128) {
235 if (mac->umac_ctx != NULL)
236 umac128_delete(mac->umac_ctx);
Damien Miller4e8d9372014-02-04 11:02:42 +1100237 } else if (mac->hmac_ctx != NULL)
238 ssh_hmac_free(mac->hmac_ctx);
239 mac->hmac_ctx = NULL;
Damien Millere45796f2007-06-11 14:01:42 +1000240 mac->umac_ctx = NULL;
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000241}
242
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000243/* XXX copied from ciphers_valid */
244#define MAC_SEP ","
245int
246mac_valid(const char *names)
247{
248 char *maclist, *cp, *p;
249
250 if (names == NULL || strcmp(names, "") == 0)
markus@openbsd.org128343b2015-01-13 19:31:40 +0000251 return 0;
252 if ((maclist = cp = strdup(names)) == NULL)
253 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000254 for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
Damien Miller9f0f5c62001-12-21 14:45:46 +1100255 (p = strsep(&cp, MAC_SEP))) {
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000256 if (mac_setup(NULL, p) < 0) {
Darren Tuckera627d422013-06-02 07:31:17 +1000257 free(maclist);
markus@openbsd.org128343b2015-01-13 19:31:40 +0000258 return 0;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000259 }
260 }
Darren Tuckera627d422013-06-02 07:31:17 +1000261 free(maclist);
markus@openbsd.org128343b2015-01-13 19:31:40 +0000262 return 1;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000263}