mbedtls_ecp_point_read_binary from compressed fmt

mbedtls_ecp_point_read_binary from MBEDTLS_ECP_PF_COMPRESSED format

Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
diff --git a/library/ecp.c b/library/ecp.c
index 009be61..5086457 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -767,6 +767,68 @@
     return( ret );
 }
 
+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+static int mbedtls_ecp_sw_derive_y( const mbedtls_ecp_group *grp,
+                                    const mbedtls_mpi *X,
+                                    mbedtls_mpi *Y,
+                                    int parity_bit )
+{
+    /* y^2 = x^3 + ax + b
+     * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4)
+     *
+     * Note: this method for extracting square root does not validate that w
+     * was indeed a square so this function will return garbage in Y if X
+     * does not correspond to a point on the curve.
+     */
+
+    /* Check prerequisite p = 3 mod 4 */
+    if( mbedtls_mpi_get_bit( &grp->P, 0 ) != 1 ||
+        mbedtls_mpi_get_bit( &grp->P, 1 ) != 1 )
+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
+
+    int ret;
+    mbedtls_mpi exp;
+    mbedtls_mpi_init(&exp);
+
+    /* use Y to store intermediate results */
+    /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
+    /* x^2 */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( Y, X, X ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( Y, Y, &grp->P ) );
+    /* x^2 + a */
+    if( !grp->A.p ) /* special case for A = -3; temporarily set exp = -3 */
+        MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &exp, -3 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( Y, Y, grp->A.p ? &grp->A : &exp ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( Y, Y, &grp->P ) );
+    /* (x^2 + a)x */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( Y, Y, X ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( Y, Y, &grp->P ) );
+    /* (x^2 + a)x + b */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( Y, Y, &grp->B ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( Y, Y, &grp->P ) );
+
+    /* w = y^2 */ /* Y contains y^2 intermediate result */
+    /* exp = ((p+1)/4) */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &exp, &grp->P, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &exp, 2 ) );
+    /* sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( Y, Y /*y^2*/, &exp, &grp->P, NULL ) );
+
+    /* check parity bit match or else invert Y */
+    /* This quick inversion implementation is valid because Y != 0 for all
+     * Short Weierstrass curves supported by mbedtls, as each supported curve
+     * has an order that is a large prime, so each supported curve does not
+     * have any point of order 2, and a point with Y == 0 would be of order 2 */
+    if( mbedtls_mpi_get_bit( Y, 0 ) != parity_bit )
+        MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( Y, &grp->P, Y ) );
+
+cleanup:
+
+    mbedtls_mpi_free(&exp);
+    return( ret );
+}
+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
+
 /*
  * Import a point from unsigned binary data (SEC1 2.3.4 and RFC7748)
  */
@@ -808,16 +870,29 @@
                 return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
         }
 
-        if( buf[0] != 0x04 )
-            return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-
-        if( ilen != 2 * plen + 1 )
+        if( ilen < 1 + plen )
             return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
 
         MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &pt->X, buf + 1, plen ) );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &pt->Y,
-                                                  buf + 1 + plen, plen ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &pt->Z, 1 ) );
+
+        if( buf[0] == 0x04 )
+        {
+            /* format == MBEDTLS_ECP_PF_UNCOMPRESSED */
+            if( ilen != 1 + plen * 2 )
+                return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+            return( mbedtls_mpi_read_binary( &pt->Y, buf + 1 + plen, plen ) );
+        }
+        else if( buf[0] == 0x02 || buf[0] == 0x03 )
+        {
+            /* format == MBEDTLS_ECP_PF_COMPRESSED */
+            if( ilen != 1 + plen )
+                return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+            return( mbedtls_ecp_sw_derive_y( grp, &pt->X, &pt->Y,
+                                             ( buf[0] & 1 ) ) );
+        }
+        else
+            return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
     }
 #endif