Merge remote-tracking branch 'hanno/mpi_read_file_underflow_backport-1.3' into mbedtls-1.3

* hanno/mpi_read_file_underflow_backport-1.3:
  Fix potential stack underflow in mpi_read_file.
diff --git a/ChangeLog b/ChangeLog
index cedfc18..8449329 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,38 @@
 mbed TLS ChangeLog (Sorted per branch, date)
 
-= mbed TLS x.x.x branch released xxxx-xx-xx
+= mbed TLS 1.3.x branch released xxxx-xx-xx
+
+Security
+   * Add exponent blinding to RSA private operations as a countermeasure
+     against side-channel attacks like the cache attack described in
+     https://arxiv.org/abs/1702.08719v2.
+     Found and fix proposed by Michael Schwarz, Samuel Weiser, Daniel Gruss,
+     Clémentine Maurice and Stefan Mangard.
 
 Bugfix
+   * Fix insufficient support for signature-hash-algorithm extension,
+     resulting in compatibility problems with Chrome. Found by hfloyrd. #823
+   * Wipe stack buffers in RSA private key operations
+     (rsa_rsaes_pkcs1_v15_decrypt(), rsa_rsaes_oaep_decrypt).
+     Found by Laurent Simon.
+   * Accept empty trusted CA chain in authentication mode
+     SSL_VERIFY_OPTIONAL. Fixes #864. Found by jethrogb.
+   * Fix implementation of ssl_parse_certificate
+     to not annihilate fatal errors in authentication mode
+     SSL_VERIFY_OPTIONAL and to reflect bad EC curves
+     within verification result.
+   * Fix modular inversion function on invalid modulus 1.
+     Found by blaufish. Fixes #641.
+   * Fix incorrect sign computation in modular exponentiation
+     when dealing with negative MPI. Found by Guido Vranken.
    * Fix potential stack underflow in mpi_read_file.
      Found by Guido Vranken.
 
+Changes
+   * Clarify ECDSA documentation and improve the sample code to avoid
+     misunderstandings and potentially dangerous use of the API. Pointed out
+     by Jean-Philippe Aumasson.
+
 = mbed TLS 1.3.19 branch released 2017-03-08
 
 Security
diff --git a/include/polarssl/bignum.h b/include/polarssl/bignum.h
index 3b55151..c11db96 100644
--- a/include/polarssl/bignum.h
+++ b/include/polarssl/bignum.h
@@ -699,6 +699,8 @@
  *
  * \return         0 if successful,
  *                 POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ *                 POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is <= 1,
+ *                 POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N.
  */
 int mpi_gcd( mpi *G, const mpi *A, const mpi *B );
 
diff --git a/include/polarssl/ecdsa.h b/include/polarssl/ecdsa.h
index 5eb83d9..47b644e 100644
--- a/include/polarssl/ecdsa.h
+++ b/include/polarssl/ecdsa.h
@@ -26,6 +26,23 @@
 
 #include "ecp.h"
 #include "md.h"
+/*
+ *  RFC 4492 page 20:
+ *
+ *      Ecdsa-Sig-Value ::= SEQUENCE {
+ *          r       INTEGER,
+ *          s       INTEGER
+ *      }
+ *
+ * Size is at most
+ *      1 (tag) + 1 (len) + 1 (initial 0) + ECP_MAX_BYTES for each of r and s,
+ *      twice that + 1 (tag) + 2 (len) for the sequence
+ * (assuming ECP_MAX_BYTES is less than 126 for r and s,
+ * and less than 124 (total len <= 255) for the sequence)
+ *
+ */
+/** Maximum size of an ECDSA signature in bytes */
+#define POLARSSL_ECDSA_MAX_LEN  ( 3 + 2 * ( 3 + POLARSSL_ECP_MAX_BYTES ) )
 
 /**
  * \brief           ECDSA context structure
@@ -58,6 +75,10 @@
  * \param f_rng     RNG function
  * \param p_rng     RNG parameter
  *
+ * \note            If the bitlength of the message hash is larger than the
+ *                  bitlength of the group order, then the hash is truncated as
+ *                  prescribed by SEC1 4.1.3 step 5.
+ *
  * \return          0 if successful,
  *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
  */
@@ -78,6 +99,10 @@
  * \param blen      Length of buf
  * \param md_alg    MD algorithm used to hash the message
  *
+ * \note            If the bitlength of the message hash is larger than the
+ *                  bitlength of the group order, then the hash is truncated as
+ *                  prescribed by SEC1 4.1.3 step 5.
+ *
  * \return          0 if successful,
  *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
  */
@@ -96,6 +121,10 @@
  * \param r         First integer of the signature
  * \param s         Second integer of the signature
  *
+ * \note            If the bitlength of the message hash is larger than the
+ *                  bitlength of the group order, then the hash is truncated as
+ *                  prescribed by SEC1 4.1.4 step 3.
+ *
  * \return          0 if successful,
  *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA if signature is invalid
  *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
@@ -121,6 +150,10 @@
  *                  size of the curve used, plus 7 (eg. 71 bytes if a 256-bit
  *                  curve is used).
  *
+ * \note            If the bitlength of the message hash is larger than the
+ *                  bitlength of the group order, then the hash is truncated as
+ *                  prescribed by SEC1 4.1.3 step 5.
+ *
  * \return          0 if successful,
  *                  or a POLARSSL_ERR_ECP, POLARSSL_ERR_MPI or
  *                  POLARSSL_ERR_ASN1 error code
@@ -168,6 +201,10 @@
  * \param sig       Signature to read and verify
  * \param slen      Size of sig
  *
+ * \note            If the bitlength of the message hash is larger than the
+ *                  bitlength of the group order, then the hash is truncated as
+ *                  prescribed by SEC1 4.1.4 step 3.
+ *
  * \return          0 if successful,
  *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA if signature is invalid,
  *                  POLARSSL_ERR_ECP_SIG_LEN_MISTMATCH if the signature is
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index c7cd541..74b1317 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -539,6 +539,7 @@
 typedef struct _ssl_context ssl_context;
 typedef struct _ssl_transform ssl_transform;
 typedef struct _ssl_handshake_params ssl_handshake_params;
+typedef struct _ssl_sig_hash_set_t ssl_sig_hash_set_t;
 #if defined(POLARSSL_SSL_SESSION_TICKETS)
 typedef struct _ssl_ticket_keys ssl_ticket_keys;
 #endif
@@ -625,6 +626,24 @@
 #endif
 };
 
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+/*
+ * Abstraction for a grid of allowed signature-hash-algorithm pairs.
+ */
+struct _ssl_sig_hash_set_t
+{
+    /* At the moment, we only need to remember a single suitable
+     * hash algorithm per signature algorithm. As long as that's
+     * the case - and we don't need a general lookup function -
+     * we can implement the sig-hash-set as a map from signatures
+     * to hash algorithms. */
+    md_type_t rsa;
+    md_type_t ecdsa;
+};
+#endif /* POLARSSL_SSL_PROTO_TLS1_2) &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
 /*
  * This structure contains the parameters only needed during handshake.
  */
@@ -633,7 +652,10 @@
     /*
      * Handshake specific crypto variables
      */
-    int sig_alg;                        /*!<  Hash algorithm for signature   */
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    ssl_sig_hash_set_t hash_algs;       /*!< Set of suitable sig-hash pairs  */
+#endif
     int cert_type;                      /*!<  Requested cert type            */
     int verify_sig_alg;                 /*!<  Signature algorithm for verify */
 #if defined(POLARSSL_DHM_C)
@@ -1957,15 +1979,40 @@
 
 #if defined(POLARSSL_PK_C)
 unsigned char ssl_sig_from_pk( pk_context *pk );
+unsigned char ssl_sig_from_pk_alg( pk_type_t type );
 pk_type_t ssl_pk_alg_from_sig( unsigned char sig );
 #endif
 
 md_type_t ssl_md_alg_from_hash( unsigned char hash );
