From 08f3ef1861478587211971494541a6b0e97cfdc7 Mon Sep 17 00:00:00 2001
From: Nicholas Wilson <nicholas.wilson@realvnc.com>
Date: Tue, 10 Nov 2015 13:10:01 +0000
Subject: [PATCH 1/2] Basic support for Curve448, similar to the current level
 of support for Curve25519

---
 include/mbedtls/config.h         |   1 +
 include/mbedtls/ecp.h            |   3 +-
 library/ecp.c                    |  24 ++++--
 library/ecp_curves.c             | 128 ++++++++++++++++++++++++++++++-
 library/version_features.c       |   3 +
 programs/test/benchmark.c        |  91 +++++++++++++---------
 tests/suites/test_suite_ecp.data |   4 +
 7 files changed, 209 insertions(+), 45 deletions(-)

diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index 48c32d4aa..9585e6922 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -609,6 +609,7 @@
 #define MBEDTLS_ECP_DP_BP384R1_ENABLED
 #define MBEDTLS_ECP_DP_BP512R1_ENABLED
 #define MBEDTLS_ECP_DP_CURVE25519_ENABLED
+#define MBEDTLS_ECP_DP_CURVE448_ENABLED
 
 /**
  * \def MBEDTLS_ECP_NIST_OPTIM
diff --git a/include/mbedtls/ecp.h b/include/mbedtls/ecp.h
index b00ba4da8..710fc3a6b 100644
--- a/include/mbedtls/ecp.h
+++ b/include/mbedtls/ecp.h
@@ -72,7 +72,8 @@ typedef enum
     MBEDTLS_ECP_DP_BP256R1,        /*!< 256-bits Brainpool curve */
     MBEDTLS_ECP_DP_BP384R1,        /*!< 384-bits Brainpool curve */
     MBEDTLS_ECP_DP_BP512R1,        /*!< 512-bits Brainpool curve */
-    MBEDTLS_ECP_DP_CURVE25519,           /*!< Curve25519               */
+    MBEDTLS_ECP_DP_CURVE25519,     /*!< Curve25519               */
+    MBEDTLS_ECP_DP_CURVE448,       /*!< Curve448                 */
     MBEDTLS_ECP_DP_SECP192K1,      /*!< 192-bits "Koblitz" curve */
     MBEDTLS_ECP_DP_SECP224K1,      /*!< 224-bits "Koblitz" curve */
     MBEDTLS_ECP_DP_SECP256K1,      /*!< 256-bits "Koblitz" curve */
diff --git a/library/ecp.c b/library/ecp.c
index b41baef27..92a188b66 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -26,6 +26,7 @@
  * GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone
  * FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
  * RFC 4492 for the related TLS structures and constants
+ * RFC 7748 for the Curve448 and Curve25519 curve definitions
  *
  * [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf
  *
@@ -99,7 +100,8 @@ static unsigned long add_count, dbl_count, mul_count;
 #define ECP_SHORTWEIERSTRASS
 #endif
 
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || \
+    defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
 #define ECP_MONTGOMERY
 #endif
 
@@ -1852,6 +1854,8 @@ cleanup:
 static int ecp_check_pubkey_mx( const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt )
 {
     /* [Curve25519 p. 5] Just check X is the correct number of bytes */
+    /* Allow any public value, if it's too big then we'll just reduce it mod p
+     * (RFC 7748 sec. 5 para. 3). */
     if( mbedtls_mpi_size( &pt->X ) > ( grp->nbits + 7 ) / 8 )
         return( MBEDTLS_ERR_ECP_INVALID_KEY );
 
@@ -1887,14 +1891,18 @@ int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, const mbedtls_mpi *
 #if defined(ECP_MONTGOMERY)
     if( ecp_get_type( grp ) == ECP_TYPE_MONTGOMERY )
     {
-        /* see [Curve25519] page 5 */
+        /* see RFC 7748 sec. 5 para. 5 */
         if( mbedtls_mpi_get_bit( d, 0 ) != 0 ||
             mbedtls_mpi_get_bit( d, 1 ) != 0 ||
-            mbedtls_mpi_get_bit( d, 2 ) != 0 ||
             mbedtls_mpi_bitlen( d ) - 1 != grp->nbits ) /* mbedtls_mpi_bitlen is one-based! */
             return( MBEDTLS_ERR_ECP_INVALID_KEY );
         else
-            return( 0 );
+
+        /* see [Curve25519] page 5 */
+        if( grp->nbits == 254 && mbedtls_mpi_get_bit( d, 2 ) != 0 )
+            return( MBEDTLS_ERR_ECP_INVALID_KEY );
+
+        return( 0 );
     }
 #endif /* ECP_MONTGOMERY */
 #if defined(ECP_SHORTWEIERSTRASS)
@@ -1941,10 +1949,14 @@ int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
         else
             MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, grp->nbits, 1 ) );
 
