- pvalchev@cvs.openbsd.org 2007/06/07 19:37:34
     [kex.h mac.c mac.h monitor_wrap.c myproposal.h packet.c ssh.1]
     [ssh_config.5 sshd.8 sshd_config.5]
     Add a new MAC algorithm for data integrity, UMAC-64 (not default yet,
     must specify umac-64@openssh.com). Provides about 20% end-to-end speedup
     compared to hmac-md5. Represents a different approach to message
     authentication to that of HMAC that may be beneficial if HMAC based on
     one of its underlying hash algorithms is found to be vulnerable to a
     new attack.  http://www.ietf.org/rfc/rfc4418.txt
     in conjunction with and OK djm@
diff --git a/mac.c b/mac.c
index 6a5fd47..3446465 100644
--- a/mac.c
+++ b/mac.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mac.c,v 1.13 2007/06/05 06:52:37 djm Exp $ */
+/* $OpenBSD: mac.c,v 1.14 2007/06/07 19:37:34 pvalchev Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  *
@@ -42,35 +42,57 @@
 #include "mac.h"
 #include "misc.h"
 
+#include "umac.h"
+
+#define SSH_EVP		1	/* OpenSSL EVP-based MAC */
+#define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
+
 struct {
 	char		*name;
+	int		type;
 	const EVP_MD *	(*mdfunc)(void);
 	int		truncatebits;	/* truncate digest if != 0 */
+	int		key_len;	/* just for UMAC */
+	int		len;		/* just for UMAC */
 } macs[] = {
-	{ "hmac-sha1",			EVP_sha1, 0, },
-	{ "hmac-sha1-96",		EVP_sha1, 96 },
-	{ "hmac-md5",			EVP_md5, 0 },
-	{ "hmac-md5-96",		EVP_md5, 96 },
-	{ "hmac-ripemd160",		EVP_ripemd160, 0 },
-	{ "hmac-ripemd160@openssh.com",	EVP_ripemd160, 0 },
-	{ NULL,				NULL, 0 }
+	{ "hmac-sha1",			SSH_EVP, EVP_sha1, 0, -1, -1 },
+	{ "hmac-sha1-96",		SSH_EVP, EVP_sha1, 96, -1, -1 },
+	{ "hmac-md5",			SSH_EVP, EVP_md5, 0, -1, -1 },
+	{ "hmac-md5-96",		SSH_EVP, EVP_md5, 96, -1, -1 },
+	{ "hmac-ripemd160",		SSH_EVP, EVP_ripemd160, 0, -1, -1 },
+	{ "hmac-ripemd160@openssh.com",	SSH_EVP, EVP_ripemd160, 0, -1, -1 },
+	{ "umac-64@openssh.com",	SSH_UMAC, NULL, 0, 128, 64 },
+	{ NULL,				0, NULL, 0, -1, -1 }
 };
 
+static void
+mac_setup_by_id(Mac *mac, int which)
+{
+	int evp_len;
+	mac->type = macs[which].type;
+	if (mac->type == SSH_EVP) {
+		mac->evp_md = (*macs[which].mdfunc)();
+		if ((evp_len = EVP_MD_size(mac->evp_md)) <= 0)
+			fatal("mac %s len %d", mac->name, evp_len);
+		mac->key_len = mac->mac_len = (u_int)evp_len;
+	} else {
+		mac->mac_len = macs[which].len / 8;
+		mac->key_len = macs[which].key_len / 8;
+		mac->umac_ctx = NULL;
+	}
+	if (macs[which].truncatebits != 0)
+		mac->mac_len = macs[which].truncatebits / 8;
+}
+
 int
 mac_setup(Mac *mac, char *name)
 {
-	int i, evp_len;
+	int i;
 
 	for (i = 0; macs[i].name; i++) {
 		if (strcmp(name, macs[i].name) == 0) {
-			if (mac != NULL) {
-				mac->md = (*macs[i].mdfunc)();
-				if ((evp_len = EVP_MD_size(mac->md)) <= 0)
-					fatal("mac %s len %d", name, evp_len);
-				mac->key_len = mac->mac_len = (u_int)evp_len;
-				if (macs[i].truncatebits != 0)
-					mac->mac_len = macs[i].truncatebits/8;
-			}
+			if (mac != NULL)
+				mac_setup_by_id(mac, i);
 			debug2("mac_setup: found %s", name);
 			return (0);
 		}
@@ -79,34 +101,65 @@
 	return (-1);
 }
 
-void
+int
 mac_init(Mac *mac)
 {
 	if (mac->key == NULL)
 		fatal("mac_init: no key");
-	HMAC_Init(&mac->ctx, mac->key, mac->key_len, mac->md);
+	switch (mac->type) {
+	case SSH_EVP:
+		if (mac->evp_md == NULL)
+			return -1;
+		HMAC_Init(&mac->evp_ctx, mac->key, mac->key_len, mac->evp_md);
+		return 0;
+	case SSH_UMAC:
+		mac->umac_ctx = umac_new(mac->key);
+		return 0;
+	default:
+		return -1;
+	}
 }
 
 u_char *
 mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen)
 {
 	static u_char m[EVP_MAX_MD_SIZE];
-	u_char b[4];
+	u_char b[4], nonce[8];
 
 	if (mac->mac_len > sizeof(m))
-		fatal("mac_compute: mac too long");
-	put_u32(b, seqno);
-	HMAC_Init(&mac->ctx, NULL, 0, NULL);	/* reset HMAC context */
-	HMAC_Update(&mac->ctx, b, sizeof(b));
-	HMAC_Update(&mac->ctx, data, datalen);
-	HMAC_Final(&mac->ctx, m, NULL);
+		fatal("mac_compute: mac too long %u %lu",
+		    mac->mac_len, sizeof(m));
+
+	switch (mac->type) {
+	case SSH_EVP:
+		put_u32(b, seqno);
+		/* reset HMAC context */
+		HMAC_Init(&mac->evp_ctx, NULL, 0, NULL);
+		HMAC_Update(&mac->evp_ctx, b, sizeof(b));
+		HMAC_Update(&mac->evp_ctx, data, datalen);
+		HMAC_Final(&mac->evp_ctx, m, NULL);
+		break;
+	case SSH_UMAC:
+		put_u64(nonce, seqno);
+		umac_update(mac->umac_ctx, data, datalen);
+		umac_final(mac->umac_ctx, m, nonce);
+		break;
+	default:
+		fatal("mac_compute: unknown MAC type");
+	}
 	return (m);
 }
 
 void
 mac_clear(Mac *mac)
 {
-	HMAC_cleanup(&mac->ctx);
+	if (mac->type == SSH_UMAC) {
+		if (mac->umac_ctx != NULL)
+			umac_delete(mac->umac_ctx);
+	} else if (mac->evp_md != NULL)
+		HMAC_cleanup(&mac->evp_ctx);
+	mac->evp_md = NULL;
+	mac->umac_ctx = NULL;
 }
 
 /* XXX copied from ciphers_valid */