+unsigned char ssl_hash_from_md_alg( md_type_t md );
 
 #if defined(POLARSSL_SSL_SET_CURVES)
 int ssl_curve_is_acceptable( const ssl_context *ssl, ecp_group_id grp_id );
 #endif
 
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+
+/* Find an entry in a signature-hash set matching a given hash algorithm. */
+md_type_t ssl_sig_hash_set_find( ssl_sig_hash_set_t *set,
+                                 pk_type_t sig_alg );
+/* Add a signature-hash-pair to a signature-hash set */
+void ssl_sig_hash_set_add( ssl_sig_hash_set_t *set,
+                           pk_type_t sig_alg,
+                           md_type_t md_alg );
+/* Allow exactly one hash algorithm for each signature. */
+void ssl_sig_hash_set_const_hash( ssl_sig_hash_set_t *set,
+                                  md_type_t md_alg );
+
+/* Setup an empty signature-hash set */
+static inline void ssl_sig_hash_set_init( ssl_sig_hash_set_t *set )
+{
+    ssl_sig_hash_set_const_hash( set, POLARSSL_MD_NONE );
+}
+
+#endif /* POLARSSL_SSL_PROTO_TLS1_2) &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
 #if defined(POLARSSL_X509_CRT_PARSE_C)
 static inline pk_context *ssl_own_key( ssl_context *ssl )
 {
@@ -1980,6 +2027,12 @@
 }
 
 /*
+ * Check if a hash proposed by the peer is in our list.
+ * Return 0 if we're willing to use it, -1 otherwise.
+ */
+int ssl_check_sig_hash( md_type_t md );
+
+/*
  * Check usage of a certificate wrt extensions:
  * keyUsage, extendedKeyUsage (later), and nSCertType (later).
  *
diff --git a/include/polarssl/ssl_ciphersuites.h b/include/polarssl/ssl_ciphersuites.h
index f0519ca..2666b3b 100644
--- a/include/polarssl/ssl_ciphersuites.h
+++ b/include/polarssl/ssl_ciphersuites.h
@@ -290,6 +290,7 @@
 
 #if defined(POLARSSL_PK_C)
 pk_type_t ssl_get_ciphersuite_sig_pk_alg( const ssl_ciphersuite_t *info );
+pk_type_t ssl_get_ciphersuite_sig_alg( const ssl_ciphersuite_t *info );
 #endif
 
 int ssl_ciphersuite_uses_ec( const ssl_ciphersuite_t *info );
diff --git a/include/polarssl/x509.h b/include/polarssl/x509.h
index cd01539..adaacff 100644
--- a/include/polarssl/x509.h
+++ b/include/polarssl/x509.h
@@ -97,6 +97,8 @@
 #define BADCERT_KEY_USAGE         0x0800  /**< Usage does not match the keyUsage extension. */
 #define BADCERT_EXT_KEY_USAGE     0x1000  /**< Usage does not match the extendedKeyUsage extension. */
 #define BADCERT_NS_CERT_TYPE      0x2000  /**< Usage does not match the nsCertType extension. */
+#define BADCERT_BAD_KEY          0x10000  /**< Bad key (e.g. unsupported elliptic curve in use) */
+
 /* \} name */
 /* \} addtogroup x509_module */
 
diff --git a/library/bignum.c b/library/bignum.c
index 5873b66..4829d91 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -1788,7 +1788,7 @@
      */
     mpi_montred( X, N, mm, &T );
 
-    if( neg )
+    if( neg && E->n != 0 && ( E->p[0] & 1 ) != 0 )
     {
         X->s = -1;
         MPI_CHK( mpi_add_mpi( X, N, X ) );
@@ -1891,7 +1891,7 @@
     int ret;
     mpi G, TA, TU, U1, U2, TB, TV, V1, V2;
 
-    if( mpi_cmp_int( N, 0 ) <= 0 )
+    if( mpi_cmp_int( N, 1 ) <= 0 )
         return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
 
     mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 );
diff --git a/library/rsa.c b/library/rsa.c
index 79726c1..1cdf0d6 100644
--- a/library/rsa.c
+++ b/library/rsa.c
@@ -24,6 +24,11 @@
  *
  *  http://theory.lcs.mit.edu/~rivest/rsapaper.pdf
  *  http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf
+ *  [3] Malware Guard Extension: Using SGX to Conceal Cache Attacks
+ *      Michael Schwarz, Samuel Weiser, Daniel Gruss, Clémentine Maurice and
+ *      Stefan Mangard
+ *      https://arxiv.org/abs/1702.08719v2
+ *
  */
 
 #if !defined(POLARSSL_CONFIG_FILE)
@@ -56,6 +61,11 @@
 #define polarssl_free   free
 #endif
 
+/* Implementation that should never be optimized out by the compiler */
+static void polarssl_zeroize( void *v, size_t n ) {
+    volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
+}
+
 /*
  * Initialize an RSA context
  */
@@ -351,6 +361,27 @@
 }
 
 /*
+ * Exponent blinding supposed to prevent side-channel attacks using multiple
+ * traces of measurements to recover the RSA key. The more collisions are there,
+ * the more bits of the key can be recovered. See [3].
+ *
+ * Collecting n collisions with m bit long blinding value requires 2^(m-m/n)
+ * observations on avarage.
+ *
+ * For example with 28 byte blinding to achieve 2 collisions the adversary has
+ * to make 2^112 observations on avarage.
+ *
+ * (With the currently (as of 2017 April) known best algorithms breaking 2048
+ * bit RSA requires approximately as much time as trying out 2^112 random keys.
+ * Thus in this sense with 28 byte blinding the security is not reduced by
+ * side-channel attacks like the one in [3])
+ *
+ * This countermeasure does not help if the key recovery is possible with a
+ * single trace.
+ */
+#define RSA_EXPONENT_BLINDING 28
+
+/*
  * Do an RSA private key operation
  */
 int rsa_private( rsa_context *ctx,
@@ -362,8 +393,28 @@
     int ret;
     size_t olen;
     mpi T, T1, T2;
+    mpi P1, Q1, R;
+#if defined(POLARSSL_RSA_NO_CRT)
+    mpi D_blind;
+    mpi *D = &ctx->D;
+#else
+    mpi DP_blind, DQ_blind;
+    mpi *DP = &ctx->DP;
+    mpi *DQ = &ctx->DQ;
+#endif
 
     mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 );
+    mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &R );
+
+    if( f_rng != NULL )
+    {
+#if defined(POLARSSL_RSA_NO_CRT)
+        mpi_init( &D_blind );
+#else
+        mpi_init( &DP_blind );
+        mpi_init( &DQ_blind );
+#endif
+    }
 
 #if defined(POLARSSL_THREADING_C)
     if( ( ret = polarssl_mutex_lock( &ctx->mutex ) ) != 0 )
@@ -386,19 +437,60 @@
         MPI_CHK( rsa_prepare_blinding( ctx, f_rng, p_rng ) );
         MPI_CHK( mpi_mul_mpi( &T, &T, &ctx->Vi ) );
         MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) );
+
+        /*
+         * Exponent blinding
+         */
+        MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
+        MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
+
+#if defined(POLARSSL_RSA_NO_CRT)
+        /*
+         * D_blind = ( P - 1 ) * ( Q - 1 ) * R + D
+         */
+        MPI_CHK( mpi_fill_random( &R, RSA_EXPONENT_BLINDING,
+                         f_rng, p_rng ) );
+        MPI_CHK( mpi_mul_mpi( &D_blind, &P1, &Q1 ) );
+        MPI_CHK( mpi_mul_mpi( &D_blind, &D_blind, &R ) );
+        MPI_CHK( mpi_add_mpi( &D_blind, &D_blind, &ctx->D ) );
+
+        D = &D_blind;
+#else
+        /*
+         * DP_blind = ( P - 1 ) * R + DP
+         */
+        MPI_CHK( mpi_fill_random( &R, RSA_EXPONENT_BLINDING,
+                         f_rng, p_rng ) );
+        MPI_CHK( mpi_mul_mpi( &DP_blind, &P1, &R ) );
+        MPI_CHK( mpi_add_mpi( &DP_blind, &DP_blind,
+                    &ctx->DP ) );
+
+        DP = &DP_blind;
+
+        /*
+         * DQ_blind = ( Q - 1 ) * R + DQ
+         */
+        MPI_CHK( mpi_fill_random( &R, RSA_EXPONENT_BLINDING,
+                         f_rng, p_rng ) );
+        MPI_CHK( mpi_mul_mpi( &DQ_blind, &Q1, &R ) );
+        MPI_CHK( mpi_add_mpi( &DQ_blind, &DQ_blind,
+                    &ctx->DQ ) );
+
+        DQ = &DQ_blind;
+#endif /* POLARSSL_RSA_NO_CRT */
     }
 
 #if defined(POLARSSL_RSA_NO_CRT)
