Browse Source

TLS: Add support for DHE-RSA cipher suites

This extends the internal TLS implementation to support DHE-RSA
cipher suites in both server and client roles.

Signed-off-by: Jouni Malinen <j@w1.fi>
Jouni Malinen 11 years ago
parent
commit
65074a2a7c

+ 6 - 1
src/tls/tlsv1_client.c

@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -459,10 +459,15 @@ struct tlsv1_client * tlsv1_client_init(void)
 
 	count = 0;
 	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_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;

+ 156 - 5
src/tls/tlsv1_client_read.c

@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -410,9 +410,10 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 
 
 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;
 
 	tlsv1_client_free_dh(conn);
 
@@ -421,6 +422,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
 	if (end - pos < 3)
 		goto fail;
+	server_params = pos;
 	conn->dh_p_len = WPA_GET_BE16(pos);
 	pos += 2;
 	if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
@@ -465,6 +467,153 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 	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], *hpos, *sbuf;
+		size_t hlen, buflen;
+		enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+		u16 slen;
+		struct crypto_hash *ctx;
+
+		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)
+				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;
+
+			ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+			if (ctx == NULL)
+				goto fail;
+			crypto_hash_update(ctx, conn->client_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, conn->server_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, server_params,
+					   server_params_end - server_params);
+			hlen = SHA256_MAC_LEN;
+			if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+				goto fail;
+		} else {
+#endif /* CONFIG_TLSV12 */
+		if (alg == SIGN_ALG_RSA) {
+			ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+			if (ctx == NULL)
+				goto fail;
+			crypto_hash_update(ctx, conn->client_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, conn->server_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, server_params,
+					   server_params_end - server_params);
+			hlen = sizeof(hash);
+			if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+				goto fail;
+			hpos += hlen;
+		}
+		ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+		if (ctx == NULL)
+			goto fail;
+		crypto_hash_update(ctx, conn->client_random, TLS_RANDOM_LEN);
+		crypto_hash_update(ctx, conn->server_random, TLS_RANDOM_LEN);
+		crypto_hash_update(ctx, server_params,
+				   server_params_end - server_params);
+		hlen = hash + sizeof(hash) - hpos;
+		if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+			goto fail;
+		hpos += hlen;
+		hlen = hpos - hash;
+#ifdef CONFIG_TLSV12
+		}
+#endif /* CONFIG_TLSV12 */
+
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+			    hash, hlen);
+
+		if (end - pos < 2)
+			goto fail;
+		slen = WPA_GET_BE16(pos);
+		pos += 2;
+		if (end - pos < slen)
+			goto fail;
+
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+		if (conn->server_rsa_key == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: No server public key to verify signature");
+			goto fail;
+		}
+
+		buflen = end - pos;
+		sbuf = os_malloc(end - pos);
+		if (crypto_public_key_decrypt_pkcs1(conn->server_rsa_key,
+						    pos, end - pos, sbuf,
+						    &buflen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+			os_free(sbuf);
+			goto fail;
+		}
+
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+				sbuf, 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(sbuf,
+				      "\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(sbuf, sbuf + 19, buflen - 19);
+				buflen -= 19;
+			} else {
+				wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
+				os_free(sbuf);
+				goto fail;
+			}
+		}
+#endif /* CONFIG_TLSV12 */
+
+		if (buflen != hlen || os_memcmp(sbuf, hash, buflen) != 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in ServerKeyExchange - did not match calculated hash");
+			os_free(sbuf);
+			goto fail;
+		}
+
+		os_free(sbuf);
+	}
 
 	return 0;
 
@@ -543,8 +692,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
 
 	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;

+ 4 - 4
src/tls/tlsv1_client_write.c

@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -205,7 +205,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn,
 }
 
 
-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;
@@ -399,8 +399,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn,
 	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)

+ 13 - 1
src/tls/tlsv1_common.c

@@ -1,6 +1,6 @@
 /*
  * TLSv1 common routines
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -33,6 +33,10 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
 	  TLS_HASH_SHA },
 	{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
 	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC,
+	  TLS_HASH_SHA},
+	{ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
  	{ TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
  	{ TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
@@ -41,16 +45,24 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
 	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
 	{ TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
 	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
 	{ TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
 	{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
 	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_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_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_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_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,

+ 6 - 1
src/tls/tlsv1_server.c

@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -361,10 +361,15 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
 
 	count = 0;
 	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_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;

+ 5 - 5
src/tls/tlsv1_server_read.c

@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -582,7 +582,7 @@ static int tls_process_client_key_exchange_rsa(
 }
 
 
-static int tls_process_client_key_exchange_dh_anon(
+static int tls_process_client_key_exchange_dh(
 	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
 {
 	const u8 *dh_yc;
@@ -747,11 +747,11 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
 	else
 		keyx = suite->key_exchange;
 
-	if (keyx == TLS_KEY_X_DH_anon &&
-	    tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+	if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) &&
+	    tls_process_client_key_exchange_dh(conn, pos, end) < 0)
 		return -1;
 
-	if (keyx != TLS_KEY_X_DH_anon &&
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA &&
 	    tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
 		return -1;
 

+ 178 - 4
src/tls/tlsv1_server_write.c

@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -245,7 +245,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 {
 	tls_key_exchange keyx;
 	const struct tls_cipher_suite *suite;
-	u8 *pos, *rhdr, *hs_start, *hs_length;
+	u8 *pos, *rhdr, *hs_start, *hs_length, *server_params;
 	size_t rlen;
 	u8 *dh_ys;
 	size_t dh_ys_len;
@@ -261,8 +261,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 		return 0;
 	}
 
-	if (keyx != TLS_KEY_X_DH_anon) {
-		/* TODO? */
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) {
 		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
 			   "supported with key exchange type %d", keyx);
 		return -1;
