- Initial bare version of TLS 1.2

diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 5a81f8b..b5c89a9 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -113,6 +113,49 @@
     return( 0 );
 }
 
+static int tls_prf_sha256( unsigned char *secret, size_t slen, char *label,
+                           unsigned char *random, size_t rlen,
+                           unsigned char *dstbuf, size_t dlen )
+{
+    size_t nb;
+    size_t i, j, k;
+    unsigned char tmp[128];
+    unsigned char h_i[32];
+
+    if( sizeof( tmp ) < 32 + strlen( label ) + rlen )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    nb = strlen( label );
+    memcpy( tmp + 32, label, nb );
+    memcpy( tmp + 32 + nb, random, rlen );
+    nb += rlen;
+
+    /*
+     * Compute P_<hash>(secret, label + random)[0..dlen]
+     */
+    sha2_hmac( secret, slen, tmp + 32, nb, tmp, 0 );
+
+    for( i = 0; i < dlen; i += 32 )
+    {
+        sha2_hmac( secret, slen, tmp, 32 + nb, h_i, 0 );
+        sha2_hmac( secret, slen, tmp, 32,      tmp, 0 );
+
+        k = ( i + 32 > dlen ) ? dlen % 32 : 32;
+
+        for( j = 0; j < k; j++ )
+            dstbuf[i + j]  = h_i[j];
+    }
+
+    memset( tmp, 0, sizeof( tmp ) );
+    memset( h_i, 0, sizeof( h_i ) );
+
+    return( 0 );
+}
+
+static void ssl_calc_finished_ssl   (ssl_context *,unsigned char *,int);
+static void ssl_calc_finished_tls   (ssl_context *,unsigned char *,int);
+static void ssl_calc_finished_tls1_2(ssl_context *,unsigned char *,int);
+
 int ssl_derive_keys( ssl_context *ssl )
 {
     int i;
@@ -128,6 +171,24 @@
     SSL_DEBUG_MSG( 2, ( "=> derive keys" ) );
 
     /*
+     * Set appropriate PRF function.
+     */
+    if( ssl->minor_ver < SSL_MINOR_VERSION_3 )
+        ssl->tls_prf = tls1_prf;
+    else
+        ssl->tls_prf = tls_prf_sha256;
+
+    /*
+     * Set appropriate SSL / TLS / TLS1.2 functions
+     */
+    if( ssl->minor_ver == SSL_MINOR_VERSION_0 )
+        ssl->calc_finished = ssl_calc_finished_ssl;
+    else if( ssl->minor_ver < SSL_MINOR_VERSION_3 )
+        ssl->calc_finished = ssl_calc_finished_tls;
+    else
+        ssl->calc_finished = ssl_calc_finished_tls1_2;
+
+    /*
      * SSLv3:
      *   master =
      *     MD5( premaster + SHA1( 'A'   + premaster + randbytes ) ) +
@@ -161,9 +222,9 @@
                 md5_finish( &md5, ssl->session->master + i * 16 );
             }
         }
-        else
-            tls1_prf( ssl->premaster, len, "master secret",
-                      ssl->randbytes, 64, ssl->session->master, 48 );
+        else 
+            ssl->tls_prf( ssl->premaster, len, "master secret",
+                          ssl->randbytes, 64, ssl->session->master, 48 );
 
         memset( ssl->premaster, 0, sizeof( ssl->premaster ) );
     }
@@ -215,8 +276,8 @@
         memset( sha1sum, 0, sizeof( sha1sum ) );
     }
     else
-        tls1_prf( ssl->session->master, 48, "key expansion",
-                  ssl->randbytes, 64, keyblk, 256 );
+        ssl->tls_prf( ssl->session->master, 48, "key expansion",
+                      ssl->randbytes, 64, keyblk, 256 );
 
     SSL_DEBUG_MSG( 3, ( "ciphersuite = %s", ssl_get_ciphersuite( ssl ) ) );
     SSL_DEBUG_BUF( 3, "master secret", ssl->session->master, 48 );
@@ -426,6 +487,7 @@
 {
     md5_context md5;
     sha1_context sha1;
+    sha2_context sha2;
     unsigned char pad_1[48];
     unsigned char pad_2[48];
 
@@ -433,6 +495,7 @@
 
     memcpy( &md5 , &ssl->fin_md5 , sizeof(  md5_context ) );
     memcpy( &sha1, &ssl->fin_sha1, sizeof( sha1_context ) );
+    memcpy( &sha2, &ssl->fin_sha2, sizeof( sha2_context ) );
 
     if( ssl->minor_ver == SSL_MINOR_VERSION_0 )
     {
@@ -459,11 +522,15 @@
         sha1_update( &sha1, hash + 16, 20 );
         sha1_finish( &sha1, hash + 16 );
     }
-    else /* TLSv1 */
+    else if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) /* TLSv1 */
     {
          md5_finish( &md5,  hash );
         sha1_finish( &sha1, hash + 16 );
     }
