From 5c47af9a7a0bdf9d37a99333816d8fc041aad9d3 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 25 Sep 2011 17:24:46 +0300 Subject: [PATCH] TLS: Add support for TLS v1.1 (RFC 4346) with internal TLS This is disabled by defautl and can be enabled with CONFIG_TLSV11=y build configuration parameter. --- hostapd/Makefile | 4 ++++ hostapd/defconfig | 36 +++++++++++++++++++++++++++++ src/tls/tlsv1_client.c | 30 ++++++++++++++++-------- src/tls/tlsv1_client_read.c | 16 +++++++++---- src/tls/tlsv1_client_write.c | 5 ++-- src/tls/tlsv1_common.h | 12 +++++++--- src/tls/tlsv1_record.c | 45 +++++++++++++++++++++++++++++++----- src/tls/tlsv1_record.h | 6 +++-- src/tls/tlsv1_server.c | 2 +- src/tls/tlsv1_server_read.c | 15 ++++++++++-- src/tls/tlsv1_server_write.c | 7 +++--- wpa_supplicant/Makefile | 4 ++++ wpa_supplicant/defconfig | 7 ++++++ 13 files changed, 157 insertions(+), 32 deletions(-) diff --git a/hostapd/Makefile b/hostapd/Makefile index d2b85af85..047961e86 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -434,6 +434,10 @@ ifndef CONFIG_TLS CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV11 +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_openssl.o diff --git a/hostapd/defconfig b/hostapd/defconfig index 26be2a83b..d9b4b6d13 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -208,3 +208,39 @@ CONFIG_IPV6=y # considered for builds that are known to be used on devices that meet the # requirements described above. #CONFIG_NO_RANDOM_POOL=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA) +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# Whether to enable TLS/IA support, which is required for EAP-TTLSv1. +# You need CONFIG_TLS=gnutls for this to have any effect. Please note that +# even though the core GnuTLS library is released under LGPL, this extra +# library uses GPL and as such, the terms of GPL apply to the combination +# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not +# apply for distribution of the resulting binary. +#CONFIG_GNUTLS_EXTRA=y + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. +#CONFIG_TLSV11=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index d87ea4f81..75b8612b6 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,5 +1,5 @@ /* - * TLSv1 client (RFC 2246) + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify @@ -80,8 +80,9 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); - key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + - conn->rl.iv_size); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { @@ -107,12 +108,21 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; - /* client_write_IV */ - os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; - /* server_write_IV */ - os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; + if (conn->rl.tls_version == TLS_VERSION_1) { + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } return 0; } @@ -358,6 +368,8 @@ struct tlsv1_client * tlsv1_client_init(void) suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; + conn->rl.tls_version = TLS_VERSION; + return conn; } diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index faa891aaf..457c3b00f 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* - * TLSv1 client - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - read handshake message + * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -38,6 +38,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; + u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " @@ -79,15 +80,22 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; - if (WPA_GET_BE16(pos) != TLS_VERSION) { + tls_version = WPA_GET_BE16(pos); + if (tls_version != TLS_VERSION_1 && + (tls_version != TLS_VERSION_1_1 || + TLS_VERSION == TLS_VERSION_1)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ServerHello"); + "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + conn->rl.tls_version = tls_version; + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 91ac5af4a..9d53dd137 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,5 +1,5 @@ /* - * TLSv1 client - write handshake message + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - write handshake message * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify @@ -774,7 +774,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 763a4af3d..712d2764b 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -1,6 +1,6 @@ /* - * TLSv1 common definitions - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) common definitions + * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,7 +17,13 @@ #include "crypto/crypto.h" -#define TLS_VERSION 0x0301 /* TLSv1 */ +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c index 8efdae67d..332f8d4b2 100644 --- a/src/tls/tlsv1_record.c +++ b/src/tls/tlsv1_record.c @@ -1,5 +1,5 @@ /* - * TLSv1 Record Protocol + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify @@ -139,7 +139,7 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) * @buf: Buffer for the generated TLS message (needs to have extra space for - * header and HMAC) + * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size * @payload: Payload to be sent * @payload_len: Length of the payload @@ -156,6 +156,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; + int explicit_iv; pos = buf; if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) @@ -165,7 +166,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; @@ -173,6 +174,22 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos += 2; cpayload = pos; + explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && + rl->iv_size && rl->tls_version == TLS_VERSION_1_1; + if (explicit_iv) { + /* opaque IV[Cipherspec.block_length] */ + if (pos + rl->iv_size > buf + buf_size) + return -1; + + /* + * Use random number R per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + + if (os_get_random(pos, rl->iv_size)) + return -1; + pos += rl->iv_size; + } /* * opaque fragment[TLSPlaintext.length] @@ -343,6 +360,9 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, return -1; } plen = in_len; + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data", out_data, plen); + if (rl->iv_size) { /* * TLS v1.0 defines different alert values for various @@ -355,6 +375,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, * attacks more difficult. */ + if (rl->tls_version == TLS_VERSION_1_1) { + /* Remove opaque IV[Cipherspec.block_length] */ + if (plen < rl->iv_size) { + wpa_printf(MSG_DEBUG, "TLSv1.1: Not " + "enough room for IV"); + force_mac_error = 1; + goto check_mac; + } + os_memmove(out_data, out_data + rl->iv_size, + plen - rl->iv_size); + plen -= rl->iv_size; + } + /* Verify and remove padding */ if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" @@ -387,9 +420,9 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, } check_mac: - wpa_hexdump(MSG_MSGDUMP, - "TLSv1: Record Layer - Decrypted data", - out_data, plen); + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data with IV and padding removed", + out_data, plen); if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h index acbdccaee..0d6281622 100644 --- a/src/tls/tlsv1_record.h +++ b/src/tls/tlsv1_record.h @@ -1,6 +1,6 @@ /* - * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -35,6 +35,8 @@ enum { }; struct tlsv1_record_layer { + u16 tls_version; + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 write_key[TLS_MAX_WRITE_KEY_LEN]; diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 06eab61ca..1f48aa5f4 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,5 +1,5 @@ /* - * TLSv1 server (RFC 2246) + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index fd7443656..5b7ccc39b 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -85,15 +85,26 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_version = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", conn->client_version >> 8, conn->client_version & 0xff); - if (conn->client_version < TLS_VERSION) { + if (conn->client_version < TLS_VERSION_1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello"); + "ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + if (TLS_VERSION == TLS_VERSION_1) + conn->rl.tls_version = TLS_VERSION_1; + else if (conn->client_version > TLS_VERSION_1_1) + conn->rl.tls_version = TLS_VERSION_1_1; + else + conn->rl.tls_version = conn->client_version; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + conn->rl.tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index a3d32b0ab..63d70a2df 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -1,5 +1,5 @@ /* - * TLSv1 server - write handshake message + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server - write handshake message * Copyright (c) 2006-2011, Jouni Malinen * * This program is free software; you can redistribute it and/or modify @@ -87,7 +87,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += 3; /* body - ServerHello */ /* ProtocolVersion server_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); @@ -764,7 +764,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index de4d6adf3..41fd1fd39 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -806,6 +806,10 @@ ifndef CONFIG_TLS CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV11 +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index dfdc2d30c..e1ede9ffe 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -335,6 +335,13 @@ CONFIG_PEERKEY=y # apply for distribution of the resulting binary. #CONFIG_GNUTLS_EXTRA=y +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. It should be noted that some existing TLS v1.0 -based +# implementation may not be compatible with TLS v1.1 message (ClientHello is +# sent prior to negotiating which version will be used) +#CONFIG_TLSV11=y + # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of # LibTomMath can be used. See beginning of libtommath.c for details on benefits