-    MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) );
+    MPI_CHK( mpi_exp_mod( &T, &T, D, &ctx->N, &ctx->RN ) );
 #else
     /*
-     * faster decryption using the CRT
+     * Faster decryption using the CRT
      *
      * T1 = input ^ dP mod P
      * T2 = input ^ dQ mod Q
      */
-    MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) );
-    MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) );
+    MPI_CHK( mpi_exp_mod( &T1, &T, DP, &ctx->P, &ctx->RP ) );
+    MPI_CHK( mpi_exp_mod( &T2, &T, DQ, &ctx->Q, &ctx->RQ ) );
 
     /*
      * T = (T1 - T2) * (Q^-1 mod P) mod P
@@ -434,6 +526,17 @@
 #endif
 
     mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 );
+    mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &R );
+
+    if( f_rng != NULL )
+    {
+#if defined(POLARSSL_RSA_NO_CRT)
+        mpi_free( &D_blind );
+#else
+        mpi_free( &DP_blind );
+        mpi_free( &DQ_blind );
+#endif
+    }
 
     if( ret != 0 )
         return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret );
@@ -487,6 +590,8 @@
 
         dlen -= use_len;
     }
+
+    polarssl_zeroize( mask, sizeof( mask ) );
 }
 #endif /* POLARSSL_PKCS1_V21 */
 
@@ -720,7 +825,7 @@
           : rsa_private( ctx, f_rng, p_rng, input, buf );
 
     if( ret != 0 )
-        return( ret );
+        goto cleanup;
 
     /*
      * Unmask data and generate lHash
@@ -785,15 +890,26 @@
      * the different error conditions.
      */
     if( bad != 0 )
-        return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    {
+        ret = POLARSSL_ERR_RSA_INVALID_PADDING;
+        goto cleanup;
+    }
 
     if( ilen - ( p - buf ) > output_max_len )
-        return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE );
+    {
+        ret = POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE;
+        goto cleanup;
+    }
 
     *olen = ilen - (p - buf);
     memcpy( output, p, *olen );
+    ret = 0;
 
-    return( 0 );
+cleanup:
+    polarssl_zeroize( buf, sizeof( buf ) );
+    polarssl_zeroize( lhash, sizeof( lhash ) );
+
+    return( ret );
 }
 #endif /* POLARSSL_PKCS1_V21 */
 
@@ -827,7 +943,7 @@
           : rsa_private( ctx, f_rng, p_rng, input, buf );
 
     if( ret != 0 )
-        return( ret );
+        goto cleanup;
 
     p = buf;
     bad = 0;
@@ -872,15 +988,25 @@
     bad |= ( pad_count < 8 );
 
     if( bad )
-        return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    {
+        ret = POLARSSL_ERR_RSA_INVALID_PADDING;
+        goto cleanup;
+    }
 
     if( ilen - ( p - buf ) > output_max_len )
-        return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE );
+    {
+        ret = POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE;
+        goto cleanup;
+    }
 
     *olen = ilen - (p - buf);
     memcpy( output, p, *olen );
+    ret = 0;
 
-    return( 0 );
+cleanup:
+    polarssl_zeroize( buf, sizeof( buf ) );
+
+    return( ret );
 }
 #endif /* POLARSSL_PKCS1_V15 */
 
@@ -985,6 +1111,7 @@
     if( ( ret = md_init_ctx( &md_ctx, md_info ) ) != 0 )
     {
         md_free( &md_ctx );
+        /* No need to zeroize salt: we didn't use it. */
         return( ret );
     }
 
@@ -995,6 +1122,7 @@
     md_update( &md_ctx, hash, hashlen );
     md_update( &md_ctx, salt, slen );
     md_finish( &md_ctx, p );
+    polarssl_zeroize( salt, sizeof( salt ) );
 
     // Compensate for boundary condition when applying mask
     //
diff --git a/library/ssl_ciphersuites.c b/library/ssl_ciphersuites.c
index 0fee1e6..8204731 100644
--- a/library/ssl_ciphersuites.c
+++ b/library/ssl_ciphersuites.c
@@ -1803,6 +1803,24 @@
             return( POLARSSL_PK_NONE );
     }
 }
+
+pk_type_t ssl_get_ciphersuite_sig_alg( const ssl_ciphersuite_t *info )
+{
+    switch( info->key_exchange )
+    {
+        case POLARSSL_KEY_EXCHANGE_RSA:
+        case POLARSSL_KEY_EXCHANGE_DHE_RSA:
+        case POLARSSL_KEY_EXCHANGE_ECDHE_RSA:
+            return( POLARSSL_PK_RSA );
+
+        case POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA:
+            return( POLARSSL_PK_ECDSA );
+
+        default:
+            return( POLARSSL_PK_NONE );
+    }
+}
+
 #endif /* POLARSSL_PK_C */
 
 #if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C)
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 90d5ac7..8ad990b 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -467,6 +467,18 @@
 
 #if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
     defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+/*
+ * Status of the implementation of signature-algorithms extension:
+ *
+ * Currently, we are only considering the signature-algorithm extension
+ * to pick a ciphersuite which allows us to send the ServerKeyExchange
+ * message with a signature-hash combination that the user allows.
+ *
+ * We do *not* check whether all certificates in our certificate
+ * chain are signed with an allowed signature-hash pair.
+ * This needs to be done at a later stage.
+ *
+ */
 static int ssl_parse_signature_algorithms_ext( ssl_context *ssl,
                                                const unsigned char *buf,
                                                size_t len )
@@ -474,8 +486,9 @@
     size_t sig_alg_list_size;
     const unsigned char *p;
     const unsigned char *end = buf + len;
-    const int *md_cur;
 
+    md_type_t md_cur;
+    pk_type_t sig_cur;
 
     sig_alg_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) );
     if( sig_alg_list_size + 2 != len ||
@@ -485,35 +498,48 @@
         return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
     }
 
-    /*
-     * For now, ignore the SignatureAlgorithm part and rely on offered
-     * ciphersuites only for that part. To be fixed later.
+    /* Currently we only guarantee signing the ServerKeyExchange message according
+     * to the constraints specified in this extension (see above), so it suffices
+     * to remember only one suitable hash for each possible signature algorithm.
      *
-     * So, just look at the HashAlgorithm part.
+     * This will change when we also consider certificate signatures,
+     * in which case we will need to remember the whole signature-hash
+     * pair list from the extension.
      */
-    for( md_cur = md_list(); *md_cur != POLARSSL_MD_NONE; md_cur++ ) {
-#if !defined(POLARSSL_SSL_ENABLE_MD5_SIGNATURES)
-        /* Skip MD5 */
-        if( *md_cur == POLARSSL_MD_MD5 )
-            continue;
-#endif
 
-        for( p = buf + 2; p < end; p += 2 ) {
-            if( *md_cur == (int) ssl_md_alg_from_hash( p[0] ) ) {
-                ssl->handshake->sig_alg = p[0];
-                goto have_sig_alg;
-            }
+    for( p = buf + 2; p < end; p += 2 ) {
+
+        /* Silently ignore unknown signature or hash algorithms. */
+
+        if( (sig_cur = ssl_pk_alg_from_sig( p[1] ) ) == POLARSSL_PK_NONE )
+        {
+            SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: unknown sig alg encoding %d",
+                                p[1] ) );
+            continue;
+        }
+
+        /* Check if we support the hash the user proposes */
+        md_cur = ssl_md_alg_from_hash( p[0] );
+        if( md_cur == POLARSSL_MD_NONE )
+        {
+            SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: "
+                                "unknown hash alg encoding %d", p[0] ) );
+            continue;
+        }
+
+        if( ssl_check_sig_hash( md_cur ) == 0 )
+        {
+            ssl_sig_hash_set_add( &ssl->handshake->hash_algs, sig_cur, md_cur );
+            SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: match sig %d and hash %d",
+                                        sig_cur, md_cur ) );
+        }
+        else
+        {
+            SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: hash alg %d not supported",
+                                md_cur ) );
         }
     }
 
