Add mbedtls_ct_error_if, with tests

Signed-off-by: Dave Rodgman <dave.rodgman@arm.com>
diff --git a/library/constant_time_impl.h b/library/constant_time_impl.h
index 513f146..d275085 100644
--- a/library/constant_time_impl.h
+++ b/library/constant_time_impl.h
@@ -331,7 +331,6 @@
     return (unsigned char) (~(low_mask | high_mask)) & to;
 }
 
-
 /* ============================================================================
  * Everything below here is trivial wrapper functions
  */
@@ -397,6 +396,17 @@
 
 #endif /* MBEDTLS_BIGNUM_C */
 
+static inline int mbedtls_ct_error_if(mbedtls_ct_condition_t condition, int if1, int if0)
+{
+    return -((int) mbedtls_ct_if(condition, (mbedtls_ct_uint_t) (-if1),
+                                 (mbedtls_ct_uint_t) (-if0)));
+}
+
+static inline int mbedtls_ct_error_if_else_0(mbedtls_ct_condition_t condition, int if1)
+{
+    return -((int) (condition & (-if1)));
+}
+
 static inline mbedtls_ct_condition_t mbedtls_ct_uint_eq(mbedtls_ct_uint_t x,
                                                         mbedtls_ct_uint_t y)
 {
diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h
index d3d8945..3731107 100644
--- a/library/constant_time_internal.h
+++ b/library/constant_time_internal.h
@@ -411,6 +411,35 @@
                                                          unsigned char c,
                                                          unsigned char t);
 
