]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TLS: Add support for DHE-RSA cipher suites
authorJouni Malinen <j@w1.fi>
Sun, 9 Mar 2014 13:43:50 +0000 (15:43 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 9 Mar 2014 13:43:50 +0000 (15:43 +0200)
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>
src/tls/tlsv1_client.c
src/tls/tlsv1_client_read.c
src/tls/tlsv1_client_write.c
src/tls/tlsv1_common.c
src/tls/tlsv1_server.c
src/tls/tlsv1_server_read.c
src/tls/tlsv1_server_write.c

index 12148b61ddfc497e322722820c37825bc1507cce..4a4f0b69d3c7eb337029f570b22d273f48f65f68 100644 (file)
@@ -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;
index 3269ecf668ee3647dafd95237ce913ec79918c3e..8313efbe2d28d6dd17fa1d5eca3dcd27ad8a0c1c 100644 (file)
@@ -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;
index d789efb4255e2b2866ce9ed221cdcc9447cb9089..839eb90abd40cfde47d103e5969a51d736ec2889 100644 (file)
@@ -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)
index 4578b22727919b4a799fc8787f6ccd117fc756f9..d05df856d8c8129201c2295cb0a02de66c0d187b 100644 (file)
@@ -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,
index 55f5a78fbeceebe844f624edb99a13f6dca5d7ae..db2059b024bc68c635fdea4279e39e1ea65a2275 100644 (file)
@@ -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;
index 6f6539b1bfb5d248959c4ac7ac8d5d8734d71203..137fd3abddc0e8e56521ed50c24245c1c2b37801 100644 (file)
@@ -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;
 
index 6d8e55ed49a098af9247f70338b2b1a13fad661e..39dbad4fe21082af02c4f23e7f3387ef4b9918df 100644 (file)
@@ -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,