-    /* Some key echanges do not need signatures at all */
-    SSL_DEBUG_MSG( 3, ( "no signature_algorithm in common" ) );
-    return( 0 );
-
-have_sig_alg:
-    SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: %d",
-                   ssl->handshake->sig_alg ) );
-
     return( 0 );
 }
 #endif /* POLARSSL_SSL_PROTO_TLS1_2 &&
@@ -932,6 +958,11 @@
 {
     const ssl_ciphersuite_t *suite_info;
 
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    pk_type_t sig_type;
+#endif
+
     suite_info = ssl_ciphersuite_from_id( suite_id );
     if( suite_info == NULL )
     {
@@ -979,6 +1010,26 @@
     }
 #endif
 
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    /* If the ciphersuite requires signing, check whether
+     * a suitable hash algorithm is present. */
+    if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
+    {
+        sig_type = ssl_get_ciphersuite_sig_alg( suite_info );
+        if( sig_type != POLARSSL_PK_NONE &&
+            ssl_sig_hash_set_find( &ssl->handshake->hash_algs, sig_type ) == POLARSSL_MD_NONE )
+        {
+            SSL_DEBUG_MSG( 3, ( "ciphersuite mismatch: no suitable hash algorithm "
+                                "for signature algorithm %d", sig_type ) );
+            return( 0 );
+        }
+    }
+
+#endif /* POLARSSL_SSL_PROTO_TLS1_2 &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
+
 #if defined(POLARSSL_X509_CRT_PARSE_C)
     /*
      * Final check: if ciphersuite requires us to have a
@@ -1287,6 +1338,15 @@
     const int *ciphersuites;
     const ssl_ciphersuite_t *ciphersuite_info;
 
+    /* If there is no signature-algorithm extension present,
+     * we need to fall back to the default values for allowed
+     * signature-hash pairs. */
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    int sig_hash_alg_ext_present = 0;
+#endif /* POLARSSL_SSL_PROTO_TLS1_2 &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
     SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) );
 
 #if defined(POLARSSL_SSL_RENEGOTIATION)
@@ -1621,6 +1681,8 @@
                 ret = ssl_parse_signature_algorithms_ext( ssl, ext + 4, ext_size );
                 if( ret != 0 )
                     return( ret );
+
+                sig_hash_alg_ext_present = 1;
                 break;
     #endif /* POLARSSL_SSL_PROTO_TLS1_2 &&
               POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
@@ -1764,6 +1826,27 @@
         return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
     }
 
+
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+
+    /*
+     * Try to fall back to default hash SHA1 if the client
+     * hasn't provided any preferred signature-hash combinations.
+     */
+    if( sig_hash_alg_ext_present == 0 )
+    {
+        md_type_t md_default = POLARSSL_MD_SHA1;
+
+        if( ssl_check_sig_hash( md_default ) != 0 )
+            md_default = POLARSSL_MD_NONE;
+
+        ssl_sig_hash_set_const_hash( &ssl->handshake->hash_algs, md_default );
+    }
+
+#endif /* POLARSSL_SSL_PROTO_TLS1_2 &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
     /*
      * Search for a matching ciphersuite
      * (At the end because we need information from the EC-based extensions
@@ -1821,6 +1904,28 @@
     ssl->in_left = 0;
     ssl->state++;
 
+    /* Debugging-only output for testsuite */
+#if defined(POLARSSL_DEBUG_C)                         && \
+    defined(POLARSSL_SSL_PROTO_TLS1_2)                && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
+    {
+        pk_type_t sig_alg = ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info );
+        if( sig_alg != POLARSSL_PK_NONE )
+        {
+            md_type_t md_alg = ssl_sig_hash_set_find( &ssl->handshake->hash_algs,
+                                                      sig_alg );
+            SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: %d",
+                                ssl_hash_from_md_alg( md_alg ) ) );
+        }
+        else
+        {
+            SSL_DEBUG_MSG( 3, ( "no hash algorithm for signature algorithm %d - should not happen",
+                                sig_alg ) );
+        }
+    }
+#endif
+
     SSL_DEBUG_MSG( 2, ( "<= parse client hello" ) );
 
     return( 0 );
@@ -2665,17 +2770,25 @@
         size_t signature_len = 0;
         unsigned int hashlen = 0;
         unsigned char hash[64];
-        md_type_t md_alg = POLARSSL_MD_NONE;
 
         /*
-         * Choose hash algorithm. NONE means MD5 + SHA1 here.
+         * Choose hash algorithm:
+         * - For TLS 1.2, obey signature-hash-algorithm extension to choose appropriate hash.
+         * - For SSL3, TLS1.0, TLS1.1 and ECDHE_ECDSA, use SHA1 (RFC 4492, Sec. 5.4)
+         * - Otherwise, use MD5 + SHA1 (RFC 4346, Sec. 7.4.3)
          */
+
+        md_type_t md_alg;
+
 #if defined(POLARSSL_SSL_PROTO_TLS1_2)
+        pk_type_t sig_alg = ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info );
         if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
         {
-            md_alg = ssl_md_alg_from_hash( ssl->handshake->sig_alg );
+            /* For TLS 1.2, obey signature-hash-algorithm extension
+             * (RFC 5246, Sec. 7.4.1.4.1). */
 
-            if( md_alg == POLARSSL_MD_NONE )
+            if( sig_alg == POLARSSL_PK_NONE ||
+                ( md_alg = ssl_sig_hash_set_find( &ssl->handshake->hash_algs, sig_alg ) ) == POLARSSL_MD_NONE )
             {
                 SSL_DEBUG_MSG( 1, ( "should never happen" ) );
                 return( POLARSSL_ERR_SSL_INTERNAL_ERROR );
@@ -2696,6 +2809,8 @@
             md_alg = POLARSSL_MD_NONE;
         }
 
+        SSL_DEBUG_MSG( 3, ( "pick hash algorithm %d for signing", md_alg ) );
+
         /*
          * Compute the hash to be signed
          */
@@ -2794,8 +2909,8 @@
 #if defined(POLARSSL_SSL_PROTO_TLS1_2)
         if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
         {
-            *(p++) = ssl->handshake->sig_alg;
-            *(p++) = ssl_sig_from_pk( ssl_own_key( ssl ) );
+            *(p++) = ssl_hash_from_md_alg( md_alg );
+            *(p++) = ssl_sig_from_pk_alg( sig_alg );
 
             n += 2;
         }
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 8604997..5779229 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2841,12 +2841,6 @@
 
     if( ssl->authmode != SSL_VERIFY_NONE )
     {
-        if( ssl->ca_chain == NULL )
-        {
-            SSL_DEBUG_MSG( 1, ( "got no CA chain" ) );
-            return( POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED );
-        }
-
         /*
          * Main check: verify certificate
          */
@@ -2872,6 +2866,8 @@
             if( pk_can_do( pk, POLARSSL_PK_ECKEY ) &&
                 ! ssl_curve_is_acceptable( ssl, pk_ec( *pk )->grp.id ) )
             {
+                ssl->session_negotiate->verify_result |= BADCERT_BAD_KEY;
+
                 SSL_DEBUG_MSG( 1, ( "bad certificate (EC key curve)" ) );
                 if( ret == 0 )
                     ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE;
@@ -2889,8 +2885,36 @@
                 ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE;
         }
 
-        if( ssl->authmode != SSL_VERIFY_REQUIRED )
+        /* x509_crt_verify_with_profile is supposed to report a
+         * verification failure through POLARSSL_ERR_X509_CERT_VERIFY_FAILED,
+         * with details encoded in the verification flags. All other kinds
+         * of error codes, including those from the user provided f_vrfy
+         * functions, are treated as fatal and lead to a failure of
+         * ssl_parse_certificate even if verification was optional. */
+        if( ssl->authmode == SSL_VERIFY_OPTIONAL &&
+            ( ret == POLARSSL_ERR_X509_CERT_VERIFY_FAILED ||
+              ret == POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ) )
+        {
             ret = 0;
+        }
+
+        if( ssl->ca_chain == NULL && ssl->authmode == SSL_VERIFY_REQUIRED )
+        {
+            SSL_DEBUG_MSG( 1, ( "got no CA chain" ) );
+            ret = POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED;
+        }
+
+#if defined(POLARSSL_DEBUG_C)
+        if( ssl->session_negotiate->verify_result != 0 )
+        {
+            SSL_DEBUG_MSG( 3, ( "! Certificate verification flags %x",
+                                ssl->session_negotiate->verify_result ) );
+        }
+        else
+        {
+            SSL_DEBUG_MSG( 3, ( "Certificate verification flags clear" ) );
+        }
+#endif /* POLARSSL_DEBUG_C */
     }
 
     SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) );