+    else
+    {
+        sha2_finish( &sha2, hash );
+    }
 
     SSL_DEBUG_BUF( 3, "calculated verify result", hash, 36 );
     SSL_DEBUG_MSG( 2, ( "<= calc verify" ) );
@@ -630,10 +697,10 @@
         enc_msg = ssl->out_msg;
 
         /*
-         * Prepend per-record IV for block cipher in TLS v1.1 as per
-         * Method 1 (6.2.3.2. in RFC4346)
+         * Prepend per-record IV for block cipher in TLS v1.1 and up as per
+         * Method 1 (6.2.3.2. in RFC4346 and RFC5246)
          */
-        if( ssl->minor_ver == SSL_MINOR_VERSION_2 )
+        if( ssl->minor_ver >= SSL_MINOR_VERSION_2 )
         {
             /*
              * Generate IV
@@ -781,9 +848,9 @@
         dec_msg_result = ssl->in_msg;
 
         /*
-         * Initialize for prepended IV for block cipher in TLS v1.1
+         * Initialize for prepended IV for block cipher in TLS v1.1 and up
          */
-        if( ssl->minor_ver == SSL_MINOR_VERSION_2 )
+        if( ssl->minor_ver >= SSL_MINOR_VERSION_2 )
         {
             dec_msg += ssl->ivlen;
             dec_msglen -= ssl->ivlen;
@@ -1053,6 +1120,7 @@
 
          md5_update( &ssl->fin_md5 , ssl->out_msg, len );
         sha1_update( &ssl->fin_sha1, ssl->out_msg, len );
+        sha2_update( &ssl->fin_sha2, ssl->out_msg, len );
     }
 
     if( ssl->do_crypt != 0 )
@@ -1127,6 +1195,7 @@
 
          md5_update( &ssl->fin_md5 , ssl->in_msg, ssl->in_hslen );
         sha1_update( &ssl->fin_sha1, ssl->in_msg, ssl->in_hslen );
+        sha2_update( &ssl->fin_sha2, ssl->in_msg, ssl->in_hslen );
 
         return( 0 );
     }
@@ -1192,7 +1261,7 @@
         /*
          * TLS encrypted messages can have up to 256 bytes of padding
          */
-        if( ssl->minor_ver == SSL_MINOR_VERSION_1 &&
+        if( ssl->minor_ver >= SSL_MINOR_VERSION_1 &&
             ssl->in_msglen > ssl->minlen + SSL_MAX_CONTENT_LEN + 256 )
         {
             SSL_DEBUG_MSG( 1, ( "bad message length" ) );
@@ -1256,6 +1325,7 @@
 
          md5_update( &ssl->fin_md5 , ssl->in_msg, ssl->in_hslen );
         sha1_update( &ssl->fin_sha1, ssl->in_msg, ssl->in_hslen );
+        sha2_update( &ssl->fin_sha2, ssl->in_msg, ssl->in_hslen );
     }
 
     if( ssl->in_msgtype == SSL_MSG_ALERT )
@@ -1600,17 +1670,21 @@
     return( 0 );
 }
 
-static void ssl_calc_finished(
-                ssl_context *ssl, unsigned char *buf, int from,
-                md5_context *md5, sha1_context *sha1 )
+static void ssl_calc_finished_ssl(
+                ssl_context *ssl, unsigned char *buf, int from )
 {
-    int len = 12;
     char *sender;
+    md5_context  md5;
+    sha1_context sha1;
+
     unsigned char padbuf[48];
     unsigned char md5sum[16];
     unsigned char sha1sum[20];
 
-    SSL_DEBUG_MSG( 2, ( "=> calc  finished" ) );
+    SSL_DEBUG_MSG( 2, ( "=> calc  finished ssl" ) );
+
+    memcpy( &md5 , &ssl->fin_md5 , sizeof(  md5_context ) );
+    memcpy( &sha1, &ssl->fin_sha1, sizeof( sha1_context ) );
 
     /*
      * SSLv3:
@@ -1619,68 +1693,47 @@
      *          MD5( handshake + sender + master + pad1 ) )
      *   + SHA1( master + pad2 +
      *         SHA1( handshake + sender + master + pad1 ) )
-     *
-     * TLSv1:
-     *   hash = PRF( master, finished_label,
-     *               MD5( handshake ) + SHA1( handshake ) )[0..11]
      */
 
     SSL_DEBUG_BUF( 4, "finished  md5 state", (unsigned char *)
-                    md5->state, sizeof(  md5->state ) );
+                    md5.state, sizeof(  md5.state ) );
 
     SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *)