-        /* Make sure the last three bits are unset */
+        /* Make sure the last two bits are unset for Curve448, three bits for
+           Curve25519 */
         MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 0, 0 ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 1, 0 ) );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) );
+        if( grp->nbits == 254 )
+        {
+            MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) );
+        }
     }
     else
 #endif /* ECP_MONTGOMERY */
diff --git a/library/ecp_curves.c b/library/ecp_curves.c
index df5ac3eea..58630e3d0 100644
--- a/library/ecp_curves.c
+++ b/library/ecp_curves.c
@@ -627,6 +627,9 @@ static int ecp_mod_p521( mbedtls_mpi * );
 #if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
 static int ecp_mod_p255( mbedtls_mpi * );
 #endif
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+static int ecp_mod_p448( mbedtls_mpi * );
+#endif
 #if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
 static int ecp_mod_p192k1( mbedtls_mpi * );
 #endif
@@ -687,6 +690,52 @@ cleanup:
 }
 #endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
 
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+/*
+ * Specialized function for creating the Curve448 group
+ */
+static int ecp_use_curve448( mbedtls_ecp_group *grp )
+{
+    mbedtls_mpi Ns;
+    int ret;
+
+    mbedtls_mpi_init( &Ns );
+
+    /* Actually ( A + 2 ) / 4 */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &grp->A, 16, "98AA" ) );
+
+    /* P = 2^448 - 2^224 - 1 */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->P, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &grp->P, 224 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &grp->P, &grp->P, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &grp->P, 224 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &grp->P, &grp->P, 1 ) );
+    grp->pbits = mbedtls_mpi_bitlen( &grp->P );
+
+    /* Y intentionally not set, since we use x/z coordinates.
+     * This is used as a marker to identify Montgomery curves! */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.X, 5 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.Z, 1 ) );
+    mbedtls_mpi_free( &grp->G.Y );
+
+    /* N = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885 */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( &grp->N, 446, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &Ns, 16,
+                                              "8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D" ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &grp->N, &grp->N, &Ns ) );
+
+    /* Actually, the required msb for private keys */
+    grp->nbits = 447;
+
+cleanup:
+    mbedtls_mpi_free( &Ns );
+    if( ret != 0 )
+        mbedtls_ecp_group_free( grp );
+
+    return( ret );
+}
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
 /*
  * Set a group using well-known domain parameters
  */
@@ -767,6 +816,12 @@ int mbedtls_ecp_group_load( mbedtls_ecp_group *grp, mbedtls_ecp_group_id id )
             return( ecp_use_curve25519( grp ) );
 #endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
 
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+        case MBEDTLS_ECP_DP_CURVE448:
+            grp->modp = ecp_mod_p448;
+            return( ecp_use_curve448( grp ) );
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
         default:
             mbedtls_ecp_group_free( grp );
             return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
@@ -1176,7 +1231,7 @@ static int ecp_mod_p255( mbedtls_mpi *N )
     M.s = 1;
     M.n = N->n - ( P255_WIDTH - 1 );
     if( M.n > P255_WIDTH + 1 )
-        M.n = P255_WIDTH + 1;
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
     M.p = Mp;
     memset( Mp, 0, sizeof Mp );
     memcpy( Mp, N->p + P255_WIDTH - 1, M.n * sizeof( mbedtls_mpi_uint ) );