@@ -3526,7 +3550,11 @@
 #endif /* POLARSSL_SSL_PROTO_TLS1_2 */
 
     handshake->update_checksum = ssl_update_checksum_start;
-    handshake->sig_alg = SSL_HASH_SHA1;
+
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+    ssl_sig_hash_set_init( &handshake->hash_algs );
+#endif
 
 #if defined(POLARSSL_DHM_C)
     dhm_init( &handshake->dhm_ctx );
@@ -5166,6 +5194,19 @@
     return( SSL_SIG_ANON );
 }
 
+unsigned char ssl_sig_from_pk_alg( pk_type_t type )
+{
+    switch( type ) {
+        case POLARSSL_PK_RSA:
+            return( SSL_SIG_RSA );
+        case POLARSSL_PK_ECDSA:
+        case POLARSSL_PK_ECKEY:
+            return( SSL_SIG_ECDSA );
+        default:
+            return( SSL_SIG_ANON );
+    }
+}
+
 pk_type_t ssl_pk_alg_from_sig( unsigned char sig )
 {
     switch( sig )
@@ -5184,6 +5225,57 @@
 }
 #endif /* POLARSSL_PK_C */
 
+#if defined(POLARSSL_SSL_PROTO_TLS1_2) && \
+    defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+
+/* Find an entry in a signature-hash set matching a given hash algorithm. */
+md_type_t ssl_sig_hash_set_find( ssl_sig_hash_set_t *set,
+                                 pk_type_t sig_alg )
+{
+    switch( sig_alg )
+    {
+        case POLARSSL_PK_RSA:
+            return( set->rsa );
+        case POLARSSL_PK_ECDSA:
+            return( set->ecdsa );
+        default:
+            return( POLARSSL_MD_NONE );
+    }
+}
+
+/* Add a signature-hash-pair to a signature-hash set */
+void ssl_sig_hash_set_add( ssl_sig_hash_set_t *set,
+                           pk_type_t sig_alg,
+                           md_type_t md_alg )
+{
+    switch( sig_alg )
+    {
+        case POLARSSL_PK_RSA:
+            if( set->rsa == POLARSSL_MD_NONE )
+                set->rsa = md_alg;
+            break;
+
+        case POLARSSL_PK_ECDSA:
+            if( set->ecdsa == POLARSSL_MD_NONE )
+                set->ecdsa = md_alg;
+            break;
+
+        default:
+            break;
+    }
+}
+
+/* Allow exactly one hash algorithm for each signature. */
+void ssl_sig_hash_set_const_hash( ssl_sig_hash_set_t *set,
+                                   md_type_t md_alg )
+{
+    set->rsa   = md_alg;
+    set->ecdsa = md_alg;
+}
+
+#endif /* POLARSSL_SSL_PROTO_TLS1_2) &&
+          POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
 /*
  * Convert between SSL_HASH_XXX and POLARSSL_MD_XXX
  */
@@ -5216,6 +5308,38 @@
     }
 }
 
+/*
+ * Convert from POLARSSL_MD_XXX to SSL_HASH_XXX
+ */
+unsigned char ssl_hash_from_md_alg( md_type_t md )
+{
+    switch( md )
+    {
+#if defined(POLARSSL_MD5_C)
+        case POLARSSL_MD_MD5:
+            return( SSL_HASH_MD5 );
+#endif
+#if defined(POLARSSL_SHA1_C)
+        case POLARSSL_MD_SHA1:
+            return( SSL_HASH_SHA1 );
+#endif
+#if defined(POLARSSL_SHA256_C)
+        case POLARSSL_MD_SHA224:
+            return( SSL_HASH_SHA224 );
+        case POLARSSL_MD_SHA256:
+            return( SSL_HASH_SHA256 );
+#endif
+#if defined(POLARSSL_SHA512_C)
+        case POLARSSL_MD_SHA384:
+            return( SSL_HASH_SHA384 );
+        case POLARSSL_MD_SHA512:
+            return( SSL_HASH_SHA512 );
+#endif
+        default:
+            return( SSL_HASH_NONE );
+    }
+}
+
 #if defined(POLARSSL_SSL_SET_CURVES)
 /*
  * Check is a curve proposed by the peer is in our list.
@@ -5233,6 +5357,30 @@
 }
 #endif /* POLARSSL_SSL_SET_CURVES */
 
+#if defined(POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED)
+/*
+ * Check if a hash proposed by the peer is in our list.
+ * Return 0 if we're willing to use it, -1 otherwise.
+ */
+int ssl_check_sig_hash( md_type_t md )
+{
+    const int *cur;
+
+    for( cur = md_list(); *cur != POLARSSL_MD_NONE; cur++ )
+    {
+#if !defined(POLARSSL_SSL_ENABLE_MD5_SIGNATURES)
+        /* Skip MD5 */
+        if( *cur == POLARSSL_MD_MD5 )
+            continue;
+#endif
+        if( *cur == (int) md )
+            return( 0 );
+    }
+
+    return( -1 );
+}
+#endif /* POLARSSL_KEY_EXCHANGE__WITH_CERT__ENABLED */
+
 #if defined(POLARSSL_X509_CRT_PARSE_C)
 int ssl_check_cert_usage( const x509_crt *cert,
                           const ssl_ciphersuite_t *ciphersuite,
diff --git a/library/x509_crt.c b/library/x509_crt.c
index a3517f6..16a29b5 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -1403,6 +1403,7 @@
     { BADCERT_KEY_USAGE,     "Usage does not match the keyUsage extension" },
     { BADCERT_EXT_KEY_USAGE, "Usage does not match the extendedKeyUsage extension" },
     { BADCERT_NS_CERT_TYPE,  "Usage does not match the nsCertType extension" },
+    { BADCERT_BAD_KEY,       "The certificate uses an invalid key (e.g. unsupported elliptic curve)" },
     { 0, NULL }
 };
 
diff --git a/programs/pkey/ecdsa.c b/programs/pkey/ecdsa.c
index aa8eafb..1411e42 100644
--- a/programs/pkey/ecdsa.c
+++ b/programs/pkey/ecdsa.c
@@ -38,6 +38,7 @@
 #include "polarssl/entropy.h"
 #include "polarssl/ctr_drbg.h"
 #include "polarssl/ecdsa.h"
+#include "polarssl/sha256.h"
 
 #include <string.h>
 #endif
@@ -102,16 +103,20 @@
     ecdsa_context ctx_sign, ctx_verify;
     entropy_context entropy;
     ctr_drbg_context ctr_drbg;
-    unsigned char hash[] = "This should be the hash of a message.";
-    unsigned char sig[512];
+    sha256_context sha256_ctx;
+    unsigned char message[100];
+    unsigned char hash[32];
+    unsigned char sig[POLARSSL_ECDSA_MAX_LEN];
     size_t sig_len;
     const char *pers = "ecdsa";
     ((void) argv);
 
     ecdsa_init( &ctx_sign );
     ecdsa_init( &ctx_verify );
+    sha256_init( &sha256_ctx );
 
-    memset(sig, 0, sizeof( sig ) );
+    memset( sig, 0, sizeof( sig ) );
+    memset( message, 0x25, sizeof( message ) );
     ret = 1;
 
     if( argc != 1 )
@@ -155,9 +160,24 @@
     dump_pubkey( "  + Public key: ", &ctx_sign );
 
     /*
-     * Sign some message hash
+     * Compute message hash
      */
     polarssl_printf( "  . Signing message..." );