-                   sha1->state, sizeof( sha1->state ) );
+                   sha1.state, sizeof( sha1.state ) );
 
-    if( ssl->minor_ver == SSL_MINOR_VERSION_0 )
-    {
-        sender = ( from == SSL_IS_CLIENT ) ? (char *) "CLNT"
-                                           : (char *) "SRVR";
+    sender = ( from == SSL_IS_CLIENT ) ? (char *) "CLNT"
+                                       : (char *) "SRVR";
 
-        memset( padbuf, 0x36, 48 );
+    memset( padbuf, 0x36, 48 );
 
-        md5_update( md5, (unsigned char *) sender, 4 );
-        md5_update( md5, ssl->session->master, 48 );
-        md5_update( md5, padbuf, 48 );
-        md5_finish( md5, md5sum );
+    md5_update( &md5, (unsigned char *) sender, 4 );
+    md5_update( &md5, ssl->session->master, 48 );
+    md5_update( &md5, padbuf, 48 );
+    md5_finish( &md5, md5sum );
 
-        sha1_update( sha1, (unsigned char *) sender, 4 );
-        sha1_update( sha1, ssl->session->master, 48 );
-        sha1_update( sha1, padbuf, 40 );
-        sha1_finish( sha1, sha1sum );
+    sha1_update( &sha1, (unsigned char *) sender, 4 );
+    sha1_update( &sha1, ssl->session->master, 48 );
+    sha1_update( &sha1, padbuf, 40 );
+    sha1_finish( &sha1, sha1sum );
 
-        memset( padbuf, 0x5C, 48 );
+    memset( padbuf, 0x5C, 48 );
 
-        md5_starts( md5 );
-        md5_update( md5, ssl->session->master, 48 );
-        md5_update( md5, padbuf, 48 );
-        md5_update( md5, md5sum, 16 );
-        md5_finish( md5, buf );
+    md5_starts( &md5 );
+    md5_update( &md5, ssl->session->master, 48 );
+    md5_update( &md5, padbuf, 48 );
+    md5_update( &md5, md5sum, 16 );
+    md5_finish( &md5, buf );
 
-        sha1_starts( sha1 );
-        sha1_update( sha1, ssl->session->master, 48 );
-        sha1_update( sha1, padbuf , 40 );
-        sha1_update( sha1, sha1sum, 20 );
-        sha1_finish( sha1, buf + 16 );
+    sha1_starts( &sha1 );
+    sha1_update( &sha1, ssl->session->master, 48 );
+    sha1_update( &sha1, padbuf , 40 );
+    sha1_update( &sha1, sha1sum, 20 );
+    sha1_finish( &sha1, buf + 16 );
 
-        len += 24;
-    }
-    else
-    {
-        sender = ( from == SSL_IS_CLIENT )
-                 ? (char *) "client finished"
-                 : (char *) "server finished";
+    SSL_DEBUG_BUF( 3, "calc finished result", buf, 36 );
 
-         md5_finish(  md5, padbuf );
-        sha1_finish( sha1, padbuf + 16 );
-
-        tls1_prf( ssl->session->master, 48, sender,
-                  padbuf, 36, buf, len );
-    }
-
-    SSL_DEBUG_BUF( 3, "calc finished result", buf, len );
-
-    memset(  md5, 0, sizeof(  md5_context ) );
-    memset( sha1, 0, sizeof( sha1_context ) );
+    memset(  &md5, 0, sizeof(  md5_context ) );
+    memset( &sha1, 0, sizeof( sha1_context ) );
 
     memset(  padbuf, 0, sizeof(  padbuf ) );
     memset(  md5sum, 0, sizeof(  md5sum ) );
@@ -1689,20 +1742,100 @@
     SSL_DEBUG_MSG( 2, ( "<= calc  finished" ) );
 }
 
