1255767Sdes/* $OpenBSD: mac.c,v 1.24 2013/06/03 00:03:18 dtucker Exp $ */
276259Sgreen/*
376259Sgreen * Copyright (c) 2001 Markus Friedl.  All rights reserved.
476259Sgreen *
576259Sgreen * Redistribution and use in source and binary forms, with or without
676259Sgreen * modification, are permitted provided that the following conditions
776259Sgreen * are met:
876259Sgreen * 1. Redistributions of source code must retain the above copyright
976259Sgreen *    notice, this list of conditions and the following disclaimer.
1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1176259Sgreen *    notice, this list of conditions and the following disclaimer in the
1276259Sgreen *    documentation and/or other materials provided with the distribution.
1376259Sgreen *
1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2476259Sgreen */
2576259Sgreen
2676259Sgreen#include "includes.h"
2776259Sgreen
28162852Sdes#include <sys/types.h>
29162852Sdes
3076259Sgreen#include <openssl/hmac.h>
3176259Sgreen
32162852Sdes#include <stdarg.h>
33162852Sdes#include <string.h>
34162852Sdes#include <signal.h>
35162852Sdes
3676259Sgreen#include "xmalloc.h"
3776259Sgreen#include "log.h"
3876259Sgreen#include "cipher.h"
39162852Sdes#include "buffer.h"
40162852Sdes#include "key.h"
4176259Sgreen#include "kex.h"
4276259Sgreen#include "mac.h"
43162852Sdes#include "misc.h"
4476259Sgreen
45181111Sdes#include "umac.h"
46181111Sdes
47240075Sdes#include "openbsd-compat/openssl-compat.h"
48240075Sdes
49181111Sdes#define SSH_EVP		1	/* OpenSSL EVP-based MAC */
50181111Sdes#define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
51248619Sdes#define SSH_UMAC128	3
52181111Sdes
53255767Sdesstruct macalg {
5476259Sgreen	char		*name;
55181111Sdes	int		type;
5698675Sdes	const EVP_MD *	(*mdfunc)(void);
5776259Sgreen	int		truncatebits;	/* truncate digest if != 0 */
58181111Sdes	int		key_len;	/* just for UMAC */
59181111Sdes	int		len;		/* just for UMAC */
60248619Sdes	int		etm;		/* Encrypt-then-MAC */
61255767Sdes};
62255767Sdes
63255767Sdesstatic const struct macalg macs[] = {
64248619Sdes	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
65248619Sdes	{ "hmac-sha1",				SSH_EVP, EVP_sha1, 0, 0, 0, 0 },
66248619Sdes	{ "hmac-sha1-96",			SSH_EVP, EVP_sha1, 96, 0, 0, 0 },
67226046Sdes#ifdef HAVE_EVP_SHA256
68248619Sdes	{ "hmac-sha2-256",			SSH_EVP, EVP_sha256, 0, 0, 0, 0 },
69248619Sdes	{ "hmac-sha2-512",			SSH_EVP, EVP_sha512, 0, 0, 0, 0 },
70226046Sdes#endif
71248619Sdes	{ "hmac-md5",				SSH_EVP, EVP_md5, 0, 0, 0, 0 },
72248619Sdes	{ "hmac-md5-96",			SSH_EVP, EVP_md5, 96, 0, 0, 0 },
73248619Sdes	{ "hmac-ripemd160",			SSH_EVP, EVP_ripemd160, 0, 0, 0, 0 },
74248619Sdes	{ "hmac-ripemd160@openssh.com",		SSH_EVP, EVP_ripemd160, 0, 0, 0, 0 },
75248619Sdes	{ "umac-64@openssh.com",		SSH_UMAC, NULL, 0, 128, 64, 0 },
76248619Sdes	{ "umac-128@openssh.com",		SSH_UMAC128, NULL, 0, 128, 128, 0 },
77248619Sdes
78248619Sdes	/* Encrypt-then-MAC variants */
79248619Sdes	{ "hmac-sha1-etm@openssh.com",		SSH_EVP, EVP_sha1, 0, 0, 0, 1 },
80248619Sdes	{ "hmac-sha1-96-etm@openssh.com",	SSH_EVP, EVP_sha1, 96, 0, 0, 1 },
81248619Sdes#ifdef HAVE_EVP_SHA256
82248619Sdes	{ "hmac-sha2-256-etm@openssh.com",	SSH_EVP, EVP_sha256, 0, 0, 0, 1 },
83248619Sdes	{ "hmac-sha2-512-etm@openssh.com",	SSH_EVP, EVP_sha512, 0, 0, 0, 1 },
84248619Sdes#endif
85248619Sdes	{ "hmac-md5-etm@openssh.com",		SSH_EVP, EVP_md5, 0, 0, 0, 1 },
86248619Sdes	{ "hmac-md5-96-etm@openssh.com",	SSH_EVP, EVP_md5, 96, 0, 0, 1 },
87248619Sdes	{ "hmac-ripemd160-etm@openssh.com",	SSH_EVP, EVP_ripemd160, 0, 0, 0, 1 },
88248619Sdes	{ "umac-64-etm@openssh.com",		SSH_UMAC, NULL, 0, 128, 64, 1 },
89248619Sdes	{ "umac-128-etm@openssh.com",		SSH_UMAC128, NULL, 0, 128, 128, 1 },
90248619Sdes
91248619Sdes	{ NULL,					0, NULL, 0, 0, 0, 0 }
9276259Sgreen};
9376259Sgreen
94255767Sdes/* Returns a comma-separated list of supported MACs. */
95255767Sdeschar *
96255767Sdesmac_alg_list(void)
97255767Sdes{
98255767Sdes	char *ret = NULL;
99255767Sdes	size_t nlen, rlen = 0;
100255767Sdes	const struct macalg *m;
101255767Sdes
102255767Sdes	for (m = macs; m->name != NULL; m++) {
103255767Sdes		if (ret != NULL)
104255767Sdes			ret[rlen++] = '\n';
105255767Sdes		nlen = strlen(m->name);
106255767Sdes		ret = xrealloc(ret, 1, rlen + nlen + 2);
107255767Sdes		memcpy(ret + rlen, m->name, nlen + 1);
108255767Sdes		rlen += nlen;
109255767Sdes	}
110255767Sdes	return ret;
111255767Sdes}
112255767Sdes
113181111Sdesstatic void
114255767Sdesmac_setup_by_alg(Mac *mac, const struct macalg *macalg)
115181111Sdes{
116181111Sdes	int evp_len;
117255767Sdes
118255767Sdes	mac->type = macalg->type;
119181111Sdes	if (mac->type == SSH_EVP) {
120255767Sdes		mac->evp_md = macalg->mdfunc();
121181111Sdes		if ((evp_len = EVP_MD_size(mac->evp_md)) <= 0)
122181111Sdes			fatal("mac %s len %d", mac->name, evp_len);
123181111Sdes		mac->key_len = mac->mac_len = (u_int)evp_len;
124181111Sdes	} else {
125255767Sdes		mac->mac_len = macalg->len / 8;
126255767Sdes		mac->key_len = macalg->key_len / 8;
127181111Sdes		mac->umac_ctx = NULL;
128181111Sdes	}
129255767Sdes	if (macalg->truncatebits != 0)
130255767Sdes		mac->mac_len = macalg->truncatebits / 8;
131255767Sdes	mac->etm = macalg->etm;
132181111Sdes}
133181111Sdes
13476259Sgreenint
135181111Sdesmac_setup(Mac *mac, char *name)
13676259Sgreen{
137255767Sdes	const struct macalg *m;
138149749Sdes
139255767Sdes	for (m = macs; m->name != NULL; m++) {
140255767Sdes		if (strcmp(name, m->name) != 0)
141255767Sdes			continue;
142255767Sdes		if (mac != NULL)
143255767Sdes			mac_setup_by_alg(mac, m);
144255767Sdes		debug2("mac_setup: found %s", name);
145255767Sdes		return (0);
14676259Sgreen	}
147181111Sdes	debug2("mac_setup: unknown %s", name);
14876259Sgreen	return (-1);
14976259Sgreen}
15076259Sgreen
151181111Sdesint
152181111Sdesmac_init(Mac *mac)
153181111Sdes{
154181111Sdes	if (mac->key == NULL)
155181111Sdes		fatal("mac_init: no key");
156181111Sdes	switch (mac->type) {
157181111Sdes	case SSH_EVP:
158181111Sdes		if (mac->evp_md == NULL)
159181111Sdes			return -1;
160240075Sdes		HMAC_CTX_init(&mac->evp_ctx);
161181111Sdes		HMAC_Init(&mac->evp_ctx, mac->key, mac->key_len, mac->evp_md);
162181111Sdes		return 0;
163181111Sdes	case SSH_UMAC:
164181111Sdes		mac->umac_ctx = umac_new(mac->key);
165181111Sdes		return 0;
166248619Sdes	case SSH_UMAC128:
167248619Sdes		mac->umac_ctx = umac128_new(mac->key);
168248619Sdes		return 0;
169181111Sdes	default:
170181111Sdes		return -1;
171181111Sdes	}
172181111Sdes}
173181111Sdes
17476259Sgreenu_char *
17576259Sgreenmac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen)
17676259Sgreen{
177255767Sdes	static union {
178255767Sdes		u_char m[EVP_MAX_MD_SIZE];
179255767Sdes		u_int64_t for_align;
180255767Sdes	} u;
181181111Sdes	u_char b[4], nonce[8];
18276259Sgreen
183255767Sdes	if (mac->mac_len > sizeof(u))
184181111Sdes		fatal("mac_compute: mac too long %u %lu",
185255767Sdes		    mac->mac_len, (u_long)sizeof(u));
186181111Sdes
187181111Sdes	switch (mac->type) {
188181111Sdes	case SSH_EVP:
189181111Sdes		put_u32(b, seqno);
190181111Sdes		/* reset HMAC context */
191181111Sdes		HMAC_Init(&mac->evp_ctx, NULL, 0, NULL);
192181111Sdes		HMAC_Update(&mac->evp_ctx, b, sizeof(b));
193181111Sdes		HMAC_Update(&mac->evp_ctx, data, datalen);
194255767Sdes		HMAC_Final(&mac->evp_ctx, u.m, NULL);
195181111Sdes		break;
196181111Sdes	case SSH_UMAC:
197181111Sdes		put_u64(nonce, seqno);
198181111Sdes		umac_update(mac->umac_ctx, data, datalen);
199255767Sdes		umac_final(mac->umac_ctx, u.m, nonce);
200181111Sdes		break;
201248619Sdes	case SSH_UMAC128:
202248619Sdes		put_u64(nonce, seqno);
203248619Sdes		umac128_update(mac->umac_ctx, data, datalen);
204255767Sdes		umac128_final(mac->umac_ctx, u.m, nonce);
205248619Sdes		break;
206181111Sdes	default:
207181111Sdes		fatal("mac_compute: unknown MAC type");
208181111Sdes	}
209255767Sdes	return (u.m);
21076259Sgreen}
21176259Sgreen
212181111Sdesvoid
213181111Sdesmac_clear(Mac *mac)
214181111Sdes{
215181111Sdes	if (mac->type == SSH_UMAC) {
216181111Sdes		if (mac->umac_ctx != NULL)
217181111Sdes			umac_delete(mac->umac_ctx);
218248619Sdes	} else if (mac->type == SSH_UMAC128) {
219248619Sdes		if (mac->umac_ctx != NULL)
220248619Sdes			umac128_delete(mac->umac_ctx);
221181111Sdes	} else if (mac->evp_md != NULL)
222181111Sdes		HMAC_cleanup(&mac->evp_ctx);
223181111Sdes	mac->evp_md = NULL;
224181111Sdes	mac->umac_ctx = NULL;
225181111Sdes}
226181111Sdes
22776259Sgreen/* XXX copied from ciphers_valid */
22876259Sgreen#define	MAC_SEP	","
22976259Sgreenint
23076259Sgreenmac_valid(const char *names)
23176259Sgreen{
23276259Sgreen	char *maclist, *cp, *p;
23376259Sgreen
23476259Sgreen	if (names == NULL || strcmp(names, "") == 0)
23576259Sgreen		return (0);
23676259Sgreen	maclist = cp = xstrdup(names);
23776259Sgreen	for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
23892555Sdes	    (p = strsep(&cp, MAC_SEP))) {
239181111Sdes		if (mac_setup(NULL, p) < 0) {
24076259Sgreen			debug("bad mac %s [%s]", p, names);
241255767Sdes			free(maclist);
24276259Sgreen			return (0);
24376259Sgreen		} else {
24476259Sgreen			debug3("mac ok: %s [%s]", p, names);
24576259Sgreen		}
24676259Sgreen	}
24776259Sgreen	debug3("macs ok: [%s]", names);
248255767Sdes	free(maclist);
24976259Sgreen	return (1);
25076259Sgreen}
251