+    polarssl_printf( "  . Computing message hash..." );
+    fflush( stdout );
+
+    sha256_starts( &sha256_ctx, 0 );
+    sha256_update( &sha256_ctx, message, sizeof( message ) );
+    sha256_finish( &sha256_ctx, hash );
+
+    polarssl_printf( " ok\n" );
+
+    dump_buf( "  + Hash: ", hash, sizeof( hash ) );
+
+    /*
+     * Sign message hash
+     */
+    polarssl_printf( "  . Signing message hash..." );
     fflush( stdout );
 
     if( ( ret = ecdsa_write_signature( &ctx_sign,
@@ -170,7 +190,6 @@
     }
     polarssl_printf( " ok (signature length = %u)\n", (unsigned int) sig_len );
 
-    dump_buf( "  + Hash: ", hash, sizeof hash );
     dump_buf( "  + Signature: ", sig, sig_len );
 
     /*
@@ -233,6 +252,7 @@
     ecdsa_free( &ctx_sign );
     ctr_drbg_free( &ctr_drbg );
     entropy_free( &entropy );
+    sha256_free( &sha256_ctx );
 
     return( ret );
 }
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index cdadf59..e302244 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -92,6 +92,7 @@
 #define DFL_RECO_DELAY          0
 #define DFL_TICKETS             SSL_SESSION_TICKETS_ENABLED
 #define DFL_ALPN_STRING         NULL
+#define DFL_CURVES              NULL
 #define DFL_FALLBACK            -1
 #define DFL_EXTENDED_MS         -1
 #define DFL_ETM                 -1
@@ -169,6 +170,17 @@
 #define USAGE_ALPN ""
 #endif /* POLARSSL_SSL_ALPN */
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+#define USAGE_CURVES \
+    "    curves=a,b,c,d      default: \"default\" (library default)\n"  \
+    "                        example: \"secp521r1,brainpoolP512r1\"\n"  \
+    "                        - use \"none\" for empty list\n"           \
+    "                        - see ecp_curve_list()\n"                  \
+    "                          for acceptable curve names\n"
+#else
+#define USAGE_CURVES ""
+#endif /* POLARSSL_SSL_SET_CURVES */
+
 #if defined(POLARSSL_SSL_FALLBACK_SCSV)
 #define USAGE_FALLBACK \
     "    fallback=0/1        default: (library default: off)\n"
@@ -226,6 +238,7 @@
     USAGE_MAX_FRAG_LEN                                      \
     USAGE_TRUNC_HMAC                                        \
     USAGE_ALPN                                              \
+    USAGE_CURVES                                            \
     USAGE_FALLBACK                                          \
     USAGE_EMS                                               \
     USAGE_ETM                                               \
@@ -285,6 +298,7 @@
     int reconnect;              /* attempt to resume session                */
     int reco_delay;             /* delay in seconds before resuming session */
     int tickets;                /* enable / disable session tickets         */
+    const char *curves;         /* list of supported elliptic curves        */
     const char *alpn_string;    /* ALPN supported protocols                 */
     int fallback;               /* is this a fallback connection?           */
     int extended_ms;            /* negotiate extended master secret?        */
@@ -373,6 +387,11 @@
 #if defined(POLARSSL_SSL_ALPN)
     const char *alpn_list[10];
 #endif
+#if defined(POLARSSL_SSL_SET_CURVES)
+    ecp_group_id curve_list[20];
+    const ecp_curve_info *curve_cur;
+#endif
+
     const char *pers = "ssl_client2";
 
     entropy_context entropy;
@@ -453,6 +472,7 @@
     opt.reco_delay          = DFL_RECO_DELAY;
     opt.tickets             = DFL_TICKETS;
     opt.alpn_string         = DFL_ALPN_STRING;
+    opt.curves              = DFL_CURVES;
     opt.fallback            = DFL_FALLBACK;
     opt.extended_ms         = DFL_EXTENDED_MS;
     opt.etm                 = DFL_ETM;
@@ -584,6 +604,8 @@
                 default: goto usage;
             }
         }
+        else if( strcmp( p, "curves" ) == 0 )
+            opt.curves = q;
         else if( strcmp( p, "etm" ) == 0 )
         {
             switch( atoi( q ) )
@@ -775,6 +797,64 @@
     }
 #endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+    if( opt.curves != NULL )
+    {
+        p = (char *) opt.curves;
+        i = 0;
+
+        if( strcmp( p, "none" ) == 0 )
+        {
+            curve_list[0] = POLARSSL_ECP_DP_NONE;
+        }
+        else if( strcmp( p, "default" ) != 0 )
+        {
+            /* Leave room for a final NULL in curve list */
+            while( i < (int) ( sizeof( curve_list ) / sizeof( *curve_list ) ) - 1
+                   && *p != '\0' )
+            {
+                q = p;
+
+                /* Terminate the current string */
+                while( *p != ',' && *p != '\0' )
+                    p++;
+                if( *p == ',' )
+                    *p++ = '\0';
+
+                if( ( curve_cur = ecp_curve_info_from_name( q ) ) != NULL )
+                {
+                    curve_list[i++] = curve_cur->grp_id;
+                }
+                else
+                {
+                    polarssl_printf( "unknown curve %s\n", q );
+                    polarssl_printf( "supported curves: " );
+                    for( curve_cur = ecp_curve_list();
+                         curve_cur->grp_id != POLARSSL_ECP_DP_NONE;
+                         curve_cur++ )
+                    {
+                        polarssl_printf( "%s ", curve_cur->name );
+                    }
+                    polarssl_printf( "\n" );
+                    goto exit;
+                }
+            }
+
+            polarssl_printf( "Number of curves: %d\n", i );
+
+            if( i == (int) ( sizeof( curve_list ) / sizeof( *curve_list ) ) - 1
+                && *p != '\0' )
+            {
+                polarssl_printf( "curves list too long, maximum %zu",
+                                 (size_t) ( sizeof( curve_list ) / sizeof( *curve_list ) - 1 ) );
+                goto exit;
+            }
+
+            curve_list[i] = POLARSSL_ECP_DP_NONE;
+        }
+    }
+#endif /* POLARSSL_SSL_SET_CURVES */
+
 #if defined(POLARSSL_SSL_ALPN)
     if( opt.alpn_string != NULL )
     {
@@ -996,6 +1076,14 @@
         }
 #endif
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+    if( opt.curves != NULL &&
+        strcmp( opt.curves, "default" ) != 0 )
+    {
+        ssl_set_curves( &ssl, curve_list );
+    }
+#endif
+
     ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
     ssl_set_dbg( &ssl, my_debug, stdout );
 
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 095fabd..95438f9 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -106,6 +106,7 @@
 #define DFL_CACHE_TIMEOUT       -1
 #define DFL_SNI                 NULL
 #define DFL_ALPN_STRING         NULL
+#define DFL_CURVES              NULL
 #define DFL_DHM_FILE            NULL
 #define DFL_EXTENDED_MS         -1
 #define DFL_ETM                 -1
@@ -215,6 +216,17 @@
 #define USAGE_ALPN ""
 #endif /* POLARSSL_SSL_ALPN */
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+#define USAGE_CURVES \
+    "    curves=a,b,c,d      default: \"default\" (library default)\n"  \
+    "                        example: \"secp521r1,brainpoolP512r1\"\n"  \
+    "                        - use \"none\" for empty list\n"           \
+    "                        - see ecp_curve_list()\n"                  \
+    "                          for acceptable curve names\n"
+#else
+#define USAGE_CURVES ""
+#endif /* POLARSSL_SSL_SET_CURVES */
+
 #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET)
 #define USAGE_EMS \
     "    extended_ms=0/1     default: (library default: on)\n"
@@ -263,6 +275,7 @@
     USAGE_MAX_FRAG_LEN                                      \
     USAGE_TRUNC_HMAC                                        \
     USAGE_ALPN                                              \
+    USAGE_CURVES                                            \
     USAGE_EMS                                               \
     USAGE_ETM                                               \
     "\n"                                                    \
@@ -327,6 +340,7 @@
     int cache_max;              /* max number of session cache entries      */
     int cache_timeout;          /* expiration delay of session cache entries */
     char *sni;                  /* string describing sni information        */
+    const char *curves;         /* list of supported elliptic curves        */
     const char *alpn_string;    /* ALPN supported protocols                 */
     const char *dhm_file;       /* the file with the DH parameters          */
     int extended_ms;            /* allow negotiation of extended MS?        */
@@ -685,6 +699,10 @@
 #if defined(POLARSSL_SSL_ALPN)
     const char *alpn_list[10];
 #endif
+#if defined(POLARSSL_SSL_SET_CURVES)
+    ecp_group_id curve_list[20];
+    const ecp_curve_info *curve_cur;
+#endif
 #if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C)
     unsigned char alloc_buf[100000];
 #endif