@@ -369,6 +368,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 	pos += 3;
 
 	/* body - ServerDHParams */
+	server_params = pos;
 	/* dh_p */
 	if (pos + 2 + conn->cred->dh_p_len > end) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
@@ -412,6 +412,180 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 	pos += dh_ys_len;
 	os_free(dh_ys);
 
+	/*
+	 * select (SignatureAlgorithm)
+	 * {   case anonymous: struct { };
+	 *     case rsa:
+	 *         digitally-signed struct {
+	 *             opaque md5_hash[16];
+	 *             opaque sha_hash[20];
+	 *         };
+	 *     case dsa:
+	 *         digitally-signed struct {
+	 *             opaque sha_hash[20];
+	 *         };
+	 * } Signature;
+	 *
+	 * md5_hash
+	 *     MD5(ClientHello.random + ServerHello.random + ServerParams);
+	 *
+	 * sha_hash
+	 *     SHA(ClientHello.random + ServerHello.random + ServerParams);
+	 */
+
+	if (keyx == TLS_KEY_X_DHE_RSA) {
+		u8 hash[100], *hpos;
+		u8 *signed_start;
+		size_t hlen, clen;
+		enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+		struct crypto_hash *ctx;
+
+#ifdef CONFIG_TLSV12
+		if (conn->rl.tls_version == TLS_VERSION_1_2) {
+			ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+			if (ctx == NULL) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			crypto_hash_update(ctx, conn->client_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, conn->server_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, server_params,
+					   pos - server_params);
+			hlen = sizeof(hash) - 19;
+			if (crypto_hash_finish(ctx, hash + 19, &hlen) < 0) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+
+			/*
+			 * 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
+			 */
+			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 */
+			hpos = hash;
+
+			if (alg == SIGN_ALG_RSA) {
+				ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5,
+						       NULL, 0);
+				if (ctx == NULL) {
+					tlsv1_server_alert(
+						conn, TLS_ALERT_LEVEL_FATAL,
+						TLS_ALERT_INTERNAL_ERROR);
+					return -1;
+				}
+				crypto_hash_update(ctx, conn->client_random,
+						   TLS_RANDOM_LEN);
+				crypto_hash_update(ctx, conn->server_random,
+						   TLS_RANDOM_LEN);
+				crypto_hash_update(ctx, server_params,
+						   pos - server_params);
+				hlen = sizeof(hash);
+				if (crypto_hash_finish(ctx, hash, &hlen) < 0) {
+					tlsv1_server_alert(
+						conn, TLS_ALERT_LEVEL_FATAL,
+						TLS_ALERT_INTERNAL_ERROR);
+					return -1;
+				}
+				hpos += hlen;
+			}
+
+			ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+			if (ctx == NULL) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			crypto_hash_update(ctx, conn->client_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, conn->server_random,
+					   TLS_RANDOM_LEN);
+			crypto_hash_update(ctx, server_params,
+					   pos - server_params);
+			hlen = hash + sizeof(hash) - hpos;
+			if (crypto_hash_finish(ctx, hpos, &hlen) < 0) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			hpos += hlen;
+			hlen = hpos - hash;
+#ifdef CONFIG_TLSV12
+		}
+#endif /* CONFIG_TLSV12 */
+
+		wpa_hexdump(MSG_MSGDUMP,
+			    "TLSv1: ServerKeyExchange signed_params 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;
+			 */
+			if (pos + 2 > end) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			*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 signing algorithm. A digitally-signed element is
+		 * encoded as an opaque vector <0..2^16-1>, where the length is
+		 * specified by the signing algorithm and key.
+		 *
+		 * In RSA signing, a 36-byte structure of two hashes (one SHA
+		 * and one MD5) is signed (encrypted with the private key). It
+		 * is encoded with PKCS #1 block type 0 or type 1 as described
+		 * in [PKCS1].
+		 */
+		signed_start = pos; /* length to be filled */
+		pos += 2;
+		clen = end - pos;
+		if (conn->cred == NULL ||
+		    crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+						  pos, &clen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE16(signed_start, clen);
+
+		pos += clen;
+	}
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
 	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,