diff options
author | Dmitry Shmidt <dimitrysh@google.com> | 2012-01-24 16:10:04 -0800 |
---|---|---|
committer | Dmitry Shmidt <dimitrysh@google.com> | 2012-01-24 16:44:49 -0800 |
commit | 1f69aa52ea2e0a73ac502565df8c666ee49cab6a (patch) | |
tree | 8ea94735f75f461769454853da0c24cbb89cc4cc /src/tls | |
parent | bf5edf439c90418b6f4122ff5e3925123263bda4 (diff) | |
download | external_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/Makefile | 2 | ||||
-rw-r--r-- | src/tls/libtommath.c | 14 | ||||
-rw-r--r-- | src/tls/pkcs5.c | 2 | ||||
-rw-r--r-- | src/tls/tlsv1_client.c | 235 | ||||
-rw-r--r-- | src/tls/tlsv1_client.h | 10 | ||||
-rw-r--r-- | src/tls/tlsv1_client_i.h | 2 | ||||
-rw-r--r-- | src/tls/tlsv1_client_read.c | 38 | ||||
-rw-r--r-- | src/tls/tlsv1_client_write.c | 144 | ||||
-rw-r--r-- | src/tls/tlsv1_common.c | 91 | ||||
-rw-r--r-- | src/tls/tlsv1_common.h | 57 | ||||
-rw-r--r-- | src/tls/tlsv1_cred.c | 25 | ||||
-rw-r--r-- | src/tls/tlsv1_record.c | 220 | ||||
-rw-r--r-- | src/tls/tlsv1_record.h | 9 | ||||
-rw-r--r-- | src/tls/tlsv1_server.c | 88 | ||||
-rw-r--r-- | src/tls/tlsv1_server.h | 4 | ||||
-rw-r--r-- | src/tls/tlsv1_server_read.c | 145 | ||||
-rw-r--r-- | src/tls/tlsv1_server_write.c | 108 |
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; |