@@ -780,6 +798,7 @@
     opt.cache_timeout       = DFL_CACHE_TIMEOUT;
     opt.sni                 = DFL_SNI;
     opt.alpn_string         = DFL_ALPN_STRING;
+    opt.curves              = DFL_CURVES;
     opt.dhm_file            = DFL_DHM_FILE;
     opt.extended_ms         = DFL_EXTENDED_MS;
     opt.etm                 = DFL_ETM;
@@ -987,6 +1006,8 @@
                 default: goto usage;
             }
         }
+        else if( strcmp( p, "curves" ) == 0 )
+            opt.curves = q;
         else if( strcmp( p, "etm" ) == 0 )
         {
             switch( atoi( q ) )
@@ -1118,6 +1139,64 @@
     }
 #endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+    if( opt.curves != NULL )
+    {
+        p = (char *) opt.curves;
+        i = 0;
+
+        if( strcmp( p, "none" ) == 0 )
+        {
+            curve_list[0] = POLARSSL_ECP_DP_NONE;
+        }
+        else if( strcmp( p, "default" ) != 0 )
+        {
+            /* Leave room for a final NULL in curve list */
+            while( i < (int) ( sizeof( curve_list ) / sizeof( *curve_list ) ) - 1
+                   && *p != '\0' )
+            {
+                q = p;
+
+                /* Terminate the current string */
+                while( *p != ',' && *p != '\0' )
+                    p++;
+                if( *p == ',' )
+                    *p++ = '\0';
+
+                if( ( curve_cur = ecp_curve_info_from_name( q ) ) != NULL )
+                {
+                    curve_list[i++] = curve_cur->grp_id;
+                }
+                else
+                {
+                    polarssl_printf( "unknown curve %s\n", q );
+                    polarssl_printf( "supported curves: " );
+                    for( curve_cur = ecp_curve_list();
+                         curve_cur->grp_id != POLARSSL_ECP_DP_NONE;
+                         curve_cur++ )
+                    {
+                        polarssl_printf( "%s ", curve_cur->name );
+                    }
+                    polarssl_printf( "\n" );
+                    goto exit;
+                }
+            }
+
+            polarssl_printf( "Number of curves: %d\n", i );
+
+            if( i == (int) ( sizeof( curve_list ) / sizeof( *curve_list ) ) - 1
+                && *p != '\0' )
+            {
+                polarssl_printf( "curves list too long, maximum %zu",
+                                 (size_t) ( sizeof( curve_list ) / sizeof( *curve_list ) - 1 ) );
+                goto exit;
+            }
+
+            curve_list[i] = POLARSSL_ECP_DP_NONE;
+        }
+    }
+#endif /* POLARSSL_SSL_SET_CURVES */
+
 #if defined(POLARSSL_SSL_ALPN)
     if( opt.alpn_string != NULL )
     {
@@ -1397,6 +1476,14 @@
         }
 #endif
 
+#if defined(POLARSSL_SSL_SET_CURVES)
+    if( opt.curves != NULL &&
+        strcmp( opt.curves, "default" ) != 0 )
+    {
+        ssl_set_curves( &ssl, curve_list );
+    }
+#endif
+
     ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
     ssl_set_dbg( &ssl, my_debug, stdout );
 
diff --git a/tests/scripts/tcp_client.pl b/tests/scripts/tcp_client.pl
new file mode 100755
index 0000000..11cbf1b
--- /dev/null
+++ b/tests/scripts/tcp_client.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/env perl
+
+# A simple TCP client that sends some data and expects a response.
+# Usage: tcp_client.pl HOSTNAME PORT DATA1 RESPONSE1
+#   DATA: hex-encoded data to send to the server
+#   RESPONSE: regexp that must match the server's response
+
+use warnings;
+use strict;
+use IO::Socket::INET;
+
+# Pack hex digits into a binary string, ignoring whitespace.
+sub parse_hex {
+    my ($hex) = @_;
+    $hex =~ s/\s+//g;
+    return pack('H*', $hex);
+}
+
+## Open a TCP connection to the specified host and port.
+sub open_connection {
+    my ($host, $port) = @_;
+    my $socket = IO::Socket::INET->new(PeerAddr => $host,
+                                       PeerPort => $port,
+                                       Proto => 'tcp',
+                                       Timeout => 1);
+    die "Cannot connect to $host:$port: $!" unless $socket;
+    return $socket;
+}
+
+## Close the TCP connection.
+sub close_connection {
+    my ($connection) = @_;
+    $connection->shutdown(2);
+    # Ignore shutdown failures (at least for now)
+    return 1;
+}
+
+## Write the given data, expressed as hexadecimal
+sub write_data {
+    my ($connection, $hexdata) = @_;
+    my $data = parse_hex($hexdata);
+    my $total_sent = 0;
+    while ($total_sent < length($data)) {
+        my $sent = $connection->send($data, 0);
+        if (!defined $sent) {
+            die "Unable to send data: $!";
+        }
+        $total_sent += $sent;
+    }
+    return 1;
+}
+
+## Read a response and check it against an expected prefix
+sub read_response {
+    my ($connection, $expected_hex) = @_;
+    my $expected_data = parse_hex($expected_hex);
+    my $start_offset = 0;
+    while ($start_offset < length($expected_data)) {
+        my $actual_data;
+        my $ok = $connection->recv($actual_data, length($expected_data));
+        if (!defined $ok) {
+            die "Unable to receive data: $!";
+        }
+        if (($actual_data ^ substr($expected_data, $start_offset)) =~ /[^\000]/) {
+            printf STDERR ("Received \\x%02x instead of \\x%02x at offset %d\n",
+                           ord(substr($actual_data, $-[0], 1)),
+                           ord(substr($expected_data, $start_offset + $-[0], 1)),
+                           $start_offset + $-[0]);
+            return 0;
+        }
+        $start_offset += length($actual_data);
+    }
+    return 1;
+}
+
+if (@ARGV != 4) {
+    print STDERR "Usage: $0 HOSTNAME PORT DATA1 RESPONSE1\n";
+    exit(3);
+}
+my ($host, $port, $data1, $response1) = @ARGV;
+my $connection = open_connection($host, $port);
+write_data($connection, $data1);
+if (!read_response($connection, $response1)) {
+    exit(1);
+}
+close_connection($connection);
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index a018f64..6e19646 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -16,11 +16,13 @@
 : ${OPENSSL_CMD:=openssl} # OPENSSL would conflict with the build system
 : ${GNUTLS_CLI:=gnutls-cli}
 : ${GNUTLS_SERV:=gnutls-serv}
+: ${PERL:=perl}
 
 O_SRV="$OPENSSL_CMD s_server -www -cert data_files/server5.crt -key data_files/server5.key -dhparam data_files/dhparams.pem"
 O_CLI="echo 'GET / HTTP/1.0' | $OPENSSL_CMD s_client"
 G_SRV="$GNUTLS_SERV --x509certfile data_files/server5.crt --x509keyfile data_files/server5.key"
 G_CLI="echo 'GET / HTTP/1.0' | $GNUTLS_CLI --x509cafile data_files/test-ca_cat12.crt"
+TCP_CLIENT="$PERL scripts/tcp_client.pl"
 
 TESTS=0
 FAILS=0
@@ -761,6 +763,37 @@
             -s "received FALLBACK_SCSV" \
             -S "inapropriate fallback"
 
