Add unit tests for fix_negative

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/library/ecp_curves.c b/library/ecp_curves.c
index 962d5af..b167443 100644
--- a/library/ecp_curves.c
+++ b/library/ecp_curves.c
@@ -25,6 +25,8 @@
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
 
+#include "ecp_invasive.h"
+
 #include <string.h>
 
 #if !defined(MBEDTLS_ECP_ALT)
@@ -1028,13 +1030,14 @@
     STORE32; i++;                               \
     cur = c > 0 ? c : 0; STORE32;               \
     cur = 0; while( ++i < MAX32 ) { STORE32; }  \
-    if( c < 0 ) fix_negative( N, c, bits );
+    if( c < 0 ) mbedtls_ecp_fix_negative( N, c, bits );
 
 /*
  * If the result is negative, we get it in the form
  * c * 2^(bits + 32) + N, with c negative and N positive shorter than 'bits'
  */
-static inline void fix_negative( mbedtls_mpi *N, signed char c, size_t bits )
+MBEDTLS_STATIC_TESTABLE
+void mbedtls_ecp_fix_negative( mbedtls_mpi *N, signed char c, size_t bits )
 {
     size_t i;
 
diff --git a/library/ecp_invasive.h b/library/ecp_invasive.h
index 7dd8ac2..870d963 100644
--- a/library/ecp_invasive.h
+++ b/library/ecp_invasive.h
@@ -31,6 +31,20 @@
 
 #if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_ECP_C)
 
+#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) ||   \
+    defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) ||   \
+    defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
+/* Preconditions:
+ *   - bits is a multiple of 64 or is 224
+ *   - c is -1 or -2
+ *   - 0 <= N < 2^bits
+ *   - N has room for bits+64 bits
+ *
+ * Set N to c * 2^bits + N.
+ */
+void mbedtls_ecp_fix_negative( mbedtls_mpi *N, signed char c, size_t bits );
+#endif
+
 #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_ECP_C */
 
 #endif /* MBEDTLS_ECP_INVASIVE_H */
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index 59dfa4f..106791c 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -516,3 +516,127 @@
 ECP restartable muladd secp256r1 max_ops=250
 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
 ecp_muladd_restart:MBEDTLS_ECP_DP_SECP256R1:"CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C":"2B57C0235FB7489768D058FF4911C20FDBE71E3699D91339AFBB903EE17255DC":"C3875E57C85038A0D60370A87505200DC8317C8C534948BEA6559C7C18E6D4CE":"3B4E49C4FDBFC006FF993C81A50EAE221149076D6EC09DDD9FB3B787F85B6483":"2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970":"6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D":250:4:64
