aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
authorDmitry Shmidt <dimitrysh@google.com>2012-01-24 16:10:04 -0800
committerDmitry Shmidt <dimitrysh@google.com>2012-01-24 16:44:49 -0800
commit1f69aa52ea2e0a73ac502565df8c666ee49cab6a (patch)
tree8ea94735f75f461769454853da0c24cbb89cc4cc /src/tls
parentbf5edf439c90418b6f4122ff5e3925123263bda4 (diff)
downloadexternal_wpa_supplicant_8-1f69aa52ea2e0a73ac502565df8c666ee49cab6a.zip
external_wpa_supplicant_8-1f69aa52ea2e0a73ac502565df8c666ee49cab6a.tar.gz
external_wpa_supplicant_8-1f69aa52ea2e0a73ac502565df8c666ee49cab6a.tar.bz2
Update to new version 0.8.16 from BRCM
Sync with main tree commit b8349523e460493fa0b4de36c689595109e45e91 Author: Neeraj Kumar Garg <neerajkg@broadcom.com> Date: Tue Dec 27 23:21:45 2011 +0200 P2P: Reject p2p_group_add if forced frequency is not acceptable Change-Id: Icb4541a371b05c270e80440d7a7fdea7f33ff61e Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/Makefile2
-rw-r--r--src/tls/libtommath.c14
-rw-r--r--src/tls/pkcs5.c2
-rw-r--r--src/tls/tlsv1_client.c235
-rw-r--r--src/tls/tlsv1_client.h10
-rw-r--r--src/tls/tlsv1_client_i.h2
-rw-r--r--src/tls/tlsv1_client_read.c38
-rw-r--r--src/tls/tlsv1_client_write.c144
-rw-r--r--src/tls/tlsv1_common.c91
-rw-r--r--src/tls/tlsv1_common.h57
-rw-r--r--src/tls/tlsv1_cred.c25
-rw-r--r--src/tls/tlsv1_record.c220
-rw-r--r--src/tls/tlsv1_record.h9
-rw-r--r--src/tls/tlsv1_server.c88
-rw-r--r--src/tls/tlsv1_server.h4
-rw-r--r--src/tls/tlsv1_server_read.c145
-rw-r--r--src/tls/tlsv1_server_write.c108
17 files changed, 927 insertions, 267 deletions
diff --git a/src/tls/Makefile b/src/tls/Makefile
index a2da096..27cdfca 100644
--- a/src/tls/Makefile
+++ b/src/tls/Makefile
@@ -11,6 +11,8 @@ include ../lib.rules
CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+CFLAGS += -DCONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV12
LIB_OBJS= \
asn1.o \
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 1374264..7c9857f 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -572,7 +572,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
/* this is a shell function that calls either the normal or Montgomery
* exptmod functions. Originally the call to the montgomery code was
- * embedded in the normal function but that wasted alot of stack space
+ * embedded in the normal function but that wasted a lot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
@@ -2207,7 +2207,7 @@ static int mp_2expt (mp_int * a, int b)
/* zero a as per default */
mp_zero (a);
- /* grow a to accomodate the single bit */
+ /* grow a to accommodate the single bit */
if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
return res;
}
@@ -2319,7 +2319,7 @@ CLEANUP:
}
-/* multiplies |a| * |b| and only computes upto digs digits of result
+/* multiplies |a| * |b| and only computes up to digs digits of result
* HAC pp. 595, Algorithm 14.12 Modified so you can control how
* many digits of output are created.
*/
@@ -2678,7 +2678,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho)
*
* Based on Algorithm 14.32 on pp.601 of HAC.
*/
-int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
{
int ix, res, olduse;
mp_word W[MP_WARRAY];
@@ -2829,7 +2829,7 @@ static int mp_mul_2(mp_int * a, mp_int * b)
{
int x, res, oldused;
- /* grow to accomodate result */
+ /* grow to accommodate result */
if (b->alloc < a->used + 1) {
if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
return res;
@@ -2891,8 +2891,8 @@ static int mp_mul_2(mp_int * a, mp_int * b)
/*
* shifts with subtractions when the result is greater than b.
*
- * The method is slightly modified to shift B unconditionally upto just under
- * the leading bit of b. This saves alot of multiple precision shifting.
+ * The method is slightly modified to shift B unconditionally up to just under
+ * the leading bit of b. This saves a lot of multiple precision shifting.
*/
static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
{
diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c
index 4291b84..fd9e346 100644
--- a/src/tls/pkcs5.c
+++ b/src/tls/pkcs5.c
@@ -32,7 +32,7 @@ struct pkcs5_params {
};
-enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
{
if (oid->len == 7 &&
oid->oid[0] == 1 /* iso */ &&
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 8b7e26f..d0da588 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,5 +1,5 @@
/*
- * TLSv1 client (RFC 2246)
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@@ -67,7 +67,8 @@ int tls_derive_keys(struct tlsv1_client *conn,
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
- if (tls_prf(pre_master_secret, pre_master_secret_len,
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
@@ -80,9 +81,11 @@ 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);
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ 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->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
@@ -107,12 +110,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;
}
@@ -126,17 +138,23 @@ int tls_derive_keys(struct tlsv1_client *conn,
* @out_len: Length of the output buffer.
* @appl_data: Pointer to application data pointer, or %NULL if dropped
* @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
* Returns: Pointer to output data, %NULL on failure
*/
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
size_t *out_len, u8 **appl_data,
- size_t *appl_data_len)
+ size_t *appl_data_len, int *need_more_data)
{
const u8 *pos, *end;
- u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+ u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int no_appl_data;
+ int used;
+
+ if (need_more_data)
+ *need_more_data = 0;
if (conn->state == CLIENT_HELLO) {
if (in_len)
@@ -144,6 +162,19 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
return tls_send_client_hello(conn, out_len);
}
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
+
if (in_data == NULL || in_len == 0)
return NULL;
@@ -156,13 +187,33 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
- if (tlsv1_record_receive(&conn->rl, pos, end - pos,
- in_msg, &in_msg_len, &alert)) {
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ os_free(in_msg);
+ if (need_more_data)
+ *need_more_data = 1;
+ return NULL;
+ }
ct = pos[0];
in_pos = in_msg;
@@ -180,7 +231,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
in_pos += in_msg_len;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
os_free(in_msg);
@@ -192,6 +243,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
failed:
os_free(in_msg);
if (conn->alert_level) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
conn->state = FAILED;
os_free(msg);
msg = tlsv1_client_send_alert(conn, conn->alert_level,
@@ -202,6 +255,11 @@ failed:
*out_len = 0;
}
+ if (need_more_data == NULL || !(*need_more_data)) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ }
+
return msg;
}
@@ -227,10 +285,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -246,58 +302,116 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @in_data: Pointer to input buffer (encrypted TLS data)
* @in_len: Input buffer length
- * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
- * @out_len: Maximum out_data length
- * Returns: Number of bytes written to out_data, -1 on failure
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
+ * Returns: Decrypted data or %NULL on failure
*
* This function is used after TLS handshake has been completed successfully to
* receive data from the encrypted tunnel.
*/
-int tlsv1_client_decrypt(struct tlsv1_client *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len)
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data)
{
const u8 *in_end, *pos;
- int res;
- u8 alert, *out_end, *out_pos;
+ int used;
+ u8 alert, *out_pos, ct;
size_t olen;
+ struct wpabuf *buf = NULL;
+
+ if (need_more_data)
+ *need_more_data = 0;
+
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
pos = in_data;
in_end = in_data + in_len;
- out_pos = out_data;
- out_end = out_data + out_len;
while (pos < in_end) {
- if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
- "0x%x", pos[0]);
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
- TLS_ALERT_UNEXPECTED_MESSAGE);
- return -1;
+ ct = pos[0];
+ if (wpabuf_resize(&buf, in_end - pos) < 0) {
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
}
-
- olen = out_end - out_pos;
- res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
- out_pos, &olen, &alert);
- if (res < 0) {
+ out_pos = wpabuf_put(buf, 0);
+ olen = wpabuf_tailroom(buf);
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
- return -1;
+ goto fail;
}
- out_pos += olen;
- if (out_pos > out_end) {
- wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
- "for processing the received record");
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
- TLS_ALERT_INTERNAL_ERROR);
- return -1;
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, in_end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ if (need_more_data)
+ *need_more_data = 1;
+ return buf;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ alert = TLS_ALERT_DECODE_ERROR;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ alert = out_pos[1];
+ goto fail;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x when decrypting application data",
+ pos[0]);
+ alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+ goto fail;
+ }
+
+ wpabuf_put(buf, olen);
+
+ pos += used;
}
- return out_pos - out_data;
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ return buf;
+
+fail:
+ wpabuf_free(buf);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return NULL;
}
@@ -352,14 +466,18 @@ struct tlsv1_client * tlsv1_client_init(void)
count = 0;
suites = conn->cipher_suites;
#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
conn->num_cipher_suites = count;
+ conn->rl.tls_version = TLS_VERSION;
+
return conn;
}
@@ -378,6 +496,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn)
os_free(conn->client_hello_ext);
tlsv1_client_free_dh(conn);
tlsv1_cred_free(conn->cred);
+ wpabuf_free(conn->partial_input);
os_free(conn);
}
@@ -421,7 +540,8 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
@@ -453,15 +573,24 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
cipher = "DES-CBC3-SHA";
break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ cipher = "ADH-AES-128-SHA256";
+ break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
cipher = "ADH-AES-128-SHA";
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
cipher = "AES-256-SHA";
break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ cipher = "AES-256-SHA256";
+ break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
cipher = "AES-128-SHA";
break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ cipher = "AES-128-SHA256";
+ break;
default:
return -1;
}
@@ -613,8 +742,10 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
count = 0;
suites = conn->cipher_suites;
#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index a620d62..ef5e694 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -1,5 +1,5 @@
/*
- * TLSv1 client (RFC 2246)
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@@ -29,13 +29,13 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
size_t *out_len, u8 **appl_data,
- size_t *appl_data_len);
+ size_t *appl_data_len, int *need_more_data);
int tlsv1_client_encrypt(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t out_len);
-int tlsv1_client_decrypt(struct tlsv1_client *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len);
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data);
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
size_t buflen);
int tlsv1_client_shutdown(struct tlsv1_client *conn);
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
index f091bcf..92912ca 100644
--- a/src/tls/tlsv1_client_i.h
+++ b/src/tls/tlsv1_client_i.h
@@ -68,6 +68,8 @@ struct tlsv1_client {
tlsv1_client_session_ticket_cb session_ticket_cb;
void *session_ticket_cb_ctx;
+
+ struct wpabuf *partial_input;
};
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index faa891a..eb0cbef 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 <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,6 +17,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -38,6 +39,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 +81,20 @@ 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_ok(tls_version)) {
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_str(tls_version));
+ conn->rl.tls_version = tls_version;
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
@@ -816,6 +823,21 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
pos, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_server == NULL ||
+ crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+ < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_server = NULL;
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_server == NULL ||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
@@ -837,9 +859,15 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
return -1;
}
conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
verify_data, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index 0898df9..35a238b 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -1,6 +1,6 @@
/*
* TLSv1 client - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,6 +17,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "crypto/random.h"
#include "x509v3.h"
@@ -116,7 +117,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, out_len) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ out_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -192,7 +194,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -413,7 +416,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -433,7 +437,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
{
u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
size_t rlen, hlen, clen;
- u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+ u8 hash[100], *hpos;
enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
pos = *msgpos;
@@ -473,6 +477,40 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
hpos = hash;
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ os_memmove(hash + 19, hash, hlen);
+ hlen += 19;
+ os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+ "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+ } else {
+#endif /* CONFIG_TLSV12 */
+
if (alg == SIGN_ALG_RSA) {
hlen = MD5_MAC_LEN;
if (conn->verify.md5_cert == NULL ||
@@ -503,8 +541,29 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
if (alg == SIGN_ALG_RSA)
hlen += MD5_MAC_LEN;
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ *pos++ = TLS_HASH_ALG_SHA256;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ }
+#endif /* CONFIG_TLSV12 */
+
/*
* RFC 2246, 4.7:
* In digital signing, one-way hash functions are used as input for a
@@ -534,7 +593,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -553,17 +613,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -578,7 +637,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
@@ -587,17 +646,30 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
static int tls_write_client_finished(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
- pos = *msgpos;
-
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
/* Encrypted Handshake Message: Finished */
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_client == NULL ||
+ crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_client == NULL ||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
@@ -619,43 +691,44 @@ static int tls_write_client_finished(struct tlsv1_client *conn,
return -1;
}
conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
- pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -778,7 +851,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.c b/src/tls/tlsv1_common.c
index 2f9dd0f..871359a 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -1,6 +1,6 @@
/*
* TLSv1 common routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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
@@ -15,6 +15,8 @@
#include "includes.h"
#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -50,7 +52,15 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
TLS_HASH_SHA },
{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
- TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
};
#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
@@ -202,6 +212,19 @@ int tls_verify_hash_init(struct tls_verify_hash *verify)
tls_verify_hash_free(verify);
return -1;
}
+#ifdef CONFIG_TLSV12
+ verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ if (verify->sha256_client == NULL || verify->sha256_server == NULL ||
+ verify->sha256_cert == NULL) {
+ tls_verify_hash_free(verify);
+ return -1;
+ }
+#endif /* CONFIG_TLSV12 */
return 0;
}
@@ -221,6 +244,14 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
crypto_hash_update(verify->md5_cert, buf, len);
crypto_hash_update(verify->sha1_cert, buf, len);
}
+#ifdef CONFIG_TLSV12
+ if (verify->sha256_client)
+ crypto_hash_update(verify->sha256_client, buf, len);
+ if (verify->sha256_server)
+ crypto_hash_update(verify->sha256_server, buf, len);
+ if (verify->sha256_cert)
+ crypto_hash_update(verify->sha256_cert, buf, len);
+#endif /* CONFIG_TLSV12 */
}
@@ -238,4 +269,60 @@ void tls_verify_hash_free(struct tls_verify_hash *verify)
verify->sha1_client = NULL;
verify->sha1_server = NULL;
verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+ crypto_hash_finish(verify->sha256_client, NULL, NULL);
+ crypto_hash_finish(verify->sha256_server, NULL, NULL);
+ crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+ verify->sha256_client = NULL;
+ verify->sha256_server = NULL;
+ verify->sha256_cert = NULL;
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+ if (ver == TLS_VERSION_1)
+ return 1;
+#ifdef CONFIG_TLSV11
+ if (ver == TLS_VERSION_1_1)
+ return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+ if (ver == TLS_VERSION_1_2)
+ return 1;
+#endif /* CONFIG_TLSV12 */
+
+ return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+ switch (ver) {
+ case TLS_VERSION_1:
+ return "1.0";
+ case TLS_VERSION_1_1:
+ return "1.1";
+ case TLS_VERSION_1_2:
+ return "1.2";
+ }
+
+ return "?";
+}
+
+
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+#ifdef CONFIG_TLSV12
+ if (ver >= TLS_VERSION_1_2) {
+ tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+ out, outlen);
+ return 0;
+ }
+#endif /* CONFIG_TLSV12 */
+
+ return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
+ outlen);
}
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index 763a4af..027daa4 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 <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,18 @@
#include "crypto/crypto.h"
-#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */
+#ifdef CONFIG_TLSV12
+#define TLS_VERSION TLS_VERSION_1_2
+#else /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
+#endif /* CONFIG_TLSV12 */
#define TLS_RANDOM_LEN 32
#define TLS_PRE_MASTER_SECRET_LEN 48
#define TLS_MASTER_SECRET_LEN 48
@@ -82,10 +93,42 @@ enum {
#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */
#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */
#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */
+#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */
/* CompressionMethod */
#define TLS_COMPRESSION_NULL 0
+/* HashAlgorithm */
+enum {
+ TLS_HASH_ALG_NONE = 0,
+ TLS_HASH_ALG_MD5 = 1,
+ TLS_HASH_ALG_SHA1 = 2,
+ TLS_HASH_ALG_SHA224 = 3,
+ TLS_HASH_ALG_SHA256 = 4,
+ TLS_HASH_ALG_SHA384 = 5,
+ TLS_HASH_ALG_SHA512 = 6
+};
+
+/* SignatureAlgorithm */
+enum {
+ TLS_SIGN_ALG_ANONYMOUS = 0,
+ TLS_SIGN_ALG_RSA = 1,
+ TLS_SIGN_ALG_DSA = 2,
+ TLS_SIGN_ALG_ECDSA = 3,
+};
+
/* AlertLevel */
#define TLS_ALERT_LEVEL_WARNING 1
#define TLS_ALERT_LEVEL_FATAL 2
@@ -169,7 +212,8 @@ typedef enum {
typedef enum {
TLS_HASH_NULL,
TLS_HASH_MD5,
- TLS_HASH_SHA
+ TLS_HASH_SHA,
+ TLS_HASH_SHA256
} tls_hash;
struct tls_cipher_suite {
@@ -197,10 +241,13 @@ struct tls_cipher_data {
struct tls_verify_hash {
struct crypto_hash *md5_client;
struct crypto_hash *sha1_client;
+ struct crypto_hash *sha256_client;
struct crypto_hash *md5_server;
struct crypto_hash *sha1_server;
+ struct crypto_hash *sha256_server;
struct crypto_hash *md5_cert;
struct crypto_hash *sha1_cert;
+ struct crypto_hash *sha256_cert;
};
@@ -212,5 +259,9 @@ int tls_verify_hash_init(struct tls_verify_hash *verify);
void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
size_t len);
void tls_verify_hash_free(struct tls_verify_hash *verify);
+int tls_version_ok(u16 ver);
+const char * tls_version_str(u16 ver);
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
#endif /* TLSV1_COMMON_H */
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index aa467ef..d846480 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -46,7 +46,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred)
static int tlsv1_add_cert_der(struct x509_certificate **chain,
const u8 *buf, size_t len)
{
- struct x509_certificate *cert;
+ struct x509_certificate *cert, *p;
char name[128];
cert = x509_certificate_parse(buf, len);
@@ -56,8 +56,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain,
return -1;
}
- cert->next = *chain;
- *chain = cert;
+ p = *chain;
+ while (p && p->next)
+ p = p->next;
+ if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+ /*
+ * The new certificate is the issuer of the last certificate in
+ * the chain - add the new certificate to the end.
+ */
+ p->next = cert;
+ } else {
+ /* Add to the beginning of the chain */
+ cert->next = *chain;
+ *chain = cert;
+ }
x509_name_string(&cert->subject, name, sizeof(name));
wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
@@ -232,10 +244,17 @@ static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
if (!end)
return NULL;
} else {
+ const u8 *pos2;
pos += os_strlen(pem_key_begin);
end = search_tag(pem_key_end, pos, key + len - pos);
if (!end)
return NULL;
+ pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+ if (pos2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+ "format (Proc-Type/DEK-Info)");
+ return NULL;
+ }
}
der = base64_decode(pos, end - pos, &der_len);
diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c
index e811f0e..0314551 100644
--- a/src/tls/tlsv1_record.c
+++ b/src/tls/tlsv1_record.c
@@ -1,6 +1,6 @@
/*
* TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,6 +17,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
@@ -52,6 +53,9 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
} else if (suite->hash == TLS_HASH_SHA) {
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
rl->hash_size = SHA1_MAC_LEN;
+ } else if (suite->hash == TLS_HASH_SHA256) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+ rl->hash_size = SHA256_MAC_LEN;
}
data = tls_get_cipher_data(suite->cipher);
@@ -138,10 +142,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
* tlsv1_record_send - TLS record layer: Send a message
* @rl: Pointer to TLS record layer data
* @content_type: Content type (TLS_CONTENT_TYPE_*)
- * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
- * beginning for record layer to fill in; payload filled in after this and
- * extra space in the end for HMAC).
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
* @buf_size: Maximum buf size
+ * @payload: Payload to be sent
* @payload_len: Length of the payload
* @out_len: Buffer for returning the used buf length
* Returns: 0 on success, -1 on failure
@@ -150,29 +154,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
* the data using the current write cipher.
*/
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len)
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len)
{
- u8 *pos, *ct_start, *length, *payload;
+ 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)
+ return -1;
+
/* ContentType type */
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;
WPA_PUT_BE16(length, payload_len);
pos += 2;
- /* opaque fragment[TLSPlaintext.length] */
- payload = pos;
+ 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]
+ * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+ */
+ if (pos + payload_len > buf + buf_size)
+ return -1;
+ os_memmove(pos, payload, payload_len);
pos += payload_len;
if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ /*
+ * MAC calculated over seq_num + TLSCompressed.type +
+ * TLSCompressed.version + TLSCompressed.length +
+ * TLSCompressed.fragment
+ */
hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
rl->hash_size);
if (hmac == NULL) {
@@ -182,7 +219,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
}
crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
- crypto_hash_update(hmac, ct_start, pos - ct_start);
+ crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+ crypto_hash_update(hmac, payload, payload_len);
clen = buf + buf_size - pos;
if (clen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
@@ -200,7 +238,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
pos, clen);
pos += clen;
if (rl->iv_size) {
- size_t len = pos - payload;
+ size_t len = pos - cpayload;
size_t pad;
pad = (len + 1) % rl->iv_size;
if (pad)
@@ -214,8 +252,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
pos += pad + 1;
}
- if (crypto_cipher_encrypt(rl->write_cbc, payload,
- payload, pos - payload) < 0)
+ if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+ cpayload, pos - cpayload) < 0)
return -1;
}
@@ -237,7 +275,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
* @out_len: Set to maximum out_data length by caller; used to return the
* length of the used data
* @alert: Buffer for returning an alert value on failure
- * Returns: 0 on success, -1 on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ * complete (more data needed), or -1 on failure
*
* This function decrypts the received message, verifies HMAC and TLS record
* layer header.
@@ -250,40 +289,35 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
u8 padlen;
struct crypto_hash *hmac;
u8 len[2], hash[100];
-
- wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
- in_data, in_len);
+ int force_mac_error = 0;
+ u8 ct;
if (in_len < TLS_RECORD_HEADER_LEN) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)",
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+ "need more data",
(unsigned long) in_len);
- *alert = TLS_ALERT_DECODE_ERROR;
- return -1;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, in_len);
+ return 0;
}
+ ct = in_data[0];
+ rlen = WPA_GET_BE16(in_data + 3);
wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
- "length %d", in_data[0], in_data[1], in_data[2],
- WPA_GET_BE16(in_data + 3));
-
- if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE &&
- in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
- in_data[0] != TLS_CONTENT_TYPE_ALERT &&
- in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x",
- in_data[0]);
- *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
- return -1;
- }
-
- if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+ "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+ /*
+ * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+ * protocol version in record layer. As such, accept any {03,xx} value
+ * to remain compatible with existing implementations.
+ */
+ if (in_data[1] != 0x03) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
- "%d.%d", in_data[1], in_data[2]);
+ "%u.%u", in_data[1], in_data[2]);
*alert = TLS_ALERT_PROTOCOL_VERSION;
return -1;
}
- rlen = WPA_GET_BE16(in_data + 3);
-
/* TLSCiphertext must not be more than 2^14+2048 bytes */
if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
@@ -299,7 +333,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
"(rlen=%lu > in_len=%lu)",
(unsigned long) rlen, (unsigned long) in_len);
- *alert = TLS_ALERT_DECODE_ERROR;
+ return 0;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, rlen);
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+ ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+ ct != TLS_CONTENT_TYPE_ALERT &&
+ ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+ "content type 0x%x", ct);
+ *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
return -1;
}
@@ -312,58 +358,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
return -1;
}
- os_memcpy(out_data, in_data, in_len);
- *out_len = in_len;
-
if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
- if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+ size_t plen;
+ if (crypto_cipher_decrypt(rl->read_cbc, in_data,
out_data, in_len) < 0) {
*alert = TLS_ALERT_DECRYPTION_FAILED;
return -1;
}
+ plen = in_len;
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+ "data", out_data, plen);
+
if (rl->iv_size) {
- if (in_len == 0) {
+ /*
+ * TLS v1.0 defines different alert values for various
+ * failures. That may information to aid in attacks, so
+ * use the same bad_record_mac alert regardless of the
+ * issues.
+ *
+ * In addition, instead of returning immediately on
+ * error, run through the MAC check to make timing
+ * 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"
" (no pad)");
- *alert = TLS_ALERT_DECODE_ERROR;
- return -1;
+ force_mac_error = 1;
+ goto check_mac;
}
- padlen = out_data[in_len - 1];
- if (padlen >= in_len) {
+ padlen = out_data[plen - 1];
+ if (padlen >= plen) {
wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
- "length (%u, in_len=%lu) in "
+ "length (%u, plen=%lu) in "
"received record",
- padlen, (unsigned long) in_len);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ padlen, (unsigned long) plen);
+ force_mac_error = 1;
+ goto check_mac;
}
- for (i = in_len - padlen; i < in_len; i++) {
+ for (i = plen - padlen - 1; i < plen - 1; i++) {
if (out_data[i] != padlen) {
wpa_hexdump(MSG_DEBUG,
"TLSv1: Invalid pad in "
"received record",
- out_data + in_len - padlen,
- padlen);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ out_data + plen - padlen -
+ 1, padlen + 1);
+ force_mac_error = 1;
+ goto check_mac;
}
}
- *out_len -= padlen + 1;
- }
+ plen -= padlen + 1;
- wpa_hexdump(MSG_MSGDUMP,
- "TLSv1: Record Layer - Decrypted data",
- out_data, in_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+ "Decrypted data with IV and padding "
+ "removed", out_data, plen);
+ }
- if (*out_len < rl->hash_size) {
+ check_mac:
+ if (plen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
"hash value");
- *alert = TLS_ALERT_INTERNAL_ERROR;
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
- *out_len -= rl->hash_size;
+ plen -= rl->hash_size;
hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
rl->hash_size);
@@ -377,22 +451,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
- WPA_PUT_BE16(len, *out_len);
+ WPA_PUT_BE16(len, plen);
crypto_hash_update(hmac, len, 2);
- crypto_hash_update(hmac, out_data, *out_len);
+ crypto_hash_update(hmac, out_data, plen);
hlen = sizeof(hash);
if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
"to calculate HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (hlen != rl->hash_size ||
- os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+ os_memcmp(hash, out_data + plen, hlen) != 0 ||
+ force_mac_error) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
- "received message");
+ "received message (force_mac_error=%d)",
+ force_mac_error);
*alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
+
+ *out_len = plen;
+ } else {
+ os_memcpy(out_data, in_data, in_len);
+ *out_len = in_len;
}
/* TLSCompressed must not be more than 2^14+1024 bytes */
@@ -405,5 +487,5 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
- return 0;
+ return TLS_RECORD_HEADER_LEN + rlen;
}
diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h
index 9c7c0a4..9eb9bfd 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 <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,7 @@
#include "crypto/crypto.h"
-#define TLS_MAX_WRITE_MAC_SECRET_LEN 20
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 32
#define TLS_MAX_WRITE_KEY_LEN 32
#define TLS_MAX_IV_LEN 16
#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
@@ -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];
@@ -66,7 +68,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len);
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len);
int tlsv1_record_receive(struct tlsv1_record_layer *rl,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t *out_len, u8 *alert);
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 6a61235..96e160c 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -1,6 +1,6 @@
/*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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
@@ -49,7 +49,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn,
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
- if (tls_prf(pre_master_secret, pre_master_secret_len,
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
@@ -64,7 +65,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn,
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);
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
@@ -115,6 +117,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
const u8 *pos, *end;
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
+ int used;
if (in_data == NULL || in_len == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
@@ -130,13 +133,21 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
- if (tlsv1_record_receive(&conn->rl, pos, end - pos,
- in_msg, &in_msg_len, &alert)) {
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
ct = pos[0];
in_pos = in_msg;
@@ -152,7 +163,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
in_pos += in_msg_len;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
os_free(in_msg);
@@ -201,10 +212,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -232,8 +241,8 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
u8 *out_data, size_t out_len)
{
const u8 *in_end, *pos;
- int res;
- u8 alert, *out_end, *out_pos;
+ int used;
+ u8 alert, *out_end, *out_pos, ct;
size_t olen;
pos = in_data;
@@ -242,7 +251,46 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
out_end = out_data + out_len;
while (pos < in_end) {
- if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ ct = pos[0];
+ olen = out_end - out_pos;
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ out_pos[1]);
+ return -1;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x", pos[0]);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -250,15 +298,6 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
- olen = out_end - out_pos;
- res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
- out_pos, &olen, &alert);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
- "failed");
- tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
- return -1;
- }
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@@ -268,7 +307,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
return out_pos - out_data;
@@ -412,7 +451,8 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index 00c536c..daa4353 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -1,6 +1,6 @@
/*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index fd74436..443c028 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -1,6 +1,6 @@
/*
* TLSv1 server - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,6 +17,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -85,15 +86,30 @@ 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;
+#ifdef CONFIG_TLSV12
+ else if (conn->client_version >= TLS_VERSION_1_2)
+ conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+ 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",
+ tls_version_str(conn->rl.tls_version));
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
@@ -483,6 +499,14 @@ static int tls_process_client_key_exchange_rsa(
encr_len = WPA_GET_BE16(pos);
pos += 2;
+ if (pos + encr_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+ "format: encr_len=%u left=%u",
+ encr_len, (unsigned int) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
outbuflen = outlen = end - pos;
out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
@@ -512,21 +536,21 @@ static int tls_process_client_key_exchange_rsa(
*/
if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
- pos, end - pos,
+ pos, encr_len,
out, &outlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
- "PreMasterSecret (encr_len=%d outlen=%lu)",
- (int) (end - pos), (unsigned long) outlen);
+ "PreMasterSecret (encr_len=%u outlen=%lu)",
+ encr_len, (unsigned long) outlen);
use_random = 1;
}
- if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+ if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
"length %lu", (unsigned long) outlen);
use_random = 1;
}
- if (WPA_GET_BE16(out) != conn->client_version) {
+ if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
"ClientKeyExchange does not match with version in "
"ClientHello");
@@ -822,6 +846,47 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
hpos = hash;
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ if (pos[0] != TLS_HASH_ALG_SHA256 ||
+ pos[1] != TLS_SIGN_ALG_RSA) {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+ "signature(%u) algorithm",
+ pos[0], pos[1]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos += 2;
+
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
if (alg == SIGN_ALG_RSA) {
hlen = MD5_MAC_LEN;
if (conn->verify.md5_cert == NULL ||
@@ -852,6 +917,10 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
if (alg == SIGN_ALG_RSA)
hlen += MD5_MAC_LEN;
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
if (end - pos < 2) {
@@ -891,6 +960,41 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
buf, buflen);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ if (buflen >= 19 + 32 &&
+ os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+ "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
+ "SHA-256");
+ os_memmove(buf, buf + 19, buflen - 19);
+ buflen -= 19;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
+ "DigestInfo");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ }
+#endif /* CONFIG_TLSV12 */
+
if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
"CertificateVerify - did not match with calculated "
@@ -1022,6 +1126,21 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
pos, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_client == NULL ||
+ crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_client == NULL ||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
@@ -1043,9 +1162,15 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
return -1;
}
conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
verify_data, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index e89e52e..0ca3b23 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -1,6 +1,6 @@
/*
* TLSv1 server - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* 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,6 +17,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "crypto/random.h"
#include "x509v3.h"
@@ -87,7 +88,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);
@@ -143,7 +144,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -227,7 +229,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -418,7 +421,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -483,7 +487,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -502,40 +507,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
static int tls_write_server_hello_done(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[4];
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
- hs_start = pos;
+ pos = payload;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, 0);
pos += 3;
/* body - ServerHelloDone (empty) */
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, pos - payload,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
- tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+ tls_verify_hash_add(&conn->verify, payload, pos - payload);
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -544,17 +544,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn,
static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -569,7 +568,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
@@ -578,9 +577,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
static int tls_write_server_finished(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
pos = *msgpos;
@@ -589,6 +588,21 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
/* Encrypted Handshake Message: Finished */
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_server == NULL ||
+ crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+ < 0) {
+ conn->verify.sha256_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_server == NULL ||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
@@ -610,43 +624,44 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
return -1;
}
conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -771,7 +786,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;