-int ssl_write_finished( ssl_context *ssl )
+static void ssl_calc_finished_tls(
+                ssl_context *ssl, unsigned char *buf, int from )
 {
-    int ret, hash_len;
-     md5_context  md5;
+    int len = 12;
+    char *sender;
+    md5_context  md5;
     sha1_context sha1;
+    unsigned char padbuf[36];
 
-    SSL_DEBUG_MSG( 2, ( "=> write finished" ) );
+    SSL_DEBUG_MSG( 2, ( "=> calc  finished tls" ) );
 
     memcpy( &md5 , &ssl->fin_md5 , sizeof(  md5_context ) );
     memcpy( &sha1, &ssl->fin_sha1, sizeof( sha1_context ) );
 
-    ssl_calc_finished( ssl, ssl->out_msg + 4,
-                       ssl->endpoint, &md5, &sha1 );
+    /*
+     * TLSv1:
+     *   hash = PRF( master, finished_label,
+     *               MD5( handshake ) + SHA1( handshake ) )[0..11]
+     */
 
+    SSL_DEBUG_BUF( 4, "finished  md5 state", (unsigned char *)
+                    md5.state, sizeof(  md5.state ) );
+
+    SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *)
+                   sha1.state, sizeof( sha1.state ) );
+
+    sender = ( from == SSL_IS_CLIENT )
+             ? (char *) "client finished"
+             : (char *) "server finished";
+
+    md5_finish(  &md5, padbuf );
+    sha1_finish( &sha1, padbuf + 16 );
+
+    ssl->tls_prf( ssl->session->master, 48, sender,
+            padbuf, 36, buf, len );
+
+    SSL_DEBUG_BUF( 3, "calc finished result", buf, len );
+
+    memset(  &md5, 0, sizeof(  md5_context ) );
+    memset( &sha1, 0, sizeof( sha1_context ) );
+
+    memset(  padbuf, 0, sizeof(  padbuf ) );
+
+    SSL_DEBUG_MSG( 2, ( "<= calc  finished" ) );
+}
+
+static void ssl_calc_finished_tls1_2(
+                ssl_context *ssl, unsigned char *buf, int from )
+{
+    int len = 12;
+    char *sender;
+    sha2_context sha2;
+    unsigned char padbuf[32];
+
+    SSL_DEBUG_MSG( 2, ( "=> calc  finished tls 1.2" ) );
+
+    memcpy( &sha2, &ssl->fin_sha2, sizeof( sha2_context ) );
+
+    /*
+     * TLSv1.2:
+     *   hash = PRF( master, finished_label,
+     *               Hash( handshake ) )[0.11]
+     */
+
+    SSL_DEBUG_BUF( 4, "finished sha2 state", (unsigned char *)
+                   sha2.state, sizeof( sha2.state ) );
+
+    sender = ( from == SSL_IS_CLIENT )
+             ? (char *) "client finished"
+             : (char *) "server finished";
+
+    sha2_finish( &sha2, padbuf );
+
+    ssl->tls_prf( ssl->session->master, 48, sender,
+                  padbuf, 32, buf, len );
+
+    SSL_DEBUG_BUF( 3, "calc finished result", buf, len );
+
+    memset( &sha2, 0, sizeof( sha2_context ) );
+
+    memset(  padbuf, 0, sizeof(  padbuf ) );
+
+    SSL_DEBUG_MSG( 2, ( "<= calc  finished" ) );
+}
+
+int ssl_write_finished( ssl_context *ssl )
+{
+    int ret, hash_len;
+
+    SSL_DEBUG_MSG( 2, ( "=> write finished" ) );
+
+    ssl->calc_finished( ssl, ssl->out_msg + 4, ssl->endpoint );
+
+    // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63)
     hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12;
 
     ssl->out_msglen  = 4 + hash_len;
@@ -1741,13 +1874,10 @@
     int ret;
     unsigned int hash_len;
     unsigned char buf[36];
-    md5_context  md5;
-    sha1_context sha1;
 
     SSL_DEBUG_MSG( 2, ( "=> parse finished" ) );
 
-    memcpy( &md5 , &ssl->fin_md5 , sizeof(  md5_context ) );
-    memcpy( &sha1, &ssl->fin_sha1, sizeof( sha1_context ) );
+    ssl->calc_finished( ssl, buf, ssl->endpoint ^ 1 );
 
     ssl->do_crypt = 1;
 
@@ -1763,6 +1893,7 @@
         return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
     }
 
+    // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63)
     hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12;
 
     if( ssl->in_msg[0] != SSL_HS_FINISHED ||
@@ -1772,8 +1903,6 @@
         return( POLARSSL_ERR_SSL_BAD_HS_FINISHED );
     }
 
-    ssl_calc_finished( ssl, buf, ssl->endpoint ^ 1, &md5, &sha1 );
-
     if( memcmp( ssl->in_msg + 4, buf, hash_len ) != 0 )
     {
         SSL_DEBUG_MSG( 1, ( "bad finished message" ) );
@@ -1834,6 +1963,7 @@
 
      md5_starts( &ssl->fin_md5  );
     sha1_starts( &ssl->fin_sha1 );
+    sha2_starts( &ssl->fin_sha2, 0 );
 
     return( 0 );
 }
@@ -1879,6 +2009,7 @@
 
      md5_starts( &ssl->fin_md5  );
     sha1_starts( &ssl->fin_sha1 );
+    sha2_starts( &ssl->fin_sha2, 0 );
 }
 
 /*
@@ -2199,6 +2330,9 @@
         case SSL_MINOR_VERSION_2:
             return( "TLSv1.1" );
 
+        case SSL_MINOR_VERSION_3:
+            return( "TLSv1.2" );
+
         default:
             break;
     }