Check keyUsage in SSL client and server
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 38843a3..4c8d8f9 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2699,6 +2699,9 @@
             return( POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED );
         }
 
+        /*
+         * Main check: verify certificate
+         */
         ret = x509_crt_verify( ssl->session_negotiate->peer_cert,
                                ssl->ca_chain, ssl->ca_crl, ssl->peer_cn,
                               &ssl->session_negotiate->verify_result,
@@ -2708,21 +2711,35 @@
         {
             SSL_DEBUG_RET( 1, "x509_verify_cert", ret );
         }
+
+        /*
+         * Secondary checks: always done, but change 'ret' only if it was 0
+         */
+
 #if defined(POLARSSL_SSL_SET_CURVES)
-        else
         {
-            pk_context *pk = &ssl->session_negotiate->peer_cert->pk;
+            const pk_context *pk = &ssl->session_negotiate->peer_cert->pk;
 
             /* If certificate uses an EC key, make sure the curve is OK */
             if( pk_can_do( pk, POLARSSL_PK_ECKEY ) &&
                 ! ssl_curve_is_acceptable( ssl, pk_ec( *pk )->grp.id ) )
             {
                 SSL_DEBUG_MSG( 1, ( "bad server certificate (EC key curve)" ) );
-                ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE;
+                if( ret == 0 )
+                    ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE;
             }
         }
 #endif
 
+        if( ssl_check_cert_usage( ssl->session_negotiate->peer_cert,
+                                  ciphersuite_info,
+                                  ! ssl->endpoint ) != 0 )
+        {
+            SSL_DEBUG_MSG( 1, ( "bad server certificate (usage ext.)" ) );
+            if( ret == 0 )
+                ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE;
+        }
+
         if( ssl->authmode != SSL_VERIFY_REQUIRED )
             ret = 0;
     }
@@ -4747,3 +4764,54 @@
     return( 0 );
 }
 #endif
+
+int ssl_check_cert_usage( const x509_crt *cert,
+                          const ssl_ciphersuite_t *ciphersuite,
+                          int cert_endpoint )
+{
+#if defined(POLARSSL_X509_CHECK_KEY_USAGE)
+    int usage = 0;
+#endif
+
+#if defined(POLARSSL_X509_CHECK_KEY_USAGE)
+    if( cert_endpoint == SSL_IS_SERVER )
+    {
+        /* Server part of the key exchange */
+        switch( ciphersuite->key_exchange )
+        {
+            case POLARSSL_KEY_EXCHANGE_RSA:
+            case POLARSSL_KEY_EXCHANGE_RSA_PSK:
+                usage = KU_KEY_ENCIPHERMENT;
+                break;
+
+            case POLARSSL_KEY_EXCHANGE_DHE_RSA:
+            case POLARSSL_KEY_EXCHANGE_ECDHE_RSA:
+            case POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA:
+                usage = KU_DIGITAL_SIGNATURE;
+                break;
+
+            case POLARSSL_KEY_EXCHANGE_ECDH_RSA:
+            case POLARSSL_KEY_EXCHANGE_ECDH_ECDSA:
+                usage = KU_KEY_AGREEMENT;
+                break;
+
+            /* Don't use default: we want warnings when adding new values */
+            case POLARSSL_KEY_EXCHANGE_NONE:
+            case POLARSSL_KEY_EXCHANGE_PSK:
+            case POLARSSL_KEY_EXCHANGE_DHE_PSK:
+            case POLARSSL_KEY_EXCHANGE_ECDHE_PSK:
+                usage = 0;
+        }
+    }
+    else
+    {
+        /* Client auth: we only implement rsa_sign and ecdsa_sign for now */
+        usage = KU_DIGITAL_SIGNATURE;
+    }
+
+    if( x509_crt_check_key_usage( cert, usage ) != 0 )
+        return( -1 );
+#endif /* POLARSSL_X509_CHECK_KEY_USAGE */
+
+    return( 0 );
+}