Add derive_pms, completing first working version
diff --git a/library/ecjpake.c b/library/ecjpake.c
index 651d3e7..63de6e6 100644
--- a/library/ecjpake.c
+++ b/library/ecjpake.c
@@ -745,6 +745,59 @@
     return( ret );
 }
 
+/*
+ * Derive PMS (7.4.2.7 / 7.4.2.8)
+ */
+int mbedtls_ecjpake_tls_derive_pms( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng )
+{
+    int ret;
+    mbedtls_ecp_point K, *X42;
+    mbedtls_mpi xbs, one;
+    unsigned char kx[MBEDTLS_ECP_MAX_BYTES];
+    size_t x_bytes;
+
+    *olen = mbedtls_md_get_size( ctx->md_info );
+    if( len < *olen )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    mbedtls_ecp_point_init( &K );
+    mbedtls_mpi_init( &xbs );
+    mbedtls_mpi_init( &one );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &one, 1 ) );
+    X42 = ctx->role == MBEDTLS_ECJPAKE_CLIENT ? &ctx->X4 : &ctx->X2;
+
+    /*
+     * Client:  K = ( Xs - X4  * x2 * s ) * x2
+     * Server:  K = ( Xc - X2  * x4 * s ) * x4
+     * Unified: K = ( Xp - X42 * xb * x ) * xb
+     */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &xbs, &ctx->xb, &ctx->s ) );
+    xbs.s *= -1;
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &xbs, &xbs, &ctx->grp.N ) );
+
+    MBEDTLS_MPI_CHK( mbedtls_ecp_muladd( &ctx->grp, &K,
+                                         &one, &ctx->Xp,
+                                         &xbs, X42 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( &ctx->grp, &K, &ctx->xb, &K,
+                                      f_rng, p_rng ) );
+
+    /* PMS = SHA-256( K.X ) */
+    x_bytes = ( ctx->grp.pbits + 7 ) / 8;
+    MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &K.X, kx, x_bytes ) );
+    MBEDTLS_MPI_CHK( mbedtls_md( ctx->md_info, kx, x_bytes, buf ) );
+
+cleanup:
+    mbedtls_ecp_point_free( &K );
+    mbedtls_mpi_free( &xbs );
+    mbedtls_mpi_free( &one );
+
+    return( ret );
+}
+
 #if defined(MBEDTLS_SELF_TEST)
 
 #if defined(MBEDTLS_PLATFORM_C)
@@ -888,6 +941,12 @@
     0xcc, 0x38, 0xdb, 0xdc, 0xae, 0x60, 0xd9, 0xc5, 0x4c
 };
 
+static const unsigned char ecjpake_test_pms[] = {
+    0xf3, 0xd4, 0x7f, 0x59, 0x98, 0x44, 0xdb, 0x92, 0xa5, 0x69, 0xbb, 0xe7,
+    0x98, 0x1e, 0x39, 0xd9, 0x31, 0xfd, 0x74, 0x3b, 0xf2, 0x2e, 0x98, 0xf9,
+    0xb4, 0x38, 0xf7, 0x19, 0xd3, 0xc4, 0xf3, 0x51
+};
+
 /* For tests we don't need a secure RNG;
  * use the LGC from Numerical Recipes for simplicity */
 static int ecjpake_lgc( void *p, unsigned char *out, size_t len )
@@ -926,8 +985,8 @@
     int ret;
     mbedtls_ecjpake_context cli;
     mbedtls_ecjpake_context srv;
-    unsigned char buf[512];
-    size_t len;
+    unsigned char buf[512], pms[32];
+    size_t len, pmslen;
 
     mbedtls_ecjpake_init( &cli );
     mbedtls_ecjpake_init( &srv );
@@ -966,11 +1025,20 @@
 
     TEST_ASSERT( mbedtls_ecjpake_tls_read_server_params( &cli, buf, len ) == 0 );
 
+    TEST_ASSERT( mbedtls_ecjpake_tls_derive_pms( &cli,
+                 pms, sizeof( pms ), &pmslen, ecjpake_lgc, NULL ) == 0 );
+
     TEST_ASSERT( mbedtls_ecjpake_tls_write_client_params( &cli,
                  buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
 
     TEST_ASSERT( mbedtls_ecjpake_tls_read_client_params( &srv, buf, len ) == 0 );
 
+    TEST_ASSERT( mbedtls_ecjpake_tls_derive_pms( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == pmslen );
+    TEST_ASSERT( memcmp( buf, pms, len ) == 0 );
+
     if( verbose != 0 )
         mbedtls_printf( "passed\n" );
 
@@ -1016,6 +1084,22 @@
                                     ecjpake_test_cli_kx,
                             sizeof( ecjpake_test_cli_kx ) ) == 0 );
 
+    /* Server derives PMS */
+    TEST_ASSERT( mbedtls_ecjpake_tls_derive_pms( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == sizeof( ecjpake_test_pms ) );
+    TEST_ASSERT( memcmp( buf, ecjpake_test_pms, len ) == 0 );
+
+    memset( buf, 0, len ); /* Avoid interferences with next step */
+
+    /* Client derives PMS */
+    TEST_ASSERT( mbedtls_ecjpake_tls_derive_pms( &cli,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == sizeof( ecjpake_test_pms ) );
+    TEST_ASSERT( memcmp( buf, ecjpake_test_pms, len ) == 0 );
+
     if( verbose != 0 )
         mbedtls_printf( "passed\n" );