svn commit: r248327 - user/andre/tcp-ao/sys/netinet

Andre Oppermann andre at FreeBSD.org
Fri Mar 15 13:40:00 UTC 2013


Author: andre
Date: Fri Mar 15 13:40:00 2013
New Revision: 248327
URL: http://svnweb.freebsd.org/changeset/base/248327

Log:
  First skeleton of the TCP-AO traffic and master key derivation functions.
  
   tcp_ao_kdf()       Setup of traffic key derivation for IPv4 and IPv6
   tcp_ao_kdf_hmac()  KDF_HMAC_SHA1, RFC5926, Section 3.1.1.1
   tcp_ao_kdf_cmac()  KDF_AES_128_CMAC, RFC5926, Section 3.1.1.2
   tpc_ao_kdf_md5()   legacy compatibility to TCP-MD5 (has no key-derivation)
  
  The first function is rather incomplete as I first have to analyze and decide
  on the key management and storage approach (ipsec keysock vs. setsockopt).
  
  Sponsored by:	Juniper Networks

Modified:
  user/andre/tcp-ao/sys/netinet/tcp_ao.c

Modified: user/andre/tcp-ao/sys/netinet/tcp_ao.c
==============================================================================
--- user/andre/tcp-ao/sys/netinet/tcp_ao.c	Fri Mar 15 13:10:06 2013	(r248326)
+++ user/andre/tcp-ao/sys/netinet/tcp_ao.c	Fri Mar 15 13:40:00 2013	(r248327)
@@ -55,6 +55,157 @@
  */
 
 /*
+ * There two types of key derivation in TCP-AO.
+ * One is to create the session key from the imported master key.
+ * It involves individual session parameters like the ip addresses,
+ * port numbers and inititial sequence numbers.
+ * The other is in additional step for certain MAC algorithms when
+ * the user supplied key is not exactly the required key MAC size.
+ * Here we have to run the key through a special round of key
+ * derivation first to get the desired key length.
+ */
+
+/*
+ * Context for key derivation.
+ */
+union tcp_ao_kdf_ctx {
+	struct ip4 {
+		struct in_addr src, dst;
+		uint16_t sport, dport;
+		uint32_t irs, iss;
+	} ip4_ctx;
+	struct ip6 {
+		struct in6_addr src, dst;
+		uint16_t sport, dport;
+		uint32_t irs, iss;
+	} ip6_ctx;
+	int len;
+};
+
+/*
+ * Key derivation for sessions and the derived master keys.
+ * Return values:
+ *  0      = success
+ *  EINVAL = invalid input, typically insufficient length
+ *  other  = key derivation failed
+ */
+static int
+tcp_ao_kdf(struct in_conninfo *inc, struct tcphdr *th, uint8_t *out,
+    int outlen)
+{
+	int error = 0;
+	struct tcp_ao_kdf_ctx tak;
+
+	/* Fill in context for traffic keys. */
+	switch (inc->inc_flags & INC_ISIPV6) {
+	case 0:
+		tak.ip4_ctx.src = inc->ie_faddr;
+		tak.ip4_ctx.dst = inc->ie_laddr;
+		tak.ip4_ctx.irs = htonl(th->th_ack);
+		tak.ip4_ctx.iss = htonl(th->th_seq);
+		tak.len = sizeof(tak.ip4_ctx);
+		break;
+	case INC_ISIPV6:
+		tak.ip6_ctx.src = inc->ie6_faddr;
+		tak.ip6_ctx.dst = inc->ie6_laddr;
+		tak.ip6_ctx.irs = htonl(th->th_ack);
+		tak.ip6_ctx.iss = htonl(th->th_seq);
+		tak.len = sizeof(tak.ip6_ctx);
+		break;
+	default:
+		error = EINVAL;
+		goto out;
+	}
+
+	switch (kdf) {
+	case TCP_AO_HMAC_SHA_1_96:
+		error = tcp_ao_kdf_hmac(key, keylen, out, outlen, &tak);
+		break;
+	case TCP_AO_AES_128_CMAC_96:
+		error = tcp_ao_kdf_cmac(key, keylen, out, outlen, &tak);
+		break;
+	case TCP_AO_TCPMD5:
+		error = tcp_ao_kdf_cmac(key, keylen, out, outlen, &tak);
+		break;
+	default:
+		error = EINVAL;
+	}
+	if (error)
+		goto out;
+
+	return (error);
+}
+
+static int
+tcp_ao_kdf_hmac(uint8_t *key, int keylen , uint8_t *out, int outlen,
+    struct tcp_ao_kdf_ctx *tak)
+{
+	HMAC_SHA_CTX ctx;
+	uint8_t res[SHA1_DIGEST_LENGTH];
+	char *label = "TCP-AO";
+	int error = 0;
+	uint8_t i;
+
+	for (i = 0; outlen > 0; outlen -= SHA1_DIGEST_LENGTH, i++) {
+		HMAC_SHA1_Init(&ctx, key, keylen);
+
+		HMAC_SHA1_Update(&ctx, &i, sizeof(i));
+		HMAC_SHA1_Update(&ctx, label, sizeof(*label));
+		if (tak != NULL)
+			HMAC_SHA1_Update(&ctx, tak, tak->len);
+		HMAC_SHA1_Final(res, &ctx);
+
+		bcopy(res, out, min(outlen, SHA1_DIGEST_LENGTH));
+		out += SHA1_DIGEST_LENGTH;
+	}
+	return (error);
+}
+
+static int
+tcp_ao_kdf_cmac(uint8_t *key, int keylen, uint8_t *out, int outlen,
+    struct tcp_ao_kdf_ctx *tak)
+{
+	AES_CMAC_CTX ctx;
+	uint8_t res[AES_CMAC_DIGEST_LENGTH];
+	uint8_t zero[AES_CMAC_KEY_LENGTH];
+	int error = 0;
+
+	if (tak == NULL) {
+		bzero(zero, sizeof(*zero));
+		AES_CMAC_Init(&ctx);
+		AES_CMAC_SetKey(&ctx, zero);
+		AES_CMAC_Update(&ctx, key, keylen);
+		AES_CMAC_Final(res, &ctx);
+		bcopy(res, out, min(outlen, AES_CMAC_DIGEST_LENGTH));
+	}
+
+	if (keylen != AES_CMAC_KEY_LENGTH)
+		return (EINVAL);
+
+	for (i = 0; outlen > 0; outlen -= AES_CMAC_DIGEST_LENGTH, i++) {
+		AES_CMAC_Init(&ctx);
+		AES_CMAC_SetKey(&ctx, key);
+		AES_CMAC_Update(&ctx, &i, sizeof(i));
+		AES_CMAC_Update(&ctx, label, sizeof(*label));
+		AES_CMAC_Update(&ctx, tak, tak->len);
+		AES_CMAC_Final(res, &ctx);
+
+		bcopy(res, out, min(outlen, AES_CMAC_DIGEST_LENGTH));
+		out += AES_CMAC_DIGEST_LENGTH;
+	}
+	return (error);
+}
+
+static int
+tcp_ao_kfd_md5(uint8_t *key, int keylen, uint8_t *out, int outlen,
+    struct tcp_ao_kdf_ctx *tak)
+{
+	/* XXX: No key derivation happens. */
+	return (0);
+}
+
+
+/*
  * The hash over the header has to be pieced together and a couple of
  * fields need manipulation, like zero'ing and byte order conversion.
  * Instead of doing it in-place and calling the hash update function


More information about the svn-src-user mailing list