@@ -1197,6 +1252,77 @@ cleanup:
 }
 #endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
 
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+
+/* Size of p448 in terms of mbedtls_mpi_uint */
+#define P448_WIDTH      ( 448 / 8 / sizeof( mbedtls_mpi_uint ) )
+
+/* Number of limbs fully occupied by 2^224 (max), and limbs used by it (min) */
+#define DIV_ROUND_UP( X, Y ) ( ( ( X ) + ( Y ) - 1 ) / ( Y ) )
+#define P224_WIDTH_MIN   ( 28 / sizeof( mbedtls_mpi_uint ) )
+#define P224_WIDTH_MAX   DIV_ROUND_UP( 28, sizeof( mbedtls_mpi_uint ) )
+#define P224_UNUSED_BITS ( ( P224_WIDTH_MAX * sizeof( mbedtls_mpi_uint ) * 8 ) - 224 )
+
+/*
+ * Fast quasi-reduction modulo p448 = 2^448 - 2^224 - 1
+ * Write N as A0 + 2^448 A1 and A1 as B0 + 2^224 B1, and return
+ * A0 + A1 + B1 + (B0 + B1) * 2^224.  This is different to the reference
+ * implementation of Curve448, which uses its own special 56-bit limbs rather
+ * than a generic bignum library.  We could squeeze some extra speed out on
+ * 32-bit machines by splitting N up into 32-bit limbs and doing the
+ * arithmetic using the limbs directly as we do for the NIST primes above,
+ * but for 64-bit targets it should use half the number of operations if we do
+ * the reduction with 224-bit limbs, since mpi_add_mpi will then use 64-bit adds.
+ */
+static int ecp_mod_p448( mbedtls_mpi *N )
+{
+    int ret;
+    size_t i;
+    mbedtls_mpi M, Q;
+    mbedtls_mpi_uint Mp[P448_WIDTH + 1], Qp[P448_WIDTH];
+
+    if( N->n <= P448_WIDTH )
+        return( 0 );
+
+    /* M = A1 */
+    M.s = 1;
+    M.n = N->n - ( P448_WIDTH );
+    if( M.n > P448_WIDTH )
+        /* Shouldn't be called with N larger than 2^896! */
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+    M.p = Mp;
+    memset( Mp, 0, sizeof( Mp ) );
+    memcpy( Mp, N->p + P448_WIDTH, M.n * sizeof( mbedtls_mpi_uint ) );
+
+    /* N = A0 */
+    for( i = P448_WIDTH; i < N->n; i++ )
+        N->p[i] = 0;
+
+    /* N += A1 */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &M ) );
+
+    /* Q = B1, N += B1 */
+    Q = M;
+    Q.p = Qp;
+    memcpy( Qp, Mp, sizeof( Qp ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Q, 224 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &Q ) );
+
+    /* M = (B0 + B1) * 2^224, N += M */
+    if( sizeof( mbedtls_mpi_uint ) > 4 )
+        Mp[P224_WIDTH_MIN] &= ( (mbedtls_mpi_uint)-1 ) >> ( P224_UNUSED_BITS );
+    for( i = P224_WIDTH_MAX; i < M.n; ++i )
+        Mp[i] = 0;
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &M, &M, &Q ) );
+    M.n = P448_WIDTH + 1; /* Make room for shifted carry bit from the addition */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &M, 224 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &M ) );
+
+cleanup:
+    return( ret );
+}
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
 #if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) ||   \
     defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) ||   \
     defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