+/** Choose between two error values. The values must be in the range [-32767..0].
+ *
+ * Functionally equivalent to:
+ *
+ * condition ? if1 : if0.
+ *
+ * \param condition     Condition to test.
+ * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
+ * \param if0           Value to use if \p condition == MBEDTLS_CT_FALSE.
+ *
+ * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0.
+ */
+static inline int mbedtls_ct_error_if(mbedtls_ct_condition_t condition, int if1, int if0);
+
+/** Choose between an error value and 0. The error value must be in the range [-32767..0].
+ *
+ * Functionally equivalent to:
+ *
+ * condition ? if1 : 0.
+ *
+ * Functionally equivalent to mbedtls_ct_error_if(condition, if1, 0) but
+ * results in smaller code size.
+ *
+ * \param condition     Condition to test.
+ * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
+ *
+ * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0.
+ */
+static inline int mbedtls_ct_error_if_else_0(mbedtls_ct_condition_t condition, int if1);
 
 /* ============================================================================
  * Block memory operations
diff --git a/tests/suites/test_suite_constant_time.data b/tests/suites/test_suite_constant_time.data
index 82ee869..111fef6 100644
--- a/tests/suites/test_suite_constant_time.data
+++ b/tests/suites/test_suite_constant_time.data
@@ -646,6 +646,102 @@
 mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0xffffffffffffffff
 mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0xffffffffffffffff"
 
+mbedtls_ct_error_if 0 0 0
+mbedtls_ct_error_if:0:0:0
+
+mbedtls_ct_error_if 0 0 -1
+mbedtls_ct_error_if:0:0:-1
+
+mbedtls_ct_error_if 0 0 -32766
+mbedtls_ct_error_if:0:0:-32766
+
+mbedtls_ct_error_if 0 0 -32767
+mbedtls_ct_error_if:0:0:-32767
+
+mbedtls_ct_error_if 0 -1 0
+mbedtls_ct_error_if:0:-1:0
+
+mbedtls_ct_error_if 0 -1 -1
+mbedtls_ct_error_if:0:-1:-1
+
+mbedtls_ct_error_if 0 -1 -32766
+mbedtls_ct_error_if:0:-1:-32766
+
+mbedtls_ct_error_if 0 -1 -32767
+mbedtls_ct_error_if:0:-1:-32767
+
+mbedtls_ct_error_if 0 -32766 0
+mbedtls_ct_error_if:0:-32766:0
+
+mbedtls_ct_error_if 0 -32766 -1
+mbedtls_ct_error_if:0:-32766:-1
+
+mbedtls_ct_error_if 0 -32766 -32766
+mbedtls_ct_error_if:0:-32766:-32766
+
+mbedtls_ct_error_if 0 -32766 -32767
+mbedtls_ct_error_if:0:-32766:-32767
+
+mbedtls_ct_error_if 0 -32767 0
+mbedtls_ct_error_if:0:-32767:0
+
+mbedtls_ct_error_if 0 -32767 -1
+mbedtls_ct_error_if:0:-32767:-1
+
+mbedtls_ct_error_if 0 -32767 -32766
+mbedtls_ct_error_if:0:-32767:-32766
+
+mbedtls_ct_error_if 0 -32767 -32767
+mbedtls_ct_error_if:0:-32767:-32767
+
+mbedtls_ct_error_if 1 0 0
+mbedtls_ct_error_if:1:0:0
+
+mbedtls_ct_error_if 1 0 -1
+mbedtls_ct_error_if:1:0:-1
+
+mbedtls_ct_error_if 1 0 -32766
+mbedtls_ct_error_if:1:0:-32766
+
+mbedtls_ct_error_if 1 0 -32767
+mbedtls_ct_error_if:1:0:-32767
+
+mbedtls_ct_error_if 1 -1 0
+mbedtls_ct_error_if:1:-1:0
+
+mbedtls_ct_error_if 1 -1 -1
+mbedtls_ct_error_if:1:-1:-1
+
+mbedtls_ct_error_if 1 -1 -32766
+mbedtls_ct_error_if:1:-1:-32766
+
+mbedtls_ct_error_if 1 -1 -32767
+mbedtls_ct_error_if:1:-1:-32767
+
+mbedtls_ct_error_if 1 -32766 0
+mbedtls_ct_error_if:1:-32766:0
+
+mbedtls_ct_error_if 1 -32766 -1
+mbedtls_ct_error_if:1:-32766:-1
+
+mbedtls_ct_error_if 1 -32766 -32766
+mbedtls_ct_error_if:1:-32766:-32766
+
+mbedtls_ct_error_if 1 -32766 -32767
+mbedtls_ct_error_if:1:-32766:-32767
+
+mbedtls_ct_error_if 1 -32767 0
+mbedtls_ct_error_if:1:-32767:0
+
+mbedtls_ct_error_if 1 -32767 -1
+mbedtls_ct_error_if:1:-32767:-1
+
+mbedtls_ct_error_if 1 -32767 -32766
+mbedtls_ct_error_if:1:-32767:-32766
+
+mbedtls_ct_error_if 1 -32767 -32767
+mbedtls_ct_error_if:1:-32767:-32767
+
 mbedtls_ct_zeroize_if 0x0 0
 mbedtls_ct_zeroize_if:"0x0":0
 
diff --git a/tests/suites/test_suite_constant_time.function b/tests/suites/test_suite_constant_time.function
index 3d5fa7f..b9a78c8 100644
--- a/tests/suites/test_suite_constant_time.function
+++ b/tests/suites/test_suite_constant_time.function
@@ -115,6 +115,27 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mbedtls_ct_error_if(int cond, int t, int f)
+{
+    mbedtls_ct_condition_t c = mbedtls_ct_bool(cond);
+
+    mbedtls_ct_uint_t expected = c ? t : f;
+    mbedtls_ct_uint_t expected0 = c ? t : 0;
+
+    TEST_CF_SECRET(&c, sizeof(c));
+    TEST_CF_SECRET(&t, sizeof(t));
+    TEST_CF_SECRET(&f, sizeof(f));
+
+    TEST_EQUAL(mbedtls_ct_error_if(c, t, f), expected);
+    TEST_EQUAL(mbedtls_ct_error_if_else_0(c, t), expected0);
+
+    TEST_CF_PUBLIC(&c, sizeof(c));
+    TEST_CF_PUBLIC(&t, sizeof(t));
+    TEST_CF_PUBLIC(&f, sizeof(f));
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mbedtls_ct_if(char *c_str, char *t_str, char *f_str)
 {
     mbedtls_ct_condition_t c = mbedtls_ct_bool(strtoull(c_str, NULL, 16));