/*
* TLSv1 client - write 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 "crypto/random.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
os_get_time(&now);
WPA_PUT_BE32(conn->client_random, now.sec);
- if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+ if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"client_random");
return NULL;
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);
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);
}
-static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
{
/* ClientDiffieHellmanPublic */
u8 *csecret, *csecret_start, *dh_yc, *shared;
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- if (os_get_random(csecret, csecret_len)) {
+ if (random_get_bytes(csecret, csecret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
hs_length = pos;
pos += 3;
/* body - ClientKeyExchange */
- if (keyx == TLS_KEY_X_DH_anon) {
- if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+ if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) {
+ if (tlsv1_key_x_dh(conn, &pos, end) < 0)
return -1;
} else {
if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
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);
{
u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
size_t rlen, hlen, clen;
- u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
- enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+ u8 hash[100], *hpos;
pos = *msgpos;
hpos = hash;
- if (alg == SIGN_ALG_RSA) {
- hlen = MD5_MAC_LEN;
- if (conn->verify.md5_cert == NULL ||
- crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
- {
+#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);
- conn->verify.md5_cert = NULL;
- crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
- conn->verify.sha1_cert = NULL;
return -1;
}
- hpos += MD5_MAC_LEN;
- } else
- crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+ 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 */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_cert == NULL ||
+ crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_cert = NULL;
+ crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+ conn->verify.sha1_cert = NULL;
+ return -1;
+ }
+ hpos += MD5_MAC_LEN;
conn->verify.md5_cert = NULL;
hlen = SHA1_MAC_LEN;
}
conn->verify.sha1_cert = NULL;
- if (alg == SIGN_ALG_RSA)
- hlen += MD5_MAC_LEN;
+ 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
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);
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);
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
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) {
return -1;
}
conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
- 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)) {
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ 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;
}
*out_len = 0;
- msglen = 1000;
+ msglen = 2000;
if (conn->certificate_requested)
msglen += tls_client_cert_chain_der_len(conn);
/* 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;