Add ecp_check_pub_priv()
diff --git a/include/polarssl/ecp.h b/include/polarssl/ecp.h
index 7192f1e..9b83bc5 100644
--- a/include/polarssl/ecp.h
+++ b/include/polarssl/ecp.h
@@ -635,6 +635,18 @@
 int ecp_gen_key( ecp_group_id grp_id, ecp_keypair *key,
                 int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
 
+/**
+ * \brief           Check a public-private key pair
+ *
+ * \param pub       Keypair structure holding a public key
+ * \param prv       Keypair structure holding a private (plus public) key
+ *
+ * \return          0 if successfull (keys are valid and match), or
+ *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA, or
+ *                  a POLARSSL_ERR_ECP_XXX or POLARSSL_ERR_MPI_XXX code.
+ */
+int ecp_check_pub_priv( const ecp_keypair *pub, const ecp_keypair *prv );
+
 #if defined(POLARSSL_SELF_TEST)
 /**
  * \brief          Checkup routine
diff --git a/library/ecp.c b/library/ecp.c
index ece781d..41e25e9 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -1897,6 +1897,48 @@
     return( ecp_gen_keypair( &key->grp, &key->d, &key->Q, f_rng, p_rng ) );
 }
 
+/*
+ * Check a public-private key pair
+ */
+int ecp_check_pub_priv( const ecp_keypair *pub, const ecp_keypair *prv )
+{
+    int ret;
+    ecp_point Q;
+    ecp_group grp;
+
+    if( pub->grp.id == POLARSSL_ECP_DP_NONE ||
+        pub->grp.id != prv->grp.id ||
+        mpi_cmp_mpi( &pub->Q.X, &prv->Q.X ) ||
+        mpi_cmp_mpi( &pub->Q.Y, &prv->Q.Y ) ||
+        mpi_cmp_mpi( &pub->Q.Z, &prv->Q.Z ) )
+    {
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+    }
+
+    ecp_point_init( &Q );
+    ecp_group_init( &grp );
+
+    /* ecp_mul() needs a non-const group... */
+    ecp_group_copy( &grp, &prv->grp );
+
+    /* Also checks d is valid */
+    MPI_CHK( ecp_mul( &grp, &Q, &prv->d, &prv->grp.G, NULL, NULL ) );
+
+    if( mpi_cmp_mpi( &Q.X, &prv->Q.X ) ||
+        mpi_cmp_mpi( &Q.Y, &prv->Q.Y ) ||
+        mpi_cmp_mpi( &Q.Z, &prv->Q.Z ) )
+    {
+        ret = POLARSSL_ERR_ECP_BAD_INPUT_DATA;
+        goto cleanup;
+    }
+
+cleanup:
+    ecp_point_free( &Q );
+    ecp_group_free( &grp );
+
+    return( ret );
+}
+
 #if defined(POLARSSL_SELF_TEST)
 
 /*
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index d871a8d..a5dc528 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -324,6 +324,33 @@
 depends_on:POLARSSL_ECP_DP_M255_ENABLED
 ecp_check_privkey:POLARSSL_ECP_DP_M255:"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8":0
 
+ECP check public-private #1 (OK)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":0
+
+ECP check public-private #2 (group none)
+ecp_check_pub_priv:POLARSSL_ECP_DP_NONE:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ECP_DP_NONE:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP check public-private #3 (group mismatch)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED:POLARSSL_ECP_DP_SECP384R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP384R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP check public-private #4 (Qx mismatch)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596293":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP check public-private #5 (Qy mismatch)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edfe":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP check public-private #6 (wrong Qx)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596293":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596293":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP check public-private #7 (wrong Qy)
+depends_on:POLARSSL_ECP_DP_SECP256R1_ENABLED
+ecp_check_pub_priv:POLARSSL_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edfe":POLARSSL_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edfe":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
 ECP gen keypair
 depends_on:POLARSSL_ECP_DP_SECP192R1_ENABLED
 ecp_gen_keypair:POLARSSL_ECP_DP_SECP192R1
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 62dc606..1c22a84 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -599,6 +599,32 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void ecp_check_pub_priv( int id_pub, char *Qx_pub, char *Qy_pub,
+                         int id, char *d, char *Qx, char *Qy, int ret )
+{
+    ecp_keypair pub, prv;
+
+    ecp_keypair_init( &pub );
+    ecp_keypair_init( &prv );
+
+    if( id_pub != POLARSSL_ECP_DP_NONE )
+        TEST_ASSERT( ecp_use_known_dp( &pub.grp, id_pub ) == 0 );
+    TEST_ASSERT( ecp_point_read_string( &pub.Q, 16, Qx_pub, Qy_pub ) == 0 );
+
+    if( id != POLARSSL_ECP_DP_NONE )
+        TEST_ASSERT( ecp_use_known_dp( &prv.grp, id ) == 0 );
+    TEST_ASSERT( ecp_point_read_string( &prv.Q, 16, Qx, Qy ) == 0 );
+    TEST_ASSERT( mpi_read_string( &prv.d, 16, d ) == 0 );
+
+    TEST_ASSERT( ecp_check_pub_priv( &pub, &prv ) == ret );
+
+exit:
+    ecp_keypair_free( &pub );
+    ecp_keypair_free( &prv );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void ecp_gen_keypair( int id )
 {
     ecp_group grp;