+## ClientHello generated with
+## "openssl s_client -CAfile tests/data_files/test-ca.crt -tls1_1 -connect localhost:4433 -cipher ..."
+## then manually twiddling the ciphersuite list.
+## The ClientHello content is spelled out below as a hex string as
+## "prefix ciphersuite1 ciphersuite2 ciphersuite3 ciphersuite4 suffix".
+## The expected response is an inappropriate_fallback alert.
+requires_openssl_with_fallback_scsv
+run_test    "Fallback SCSV: beginning of list" \
+            "$P_SRV debug_level=2" \
+            "$TCP_CLIENT localhost $PORT '160301003e0100003a03022aafb94308dc22ca1086c65acc00e414384d76b61ecab37df1633b1ae1034dbe000008 5600 0031 0032 0033 0100000900230000000f000101' '15030200020256'" \
+            0 \
+            -s "received FALLBACK_SCSV" \
+            -s "inapropriate fallback"
+
+requires_openssl_with_fallback_scsv
+run_test    "Fallback SCSV: end of list" \
+            "$P_SRV debug_level=2" \
+            "$TCP_CLIENT localhost $PORT '160301003e0100003a03022aafb94308dc22ca1086c65acc00e414384d76b61ecab37df1633b1ae1034dbe000008 0031 0032 0033 5600 0100000900230000000f000101' '15030200020256'" \
+            0 \
+            -s "received FALLBACK_SCSV" \
+            -s "inapropriate fallback"
+
+## Here the expected response is a valid ServerHello prefix, up to the random.
+requires_openssl_with_fallback_scsv
+run_test    "Fallback SCSV: not in list" \
+            "$P_SRV debug_level=2" \
+            "$TCP_CLIENT localhost $PORT '160301003e0100003a03022aafb94308dc22ca1086c65acc00e414384d76b61ecab37df1633b1ae1034dbe000008 0056 0031 0032 0033 0100000900230000000f000101' '16030200300200002c0302'" \
+            0 \
+            -S "received FALLBACK_SCSV" \
+            -S "inapropriate fallback"
+
 # Tests for CBC 1/n-1 record splitting
 
 run_test    "CBC Record splitting: TLS 1.2, no splitting" \
@@ -1399,6 +1432,54 @@
             -C "! ssl_handshake returned" \
             -C "X509 - Certificate verification failed"
 
+run_test    "Authentication: server goodcert, client optional, no trusted CA" \
+            "$P_SRV" \
+            "$P_CLI debug_level=3 auth_mode=optional ca_file=none ca_path=none" \
+            0 \
+            -c "x509_verify_cert() returned" \
+            -c "! The certificate is not correctly signed by the trusted CA" \
+            -c "! Certificate verification flags"\
+            -C "! ssl_handshake returned" \
+            -C "X509 - Certificate verification failed" \
+            -C "SSL - No CA Chain is set, but required to operate"
+
+run_test    "Authentication: server goodcert, client required, no trusted CA" \
+            "$P_SRV" \
+            "$P_CLI debug_level=3 auth_mode=required ca_file=none ca_path=none" \
+            1 \
+            -c "x509_verify_cert() returned" \
+            -c "! The certificate is not correctly signed by the trusted CA" \
+            -c "! Certificate verification flags"\
+            -c "! ssl_handshake returned" \
+            -c "SSL - No CA Chain is set, but required to operate"
+
+# The purpose of the next two tests is to test the client's behaviour when receiving a server
+# certificate with an unsupported elliptic curve. This should usually not happen because
+# the client informs the server about the supported curves - it does, though, in the
+# corner case of a static ECDH suite, because the server doesn't check the curve on that
+# occasion (to be fixed). If that bug's fixed, the test needs to be altered to use a
+# different means to have the server ignoring the client's supported curve list.
+
+requires_config_enabled POLARSSL_SSL_SET_CURVES
+run_test    "Authentication: server ECDH p256v1, client required, p256v1 unsupported" \
+            "$P_SRV debug_level=1 key_file=data_files/server5.key \
+             crt_file=data_files/server5.ku-ka.crt" \
+            "$P_CLI debug_level=3 auth_mode=required curves=secp521r1" \
+            1 \
+            -c "bad certificate (EC key curve)"\
+            -c "! Certificate verification flags"\
+            -C "bad server certificate (ECDH curve)" # Expect failure at earlier verification stage
+
+requires_config_enabled POLARSSL_SSL_SET_CURVES
+run_test    "Authentication: server ECDH p256v1, client optional, p256v1 unsupported" \
+            "$P_SRV debug_level=1 key_file=data_files/server5.key \
+             crt_file=data_files/server5.ku-ka.crt" \
+            "$P_CLI debug_level=3 auth_mode=optional curves=secp521r1" \
+            1 \
+            -c "bad certificate (EC key curve)"\
+            -c "! Certificate verification flags"\
+            -c "bad server certificate (ECDH curve)" # Expect failure only at ECDH params check
+
 run_test    "Authentication: server badcert, client none" \
             "$P_SRV crt_file=data_files/server5-badsign.crt \
              key_file=data_files/server5.key" \
@@ -2387,7 +2468,7 @@
 
 # A test for extensions in SSLv3
 
-requires_config_enabled MBEDTLS_SSL_PROTO_SSL3
+requires_config_enabled POLARSSL_SSL_PROTO_SSL3
 run_test    "SSLv3 with extensions, server side" \
             "$P_SRV min_version=ssl3 debug_level=3" \
             "$P_CLI force_version=ssl3 tickets=1 max_frag_len=4096 alpn=abc,1234" \
diff --git a/tests/suites/test_suite_mpi.data b/tests/suites/test_suite_mpi.data
index 81fc73f..52cbd7e 100644
--- a/tests/suites/test_suite_mpi.data
+++ b/tests/suites/test_suite_mpi.data
@@ -521,7 +521,7 @@
 mpi_exp_mod:10:"433019240910377478217373572959560109819648647016096560523769010881172869083338285573756574557395862965095016483867813043663981946477698466501451832407592327356331263124555137732393938242285782144928753919588632679050799198937132922145084847":10:"5781538327977828897150909166778407659250458379645823062042492461576758526757490910073628008613977550546382774775570888130029763571528699574717583228939535960234464230882573615930384979100379102915657483866755371559811718767760594919456971354184113721":10:"583137007797276923956891216216022144052044091311388601652961409557516421612874571554415606746479105795833145583959622117418531166391184939066520869800857530421873250114773204354963864729386957427276448683092491947566992077136553066273207777134303397724679138833126700957":10:"":10:"114597449276684355144920670007147953232659436380163461553186940113929777196018164149703566472936578890991049344459204199888254907113495794730452699842273939581048142004834330369483813876618772578869083248061616444392091693787039636316845512292127097865026290173004860736":0
 
 Test mpi_exp_mod (Negative base)
-mpi_exp_mod:10:"-10000000000":10:"10000000000":10:"99999":10:"":10:"99998":0
+mpi_exp_mod:10:"-10000000000":10:"10000000000":10:"99999":10:"":10:"1":0
 
 Test mpi_exp_mod (Negative base)
 mpi_exp_mod:16:"-9f13012cd92aa72fb86ac8879d2fde4f7fd661aaae43a00971f081cc60ca277059d5c37e89652e2af2585d281d66ef6a9d38a117e9608e9e7574cd142dc55278838a2161dd56db9470d4c1da2d5df15a908ee2eb886aaa890f23be16de59386663a12f1afbb325431a3e835e3fd89b98b96a6f77382f458ef9a37e1f84a03045c8676ab55291a94c2228ea15448ee96b626b998":16:"40a54d1b9e86789f06d9607fb158672d64867665c73ee9abb545fc7a785634b354c7bae5b962ce8040cf45f2c1f3d3659b2ee5ede17534c8fc2ec85c815e8df1fe7048d12c90ee31b88a68a081f17f0d8ce5f4030521e9400083bcea73a429031d4ca7949c2000d597088e0c39a6014d8bf962b73bb2e8083bd0390a4e00b9b3":16:"eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3":16:"":16:"21acc7199e1b90f9b4844ffe12c19f00ec548c5d32b21c647d48b6015d8eb9ec9db05b4f3d44db4227a2b5659c1a7cceb9d5fa8fa60376047953ce7397d90aaeb7465e14e820734f84aa52ad0fc66701bcbb991d57715806a11531268e1e83dd48288c72b424a6287e9ce4e5cc4db0dd67614aecc23b0124a5776d36e5c89483":0
@@ -550,6 +550,9 @@
 Base test mpi_inv_mod #4
 mpi_inv_mod:10:"2":10:"4":10:"0":POLARSSL_ERR_MPI_NOT_ACCEPTABLE
 
+Base test mbedtls_mpi_inv_mod #5
+mpi_inv_mod:10:"3":10:"1":10:"0":POLARSSL_ERR_MPI_BAD_INPUT_DATA
+
 Test mpi_inv_mod #1
 mpi_inv_mod:16:"aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c":16:"fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d":16:"8d6a5c1d7adeae3e94b9bcd2c47e0d46e778bc8804a2cc25c02d775dc3d05b0c":0