+
+ECP fix_negative: 0, -1, 224
+fix_negative:"00":-1:224
+
+ECP fix_negative: 1, -1, 224
+fix_negative:"01":-1:224
+
+ECP fix_negative: 2^32-1, -1, 224
+fix_negative:"ffffffff":-1:224
+
+ECP fix_negative: 2^32, -1, 224
+fix_negative:"0100000000":-1:224
+
+ECP fix_negative: 2^64-1, -1, 224
+fix_negative:"ffffffffffffffff":-1:224
+
+ECP fix_negative: 2^64, -1, 224
+fix_negative:"010000000000000000":-1:224
+
+ECP fix_negative: 2^128-1, -1, 224
+fix_negative:"ffffffffffffffffffffffffffffffff":-1:224
+
+ECP fix_negative: 2^128, -1, 224
+fix_negative:"0100000000000000000000000000000000":-1:224
+
+ECP fix_negative: 2^128+1, -1, 224
+fix_negative:"0100000000000000000000000000000001":-1:224
+
+ECP fix_negative: 2^224-1, -1, 224
+fix_negative:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff":-1:224
+
+ECP fix_negative: 0, -2, 224
+fix_negative:"00":-2:224
+
+ECP fix_negative: 1, -2, 224
+fix_negative:"01":-2:224
+
+ECP fix_negative: 2^32-1, -2, 224
+fix_negative:"ffffffff":-2:224
+
+ECP fix_negative: 2^32, -2, 224
+fix_negative:"0100000000":-2:224
+
+ECP fix_negative: 2^64-1, -2, 224
+fix_negative:"ffffffffffffffff":-2:224
+
+ECP fix_negative: 2^64, -2, 224
+fix_negative:"010000000000000000":-2:224
+
+ECP fix_negative: 2^128-1, -2, 224
+fix_negative:"ffffffffffffffffffffffffffffffff":-2:224
+
+ECP fix_negative: 2^128, -2, 224
+fix_negative:"0100000000000000000000000000000000":-2:224
+
+ECP fix_negative: 2^128+1, -2, 224
+fix_negative:"0100000000000000000000000000000001":-2:224
+
+ECP fix_negative: 2^224-1, -2, 224
+fix_negative:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff":-2:224
+
+ECP fix_negative: 0, -1, 256
+fix_negative:"00":-1:256
+
+ECP fix_negative: 1, -1, 256
+fix_negative:"01":-1:256
+
+ECP fix_negative: 2^32-1, -1, 256
+fix_negative:"ffffffff":-1:256
+
+ECP fix_negative: 2^32, -1, 256
+fix_negative:"0100000000":-1:256
+
+ECP fix_negative: 2^64-1, -1, 256
+fix_negative:"ffffffffffffffff":-1:256
+
+ECP fix_negative: 2^64, -1, 256
+fix_negative:"010000000000000000":-1:256
+
+ECP fix_negative: 2^128-1, -1, 256
+fix_negative:"ffffffffffffffffffffffffffffffff":-1:256
+
+ECP fix_negative: 2^128, -1, 256
+fix_negative:"0100000000000000000000000000000000":-1:256
+
+ECP fix_negative: 2^128+1, -1, 256
+fix_negative:"0100000000000000000000000000000001":-1:256
+
+ECP fix_negative: 2^256-1, -1, 256
+fix_negative:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":-1:256
+
+ECP fix_negative: 0, -2, 256
+fix_negative:"00":-2:256
+
+ECP fix_negative: 1, -2, 256
+fix_negative:"01":-2:256
+
+ECP fix_negative: 2^32-1, -2, 256
+fix_negative:"ffffffff":-2:256
+
+ECP fix_negative: 2^32, -2, 256
+fix_negative:"0100000000":-2:256
+
+ECP fix_negative: 2^64-1, -2, 256
+fix_negative:"ffffffffffffffff":-2:256
+
+ECP fix_negative: 2^64, -2, 256
+fix_negative:"010000000000000000":-2:256
+
+ECP fix_negative: 2^128-1, -2, 256
+fix_negative:"ffffffffffffffffffffffffffffffff":-2:256
+
+ECP fix_negative: 2^128, -2, 256
+fix_negative:"0100000000000000000000000000000000":-2:256
+
+ECP fix_negative: 2^128+1, -2, 256
+fix_negative:"0100000000000000000000000000000001":-2:256
+
+ECP fix_negative: 2^256-1, -2, 256
+fix_negative:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":-2:256
+
+# The first call to fix_negative in the test case of issue #4296.
+ECP fix_negative: #4296.1
+fix_negative:"8A4DD4C8B42C5EAED15FE4F4579F4CE513EC90A94010BF000000000000000000":-1:256
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 8d47eda..0ca2fdf 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -1,6 +1,15 @@
 /* BEGIN_HEADER */
 #include "mbedtls/ecp.h"
 
+#include "ecp_invasive.h"
+
+#if defined(MBEDTLS_TEST_HOOKS) &&                  \
+    ( defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) ||  \
+      defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) ||  \
+      defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) )
+#define HAVE_FIX_NEGATIVE
+#endif
+
 #define ECP_PF_UNKNOWN     -1
 
 #define ECP_PT_RESET( x )           \
@@ -1198,6 +1207,36 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:HAVE_FIX_NEGATIVE */
+void fix_negative( data_t *N_bin, int c, int bits )
+{
+    mbedtls_mpi C, M, N;
+
+    mbedtls_mpi_init( &C );
+    mbedtls_mpi_init( &M );
+    mbedtls_mpi_init( &N );
+
+    /* C = - c * 2^bits */
+    TEST_EQUAL( 0, mbedtls_mpi_lset( &C, -c ) );
+    TEST_EQUAL( 0, mbedtls_mpi_shift_l( &C, bits ) );
+
+    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &N, N_bin->x, N_bin->len ) );
+    TEST_EQUAL( 0, mbedtls_mpi_grow( &N, C.n ) );
+
+    /* M = - ( C - N ) */
+    TEST_EQUAL( 0, mbedtls_mpi_sub_mpi( &M, &N, &C ) );
+
+    mbedtls_ecp_fix_negative( &N, c, bits );
+
+    TEST_EQUAL( 0, mbedtls_mpi_cmp_mpi( &N, &M ) );
+
+exit:
+    mbedtls_mpi_free( &C );
+    mbedtls_mpi_free( &M );
+    mbedtls_mpi_free( &N );
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */
 void ecp_selftest(  )
 {