diff --git a/library/version_features.c b/library/version_features.c
index 1b06ff322..a452caf5e 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -309,6 +309,9 @@ static const char *features[] = {
 #if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
     "MBEDTLS_ECP_DP_CURVE25519_ENABLED",
 #endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+    "MBEDTLS_ECP_DP_CURVE448_ENABLED",
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
 #if defined(MBEDTLS_ECP_NIST_OPTIM)
     "MBEDTLS_ECP_NIST_OPTIM",
 #endif /* MBEDTLS_ECP_NIST_OPTIM */
diff --git a/programs/test/benchmark.c b/programs/test/benchmark.c
index 1945b30d9..cecf3e363 100644
--- a/programs/test/benchmark.c
+++ b/programs/test/benchmark.c
@@ -766,9 +766,16 @@ int main( int argc, char *argv[] )
     if( todo.ecdh )
     {
         mbedtls_ecdh_context ecdh;
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
         mbedtls_mpi z;
+        const mbedtls_ecp_curve_info montgomery_curve_list[] = {
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+            { MBEDTLS_ECP_DP_CURVE25519, 0, 0, "Curve25519" },
 #endif
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+            { MBEDTLS_ECP_DP_CURVE448, 0, 0, "Curve448" },
+#endif
+            { MBEDTLS_ECP_DP_NONE, 0, 0, 0 }
+        };
         const mbedtls_ecp_curve_info *curve_info;
         size_t olen;
 
@@ -797,27 +804,32 @@ int main( int argc, char *argv[] )
             mbedtls_ecdh_free( &ecdh );
         }
 
-        /* Curve25519 needs to be handled separately */
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
-        mbedtls_ecdh_init( &ecdh );
-        mbedtls_mpi_init( &z );
-
-        if( mbedtls_ecp_group_load( &ecdh.grp, MBEDTLS_ECP_DP_CURVE25519 ) != 0 ||
-            mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp, myrand, NULL ) != 0 )
+        /* Montgomery curves need to be handled separately */
+        for ( curve_info = montgomery_curve_list;
+              curve_info->grp_id != MBEDTLS_ECP_DP_NONE;
+              curve_info++ )
         {
-            mbedtls_exit( 1 );
+            mbedtls_ecdh_init( &ecdh );
+            mbedtls_mpi_init( &z );
+
+            if( mbedtls_ecp_group_load( &ecdh.grp, curve_info->grp_id ) != 0 ||
+                mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp, myrand, NULL ) != 0 )
+            {
+                mbedtls_exit( 1 );
+            }
+
+            mbedtls_snprintf( title, sizeof(title), "ECDHE-%s",
+                              curve_info->name );
+            TIME_PUBLIC(  title, "handshake",
+                    ret |= mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Q,
+                                            myrand, NULL );
+                    ret |= mbedtls_ecdh_compute_shared( &ecdh.grp, &z, &ecdh.Qp, &ecdh.d,
+                                                myrand, NULL ) );
+
+            mbedtls_ecdh_free( &ecdh );
+            mbedtls_mpi_free( &z );
         }
 
-        TIME_PUBLIC(  "ECDHE-Curve25519", "handshake",
-                ret |= mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Q,
-                                        myrand, NULL );
-                ret |= mbedtls_ecdh_compute_shared( &ecdh.grp, &z, &ecdh.Qp, &ecdh.d,
-                                            myrand, NULL ) );
-
-        mbedtls_ecdh_free( &ecdh );
-        mbedtls_mpi_free( &z );
-#endif
-
         for( curve_info = mbedtls_ecp_curve_list();
              curve_info->grp_id != MBEDTLS_ECP_DP_NONE;
              curve_info++ )
@@ -843,26 +855,31 @@ int main( int argc, char *argv[] )
             mbedtls_ecdh_free( &ecdh );
         }
 
-        /* Curve25519 needs to be handled separately */
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
-        mbedtls_ecdh_init( &ecdh );
-        mbedtls_mpi_init( &z );
-
-        if( mbedtls_ecp_group_load( &ecdh.grp, MBEDTLS_ECP_DP_CURVE25519 ) != 0 ||
-            mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp,
-                             myrand, NULL ) != 0 ||
-            mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Q, myrand, NULL ) != 0 )
+        /* Montgomery curves need to be handled separately */
+        for ( curve_info = montgomery_curve_list;
+              curve_info->grp_id != MBEDTLS_ECP_DP_NONE;
+              curve_info++)
         {
-            mbedtls_exit( 1 );
+            mbedtls_ecdh_init( &ecdh );
+            mbedtls_mpi_init( &z );
+
+            if( mbedtls_ecp_group_load( &ecdh.grp, curve_info->grp_id ) != 0 ||
+                mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp,
+                                 myrand, NULL ) != 0 ||
+                mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Q, myrand, NULL ) != 0 )
+            {
+                mbedtls_exit( 1 );
+            }
+
+            mbedtls_snprintf( title, sizeof(title), "ECDH-%s",
+                              curve_info->name );
+            TIME_PUBLIC(  title, "handshake",
+                    ret |= mbedtls_ecdh_compute_shared( &ecdh.grp, &z, &ecdh.Qp, &ecdh.d,
+                                                myrand, NULL ) );
+
+            mbedtls_ecdh_free( &ecdh );
+            mbedtls_mpi_free( &z );
         }
