/*
* TLSv1 client - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, 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
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#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"
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; "
/* 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;
if (conn->cred &&
x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason) < 0) {
+ &reason, conn->disable_time_checks)
+ < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
}
+static unsigned int count_bits(const u8 *val, size_t len)
+{
+ size_t i;
+ unsigned int bits;
+ u8 tmp;
+
+ for (i = 0; i < len; i++) {
+ if (val[i])
+ break;
+ }
+ if (i == len)
+ return 0;
+
+ bits = (len - i - 1) * 8;
+ tmp = val[i];
+ while (tmp) {
+ bits++;
+ tmp >>= 1;
+ }
+
+ return bits;
+}
+
+
static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
- const u8 *buf, size_t len)
+ const u8 *buf, size_t len,
+ tls_key_exchange key_exchange)
{
- const u8 *pos, *end;
+ const u8 *pos, *end, *server_params, *server_params_end;
+ u8 alert;
+ unsigned int bits;
+ u16 val;
tlsv1_client_free_dh(conn);
if (end - pos < 3)
goto fail;
- conn->dh_p_len = WPA_GET_BE16(pos);
+ server_params = pos;
+ val = WPA_GET_BE16(pos);
pos += 2;
- if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
- wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
- (unsigned long) conn->dh_p_len);
+ if (val == 0 || val > (size_t) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
+ goto fail;
+ }
+ conn->dh_p_len = val;
+ bits = count_bits(pos, conn->dh_p_len);
+ if (bits < 768) {
+ wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+ bits);
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+ pos, conn->dh_p_len);
goto fail;
}
conn->dh_p = os_malloc(conn->dh_p_len);
if (end - pos < 3)
goto fail;
- conn->dh_g_len = WPA_GET_BE16(pos);
+ val = WPA_GET_BE16(pos);
pos += 2;
- if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+ if (val == 0 || val > (size_t) (end - pos))
goto fail;
+ conn->dh_g_len = val;
conn->dh_g = os_malloc(conn->dh_g_len);
if (conn->dh_g == NULL)
goto fail;
if (end - pos < 3)
goto fail;
- conn->dh_ys_len = WPA_GET_BE16(pos);
+ val = WPA_GET_BE16(pos);
pos += 2;
- if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+ if (val == 0 || val > (size_t) (end - pos))
goto fail;
+ conn->dh_ys_len = val;
conn->dh_ys = os_malloc(conn->dh_ys_len);
if (conn->dh_ys == NULL)
goto fail;
pos += conn->dh_ys_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
conn->dh_ys, conn->dh_ys_len);
+ server_params_end = pos;
+
+ if (key_exchange == TLS_KEY_X_DHE_RSA) {
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+ int hlen;
+
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+ /*
+ * 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)
+ goto fail;
+ 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]);
+ goto fail;
+ }
+ pos += 2;
+
+ hlen = tlsv12_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ server_params_end - server_params, hash);
+#else /* CONFIG_TLSV12 */
+ goto fail;
+#endif /* CONFIG_TLSV12 */
+ } else {
+ hlen = tls_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ server_params_end - server_params, hash);
+ }
+
+ if (hlen < 0)
+ goto fail;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+ hash, hlen);
+
+ if (tls_verify_signature(conn->rl.tls_version,
+ conn->server_rsa_key,
+ hash, hlen, pos, end - pos,
+ &alert) < 0)
+ goto fail;
+ }
return 0;
wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
- if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
- if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+ if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+ suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+ if (tlsv1_process_diffie_hellman(conn, pos, len,
+ suite->key_exchange) < 0) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
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) {
return -1;
}
conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ 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,
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
verify_data, TLS_VERIFY_DATA_LEN);
- if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
return -1;
}