-
-        TIME_PUBLIC(  "ECDH-Curve25519", "handshake",
-                ret |= mbedtls_ecdh_compute_shared( &ecdh.grp, &z, &ecdh.Qp, &ecdh.d,
-                                            myrand, NULL ) );
-
-        mbedtls_ecdh_free( &ecdh );
-        mbedtls_mpi_free( &z );
-#endif
     }
 #endif
 
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index a43e7d75d..8e9d9fa49 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -330,6 +330,10 @@ ECP test vectors Curve25519
 depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
 ecp_test_vec_x:MBEDTLS_ECP_DP_CURVE25519:"5AC99F33632E5A768DE7E81BF854C27C46E3FBF2ABBACD29EC4AFF517369C660":"057E23EA9F1CBE8A27168F6E696A791DE61DD3AF7ACD4EEACC6E7BA514FDA863":"47DC3D214174820E1154B49BC6CDB2ABD45EE95817055D255AA35831B70D3260":"6EB89DA91989AE37C7EAC7618D9E5C4951DBA1D73C285AE1CD26A855020EEF04":"61450CD98E36016B58776A897A9F0AEF738B99F09468B8D6B8511184D53494AB"
 
+ECP test vectors Curve448 (RFC 7748 6.2, after decodeUCoordinate)
+depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED
+ecp_test_vec_x:MBEDTLS_ECP_DP_CURVE448:"eb7298a5c0d8c29a1dab27f1a6826300917389449741a974f5bac9d98dc298d46555bce8bae89eeed400584bb046cf75579f51d125498f98":"a01fc432e5807f17530d1288da125b0cd453d941726436c8bbd9c5222c3da7fa639ce03db8d23b274a0721a1aed5227de6e3b731ccf7089b":"ad997351b6106f36b0d1091b929c4c37213e0d2b97e85ebb20c127691d0dad8f1d8175b0723745e639a3cb7044290b99e0e2a0c27a6a301c":"0936f37bc6c1bd07ae3dec7ab5dc06a73ca13242fb343efc72b9d82730b445f3d4b0bd077162a46dcfec6f9b590bfcbcf520cdb029a8b73e":"9d874a5137509a449ad5853040241c5236395435c36424fd560b0cb62b281d285275a740ce32a22dd1740f4aa9161cec95ccc61a18f4ff07"
+
 ECP test vectors secp192k1
 depends_on:MBEDTLS_ECP_DP_SECP192K1_ENABLED
 ecp_test_vect:MBEDTLS_ECP_DP_SECP192K1:"D1E13A359F6E0F0698791938E6D60246030AE4B0D8D4E9DE":"281BCA982F187ED30AD5E088461EBE0A5FADBB682546DF79":"3F68A8E9441FB93A4DD48CB70B504FCC9AA01902EF5BE0F3":"BE97C5D2A1A94D081E3FACE53E65A27108B7467BDF58DE43":"5EB35E922CD693F7947124F5920022C4891C04F6A8B8DCB2":"60ECF73D0FC43E0C42E8E155FFE39F9F0B531F87B34B6C3C":"372F5C5D0E18313C82AEF940EC3AFEE26087A46F1EBAE923":"D5A9F9182EC09CEAEA5F57EA10225EC77FA44174511985FD"

From 557e77d9a31c5bad6930dde800ba46939151a834 Mon Sep 17 00:00:00 2001
From: Gilles Peskine <Gilles.Peskine@arm.com>
Date: Wed, 4 Apr 2018 09:18:11 +0200
Subject: [PATCH 2/2] Add ChangeLog entry

---
 ChangeLog | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 3572b8d30..79529430b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,10 @@ Features
    * Add option MBEDTLS_AES_FEWER_TABLES to dynamically compute 3/4 of the AES tables
      during runtime, thereby reducing the RAM/ROM footprint by ~6kb. Suggested
      and contributed by jkivilin in #394.
+   * Add initial support for Curve448 (RFC 7748). Only mbedtls_ecp_mul() and
+     ECDH primitive functions (mbedtls_ecdh_gen_public(),
+     mbedtls_ecdh_compute_shared()) are supported for now. Contributed by
+     Nicholas Wilson (#348).
 
 Bugfix
    * Fix spurious uninitialized variable warning in cmac.c. Fix independently