Merge remote-tracking branch 'origin/development' into safer-ct5
diff --git a/library/base64.c b/library/base64.c
index 3eb9e7c..fa22e53 100644
--- a/library/base64.c
+++ b/library/base64.c
@@ -24,6 +24,7 @@
 #if defined(MBEDTLS_BASE64_C)
 
 #include "mbedtls/base64.h"
+#include "base64_internal.h"
 #include "constant_time_internal.h"
 
 #include <stdint.h>
@@ -33,6 +34,39 @@
 #include "mbedtls/platform.h"
 #endif /* MBEDTLS_SELF_TEST */
 
+MBEDTLS_STATIC_TESTABLE
+unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
+{
+    unsigned char digit = 0;
+    /* For each range of values, if value is in that range, mask digit with
+     * the corresponding value. Since value can only be in a single range,
+     * only at most one masking will change digit. */
+    digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
+    digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
+    digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
+    digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
+    digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
+    return digit;
+}
+
+MBEDTLS_STATIC_TESTABLE
+signed char mbedtls_ct_base64_dec_value(unsigned char c)
+{
+    unsigned char val = 0;
+    /* For each range of digits, if c is in that range, mask val with
+     * the corresponding value. Since c can only be in a single range,
+     * only at most one masking will change val. Set val to one plus
+     * the desired value so that it stays 0 if c is in none of the ranges. */
+    val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' +  0 + 1);
+    val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
+    val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
+    val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
+    val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
+    /* At this point, val is 0 if c is an invalid digit and v+1 if c is
+     * a digit with the value v. */
+    return val - 1;
+}
+
 /*
  * Encode a buffer into base64 format
  */
diff --git a/library/base64_internal.h b/library/base64_internal.h
new file mode 100644
index 0000000..f9f56d7
--- /dev/null
+++ b/library/base64_internal.h
@@ -0,0 +1,57 @@
+/**
+ * \file base64_internal.h
+ *
+ * \brief RFC 1521 base64 encoding/decoding: interfaces for invasive testing
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef MBEDTLS_BASE64_INTERNAL
+#define MBEDTLS_BASE64_INTERNAL
+
+#include "common.h"
+
+#if defined(MBEDTLS_TEST_HOOKS)
+
+/** Given a value in the range 0..63, return the corresponding Base64 digit.
+ *
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
+ *
+ * \param value     A value in the range 0..63.
+ *
+ * \return          A base64 digit converted from \p value.
+ */
+unsigned char mbedtls_ct_base64_enc_char(unsigned char value);
+
+/** Given a Base64 digit, return its value.
+ *
+ * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
+ * return -1.
+ *
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
+ *
+ * \param c     A base64 digit.
+ *
+ * \return      The value of the base64 digit \p c.
+ */
+signed char mbedtls_ct_base64_dec_value(unsigned char c);
+
+#endif /* MBEDTLS_TEST_HOOKS */
+
+#endif /* MBEDTLS_BASE64_INTERNAL */
diff --git a/library/bignum.c b/library/bignum.c
index 36effaf..8ad7258 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -54,6 +54,129 @@
 #define MPI_VALIDATE(cond)                                           \
     MBEDTLS_INTERNAL_VALIDATE(cond)
 
+/*
+ * Compare signed values in constant time
+ */
+int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X,
+                          const mbedtls_mpi *Y,
+                          unsigned *ret)
+{
+    mbedtls_ct_condition_t cond, X_is_negative, Y_is_negative, result;
+
+    MPI_VALIDATE_RET(X != NULL);
+    MPI_VALIDATE_RET(Y != NULL);
+    MPI_VALIDATE_RET(ret != NULL);
+
+    if (X->n != Y->n) {
+        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+    /*
+     * Set sign_N to 1 if N >= 0, 0 if N < 0.
+     * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
+     */
+    X_is_negative = mbedtls_ct_bool((X->s & 2) >> 1);
+    Y_is_negative = mbedtls_ct_bool((Y->s & 2) >> 1);
+
+    /*
+     * If the signs are different, then the positive operand is the bigger.
+     * That is if X is negative (X_is_negative == 1), then X < Y is true and it
+     * is false if X is positive (X_is_negative == 0).
+     */
+    cond   = mbedtls_ct_bool_xor(X_is_negative, Y_is_negative); // non-zero if different sign
+    result = mbedtls_ct_bool_and(cond, X_is_negative);
+
+    /* Assuming signs are the same, compare  X and Y. We switch the comparison
+     * order if they are negative so that we get the right result, regardles of
+     * sign.
+     *
+     * Store in ret iff the signs are the same (i.e., iff cond == 0). If
+     * the signs differ, done has already been set.
+     */
+
+    /* This is used to conditionally swap the pointers in const time */
+    void * const p[2] = { X->p, Y->p };
+    size_t i = mbedtls_ct_size_if0(X_is_negative, 1);
+    mbedtls_ct_condition_t lt = mbedtls_mpi_core_lt_ct(p[i], p[i ^ 1], X->n);
+
+    result = mbedtls_ct_bool_or(result, mbedtls_ct_bool_and(mbedtls_ct_bool_not(cond), lt));
+
+    *ret = mbedtls_ct_uint_if0(result, 1);
+
+    return 0;
+}
+
+/*
+ * Conditionally assign X = Y, without leaking information
+ * about whether the assignment was made or not.
+ * (Leaking information about the respective sizes of X and Y is ok however.)
+ */
+#if defined(_MSC_VER) && defined(_M_ARM64) && (_MSC_FULL_VER < 193131103)
+/*
+ * MSVC miscompiles this function if it's inlined prior to Visual Studio 2022 version 17.1. See:
+ * https://developercommunity.visualstudio.com/t/c-compiler-miscompiles-part-of-mbedtls-library-on/1646989
+ */
+__declspec(noinline)
+#endif
+int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X,
+                                 const mbedtls_mpi *Y,
+                                 unsigned char assign)
+{
+    int ret = 0;
+    MPI_VALIDATE_RET(X != NULL);
+    MPI_VALIDATE_RET(Y != NULL);
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
+
+    mbedtls_ct_condition_t do_assign = mbedtls_ct_bool(assign);
+
+    X->s = (int) mbedtls_ct_uint_if(do_assign, Y->s, X->s);
+
+    mbedtls_mpi_core_cond_assign(X->p, Y->p, Y->n, do_assign);
+
+    mbedtls_ct_condition_t do_not_assign = mbedtls_ct_bool_not(do_assign);
+    for (size_t i = Y->n; i < X->n; i++) {
+        X->p[i] = mbedtls_ct_mpi_uint_if0(do_not_assign, X->p[i]);
+    }
+
+cleanup:
+    return ret;
+}
+
+/*
+ * Conditionally swap X and Y, without leaking information
+ * about whether the swap was made or not.
+ * Here it is not ok to simply swap the pointers, which would lead to
+ * different memory access patterns when X and Y are used afterwards.
+ */
+int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X,
+                               mbedtls_mpi *Y,
+                               unsigned char swap)
+{
+    int ret = 0;
+    int s;
+    MPI_VALIDATE_RET(X != NULL);
+    MPI_VALIDATE_RET(Y != NULL);
+
+    if (X == Y) {
+        return 0;
+    }
+
+    mbedtls_ct_condition_t do_swap = mbedtls_ct_bool(swap);
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n));
+
+    s = X->s;
+    X->s = (int) mbedtls_ct_uint_if(do_swap, Y->s, X->s);
+    Y->s = (int) mbedtls_ct_uint_if(do_swap, s, Y->s);
+
+    mbedtls_mpi_core_cond_swap(X->p, Y->p, X->n, do_swap);
+
+cleanup:
+    return ret;
+}
+
 /* Implementation that should never be optimized out by the compiler */
 static void mbedtls_mpi_zeroize(mbedtls_mpi_uint *v, size_t n)
 {
@@ -1602,10 +1725,8 @@
 
     for (size_t i = 0; i < T_size; i++) {
         MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign(R, &T[i],
-                                                     (unsigned char) mbedtls_ct_size_bool_eq(i,
-                                                                                             idx)));
+                                                     (unsigned char) mbedtls_ct_bool_eq(i, idx)));
     }
-
 cleanup:
     return ret;
 }
diff --git a/library/bignum_core.c b/library/bignum_core.c
index de57cfc..5e19590 100644
--- a/library/bignum_core.c
+++ b/library/bignum_core.c
@@ -148,54 +148,92 @@
 
 /* Whether min <= A, in constant time.
  * A_limbs must be at least 1. */
-unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
-                                      const mbedtls_mpi_uint *A,
-                                      size_t A_limbs)
+mbedtls_ct_condition_t mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
+                                                    const mbedtls_mpi_uint *A,
+                                                    size_t A_limbs)
 {
     /* min <= least significant limb? */
-    unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt(A[0], min);
+    mbedtls_ct_condition_t min_le_lsl = mbedtls_ct_bool_ge(A[0], min);
 
     /* limbs other than the least significant one are all zero? */
-    mbedtls_mpi_uint msll_mask = 0;
+    mbedtls_ct_condition_t msll_mask = MBEDTLS_CT_FALSE;
     for (size_t i = 1; i < A_limbs; i++) {
-        msll_mask |= A[i];
+        msll_mask = mbedtls_ct_bool_or(msll_mask, mbedtls_ct_bool(A[i]));
     }
-    /* The most significant limbs of A are not all zero iff msll_mask != 0. */
-    unsigned msll_nonzero = mbedtls_ct_mpi_uint_mask(msll_mask) & 1;
 
     /* min <= A iff the lowest limb of A is >= min or the other limbs
      * are not all zero. */
-    return min_le_lsl | msll_nonzero;
+    return mbedtls_ct_bool_or(msll_mask, min_le_lsl);
+}
+
+mbedtls_ct_condition_t mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
+                                              const mbedtls_mpi_uint *B,
+                                              size_t limbs)
+{
+    mbedtls_ct_condition_t ret = MBEDTLS_CT_FALSE, cond = MBEDTLS_CT_FALSE, done = MBEDTLS_CT_FALSE;
+
+    for (size_t i = limbs; i > 0; i--) {
+        /*
+         * If B[i - 1] < A[i - 1] then A < B is false and the result must
+         * remain 0.
+         *
+         * Again even if we can make a decision, we just mark the result and
+         * the fact that we are done and continue looping.
+         */
+        cond = mbedtls_ct_bool_lt(B[i - 1], A[i - 1]);
+        done = mbedtls_ct_bool_or(done, cond);
+
+        /*
+         * If A[i - 1] < B[i - 1] then A < B is true.
+         *
+         * Again even if we can make a decision, we just mark the result and
+         * the fact that we are done and continue looping.
+         */
+        cond = mbedtls_ct_bool_lt(A[i - 1], B[i - 1]);
+        ret  = mbedtls_ct_bool_or(ret, mbedtls_ct_bool_and(cond, mbedtls_ct_bool_not(done)));
+        done = mbedtls_ct_bool_or(done, cond);
+    }
+
+    /*
+     * If all the limbs were equal, then the numbers are equal, A < B is false
+     * and leaving the result 0 is correct.
+     */
+
+    return ret;
 }
 
 void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X,
                                   const mbedtls_mpi_uint *A,
                                   size_t limbs,
-                                  unsigned char assign)
+                                  mbedtls_ct_condition_t assign)
 {
     if (X == A) {
         return;
     }
 
-    mbedtls_ct_mpi_uint_cond_assign(limbs, X, A, assign);
+    /* This function is very performance-sensitive for RSA. For this reason
+     * we have the loop below, instead of calling mbedtls_ct_memcpy_if
+     * (this is more optimal since here we don't have to handle the case where
+     * we copy awkwardly sized data).
+     */
+    for (size_t i = 0; i < limbs; i++) {
+        X[i] = mbedtls_ct_mpi_uint_if(assign, A[i], X[i]);
+    }
 }
 
 void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X,
                                 mbedtls_mpi_uint *Y,
                                 size_t limbs,
-                                unsigned char swap)
+                                mbedtls_ct_condition_t swap)
 {
     if (X == Y) {
         return;
     }
 
-    /* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */
-    mbedtls_mpi_uint limb_mask = mbedtls_ct_mpi_uint_mask(swap);
-
     for (size_t i = 0; i < limbs; i++) {
         mbedtls_mpi_uint tmp = X[i];
-        X[i] = (X[i] & ~limb_mask) | (Y[i] & limb_mask);
-        Y[i] = (Y[i] & ~limb_mask) | (tmp & limb_mask);
+        X[i] = mbedtls_ct_mpi_uint_if(swap, Y[i], X[i]);
+        Y[i] = mbedtls_ct_mpi_uint_if(swap, tmp, Y[i]);
     }
 }
 
@@ -426,11 +464,10 @@
 {
     mbedtls_mpi_uint c = 0;
 
-    /* all-bits 0 if cond is 0, all-bits 1 if cond is non-0 */
-    const mbedtls_mpi_uint mask = mbedtls_ct_mpi_uint_mask(cond);
+    mbedtls_ct_condition_t do_add = mbedtls_ct_bool(cond);
 
     for (size_t i = 0; i < limbs; i++) {
-        mbedtls_mpi_uint add = mask & A[i];
+        mbedtls_mpi_uint add = mbedtls_ct_mpi_uint_if0(do_add, A[i]);
         mbedtls_mpi_uint t = c + X[i];
         c = (t < X[i]);
         t += add;
@@ -572,7 +609,11 @@
      * So the correct return value is already in X if (carry ^ borrow) = 0,
      * but is in (the lower AN_limbs limbs of) T if (carry ^ borrow) = 1.
      */
-    mbedtls_ct_mpi_uint_cond_assign(AN_limbs, X, T, (unsigned char) (carry ^ borrow));
+    mbedtls_ct_memcpy_if(mbedtls_ct_bool(carry ^ borrow),
+                         (unsigned char *) X,
+                         (unsigned char *) T,
+                         NULL,
+                         AN_limbs * sizeof(mbedtls_mpi_uint));
 }
 
 int mbedtls_mpi_core_get_mont_r2_unsafe(mbedtls_mpi *X,
@@ -597,7 +638,7 @@
                                            size_t index)
 {
     for (size_t i = 0; i < count; i++, table += limbs) {
-        unsigned char assign = mbedtls_ct_size_bool_eq(i, index);
+        mbedtls_ct_condition_t assign = mbedtls_ct_bool_eq(i, index);
         mbedtls_mpi_core_cond_assign(dest, table, limbs, assign);
     }
 }
@@ -637,7 +678,7 @@
                             int (*f_rng)(void *, unsigned char *, size_t),
                             void *p_rng)
 {
-    unsigned ge_lower = 1, lt_upper = 0;
+    mbedtls_ct_condition_t ge_lower = MBEDTLS_CT_TRUE, lt_upper = MBEDTLS_CT_FALSE;
     size_t n_bits = mbedtls_mpi_core_bitlen(N, limbs);
     size_t n_bytes = (n_bits + 7) / 8;
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@@ -682,7 +723,7 @@
 
         ge_lower = mbedtls_mpi_core_uint_le_mpi(min, X, limbs);
         lt_upper = mbedtls_mpi_core_lt_ct(X, N, limbs);
-    } while (ge_lower == 0 || lt_upper == 0);
+    } while (mbedtls_ct_bool_and(ge_lower, lt_upper) == MBEDTLS_CT_FALSE);
 
 cleanup:
     return ret;
diff --git a/library/bignum_core.h b/library/bignum_core.h
index 21a5a11..64d51b9 100644
--- a/library/bignum_core.h
+++ b/library/bignum_core.h
@@ -86,6 +86,8 @@
 #include "mbedtls/bignum.h"
 #endif
 
+#include "constant_time_internal.h"
+
 #define ciL    (sizeof(mbedtls_mpi_uint))     /** chars in limb  */
 #define biL    (ciL << 3)                     /** bits  in limb  */
 #define biH    (ciL << 2)                     /** half limb size */
@@ -142,11 +144,29 @@
  * \param A_limbs  The number of limbs of \p A.
  *                 This must be at least 1.
  *
- * \return         1 if \p min is less than or equal to \p A, otherwise 0.
+ * \return         MBEDTLS_CT_TRUE if \p min is less than or equal to \p A, otherwise MBEDTLS_CT_FALSE.
  */
-unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
-                                      const mbedtls_mpi_uint *A,
-                                      size_t A_limbs);
+mbedtls_ct_condition_t mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
+                                                    const mbedtls_mpi_uint *A,
+                                                    size_t A_limbs);
+
+/**
+ * \brief          Check if one unsigned MPI is less than another in constant
+ *                 time.
+ *
+ * \param A        The left-hand MPI. This must point to an array of limbs
+ *                 with the same allocated length as \p B.
+ * \param B        The right-hand MPI. This must point to an array of limbs
+ *                 with the same allocated length as \p A.
+ * \param limbs    The number of limbs in \p A and \p B.
+ *                 This must not be 0.
+ *
+ * \return         MBEDTLS_CT_TRUE  if \p A is less than \p B.
+ *                 MBEDTLS_CT_FALSE if \p A is greater than or equal to \p B.
+ */
+mbedtls_ct_condition_t mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
+                                              const mbedtls_mpi_uint *B,
+                                              size_t limbs);
 
 /**
  * \brief   Perform a safe conditional copy of an MPI which doesn't reveal
@@ -158,21 +178,15 @@
  * \param[in]  A        The address of the source MPI. This must be initialized.
  * \param      limbs    The number of limbs of \p A.
  * \param      assign   The condition deciding whether to perform the
- *                      assignment or not. Must be either 0 or 1:
- *                      * \c 1: Perform the assignment `X = A`.
- *                      * \c 0: Keep the original value of \p X.
+ *                      assignment or not.
  *
  * \note           This function avoids leaking any information about whether
  *                 the assignment was done or not.
- *
- * \warning        If \p assign is neither 0 nor 1, the result of this function
- *                 is indeterminate, and the resulting value in \p X might be
- *                 neither its original value nor the value in \p A.
  */
 void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X,
                                   const mbedtls_mpi_uint *A,
                                   size_t limbs,
-                                  unsigned char assign);
+                                  mbedtls_ct_condition_t assign);
 
 /**
  * \brief   Perform a safe conditional swap of two MPIs which doesn't reveal
@@ -184,21 +198,15 @@
  *                          This must be initialized.
  * \param         limbs     The number of limbs of \p X and \p Y.
  * \param         swap      The condition deciding whether to perform
- *                          the swap or not. Must be either 0 or 1:
- *                          * \c 1: Swap the values of \p X and \p Y.
- *                          * \c 0: Keep the original values of \p X and \p Y.
+ *                          the swap or not.
  *
  * \note           This function avoids leaking any information about whether
  *                 the swap was done or not.
- *
- * \warning        If \p swap is neither 0 nor 1, the result of this function
- *                 is indeterminate, and both \p X and \p Y might end up with
- *                 values different to either of the original ones.
  */
 void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X,
                                 mbedtls_mpi_uint *Y,
                                 size_t limbs,
-                                unsigned char swap);
+                                mbedtls_ct_condition_t swap);
 
 /** Import X from unsigned binary data, little-endian.
  *
diff --git a/library/bignum_mod_raw.c b/library/bignum_mod_raw.c
index bf72c18..c539197 100644
--- a/library/bignum_mod_raw.c
+++ b/library/bignum_mod_raw.c
@@ -40,7 +40,7 @@
                                      const mbedtls_mpi_mod_modulus *N,
                                      unsigned char assign)
 {
-    mbedtls_mpi_core_cond_assign(X, A, N->limbs, assign);
+    mbedtls_mpi_core_cond_assign(X, A, N->limbs, mbedtls_ct_bool(assign));
 }
 
 void mbedtls_mpi_mod_raw_cond_swap(mbedtls_mpi_uint *X,
@@ -48,7 +48,7 @@
                                    const mbedtls_mpi_mod_modulus *N,
                                    unsigned char swap)
 {
-    mbedtls_mpi_core_cond_swap(X, Y, N->limbs, swap);
+    mbedtls_mpi_core_cond_swap(X, Y, N->limbs, mbedtls_ct_bool(swap));
 }
 
 int mbedtls_mpi_mod_raw_read(mbedtls_mpi_uint *X,
diff --git a/library/constant_time.c b/library/constant_time.c
index 5265005..f2cdddf 100644
--- a/library/constant_time.c
+++ b/library/constant_time.c
@@ -22,29 +22,14 @@
  * might be translated to branches by some compilers on some platforms.
  */
 
+#include <limits.h>
+
 #include "common.h"
 #include "constant_time_internal.h"
 #include "mbedtls/constant_time.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform_util.h"
 
-#if defined(MBEDTLS_BIGNUM_C)
-#include "mbedtls/bignum.h"
-#include "bignum_core.h"
-#endif
-
-#if defined(MBEDTLS_SSL_TLS_C)
-#include "ssl_misc.h"
-#endif
-
-#if defined(MBEDTLS_RSA_C)
-#include "mbedtls/rsa.h"
-#endif
-
-#if defined(MBEDTLS_BASE64_C)
-#include "constant_time_invasive.h"
-#endif
-
 #include <string.h>
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
@@ -60,6 +45,15 @@
 #define PSA_TO_MBEDTLS_ERR(status) local_err_translation(status)
 #endif
 
+#if !defined(MBEDTLS_CT_ASM)
+/*
+ * Define an object with the value zero, such that the compiler cannot prove that it
+ * has the value zero (because it is volatile, it "may be modified in ways unknown to
+ * the implementation").
+ */
+volatile mbedtls_ct_uint_t mbedtls_ct_zero = 0;
+#endif
+
 /*
  * Define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS where assembly is present to
  * perform fast unaligned access to volatile data.
@@ -70,15 +64,12 @@
  * Some of these definitions could be moved into alignment.h but for now they are
  * only used here.
  */
-#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && defined(MBEDTLS_HAVE_ASM)
-#if ((defined(__arm__) || defined(__thumb__) || defined(__thumb2__)) && \
-    (UINTPTR_MAX == 0xfffffffful)) || defined(__aarch64__)
+#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && \
+    ((defined(MBEDTLS_CT_ARM_ASM) && (UINTPTR_MAX == 0xfffffffful)) || \
+    defined(MBEDTLS_CT_AARCH64_ASM))
 /* We check pointer sizes to avoid issues with them not matching register size requirements */
 #define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS
-#endif
-#endif
 
-#if defined(MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS)
 static inline uint32_t mbedtls_get_unaligned_volatile_uint32(volatile const unsigned char *p)
 {
     /* This is UB, even where it's safe:
@@ -86,14 +77,17 @@
      * so instead the same thing is expressed in assembly below.
      */
     uint32_t r;
-#if defined(__arm__) || defined(__thumb__) || defined(__thumb2__)
+#if defined(MBEDTLS_CT_ARM_ASM)
     asm volatile ("ldr %0, [%1]" : "=r" (r) : "r" (p) :);
-#elif defined(__aarch64__)
+#elif defined(MBEDTLS_CT_AARCH64_ASM)
     asm volatile ("ldr %w0, [%1]" : "=r" (r) : MBEDTLS_ASM_AARCH64_PTR_CONSTRAINT(p) :);
+#else
+#error No assembly defined for mbedtls_get_unaligned_volatile_uint32
 #endif
     return r;
 }
-#endif /* MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS */
+#endif /* defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) &&
+          (defined(MBEDTLS_CT_ARM_ASM) || defined(MBEDTLS_CT_AARCH64_ASM)) */
 
 int mbedtls_ct_memcmp(const void *a,
                       const void *b,
@@ -129,336 +123,70 @@
     return (int) diff;
 }
 
-unsigned mbedtls_ct_uint_mask(unsigned value)
-{
-    /* MSVC has a warning about unary minus on unsigned, but this is
-     * well-defined and precisely what we want to do here */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-    return -((value | -value) >> (sizeof(value) * 8 - 1));
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-}
-
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-
-size_t mbedtls_ct_size_mask(size_t value)
-{
-    /* MSVC has a warning about unary minus on unsigned integer types,
-     * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-    return -((value | -value) >> (sizeof(value) * 8 - 1));
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-}
-
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-
-#if defined(MBEDTLS_BIGNUM_C)
-
-mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask(mbedtls_mpi_uint value)
-{
-    /* MSVC has a warning about unary minus on unsigned, but this is
-     * well-defined and precisely what we want to do here */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-    return -((value | -value) >> (sizeof(value) * 8 - 1));
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-}
-
-#endif /* MBEDTLS_BIGNUM_C */
-
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
-
-/** Constant-flow mask generation for "less than" comparison:
- * - if \p x < \p y, return all-bits 1, that is (size_t) -1
- * - otherwise, return all bits 0, that is 0
- *
- * This function can be used to write constant-time code by replacing branches
- * with bit operations using masks.
- *
- * \param x     The first value to analyze.
- * \param y     The second value to analyze.
- *
- * \return      All-bits-one if \p x is less than \p y, otherwise zero.
- */
-static size_t mbedtls_ct_size_mask_lt(size_t x,
-                                      size_t y)
-{
-    /* This has the most significant bit set if and only if x < y */
-    const size_t sub = x - y;
-
-    /* sub1 = (x < y) ? 1 : 0 */
-    const size_t sub1 = sub >> (sizeof(sub) * 8 - 1);
-
-    /* mask = (x < y) ? 0xff... : 0x00... */
-    const size_t mask = mbedtls_ct_size_mask(sub1);
-
-    return mask;
-}
-
-size_t mbedtls_ct_size_mask_ge(size_t x,
-                               size_t y)
-{
-    return ~mbedtls_ct_size_mask_lt(x, y);
-}
-
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
-
-#if defined(MBEDTLS_BASE64_C)
-
-/* Return 0xff if low <= c <= high, 0 otherwise.
- *
- * Constant flow with respect to c.
- */
-MBEDTLS_STATIC_TESTABLE
-unsigned char mbedtls_ct_uchar_mask_of_range(unsigned char low,
-                                             unsigned char high,
-                                             unsigned char c)
-{
-    /* low_mask is: 0 if low <= c, 0x...ff if low > c */
-    unsigned low_mask = ((unsigned) c - low) >> 8;
-    /* high_mask is: 0 if c <= high, 0x...ff if c > high */
-    unsigned high_mask = ((unsigned) high - c) >> 8;
-    return ~(low_mask | high_mask) & 0xff;
-}
-
-#endif /* MBEDTLS_BASE64_C */
-
-unsigned mbedtls_ct_size_bool_eq(size_t x,
-                                 size_t y)
-{
-    /* diff = 0 if x == y, non-zero otherwise */
-    const size_t diff = x ^ y;
-
-    /* MSVC has a warning about unary minus on unsigned integer types,
-     * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-
-    /* diff_msb's most significant bit is equal to x != y */
-    const size_t diff_msb = (diff | (size_t) -diff);
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
-    /* diff1 = (x != y) ? 1 : 0 */
-    const unsigned diff1 = diff_msb >> (sizeof(diff_msb) * 8 - 1);
-
-    return 1 ^ diff1;
-}
-
 #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
 
-/** Constant-flow "greater than" comparison:
- * return x > y
- *
- * This is equivalent to \p x > \p y, but is likely to be compiled
- * to code using bitwise operation rather than a branch.
- *
- * \param x     The first value to analyze.
- * \param y     The second value to analyze.
- *
- * \return      1 if \p x greater than \p y, otherwise 0.
- */
-static unsigned mbedtls_ct_size_gt(size_t x,
-                                   size_t y)
+void mbedtls_ct_memmove_left(void *start, size_t total, size_t offset)
 {
-    /* Return the sign bit (1 for negative) of (y - x). */
-    return (y - x) >> (sizeof(size_t) * 8 - 1);
-}
+    /* Iterate over the array, reading each byte once and writing each byte once. */
+    for (size_t i = 0; i < total; i++) {
+        /* Each iteration, read one byte, and write it to start[i].
+         *
+         * The source address will either be the "true" source address, if it's in the range
+         * where data is getting moved, or (if the source address is off the end of the
+         * array), it will wrap back to the start.
+         *
+         * If the source address is out of range, mask it to zero.
+         */
 
-#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+        // The address that we will read from
+        // TODO: if offset is marked as secret, this upsets Memsan.
+        size_t j = i + offset;
 
-#if defined(MBEDTLS_BIGNUM_C)
+        // Is the address off the end of the array?
+        mbedtls_ct_condition_t not_dummy = mbedtls_ct_bool_lt(j, total);
 
-unsigned mbedtls_ct_mpi_uint_lt(const mbedtls_mpi_uint x,
-                                const mbedtls_mpi_uint y)
-{
-    mbedtls_mpi_uint ret;
-    mbedtls_mpi_uint cond;
+        // Bring read address into range
+        j = j % total;
 
-    /*
-     * Check if the most significant bits (MSB) of the operands are different.
-     */
-    cond = (x ^ y);
-    /*
-     * If the MSB are the same then the difference x-y will be negative (and
-     * have its MSB set to 1 during conversion to unsigned) if and only if x<y.
-     */
-    ret = (x - y) & ~cond;
-    /*
-     * If the MSB are different, then the operand with the MSB of 1 is the
-     * bigger. (That is if y has MSB of 1, then x<y is true and it is false if
-     * the MSB of y is 0.)
-     */
-    ret |= y & cond;
+        // Read a byte
+        uint8_t b = ((uint8_t *) start)[j];
 
+        // Set it to zero if it's out of range
+        b = mbedtls_ct_uint_if0(not_dummy, b);
 
-    ret = ret >> (sizeof(mbedtls_mpi_uint) * 8 - 1);
-
-    return (unsigned) ret;
-}
-
-#endif /* MBEDTLS_BIGNUM_C */
-
-unsigned mbedtls_ct_uint_if(unsigned condition,
-                            unsigned if1,
-                            unsigned if0)
-{
-    unsigned mask = mbedtls_ct_uint_mask(condition);
-    return (mask & if1) | (~mask & if0);
-}
-
-#if defined(MBEDTLS_BIGNUM_C)
-
-void mbedtls_ct_mpi_uint_cond_assign(size_t n,
-                                     mbedtls_mpi_uint *dest,
-                                     const mbedtls_mpi_uint *src,
-                                     unsigned char condition)
-{
-    size_t i;
-
-    /* MSVC has a warning about unary minus on unsigned integer types,
-     * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-
-    /* all-bits 1 if condition is 1, all-bits 0 if condition is 0 */
-    const mbedtls_mpi_uint mask = -condition;
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
-    for (i = 0; i < n; i++) {
-        dest[i] = (src[i] & mask) | (dest[i] & ~mask);
-    }
-}
-
-#endif /* MBEDTLS_BIGNUM_C */
-
-#if defined(MBEDTLS_BASE64_C)
-
-unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
-{
-    unsigned char digit = 0;
-    /* For each range of values, if value is in that range, mask digit with
-     * the corresponding value. Since value can only be in a single range,
-     * only at most one masking will change digit. */
-    digit |= mbedtls_ct_uchar_mask_of_range(0, 25, value) & ('A' + value);
-    digit |= mbedtls_ct_uchar_mask_of_range(26, 51, value) & ('a' + value - 26);
-    digit |= mbedtls_ct_uchar_mask_of_range(52, 61, value) & ('0' + value - 52);
-    digit |= mbedtls_ct_uchar_mask_of_range(62, 62, value) & '+';
-    digit |= mbedtls_ct_uchar_mask_of_range(63, 63, value) & '/';
-    return digit;
-}
-
-signed char mbedtls_ct_base64_dec_value(unsigned char c)
-{
-    unsigned char val = 0;
-    /* For each range of digits, if c is in that range, mask val with
-     * the corresponding value. Since c can only be in a single range,
-     * only at most one masking will change val. Set val to one plus
-     * the desired value so that it stays 0 if c is in none of the ranges. */
-    val |= mbedtls_ct_uchar_mask_of_range('A', 'Z', c) & (c - 'A' +  0 + 1);
-    val |= mbedtls_ct_uchar_mask_of_range('a', 'z', c) & (c - 'a' + 26 + 1);
-    val |= mbedtls_ct_uchar_mask_of_range('0', '9', c) & (c - '0' + 52 + 1);
-    val |= mbedtls_ct_uchar_mask_of_range('+', '+', c) & (c - '+' + 62 + 1);
-    val |= mbedtls_ct_uchar_mask_of_range('/', '/', c) & (c - '/' + 63 + 1);
-    /* At this point, val is 0 if c is an invalid digit and v+1 if c is
-     * a digit with the value v. */
-    return val - 1;
-}
-
-#endif /* MBEDTLS_BASE64_C */
-
-#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
-
-/** Shift some data towards the left inside a buffer.
- *
- * `mbedtls_ct_mem_move_to_left(start, total, offset)` is functionally
- * equivalent to
- * ```
- * memmove(start, start + offset, total - offset);
- * memset(start + offset, 0, total - offset);
- * ```
- * but it strives to use a memory access pattern (and thus total timing)
- * that does not depend on \p offset. This timing independence comes at
- * the expense of performance.
- *
- * \param start     Pointer to the start of the buffer.
- * \param total     Total size of the buffer.
- * \param offset    Offset from which to copy \p total - \p offset bytes.
- */
-static void mbedtls_ct_mem_move_to_left(void *start,
-                                        size_t total,
-                                        size_t offset)
-{
-    volatile unsigned char *buf = start;
-    size_t i, n;
-    if (total == 0) {
-        return;
-    }
-    for (i = 0; i < total; i++) {
-        unsigned no_op = mbedtls_ct_size_gt(total - offset, i);
-        /* The first `total - offset` passes are a no-op. The last
-         * `offset` passes shift the data one byte to the left and
-         * zero out the last byte. */
-        for (n = 0; n < total - 1; n++) {
-            unsigned char current = buf[n];
-            unsigned char next = buf[n+1];
-            buf[n] = mbedtls_ct_uint_if(no_op, current, next);
-        }
-        buf[total-1] = mbedtls_ct_uint_if(no_op, buf[total-1], 0);
+        // Write the byte to start[i]
+        ((uint8_t *) start)[i] = b;
     }
 }
 
 #endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
 
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-
-void mbedtls_ct_memcpy_if_eq(unsigned char *dest,
-                             const unsigned char *src,
-                             size_t len,
-                             size_t c1,
-                             size_t c2)
+void mbedtls_ct_memcpy_if(mbedtls_ct_condition_t condition,
+                          unsigned char *dest,
+                          const unsigned char *src1,
+                          const unsigned char *src2,
+                          size_t len)
 {
-    /* mask = c1 == c2 ? 0xff : 0x00 */
-    const size_t equal = mbedtls_ct_size_bool_eq(c1, c2);
+    const uint32_t mask     = (uint32_t) condition;
+    const uint32_t not_mask = (uint32_t) ~mbedtls_ct_compiler_opaque(condition);
+
+    /* If src2 is NULL and condition == 0, then this function has no effect.
+     * In this case, copy from dest back into dest. */
+    if (src2 == NULL) {
+        src2 = dest;
+    }
 
     /* dest[i] = c1 == c2 ? src[i] : dest[i] */
     size_t i = 0;
 #if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)
-    const uint32_t mask32 = (uint32_t) mbedtls_ct_size_mask(equal);
-    const unsigned char mask = (unsigned char) mask32 & 0xff;
-
     for (; (i + 4) <= len; i += 4) {
-        uint32_t a = mbedtls_get_unaligned_uint32(src  + i) &  mask32;
-        uint32_t b = mbedtls_get_unaligned_uint32(dest + i) & ~mask32;
+        uint32_t a = mbedtls_get_unaligned_uint32(src1 + i) & mask;
+        uint32_t b = mbedtls_get_unaligned_uint32(src2 + i) & not_mask;
         mbedtls_put_unaligned_uint32(dest + i, a | b);
     }
-#else
-    const unsigned char mask = (unsigned char) mbedtls_ct_size_mask(equal);
 #endif /* MBEDTLS_EFFICIENT_UNALIGNED_ACCESS */
     for (; i < len; i++) {
-        dest[i] = (src[i] & mask) | (dest[i] & ~mask);
+        dest[i] = (src1[i] & mask) | (src2[i] & not_mask);
     }
 }
 
@@ -472,547 +200,27 @@
     size_t offsetval;
 
     for (offsetval = offset_min; offsetval <= offset_max; offsetval++) {
-        mbedtls_ct_memcpy_if_eq(dest, src + offsetval, len,
-                                offsetval, offset);
+        mbedtls_ct_memcpy_if(mbedtls_ct_bool_eq(offsetval, offset), dest, src + offsetval, NULL,
+                             len);
     }
 }
 
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-
-#if defined(PSA_WANT_ALG_SHA_384)
-#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_384)
-#elif defined(PSA_WANT_ALG_SHA_256)
-#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_256)
-#else /* See check_config.h */
-#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_1)
-#endif
-
-int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
-                    psa_algorithm_t mac_alg,
-                    const unsigned char *add_data,
-                    size_t add_data_len,
-                    const unsigned char *data,
-                    size_t data_len_secret,
-                    size_t min_data_len,
-                    size_t max_data_len,
-                    unsigned char *output)
-{
-    /*
-     * This function breaks the HMAC abstraction and uses psa_hash_clone()
-     * extension in order to get constant-flow behaviour.
-     *
-     * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
-     * concatenation, and okey/ikey are the XOR of the key with some fixed bit
-     * patterns (see RFC 2104, sec. 2).
-     *
-     * We'll first compute ikey/okey, then inner_hash = HASH(ikey + msg) by
-     * hashing up to minlen, then cloning the context, and for each byte up
-     * to maxlen finishing up the hash computation, keeping only the
-     * correct result.
-     *
-     * Then we only need to compute HASH(okey + inner_hash) and we're done.
-     */
-    psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH(mac_alg);
-    const size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
-    unsigned char key_buf[MAX_HASH_BLOCK_LENGTH];
-    const size_t hash_size = PSA_HASH_LENGTH(hash_alg);
-    psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
-    size_t hash_length;
-
-    unsigned char aux_out[PSA_HASH_MAX_SIZE];
-    psa_hash_operation_t aux_operation = PSA_HASH_OPERATION_INIT;
-    size_t offset;
-    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
-
-    size_t mac_key_length;
-    size_t i;
-
-#define PSA_CHK(func_call)        \
-    do {                            \
-        status = (func_call);       \
-        if (status != PSA_SUCCESS) \
-        goto cleanup;           \
-    } while (0)
-
-    /* Export MAC key
-     * We assume key length is always exactly the output size
-     * which is never more than the block size, thus we use block_size
-     * as the key buffer size.
-     */
-    PSA_CHK(psa_export_key(key, key_buf, block_size, &mac_key_length));
-
-    /* Calculate ikey */
-    for (i = 0; i < mac_key_length; i++) {
-        key_buf[i] = (unsigned char) (key_buf[i] ^ 0x36);
-    }
-    for (; i < block_size; ++i) {
-        key_buf[i] = 0x36;
-    }
-
-    PSA_CHK(psa_hash_setup(&operation, hash_alg));
-
-    /* Now compute inner_hash = HASH(ikey + msg) */
-    PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
-    PSA_CHK(psa_hash_update(&operation, add_data, add_data_len));
-    PSA_CHK(psa_hash_update(&operation, data, min_data_len));
-
-    /* Fill the hash buffer in advance with something that is
-     * not a valid hash (barring an attack on the hash and
-     * deliberately-crafted input), in case the caller doesn't
-     * check the return status properly. */
-    memset(output, '!', hash_size);
-
-    /* For each possible length, compute the hash up to that point */
-    for (offset = min_data_len; offset <= max_data_len; offset++) {
-        PSA_CHK(psa_hash_clone(&operation, &aux_operation));
-        PSA_CHK(psa_hash_finish(&aux_operation, aux_out,
-                                PSA_HASH_MAX_SIZE, &hash_length));
-        /* Keep only the correct inner_hash in the output buffer */
-        mbedtls_ct_memcpy_if_eq(output, aux_out, hash_size,
-                                offset, data_len_secret);
-
-        if (offset < max_data_len) {
-            PSA_CHK(psa_hash_update(&operation, data + offset, 1));
-        }
-    }
-
-    /* Abort current operation to prepare for final operation */
-    PSA_CHK(psa_hash_abort(&operation));
-
-    /* Calculate okey */
-    for (i = 0; i < mac_key_length; i++) {
-        key_buf[i] = (unsigned char) ((key_buf[i] ^ 0x36) ^ 0x5C);
-    }
-    for (; i < block_size; ++i) {
-        key_buf[i] = 0x5C;
-    }
-
-    /* Now compute HASH(okey + inner_hash) */
-    PSA_CHK(psa_hash_setup(&operation, hash_alg));
-    PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
-    PSA_CHK(psa_hash_update(&operation, output, hash_size));
-    PSA_CHK(psa_hash_finish(&operation, output, hash_size, &hash_length));
-
-#undef PSA_CHK
-
-cleanup:
-    mbedtls_platform_zeroize(key_buf, MAX_HASH_BLOCK_LENGTH);
-    mbedtls_platform_zeroize(aux_out, PSA_HASH_MAX_SIZE);
-
-    psa_hash_abort(&operation);
-    psa_hash_abort(&aux_operation);
-    return PSA_TO_MBEDTLS_ERR(status);
-}
-
-#undef MAX_HASH_BLOCK_LENGTH
-
-#else
-int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
-                    const unsigned char *add_data,
-                    size_t add_data_len,
-                    const unsigned char *data,
-                    size_t data_len_secret,
-                    size_t min_data_len,
-                    size_t max_data_len,
-                    unsigned char *output)
-{
-    /*
-     * This function breaks the HMAC abstraction and uses the md_clone()
-     * extension to the MD API in order to get constant-flow behaviour.
-     *
-     * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
-     * concatenation, and okey/ikey are the XOR of the key with some fixed bit
-     * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx.
-     *
-     * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to
-     * minlen, then cloning the context, and for each byte up to maxlen
-     * finishing up the hash computation, keeping only the correct result.
-     *
-     * Then we only need to compute HASH(okey + inner_hash) and we're done.
-     */
-    const mbedtls_md_type_t md_alg = mbedtls_md_get_type(ctx->md_info);
-    /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5,
-     * all of which have the same block size except SHA-384. */
-    const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64;
-    const unsigned char * const ikey = ctx->hmac_ctx;
-    const unsigned char * const okey = ikey + block_size;
-    const size_t hash_size = mbedtls_md_get_size(ctx->md_info);
-
-    unsigned char aux_out[MBEDTLS_MD_MAX_SIZE];
-    mbedtls_md_context_t aux;
-    size_t offset;
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-    mbedtls_md_init(&aux);
-
-#define MD_CHK(func_call) \
-    do {                    \
-        ret = (func_call);  \
-        if (ret != 0)      \
-        goto cleanup;   \
-    } while (0)
-
-    MD_CHK(mbedtls_md_setup(&aux, ctx->md_info, 0));
-
-    /* After hmac_start() of hmac_reset(), ikey has already been hashed,
-     * so we can start directly with the message */
-    MD_CHK(mbedtls_md_update(ctx, add_data, add_data_len));
-    MD_CHK(mbedtls_md_update(ctx, data, min_data_len));
-
-    /* Fill the hash buffer in advance with something that is
-     * not a valid hash (barring an attack on the hash and
-     * deliberately-crafted input), in case the caller doesn't
-     * check the return status properly. */
-    memset(output, '!', hash_size);
-
-    /* For each possible length, compute the hash up to that point */
-    for (offset = min_data_len; offset <= max_data_len; offset++) {
-        MD_CHK(mbedtls_md_clone(&aux, ctx));
-        MD_CHK(mbedtls_md_finish(&aux, aux_out));
-        /* Keep only the correct inner_hash in the output buffer */
-        mbedtls_ct_memcpy_if_eq(output, aux_out, hash_size,
-                                offset, data_len_secret);
-
-        if (offset < max_data_len) {
-            MD_CHK(mbedtls_md_update(ctx, data + offset, 1));
-        }
-    }
-
-    /* The context needs to finish() before it starts() again */
-    MD_CHK(mbedtls_md_finish(ctx, aux_out));
-
-    /* Now compute HASH(okey + inner_hash) */
-    MD_CHK(mbedtls_md_starts(ctx));
-    MD_CHK(mbedtls_md_update(ctx, okey, block_size));
-    MD_CHK(mbedtls_md_update(ctx, output, hash_size));
-    MD_CHK(mbedtls_md_finish(ctx, output));
-
-    /* Done, get ready for next time */
-    MD_CHK(mbedtls_md_hmac_reset(ctx));
-
-#undef MD_CHK
-
-cleanup:
-    mbedtls_md_free(&aux);
-    return ret;
-}
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-
-#if defined(MBEDTLS_BIGNUM_C)
-
-#define MPI_VALIDATE_RET(cond)                                       \
-    MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA)
-
-/*
- * Conditionally assign X = Y, without leaking information
- * about whether the assignment was made or not.
- * (Leaking information about the respective sizes of X and Y is ok however.)
- */
-#if defined(_MSC_VER) && defined(_M_ARM64) && (_MSC_FULL_VER < 193131103)
-/*
- * MSVC miscompiles this function if it's inlined prior to Visual Studio 2022 version 17.1. See:
- * https://developercommunity.visualstudio.com/t/c-compiler-miscompiles-part-of-mbedtls-library-on/1646989
- */
-__declspec(noinline)
-#endif
-int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X,
-                                 const mbedtls_mpi *Y,
-                                 unsigned char assign)
-{
-    int ret = 0;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
-
-    /* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */
-    mbedtls_mpi_uint limb_mask = mbedtls_ct_mpi_uint_mask(assign);
-
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
-
-    X->s = (int) mbedtls_ct_uint_if(assign, Y->s, X->s);
-
-    mbedtls_mpi_core_cond_assign(X->p, Y->p, Y->n, assign);
-
-    for (size_t i = Y->n; i < X->n; i++) {
-        X->p[i] &= ~limb_mask;
-    }
-
-cleanup:
-    return ret;
-}
-
-/*
- * Conditionally swap X and Y, without leaking information
- * about whether the swap was made or not.
- * Here it is not ok to simply swap the pointers, which would lead to
- * different memory access patterns when X and Y are used afterwards.
- */
-int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X,
-                               mbedtls_mpi *Y,
-                               unsigned char swap)
-{
-    int ret = 0;
-    int s;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
-
-    if (X == Y) {
-        return 0;
-    }
-
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n));
-
-    s = X->s;
-    X->s = (int) mbedtls_ct_uint_if(swap, Y->s, X->s);
-    Y->s = (int) mbedtls_ct_uint_if(swap, s, Y->s);
-
-    mbedtls_mpi_core_cond_swap(X->p, Y->p, X->n, swap);
-
-cleanup:
-    return ret;
-}
-
-/*
- * Compare unsigned values in constant time
- */
-unsigned mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
-                                const mbedtls_mpi_uint *B,
-                                size_t limbs)
-{
-    unsigned ret, cond, done;
-
-    /* The value of any of these variables is either 0 or 1 for the rest of
-     * their scope. */
-    ret = cond = done = 0;
-
-    for (size_t i = limbs; i > 0; i--) {
-        /*
-         * If B[i - 1] < A[i - 1] then A < B is false and the result must
-         * remain 0.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = mbedtls_ct_mpi_uint_lt(B[i - 1], A[i - 1]);
-        done |= cond;
-
-        /*
-         * If A[i - 1] < B[i - 1] then A < B is true.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = mbedtls_ct_mpi_uint_lt(A[i - 1], B[i - 1]);
-        ret |= cond & (1 - done);
-        done |= cond;
-    }
-
-    /*
-     * If all the limbs were equal, then the numbers are equal, A < B is false
-     * and leaving the result 0 is correct.
-     */
-
-    return ret;
-}
-
-/*
- * Compare signed values in constant time
- */
-int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X,
-                          const mbedtls_mpi *Y,
-                          unsigned *ret)
-{
-    size_t i;
-    /* The value of any of these variables is either 0 or 1 at all times. */
-    unsigned cond, done, X_is_negative, Y_is_negative;
-
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
-    MPI_VALIDATE_RET(ret != NULL);
-
-    if (X->n != Y->n) {
-        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
-    }
-
-    /*
-     * Set sign_N to 1 if N >= 0, 0 if N < 0.
-     * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
-     */
-    X_is_negative = (X->s & 2) >> 1;
-    Y_is_negative = (Y->s & 2) >> 1;
-
-    /*
-     * If the signs are different, then the positive operand is the bigger.
-     * That is if X is negative (X_is_negative == 1), then X < Y is true and it
-     * is false if X is positive (X_is_negative == 0).
-     */
-    cond = (X_is_negative ^ Y_is_negative);
-    *ret = cond & X_is_negative;
-
-    /*
-     * This is a constant-time function. We might have the result, but we still
-     * need to go through the loop. Record if we have the result already.
-     */
-    done = cond;
-
-    for (i = X->n; i > 0; i--) {
-        /*
-         * If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
-         * X and Y are negative.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = mbedtls_ct_mpi_uint_lt(Y->p[i - 1], X->p[i - 1]);
-        *ret |= cond & (1 - done) & X_is_negative;
-        done |= cond;
-
-        /*
-         * If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
-         * X and Y are positive.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = mbedtls_ct_mpi_uint_lt(X->p[i - 1], Y->p[i - 1]);
-        *ret |= cond & (1 - done) & (1 - X_is_negative);
-        done |= cond;
-    }
-
-    return 0;
-}
-
-#endif /* MBEDTLS_BIGNUM_C */
-
 #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
 
-int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input,
-                                         size_t ilen,
-                                         unsigned char *output,
-                                         size_t output_max_len,
-                                         size_t *olen)
+void mbedtls_ct_zeroize_if(mbedtls_ct_condition_t condition, void *buf, size_t len)
 {
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t i, plaintext_max_size;
-
-    /* The following variables take sensitive values: their value must
-     * not leak into the observable behavior of the function other than
-     * the designated outputs (output, olen, return value). Otherwise
-     * this would open the execution of the function to
-     * side-channel-based variants of the Bleichenbacher padding oracle
-     * attack. Potential side channels include overall timing, memory
-     * access patterns (especially visible to an adversary who has access
-     * to a shared memory cache), and branches (especially visible to
-     * an adversary who has access to a shared code cache or to a shared
-     * branch predictor). */
-    size_t pad_count = 0;
-    unsigned bad = 0;
-    unsigned char pad_done = 0;
-    size_t plaintext_size = 0;
-    unsigned output_too_large;
-
-    plaintext_max_size = (output_max_len > ilen - 11) ? ilen - 11
-                                                        : output_max_len;
-
-    /* Check and get padding length in constant time and constant
-     * memory trace. The first byte must be 0. */
-    bad |= input[0];
-
-
-    /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
-     * where PS must be at least 8 nonzero bytes. */
-    bad |= input[1] ^ MBEDTLS_RSA_CRYPT;
-
-    /* Read the whole buffer. Set pad_done to nonzero if we find
-     * the 0x00 byte and remember the padding length in pad_count. */
-    for (i = 2; i < ilen; i++) {
-        pad_done  |= ((input[i] | (unsigned char) -input[i]) >> 7) ^ 1;
-        pad_count += ((pad_done | (unsigned char) -pad_done) >> 7) ^ 1;
+    uint32_t mask = (uint32_t) ~condition;
+    uint8_t *p = (uint8_t *) buf;
+    size_t i = 0;
+#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)
+    for (; (i + 4) <= len; i += 4) {
+        mbedtls_put_unaligned_uint32((void *) (p + i),
+                                     mbedtls_get_unaligned_uint32((void *) (p + i)) & mask);
     }
-
-
-    /* If pad_done is still zero, there's no data, only unfinished padding. */
-    bad |= mbedtls_ct_uint_if(pad_done, 0, 1);
-
-    /* There must be at least 8 bytes of padding. */
-    bad |= mbedtls_ct_size_gt(8, pad_count);
-
-    /* If the padding is valid, set plaintext_size to the number of
-     * remaining bytes after stripping the padding. If the padding
-     * is invalid, avoid leaking this fact through the size of the
-     * output: use the maximum message size that fits in the output
-     * buffer. Do it without branches to avoid leaking the padding
-     * validity through timing. RSA keys are small enough that all the
-     * size_t values involved fit in unsigned int. */
-    plaintext_size = mbedtls_ct_uint_if(
-        bad, (unsigned) plaintext_max_size,
-        (unsigned) (ilen - pad_count - 3));
-
-    /* Set output_too_large to 0 if the plaintext fits in the output
-     * buffer and to 1 otherwise. */
-    output_too_large = mbedtls_ct_size_gt(plaintext_size,
-                                          plaintext_max_size);
-
-    /* Set ret without branches to avoid timing attacks. Return:
-     * - INVALID_PADDING if the padding is bad (bad != 0).
-     * - OUTPUT_TOO_LARGE if the padding is good but the decrypted
-     *   plaintext does not fit in the output buffer.
-     * - 0 if the padding is correct. */
-    ret = -(int) mbedtls_ct_uint_if(
-        bad, -MBEDTLS_ERR_RSA_INVALID_PADDING,
-        mbedtls_ct_uint_if(output_too_large,
-                           -MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE,
-                           0));
-
-    /* If the padding is bad or the plaintext is too large, zero the
-     * data that we're about to copy to the output buffer.
-     * We need to copy the same amount of data
-     * from the same buffer whether the padding is good or not to
-     * avoid leaking the padding validity through overall timing or
-     * through memory or cache access patterns. */
-    bad = mbedtls_ct_uint_mask(bad | output_too_large);
-    for (i = 11; i < ilen; i++) {
-        input[i] &= ~bad;
+#endif
+    for (; i < len; i++) {
+        p[i] = p[i] & mask;
     }
-
-    /* If the plaintext is too large, truncate it to the buffer size.
-     * Copy anyway to avoid revealing the length through timing, because
-     * revealing the length is as bad as revealing the padding validity
-     * for a Bleichenbacher attack. */
-    plaintext_size = mbedtls_ct_uint_if(output_too_large,
-                                        (unsigned) plaintext_max_size,
-                                        (unsigned) plaintext_size);
-
-    /* Move the plaintext to the leftmost position where it can start in
-     * the working buffer, i.e. make it start plaintext_max_size from
-     * the end of the buffer. Do this with a memory access trace that
-     * does not depend on the plaintext size. After this move, the
-     * starting location of the plaintext is no longer sensitive
-     * information. */
-    mbedtls_ct_mem_move_to_left(input + ilen - plaintext_max_size,
-                                plaintext_max_size,
-                                plaintext_max_size - plaintext_size);
-
-    /* Finally copy the decrypted plaintext plus trailing zeros into the output
-     * buffer. If output_max_len is 0, then output may be an invalid pointer
-     * and the result of memcpy() would be undefined; prevent undefined
-     * behavior making sure to depend only on output_max_len (the size of the
-     * user-provided output buffer), which is independent from plaintext
-     * length, validity of padding, success of the decryption, and other
-     * secrets. */
-    if (output_max_len != 0) {
-        memcpy(output, input + ilen - plaintext_max_size, plaintext_max_size);
-    }
-
-    /* Report the amount of data we copied to the output buffer. In case
-     * of errors (bad padding or output too large), the value of *olen
-     * when this function returns is not specified. Making it equivalent
-     * to the good case limits the risks of leaking the padding validity. */
-    *olen = plaintext_size;
-
-    return ret;
 }
 
-#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+#endif /* defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) */
diff --git a/library/constant_time_impl.h b/library/constant_time_impl.h
new file mode 100644
index 0000000..91418e5
--- /dev/null
+++ b/library/constant_time_impl.h
@@ -0,0 +1,279 @@
+/**
+ *  Constant-time functions
+ *
+ *  For readability, the static inline definitions are here, and
+ *  constant_time_internal.h has only the declarations.
+ *
+ *  This results in duplicate declarations of the form:
+ *      static inline void f() { ... }
+ *      static inline void f();
+ *  when constant_time_internal.h is included. This appears to behave
+ *  exactly as if the declaration-without-definition was not present.
+ *
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef MBEDTLS_CONSTANT_TIME_IMPL_H
+#define MBEDTLS_CONSTANT_TIME_IMPL_H
+
+#include <stddef.h>
+
+#include "common.h"
+
+#if defined(MBEDTLS_BIGNUM_C)
+#include "mbedtls/bignum.h"
+#endif
+
+
+/* Disable asm under Memsan because it confuses Memsan and generates false errors */
+#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
+#define MBEDTLS_CT_NO_ASM
+#elif defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#define MBEDTLS_CT_NO_ASM
+#endif
+#endif
+
+/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */
+#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && (!defined(__ARMCC_VERSION) || \
+    __ARMCC_VERSION >= 6000000) && !defined(MBEDTLS_CT_NO_ASM)
+#define MBEDTLS_CT_ASM
+#if (defined(__arm__) || defined(__thumb__) || defined(__thumb2__))
+#define MBEDTLS_CT_ARM_ASM
+#elif defined(__aarch64__)
+#define MBEDTLS_CT_AARCH64_ASM
+#endif
+#endif
+
+#define MBEDTLS_CT_SIZE (sizeof(mbedtls_ct_uint_t) * 8)
+
+
+/* ============================================================================
+ * Core const-time primitives
+ */
+
+/* Ensure that the compiler cannot know the value of x (i.e., cannot optimise
+ * based on its value) after this function is called.
+ *
+ * If we are not using assembly, this will be fairly inefficient, so its use
+ * should be minimised.
+ */
+
+#if !defined(MBEDTLS_CT_ASM)
+extern volatile mbedtls_ct_uint_t mbedtls_ct_zero;
+#endif
+
+static inline mbedtls_ct_uint_t mbedtls_ct_compiler_opaque(mbedtls_ct_uint_t x)
+{
+#if defined(MBEDTLS_CT_ASM)
+    asm volatile ("" : [x] "+r" (x) :);
+    return x;
+#else
+    return x ^ mbedtls_ct_zero;
+#endif
+}
+
+/* Convert a number into a condition in constant time. */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x)
+{
+    /*
+     * Define mask-generation code that, as far as possible, will not use branches or conditional instructions.
+     *
+     * For some platforms / type sizes, we define assembly to assure this.
+     *
+     * Otherwise, we define a plain C fallback which (in May 2023) does not get optimised into
+     * conditional instructions or branches by trunk clang, gcc, or MSVC v19.
+     */
+    const mbedtls_ct_uint_t xo = mbedtls_ct_compiler_opaque(x);
+#if defined(_MSC_VER)
+    /* MSVC has a warning about unary minus on unsigned, but this is
+     * well-defined and precisely what we want to do here */
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+    return (mbedtls_ct_condition_t) (((mbedtls_ct_int_t) ((-xo) | -(xo >> 1))) >>
+                                     (MBEDTLS_CT_SIZE - 1));
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+}
+
+static inline mbedtls_ct_uint_t mbedtls_ct_if(mbedtls_ct_condition_t condition,
+                                              mbedtls_ct_uint_t if1,
+                                              mbedtls_ct_uint_t if0)
+{
+    mbedtls_ct_condition_t not_cond =
+        (mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(condition));
+    return (mbedtls_ct_uint_t) ((condition & if1) | (not_cond & if0));
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y)
+{
+    /* Ensure that the compiler cannot optimise the following operations over x and y,
+     * even if it knows the value of x and y.
+     */
+    const mbedtls_ct_uint_t xo = mbedtls_ct_compiler_opaque(x);
+    const mbedtls_ct_uint_t yo = mbedtls_ct_compiler_opaque(y);
+    /*
+     * Check if the most significant bits (MSB) of the operands are different.
+     * cond is true iff the MSBs differ.
+     */
+    mbedtls_ct_condition_t cond = mbedtls_ct_bool((xo ^ yo) >> (MBEDTLS_CT_SIZE - 1));
+
+    /*
+     * If the MSB are the same then the difference x-y will be negative (and
+     * have its MSB set to 1 during conversion to unsigned) if and only if x<y.
+     *
+     * If the MSB are different, then the operand with the MSB of 1 is the
+     * bigger. (That is if y has MSB of 1, then x<y is true and it is false if
+     * the MSB of y is 0.)
+     */
+
+    // Select either y, or x - y
+    mbedtls_ct_uint_t ret = mbedtls_ct_if(cond, yo, (mbedtls_ct_uint_t) (xo - yo));
+
+    // Extract only the MSB of ret
+    ret = ret >> (MBEDTLS_CT_SIZE - 1);
+
+    // Convert to a condition (i.e., all bits set iff non-zero)
+    return mbedtls_ct_bool(ret);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y)
+{
+    /* diff = 0 if x == y, non-zero otherwise */
+    const mbedtls_ct_uint_t diff = mbedtls_ct_compiler_opaque(x) ^ mbedtls_ct_compiler_opaque(y);
+
+    /* all ones if x != y, 0 otherwise */
+    return mbedtls_ct_bool(diff);
+}
+
+static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low,
+                                                         unsigned char high,
+                                                         unsigned char c,
+                                                         unsigned char t)
+{
+    const unsigned char co = (const unsigned char) mbedtls_ct_compiler_opaque(c);
+    const unsigned char to = (const unsigned char) mbedtls_ct_compiler_opaque(t);
+
+    /* low_mask is: 0 if low <= c, 0x...ff if low > c */
+    unsigned low_mask = ((unsigned) co - low) >> 8;
+    /* high_mask is: 0 if c <= high, 0x...ff if c > high */
+    unsigned high_mask = ((unsigned) high - co) >> 8;
+
+    return (unsigned char) (~(low_mask | high_mask)) & to;
+}
+
+
+/* ============================================================================
+ * Everything below here is trivial wrapper functions
+ */
+
+static inline size_t mbedtls_ct_size_if(mbedtls_ct_condition_t condition,
+                                        size_t if1,
+                                        size_t if0)
+{
+    return (size_t) mbedtls_ct_if(condition, (mbedtls_ct_uint_t) if1, (mbedtls_ct_uint_t) if0);
+}
+
+static inline unsigned mbedtls_ct_uint_if(mbedtls_ct_condition_t condition,
+                                          unsigned if1,
+                                          unsigned if0)
+{
+    return (unsigned) mbedtls_ct_if(condition, (mbedtls_ct_uint_t) if1, (mbedtls_ct_uint_t) if0);
+}
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if(mbedtls_ct_condition_t condition,
+                                                      mbedtls_mpi_uint if1,
+                                                      mbedtls_mpi_uint if0)
+{
+    return (mbedtls_mpi_uint) mbedtls_ct_if(condition,
+                                            (mbedtls_ct_uint_t) if1,
+                                            (mbedtls_ct_uint_t) if0);
+}
+
+#endif
+
+static inline size_t mbedtls_ct_size_if0(mbedtls_ct_condition_t condition, size_t if1)
+{
+    return (size_t) (condition & if1);
+}
+
+static inline unsigned mbedtls_ct_uint_if0(mbedtls_ct_condition_t condition, unsigned if1)
+{
+    return (unsigned) (condition & if1);
+}
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if0(mbedtls_ct_condition_t condition,
+                                                       mbedtls_mpi_uint if1)
+{
+    return (mbedtls_mpi_uint) (condition & if1);
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_eq(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y)
+{
+    return ~mbedtls_ct_bool_ne(x, y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_gt(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y)
+{
+    return mbedtls_ct_bool_lt(y, x);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_ge(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y)
+{
+    return ~mbedtls_ct_bool_lt(x, y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_le(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y)
+{
+    return ~mbedtls_ct_bool_gt(x, y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_xor(mbedtls_ct_condition_t x,
+                                                         mbedtls_ct_condition_t y)
+{
+    return (mbedtls_ct_condition_t) (x ^ y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_and(mbedtls_ct_condition_t x,
+                                                         mbedtls_ct_condition_t y)
+{
+    return (mbedtls_ct_condition_t) (x & y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_or(mbedtls_ct_condition_t x,
+                                                        mbedtls_ct_condition_t y)
+{
+    return (mbedtls_ct_condition_t) (x | y);
+}
+
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_not(mbedtls_ct_condition_t x)
+{
+    return (mbedtls_ct_condition_t) (~x);
+}
+
+#endif /* MBEDTLS_CONSTANT_TIME_IMPL_H */
diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h
index c4a32c7..9660758 100644
--- a/library/constant_time_internal.h
+++ b/library/constant_time_internal.h
@@ -20,224 +20,427 @@
 #ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H
 #define MBEDTLS_CONSTANT_TIME_INTERNAL_H
 
+#include <stdint.h>
+#include <stddef.h>
+
 #include "common.h"
 
 #if defined(MBEDTLS_BIGNUM_C)
 #include "mbedtls/bignum.h"
 #endif
 
-#if defined(MBEDTLS_SSL_TLS_C)
-#include "ssl_misc.h"
+/* The constant-time interface provides various operations that are likely
+ * to result in constant-time code that does not branch or use conditional
+ * instructions for secret data (for secret pointers, this also applies to
+ * the data pointed to).
+ *
+ * It has three main parts:
+ *
+ * - boolean operations
+ *   These are all named mbedtls_ct_bool_<operation>, and operate over
+ *   mbedtls_ct_condition_t.
+ *   All arguments are considered secret.
+ *   example: bool x = y | z          =>    x = mbedtls_ct_bool_or(y, z)
+ *
+ * - conditional data selection
+ *   These are all named mbedtls_ct_<type>_if and mbedtls_ct_<type>_if0
+ *   All arguments are considered secret.
+ *   example: size_t a = x ? b : c    =>    a = mbedtls_ct_size_if(x, b, c)
+ *   example: unsigned a = x ? b : 0  =>    a = mbedtls_ct_uint_if0(x, b)
+ *
+ * - block memory operations
+ *   Only some arguments are considered secret, as documented for each
+ *   function.
+ *   example: if (x) memcpy(...)      =>    mbedtls_ct_memcpy_if(x, ...)
+ *
+ * mbedtls_ct_condition_t should be treated as opaque and only manipulated
+ * via the functions in this header.
+ *
+ * mbedtls_ct_uint_t is an unsigned integer type over which constant time
+ * operations may be performed via the functions in this header. It is as big
+ * as the larger of size_t and mbedtls_mpi_uint, i.e. it is safe to cast
+ * to/from "unsigned int", "size_t", and "mbedtls_mpi_uint" (and any other
+ * not-larger integer types).
+ *
+ * For Arm (32-bit, 64-bit and Thumb), x86 and x86-64, assembly implementations
+ * are used to ensure that the generated code is constant time. For other
+ * architectures, a plain C fallback designed to yield constant-time code (this
+ * has been observed to be constant-time on latest gcc, clang and MSVC as of
+ * May 2023).
+ */
+
+#if (SIZE_MAX > 0xffffffffffffffffULL)
+/* Pointer size > 64-bit */
+typedef size_t    mbedtls_ct_condition_t;
+typedef size_t    mbedtls_ct_uint_t;
+typedef ptrdiff_t mbedtls_ct_int_t;
+#define MBEDTLS_CT_TRUE  ((mbedtls_ct_condition_t) SIZE_MAX)
+#elif (SIZE_MAX > 0xffffffff) || defined(MBEDTLS_HAVE_INT64)
+/* 32-bit < pointer size <= 64-bit, or 64-bit MPI */
+typedef uint64_t  mbedtls_ct_condition_t;
+typedef uint64_t  mbedtls_ct_uint_t;
+typedef int64_t   mbedtls_ct_int_t;
+#define MBEDTLS_CT_TRUE  ((mbedtls_ct_condition_t) UINT64_MAX)
+#else
+/* Pointer size <= 32-bit, and no 64-bit MPIs */
+typedef uint32_t  mbedtls_ct_condition_t;
+typedef uint32_t  mbedtls_ct_uint_t;
+typedef int32_t   mbedtls_ct_int_t;
+#define MBEDTLS_CT_TRUE  ((mbedtls_ct_condition_t) UINT32_MAX)
 #endif
+#define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) 0)
 
-#include <stddef.h>
-
-
-/** Turn a value into a mask:
- * - if \p value == 0, return the all-bits 0 mask, aka 0
- * - otherwise, return the all-bits 1 mask, aka (unsigned) -1
- *
- * This function can be used to write constant-time code by replacing branches
- * with bit operations using masks.
- *
- * \param value     The value to analyze.
- *
- * \return          Zero if \p value is zero, otherwise all-bits-one.
+/* constant_time_impl.h contains all the static inline implementations,
+ * so that constant_time_internal.h is more readable.
  */
-unsigned mbedtls_ct_uint_mask(unsigned value);
+#include "constant_time_impl.h"
 
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
 
-/** Turn a value into a mask:
- * - if \p value == 0, return the all-bits 0 mask, aka 0
- * - otherwise, return the all-bits 1 mask, aka (size_t) -1
- *
- * This function can be used to write constant-time code by replacing branches
- * with bit operations using masks.
- *
- * \param value     The value to analyze.
- *
- * \return          Zero if \p value is zero, otherwise all-bits-one.
+/* ============================================================================
+ * Boolean operations
  */
-size_t mbedtls_ct_size_mask(size_t value);
 
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-
-#if defined(MBEDTLS_BIGNUM_C)
-
-/** Turn a value into a mask:
- * - if \p value == 0, return the all-bits 0 mask, aka 0
- * - otherwise, return the all-bits 1 mask, aka (mbedtls_mpi_uint) -1
+/** Convert a number into a mbedtls_ct_condition_t.
  *
- * This function can be used to write constant-time code by replacing branches
- * with bit operations using masks.
+ * \param x Number to convert.
  *
- * \param value     The value to analyze.
+ * \return MBEDTLS_CT_TRUE if \p x != 0, or MBEDTLS_CT_FALSE if \p x == 0
  *
- * \return          Zero if \p value is zero, otherwise all-bits-one.
  */
-mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask(mbedtls_mpi_uint value);
+static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x);
 
-#endif /* MBEDTLS_BIGNUM_C */
-
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
-
-/** Constant-flow mask generation for "greater or equal" comparison:
- * - if \p x >= \p y, return all-bits 1, that is (size_t) -1
- * - otherwise, return all bits 0, that is 0
+/** Boolean "not equal" operation.
  *
- * This function can be used to write constant-time code by replacing branches
- * with bit operations using masks.
+ * Functionally equivalent to:
+ *
+ * \p x != \p y
  *
  * \param x     The first value to analyze.
  * \param y     The second value to analyze.
  *
- * \return      All-bits-one if \p x is greater or equal than \p y,
- *              otherwise zero.
+ * \return      MBEDTLS_CT_TRUE if \p x != \p y, otherwise MBEDTLS_CT_FALSE.
  */
-size_t mbedtls_ct_size_mask_ge(size_t x,
-                               size_t y);
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);
 
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
-
-/** Constant-flow boolean "equal" comparison:
- * return x == y
+/** Boolean "equals" operation.
  *
- * This is equivalent to \p x == \p y, but is likely to be compiled
- * to code using bitwise operation rather than a branch.
+ * Functionally equivalent to:
+ *
+ * \p x == \p y
  *
  * \param x     The first value to analyze.
  * \param y     The second value to analyze.
  *
- * \return      1 if \p x equals to \p y, otherwise 0.
+ * \return      MBEDTLS_CT_TRUE if \p x == \p y, otherwise MBEDTLS_CT_FALSE.
  */
-unsigned mbedtls_ct_size_bool_eq(size_t x,
-                                 size_t y);
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_eq(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y);
 
-#if defined(MBEDTLS_BIGNUM_C)
-
-/** Decide if an integer is less than the other, without branches.
+/** Boolean "less than" operation.
  *
- * This is equivalent to \p x < \p y, but is likely to be compiled
- * to code using bitwise operation rather than a branch.
+ * Functionally equivalent to:
+ *
+ * \p x < \p y
  *
  * \param x     The first value to analyze.
  * \param y     The second value to analyze.
  *
- * \return      1 if \p x is less than \p y, otherwise 0.
+ * \return      MBEDTLS_CT_TRUE if \p x < \p y, otherwise MBEDTLS_CT_FALSE.
  */
-unsigned mbedtls_ct_mpi_uint_lt(const mbedtls_mpi_uint x,
-                                const mbedtls_mpi_uint y);
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);
 
-/**
- * \brief          Check if one unsigned MPI is less than another in constant
- *                 time.
+/** Boolean "greater than" operation.
  *
- * \param A        The left-hand MPI. This must point to an array of limbs
- *                 with the same allocated length as \p B.
- * \param B        The right-hand MPI. This must point to an array of limbs
- *                 with the same allocated length as \p A.
- * \param limbs    The number of limbs in \p A and \p B.
- *                 This must not be 0.
+ * Functionally equivalent to:
  *
- * \return         The result of the comparison:
- *                 \c 1 if \p A is less than \p B.
- *                 \c 0 if \p A is greater than or equal to \p B.
+ * \p x > \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x > \p y, otherwise MBEDTLS_CT_FALSE.
  */
-unsigned mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
-                                const mbedtls_mpi_uint *B,
-                                size_t limbs);
-#endif /* MBEDTLS_BIGNUM_C */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_gt(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y);
 
-/** Choose between two integer values without branches.
+/** Boolean "greater or equal" operation.
  *
- * This is equivalent to `condition ? if1 : if0`, but is likely to be compiled
- * to code using bitwise operation rather than a branch.
+ * Functionally equivalent to:
+ *
+ * \p x >= \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x >= \p y,
+ *              otherwise MBEDTLS_CT_FALSE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_ge(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y);
+
+/** Boolean "less than or equal" operation.
+ *
+ * Functionally equivalent to:
+ *
+ * \p x <= \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x <= \p y,
+ *              otherwise MBEDTLS_CT_FALSE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_le(mbedtls_ct_uint_t x,
+                                                        mbedtls_ct_uint_t y);
+
+/** Boolean "xor" operation.
+ *
+ * Functionally equivalent to:
+ *
+ * \p x ^ \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \note        This is more efficient than mbedtls_ct_bool_ne if both arguments are
+ *              mbedtls_ct_condition_t.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x ^ \p y,
+ *              otherwise MBEDTLS_CT_FALSE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_xor(mbedtls_ct_condition_t x,
+                                                         mbedtls_ct_condition_t y);
+
+/** Boolean "and" operation.
+ *
+ * Functionally equivalent to:
+ *
+ * \p x && \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x && \p y,
+ *              otherwise MBEDTLS_CT_FALSE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_and(mbedtls_ct_condition_t x,
+                                                         mbedtls_ct_condition_t y);
+
+/** Boolean "or" operation.
+ *
+ * Functionally equivalent to:
+ *
+ * \p x || \p y
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      MBEDTLS_CT_TRUE if \p x || \p y,
+ *              otherwise MBEDTLS_CT_FALSE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_or(mbedtls_ct_condition_t x,
+                                                        mbedtls_ct_condition_t y);
+
+/** Boolean "not" operation.
+ *
+ * Functionally equivalent to:
+ *
+ * ! \p x
+ *
+ * \param x     The value to invert
+ *
+ * \return      MBEDTLS_CT_FALSE if \p x, otherwise MBEDTLS_CT_TRUE.
+ */
+static inline mbedtls_ct_condition_t mbedtls_ct_bool_not(mbedtls_ct_condition_t x);
+
+
+/* ============================================================================
+ * Data selection operations
+ */
+
+/** Choose between two size_t values.
+ *
+ * Functionally equivalent to:
+ *
+ * condition ? if1 : if0.
  *
  * \param condition     Condition to test.
- * \param if1           Value to use if \p condition is nonzero.
- * \param if0           Value to use if \p condition is zero.
+ * \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 is nonzero, otherwise \c if0.
+ * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0.
  */
-unsigned mbedtls_ct_uint_if(unsigned condition,
-                            unsigned if1,
-                            unsigned if0);
+static inline size_t mbedtls_ct_size_if(mbedtls_ct_condition_t condition,
+                                        size_t if1,
+                                        size_t if0);
+
+/** Choose between two unsigned values.
+ *
+ * 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 unsigned mbedtls_ct_uint_if(mbedtls_ct_condition_t condition,
+                                          unsigned if1,
+                                          unsigned if0);
 
 #if defined(MBEDTLS_BIGNUM_C)
 
-/** Conditionally assign a value without branches.
+/** Choose between two mbedtls_mpi_uint values.
  *
- * This is equivalent to `if ( condition ) dest = src`, but is likely
- * to be compiled to code using bitwise operation rather than a branch.
+ * Functionally equivalent to:
  *
- * \param n             \p dest and \p src must be arrays of limbs of size n.
- * \param dest          The MPI to conditionally assign to. This must point
- *                      to an initialized MPI.
- * \param src           The MPI to be assigned from. This must point to an
- *                      initialized MPI.
- * \param condition     Condition to test, must be 0 or 1.
+ * 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.
  */
-void mbedtls_ct_mpi_uint_cond_assign(size_t n,
-                                     mbedtls_mpi_uint *dest,
-                                     const mbedtls_mpi_uint *src,
-                                     unsigned char condition);
+static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if(mbedtls_ct_condition_t condition, \
+                                                      mbedtls_mpi_uint if1, \
+                                                      mbedtls_mpi_uint if0);
 
-#endif /* MBEDTLS_BIGNUM_C */
+#endif
 
-#if defined(MBEDTLS_BASE64_C)
-
-/** Given a value in the range 0..63, return the corresponding Base64 digit.
+/** Choose between an unsigned value and 0.
  *
- * The implementation assumes that letters are consecutive (e.g. ASCII
- * but not EBCDIC).
+ * Functionally equivalent to:
  *
- * \param value     A value in the range 0..63.
+ * condition ? if1 : 0.
  *
- * \return          A base64 digit converted from \p value.
+ * Functionally equivalent tombedtls_ct_uint_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.
  */
-unsigned char mbedtls_ct_base64_enc_char(unsigned char value);
+static inline unsigned mbedtls_ct_uint_if0(mbedtls_ct_condition_t condition, unsigned if1);
 
-/** Given a Base64 digit, return its value.
+#if defined(MBEDTLS_BIGNUM_C)
+
+/** Choose between an mbedtls_mpi_uint value and 0.
  *
- * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
- * return -1.
+ * Functionally equivalent to:
  *
- * The implementation assumes that letters are consecutive (e.g. ASCII
- * but not EBCDIC).
+ * condition ? if1 : 0.
  *
- * \param c     A base64 digit.
+ * Functionally equivalent tombedtls_ct_mpi_uint_if(condition, if1, 0) but
+ * results in smaller code size.
  *
- * \return      The value of the base64 digit \p c.
+ * \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.
  */
-signed char mbedtls_ct_base64_dec_value(unsigned char c);
+static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if0(mbedtls_ct_condition_t condition,
+                                                       mbedtls_mpi_uint if1);
 
-#endif /* MBEDTLS_BASE64_C */
+#endif
 
-#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-
-/** Conditional memcpy without branches.
+/** Constant-flow char selection
  *
- * This is equivalent to `if ( c1 == c2 ) memcpy(dest, src, len)`, but is likely
- * to be compiled to code using bitwise operation rather than a branch.
+ * \param low   Secret. Bottom of range
+ * \param high  Secret. Top of range
+ * \param c     Secret. Value to compare to range
+ * \param t     Secret. Value to return, if in range
  *
- * \param dest      The pointer to conditionally copy to.
- * \param src       The pointer to copy from. Shouldn't overlap with \p dest.
- * \param len       The number of bytes to copy.
- * \param c1        The first value to analyze in the condition.
- * \param c2        The second value to analyze in the condition.
+ * \return      \p t if \p low <= \p c <= \p high, 0 otherwise.
  */
-void mbedtls_ct_memcpy_if_eq(unsigned char *dest,
-                             const unsigned char *src,
-                             size_t len,
-                             size_t c1, size_t c2);
+static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low,
+                                                         unsigned char high,
+                                                         unsigned char c,
+                                                         unsigned char t);
 
-/** Copy data from a secret position with constant flow.
+
+/* ============================================================================
+ * Block memory operations
+ */
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+/** Conditionally set a block of memory to zero.
  *
- * This function copies \p len bytes from \p src_base + \p offset_secret to \p
- * dst, with a code flow and memory access pattern that does not depend on \p
- * offset_secret, but only on \p offset_min, \p offset_max and \p len.
- * Functionally equivalent to `memcpy(dst, src + offset_secret, len)`.
+ * Regardless of the condition, every byte will be read once and written to
+ * once.
+ *
+ * \param condition     Secret. Condition to test.
+ * \param buf           Secret. Pointer to the start of the buffer.
+ * \param len           Number of bytes to set to zero.
+ *
+ * \warning Unlike mbedtls_platform_zeroize, this does not have the same guarantees
+ * about not being optimised away if the memory is never read again.
+ */
+void mbedtls_ct_zeroize_if(mbedtls_ct_condition_t condition, void *buf, size_t len);
+
+/** Shift some data towards the left inside a buffer.
+ *
+ * Functionally equivalent to:
+ *
+ * memmove(start, start + offset, total - offset);
+ * memset(start + (total - offset), 0, offset);
+ *
+ * Timing independence comes at the expense of performance.
+ *
+ * \param start     Secret. Pointer to the start of the buffer.
+ * \param total     Total size of the buffer.
+ * \param offset    Secret. Offset from which to copy \p total - \p offset bytes.
+ */
+void mbedtls_ct_memmove_left(void *start,
+                             size_t total,
+                             size_t offset);
+
+#endif /* defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) */
+
+/** Conditional memcpy.
+ *
+ * Functionally equivalent to:
+ *
+ * if (condition) {
+ *      memcpy(dest, src1, len);
+ * } else {
+ *      if (src2 != NULL)
+ *          memcpy(dest, src2, len);
+ * }
+ *
+ * It will always read len bytes from src1.
+ * If src2 != NULL, it will always read len bytes from src2.
+ * If src2 == NULL, it will instead read len bytes from dest (as if src2 == dest).
+ *
+ * \param condition The condition
+ * \param dest      Secret. Destination pointer.
+ * \param src1      Secret. Pointer to copy from (if \p condition == MBEDTLS_CT_TRUE).
+ *                  This may be equal to \p dest, but may not overlap in other ways.
+ * \param src2      Secret (contents only - may branch to test if src2 == NULL).
+ *                  Pointer to copy from (if \p condition == MBEDTLS_CT_FALSE and \p src2 is not NULL). May be NULL.
+ *                  This may be equal to \p dest, but may not overlap it in other ways. It may overlap with \p src1.
+ * \param len       Number of bytes to copy.
+ */
+void mbedtls_ct_memcpy_if(mbedtls_ct_condition_t condition,
+                          unsigned char *dest,
+                          const unsigned char *src1,
+                          const unsigned char *src2,
+                          size_t len
+                          );
+
+/** Copy data from a secret position.
+ *
+ * Functionally equivalent to:
+ *
+ * memcpy(dst, src + offset, len)
+ *
+ * This function copies \p len bytes from \p src_base + \p offset to \p
+ * dst, with a code flow and memory access pattern that does not depend on
+ * \p offset, but only on \p offset_min, \p offset_max and \p len.
  *
  * \note                This function reads from \p dest, but the value that
  *                      is read does not influence the result and this
@@ -246,12 +449,12 @@
  *                      positives from static or dynamic analyzers, especially
  *                      if \p dest is not initialized.
  *
- * \param dest          The destination buffer. This must point to a writable
+ * \param dest          Secret. The destination buffer. This must point to a writable
  *                      buffer of at least \p len bytes.
- * \param src           The base of the source buffer. This must point to a
+ * \param src           Secret. The base of the source buffer. This must point to a
  *                      readable buffer of at least \p offset_max + \p len
- *                      bytes. Shouldn't overlap with \p dest.
- * \param offset        The offset in the source buffer from which to copy.
+ *                      bytes. Shouldn't overlap with \p dest
+ * \param offset        Secret. The offset in the source buffer from which to copy.
  *                      This must be no less than \p offset_min and no greater
  *                      than \p offset_max.
  * \param offset_min    The minimal value of \p offset.
@@ -265,99 +468,9 @@
                               size_t offset_max,
                               size_t len);
 
-/** Compute the HMAC of variable-length data with constant flow.
- *
- * This function computes the HMAC of the concatenation of \p add_data and \p
- * data, and does with a code flow and memory access pattern that does not
- * depend on \p data_len_secret, but only on \p min_data_len and \p
- * max_data_len. In particular, this function always reads exactly \p
- * max_data_len bytes from \p data.
- *
- * \param ctx               The HMAC context. It must have keys configured
- *                          with mbedtls_md_hmac_starts() and use one of the
- *                          following hashes: SHA-384, SHA-256, SHA-1 or MD-5.
- *                          It is reset using mbedtls_md_hmac_reset() after
- *                          the computation is complete to prepare for the
- *                          next computation.
- * \param add_data          The first part of the message whose HMAC is being
- *                          calculated. This must point to a readable buffer
- *                          of \p add_data_len bytes.
- * \param add_data_len      The length of \p add_data in bytes.
- * \param data              The buffer containing the second part of the
- *                          message. This must point to a readable buffer
- *                          of \p max_data_len bytes.
- * \param data_len_secret   The length of the data to process in \p data.
- *                          This must be no less than \p min_data_len and no
- *                          greater than \p max_data_len.
- * \param min_data_len      The minimal length of the second part of the
- *                          message, read from \p data.
- * \param max_data_len      The maximal length of the second part of the
- *                          message, read from \p data.
- * \param output            The HMAC will be written here. This must point to
- *                          a writable buffer of sufficient size to hold the
- *                          HMAC value.
- *
- * \retval 0 on success.
- * \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
- *         The hardware accelerator failed.
- */
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
-                    psa_algorithm_t alg,
-                    const unsigned char *add_data,
-                    size_t add_data_len,
-                    const unsigned char *data,
-                    size_t data_len_secret,
-                    size_t min_data_len,
-                    size_t max_data_len,
-                    unsigned char *output);
-#else
-int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
-                    const unsigned char *add_data,
-                    size_t add_data_len,
-                    const unsigned char *data,
-                    size_t data_len_secret,
-                    size_t min_data_len,
-                    size_t max_data_len,
-                    unsigned char *output);
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-
-#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-
-#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
-
-/** This function performs the unpadding part of a PKCS#1 v1.5 decryption
- *  operation (EME-PKCS1-v1_5 decoding).
- *
- * \note The return value from this function is a sensitive value
- *       (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen
- *       in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING
- *       is often a situation that an attacker can provoke and leaking which
- *       one is the result is precisely the information the attacker wants.
- *
- * \param input          The input buffer which is the payload inside PKCS#1v1.5
- *                       encryption padding, called the "encoded message EM"
- *                       by the terminology.
- * \param ilen           The length of the payload in the \p input buffer.
- * \param output         The buffer for the payload, called "message M" by the
- *                       PKCS#1 terminology. This must be a writable buffer of
- *                       length \p output_max_len bytes.
- * \param olen           The address at which to store the length of
- *                       the payload. This must not be \c NULL.
- * \param output_max_len The length in bytes of the output buffer \p output.
- *
- * \return      \c 0 on success.
- * \return      #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE
- *              The output buffer is too small for the unpadded payload.
- * \return      #MBEDTLS_ERR_RSA_INVALID_PADDING
- *              The input doesn't contain properly formatted padding.
- */
-int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input,
-                                         size_t ilen,
-                                         unsigned char *output,
-                                         size_t output_max_len,
-                                         size_t *olen);
-
-#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+/* Documented in include/mbedtls/constant_time.h. a and b are secret. */
+int mbedtls_ct_memcmp(const void *a,
+                      const void *b,
+                      size_t n);
 
 #endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */
diff --git a/library/constant_time_invasive.h b/library/constant_time_invasive.h
deleted file mode 100644
index c176b28..0000000
--- a/library/constant_time_invasive.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * \file constant_time_invasive.h
- *
- * \brief Constant-time module: interfaces for invasive testing only.
- *
- * The interfaces in this file are intended for testing purposes only.
- * They SHOULD NOT be made available in library integrations except when
- * building the library for testing.
- */
-/*
- *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef MBEDTLS_CONSTANT_TIME_INVASIVE_H
-#define MBEDTLS_CONSTANT_TIME_INVASIVE_H
-
-#include "common.h"
-
-#if defined(MBEDTLS_TEST_HOOKS)
-
-/** Turn a value into a mask:
- * - if \p low <= \p c <= \p high,
- *   return the all-bits 1 mask, aka (unsigned) -1
- * - otherwise, return the all-bits 0 mask, aka 0
- *
- * \param low   The value to analyze.
- * \param high  The value to analyze.
- * \param c     The value to analyze.
- *
- * \return      All-bits-one if \p low <= \p c <= \p high, otherwise zero.
- */
-unsigned char mbedtls_ct_uchar_mask_of_range(unsigned char low,
-                                             unsigned char high,
-                                             unsigned char c);
-
-#endif /* MBEDTLS_TEST_HOOKS */
-
-#endif /* MBEDTLS_CONSTANT_TIME_INVASIVE_H */
diff --git a/library/rsa.c b/library/rsa.c
index 8126ae9..4e85bbc 100644
--- a/library/rsa.c
+++ b/library/rsa.c
@@ -56,6 +56,164 @@
 
 #include "mbedtls/platform.h"
 
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+/** This function performs the unpadding part of a PKCS#1 v1.5 decryption
+ *  operation (EME-PKCS1-v1_5 decoding).
+ *
+ * \note The return value from this function is a sensitive value
+ *       (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen
+ *       in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING
+ *       is often a situation that an attacker can provoke and leaking which
+ *       one is the result is precisely the information the attacker wants.
+ *
+ * \param input          The input buffer which is the payload inside PKCS#1v1.5
+ *                       encryption padding, called the "encoded message EM"
+ *                       by the terminology.
+ * \param ilen           The length of the payload in the \p input buffer.
+ * \param output         The buffer for the payload, called "message M" by the
+ *                       PKCS#1 terminology. This must be a writable buffer of
+ *                       length \p output_max_len bytes.
+ * \param olen           The address at which to store the length of
+ *                       the payload. This must not be \c NULL.
+ * \param output_max_len The length in bytes of the output buffer \p output.
+ *
+ * \return      \c 0 on success.
+ * \return      #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE
+ *              The output buffer is too small for the unpadded payload.
+ * \return      #MBEDTLS_ERR_RSA_INVALID_PADDING
+ *              The input doesn't contain properly formatted padding.
+ */
+static int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input,
+                                                size_t ilen,
+                                                unsigned char *output,
+                                                size_t output_max_len,
+                                                size_t *olen)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t i, plaintext_max_size;
+
+    /* The following variables take sensitive values: their value must
+     * not leak into the observable behavior of the function other than
+     * the designated outputs (output, olen, return value). Otherwise
+     * this would open the execution of the function to
+     * side-channel-based variants of the Bleichenbacher padding oracle
+     * attack. Potential side channels include overall timing, memory
+     * access patterns (especially visible to an adversary who has access
+     * to a shared memory cache), and branches (especially visible to
+     * an adversary who has access to a shared code cache or to a shared
+     * branch predictor). */
+    size_t pad_count = 0;
+    mbedtls_ct_condition_t bad;
+    mbedtls_ct_condition_t pad_done;
+    size_t plaintext_size = 0;
+    mbedtls_ct_condition_t output_too_large;
+
+    plaintext_max_size = (output_max_len > ilen - 11) ? ilen - 11
+                                                        : output_max_len;
+
+    /* Check and get padding length in constant time and constant
+     * memory trace. The first byte must be 0. */
+    bad = mbedtls_ct_bool(input[0]);
+
+
+    /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
+     * where PS must be at least 8 nonzero bytes. */
+    bad = mbedtls_ct_bool_or(bad, mbedtls_ct_bool_ne(input[1], MBEDTLS_RSA_CRYPT));
+
+    /* Read the whole buffer. Set pad_done to nonzero if we find
+     * the 0x00 byte and remember the padding length in pad_count. */
+    pad_done = MBEDTLS_CT_FALSE;
+    for (i = 2; i < ilen; i++) {
+        mbedtls_ct_condition_t found = mbedtls_ct_bool_eq(input[i], 0);
+        pad_done   = mbedtls_ct_bool_or(pad_done, found);
+        pad_count += mbedtls_ct_uint_if0(mbedtls_ct_bool_not(pad_done), 1);
+    }
+
+    /* If pad_done is still zero, there's no data, only unfinished padding. */
+    bad = mbedtls_ct_bool_or(bad, mbedtls_ct_bool_not(pad_done));
+
+    /* There must be at least 8 bytes of padding. */
+    bad = mbedtls_ct_bool_or(bad, mbedtls_ct_bool_gt(8, pad_count));
+
+    /* If the padding is valid, set plaintext_size to the number of
+     * remaining bytes after stripping the padding. If the padding
+     * is invalid, avoid leaking this fact through the size of the
+     * output: use the maximum message size that fits in the output
+     * buffer. Do it without branches to avoid leaking the padding
+     * validity through timing. RSA keys are small enough that all the
+     * size_t values involved fit in unsigned int. */
+    plaintext_size = mbedtls_ct_uint_if(
+        bad, (unsigned) plaintext_max_size,
+        (unsigned) (ilen - pad_count - 3));
+
+    /* Set output_too_large to 0 if the plaintext fits in the output
+     * buffer and to 1 otherwise. */
+    output_too_large = mbedtls_ct_bool_gt(plaintext_size,
+                                          plaintext_max_size);
+
+    /* Set ret without branches to avoid timing attacks. Return:
+     * - INVALID_PADDING if the padding is bad (bad != 0).
+     * - OUTPUT_TOO_LARGE if the padding is good but the decrypted
+     *   plaintext does not fit in the output buffer.
+     * - 0 if the padding is correct. */
+    ret = -(int) mbedtls_ct_uint_if(
+        bad,
+        (unsigned) (-(MBEDTLS_ERR_RSA_INVALID_PADDING)),
+        mbedtls_ct_uint_if0(
+            output_too_large,
+            (unsigned) (-(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE)))
+        );
+
+    /* If the padding is bad or the plaintext is too large, zero the
+     * data that we're about to copy to the output buffer.
+     * We need to copy the same amount of data
+     * from the same buffer whether the padding is good or not to
+     * avoid leaking the padding validity through overall timing or
+     * through memory or cache access patterns. */
+    mbedtls_ct_zeroize_if(mbedtls_ct_bool_or(bad, output_too_large), input + 11, ilen - 11);
+
+    /* If the plaintext is too large, truncate it to the buffer size.
+     * Copy anyway to avoid revealing the length through timing, because
+     * revealing the length is as bad as revealing the padding validity
+     * for a Bleichenbacher attack. */
+    plaintext_size = mbedtls_ct_uint_if(output_too_large,
+                                        (unsigned) plaintext_max_size,
+                                        (unsigned) plaintext_size);
+
+    /* Move the plaintext to the leftmost position where it can start in
+     * the working buffer, i.e. make it start plaintext_max_size from
+     * the end of the buffer. Do this with a memory access trace that
+     * does not depend on the plaintext size. After this move, the
+     * starting location of the plaintext is no longer sensitive
+     * information. */
+    mbedtls_ct_memmove_left(input + ilen - plaintext_max_size,
+                            plaintext_max_size,
+                            plaintext_max_size - plaintext_size);
+
+    /* Finally copy the decrypted plaintext plus trailing zeros into the output
+     * buffer. If output_max_len is 0, then output may be an invalid pointer
+     * and the result of memcpy() would be undefined; prevent undefined
+     * behavior making sure to depend only on output_max_len (the size of the
+     * user-provided output buffer), which is independent from plaintext
+     * length, validity of padding, success of the decryption, and other
+     * secrets. */
+    if (output_max_len != 0) {
+        memcpy(output, input + ilen - plaintext_max_size, plaintext_max_size);
+    }
+
+    /* Report the amount of data we copied to the output buffer. In case
+     * of errors (bad padding or output too large), the value of *olen
+     * when this function returns is not specified. Making it equivalent
+     * to the good case limits the risks of leaking the padding validity. */
+    *olen = plaintext_size;
+
+    return ret;
+}
+
+#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+
 #if !defined(MBEDTLS_RSA_ALT)
 
 int mbedtls_rsa_import(mbedtls_rsa_context *ctx,
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 2d6d7ba..0145903 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2787,4 +2787,64 @@
 int mbedtls_ssl_tls13_finalize_client_hello(mbedtls_ssl_context *ssl);
 #endif
 
+#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
+
+/** Compute the HMAC of variable-length data with constant flow.
+ *
+ * This function computes the HMAC of the concatenation of \p add_data and \p
+ * data, and does with a code flow and memory access pattern that does not
+ * depend on \p data_len_secret, but only on \p min_data_len and \p
+ * max_data_len. In particular, this function always reads exactly \p
+ * max_data_len bytes from \p data.
+ *
+ * \param ctx               The HMAC context. It must have keys configured
+ *                          with mbedtls_md_hmac_starts() and use one of the
+ *                          following hashes: SHA-384, SHA-256, SHA-1 or MD-5.
+ *                          It is reset using mbedtls_md_hmac_reset() after
+ *                          the computation is complete to prepare for the
+ *                          next computation.
+ * \param add_data          The first part of the message whose HMAC is being
+ *                          calculated. This must point to a readable buffer
+ *                          of \p add_data_len bytes.
+ * \param add_data_len      The length of \p add_data in bytes.
+ * \param data              The buffer containing the second part of the
+ *                          message. This must point to a readable buffer
+ *                          of \p max_data_len bytes.
+ * \param data_len_secret   The length of the data to process in \p data.
+ *                          This must be no less than \p min_data_len and no
+ *                          greater than \p max_data_len.
+ * \param min_data_len      The minimal length of the second part of the
+ *                          message, read from \p data.
+ * \param max_data_len      The maximal length of the second part of the
+ *                          message, read from \p data.
+ * \param output            The HMAC will be written here. This must point to
+ *                          a writable buffer of sufficient size to hold the
+ *                          HMAC value.
+ *
+ * \retval 0 on success.
+ * \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
+ *         The hardware accelerator failed.
+ */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
+                    psa_algorithm_t mac_alg,
+                    const unsigned char *add_data,
+                    size_t add_data_len,
+                    const unsigned char *data,
+                    size_t data_len_secret,
+                    size_t min_data_len,
+                    size_t max_data_len,
+                    unsigned char *output);
+#else
+int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
+                    const unsigned char *add_data,
+                    size_t add_data_len,
+                    const unsigned char *data,
+                    size_t data_len_secret,
+                    size_t min_data_len,
+                    size_t max_data_len,
+                    unsigned char *output);
+#endif /* defined(MBEDTLS_USE_PSA_CRYPTO) */
+#endif /* MBEDTLS_TEST_HOOKS && defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) */
+
 #endif /* ssl_misc.h */
diff --git a/library/ssl_msg.c b/library/ssl_msg.c
index e905023..e07be05 100644
--- a/library/ssl_msg.c
+++ b/library/ssl_msg.c
@@ -60,6 +60,234 @@
 #define PSA_TO_MBEDTLS_ERR(status) local_err_translation(status)
 #endif
 
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+
+#if defined(PSA_WANT_ALG_SHA_384)
+#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_384)
+#elif defined(PSA_WANT_ALG_SHA_256)
+#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_256)
+#else /* See check_config.h */
+#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_1)
+#endif
+
+MBEDTLS_STATIC_TESTABLE
+int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
+                    psa_algorithm_t mac_alg,
+                    const unsigned char *add_data,
+                    size_t add_data_len,
+                    const unsigned char *data,
+                    size_t data_len_secret,
+                    size_t min_data_len,
+                    size_t max_data_len,
+                    unsigned char *output)
+{
+    /*
+     * This function breaks the HMAC abstraction and uses psa_hash_clone()
+     * extension in order to get constant-flow behaviour.
+     *
+     * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
+     * concatenation, and okey/ikey are the XOR of the key with some fixed bit
+     * patterns (see RFC 2104, sec. 2).
+     *
+     * We'll first compute ikey/okey, then inner_hash = HASH(ikey + msg) by
+     * hashing up to minlen, then cloning the context, and for each byte up
+     * to maxlen finishing up the hash computation, keeping only the
+     * correct result.
+     *
+     * Then we only need to compute HASH(okey + inner_hash) and we're done.
+     */
+    psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH(mac_alg);
+    const size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
+    unsigned char key_buf[MAX_HASH_BLOCK_LENGTH];
+    const size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+    psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
+    size_t hash_length;
+
+    unsigned char aux_out[PSA_HASH_MAX_SIZE];
+    psa_hash_operation_t aux_operation = PSA_HASH_OPERATION_INIT;
+    size_t offset;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    size_t mac_key_length;
+    size_t i;
+
+#define PSA_CHK(func_call)        \
+    do {                            \
+        status = (func_call);       \
+        if (status != PSA_SUCCESS) \
+        goto cleanup;           \
+    } while (0)
+
+    /* Export MAC key
+     * We assume key length is always exactly the output size
+     * which is never more than the block size, thus we use block_size
+     * as the key buffer size.
+     */
+    PSA_CHK(psa_export_key(key, key_buf, block_size, &mac_key_length));
+
+    /* Calculate ikey */
+    for (i = 0; i < mac_key_length; i++) {
+        key_buf[i] = (unsigned char) (key_buf[i] ^ 0x36);
+    }
+    for (; i < block_size; ++i) {
+        key_buf[i] = 0x36;
+    }
+
+    PSA_CHK(psa_hash_setup(&operation, hash_alg));
+
+    /* Now compute inner_hash = HASH(ikey + msg) */
+    PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
+    PSA_CHK(psa_hash_update(&operation, add_data, add_data_len));
+    PSA_CHK(psa_hash_update(&operation, data, min_data_len));
+
+    /* Fill the hash buffer in advance with something that is
+     * not a valid hash (barring an attack on the hash and
+     * deliberately-crafted input), in case the caller doesn't
+     * check the return status properly. */
+    memset(output, '!', hash_size);
+
+    /* For each possible length, compute the hash up to that point */
+    for (offset = min_data_len; offset <= max_data_len; offset++) {
+        PSA_CHK(psa_hash_clone(&operation, &aux_operation));
+        PSA_CHK(psa_hash_finish(&aux_operation, aux_out,
+                                PSA_HASH_MAX_SIZE, &hash_length));
+        /* Keep only the correct inner_hash in the output buffer */
+        mbedtls_ct_memcpy_if(mbedtls_ct_bool_eq(offset, data_len_secret),
+                             output, aux_out, NULL, hash_size);
+
+        if (offset < max_data_len) {
+            PSA_CHK(psa_hash_update(&operation, data + offset, 1));
+        }
+    }
+
+    /* Abort current operation to prepare for final operation */
+    PSA_CHK(psa_hash_abort(&operation));
+
+    /* Calculate okey */
+    for (i = 0; i < mac_key_length; i++) {
+        key_buf[i] = (unsigned char) ((key_buf[i] ^ 0x36) ^ 0x5C);
+    }
+    for (; i < block_size; ++i) {
+        key_buf[i] = 0x5C;
+    }
+
+    /* Now compute HASH(okey + inner_hash) */
+    PSA_CHK(psa_hash_setup(&operation, hash_alg));
+    PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
+    PSA_CHK(psa_hash_update(&operation, output, hash_size));
+    PSA_CHK(psa_hash_finish(&operation, output, hash_size, &hash_length));
+
+#undef PSA_CHK
+
+cleanup:
+    mbedtls_platform_zeroize(key_buf, MAX_HASH_BLOCK_LENGTH);
+    mbedtls_platform_zeroize(aux_out, PSA_HASH_MAX_SIZE);
+
+    psa_hash_abort(&operation);
+    psa_hash_abort(&aux_operation);
+    return PSA_TO_MBEDTLS_ERR(status);
+}
+
+#undef MAX_HASH_BLOCK_LENGTH
+
+#else
+MBEDTLS_STATIC_TESTABLE
+int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
+                    const unsigned char *add_data,
+                    size_t add_data_len,
+                    const unsigned char *data,
+                    size_t data_len_secret,
+                    size_t min_data_len,
+                    size_t max_data_len,
+                    unsigned char *output)
+{
+    /*
+     * This function breaks the HMAC abstraction and uses the md_clone()
+     * extension to the MD API in order to get constant-flow behaviour.
+     *
+     * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
+     * concatenation, and okey/ikey are the XOR of the key with some fixed bit
+     * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx.
+     *
+     * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to
+     * minlen, then cloning the context, and for each byte up to maxlen
+     * finishing up the hash computation, keeping only the correct result.
+     *
+     * Then we only need to compute HASH(okey + inner_hash) and we're done.
+     */
+    const mbedtls_md_type_t md_alg = mbedtls_md_get_type(ctx->md_info);
+    /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5,
+     * all of which have the same block size except SHA-384. */
+    const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64;
+    const unsigned char * const ikey = ctx->hmac_ctx;
+    const unsigned char * const okey = ikey + block_size;
+    const size_t hash_size = mbedtls_md_get_size(ctx->md_info);
+
+    unsigned char aux_out[MBEDTLS_MD_MAX_SIZE];
+    mbedtls_md_context_t aux;
+    size_t offset;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_md_init(&aux);
+
+#define MD_CHK(func_call) \
+    do {                    \
+        ret = (func_call);  \
+        if (ret != 0)      \
+        goto cleanup;   \
+    } while (0)
+
+    MD_CHK(mbedtls_md_setup(&aux, ctx->md_info, 0));
+
+    /* After hmac_start() of hmac_reset(), ikey has already been hashed,
+     * so we can start directly with the message */
+    MD_CHK(mbedtls_md_update(ctx, add_data, add_data_len));
+    MD_CHK(mbedtls_md_update(ctx, data, min_data_len));
+
+    /* Fill the hash buffer in advance with something that is
+     * not a valid hash (barring an attack on the hash and
+     * deliberately-crafted input), in case the caller doesn't
+     * check the return status properly. */
+    memset(output, '!', hash_size);
+
+    /* For each possible length, compute the hash up to that point */
+    for (offset = min_data_len; offset <= max_data_len; offset++) {
+        MD_CHK(mbedtls_md_clone(&aux, ctx));
+        MD_CHK(mbedtls_md_finish(&aux, aux_out));
+        /* Keep only the correct inner_hash in the output buffer */
+        mbedtls_ct_memcpy_if(mbedtls_ct_bool_eq(offset, data_len_secret),
+                             output, aux_out, NULL, hash_size);
+
+        if (offset < max_data_len) {
+            MD_CHK(mbedtls_md_update(ctx, data + offset, 1));
+        }
+    }
+
+    /* The context needs to finish() before it starts() again */
+    MD_CHK(mbedtls_md_finish(ctx, aux_out));
+
+    /* Now compute HASH(okey + inner_hash) */
+    MD_CHK(mbedtls_md_starts(ctx));
+    MD_CHK(mbedtls_md_update(ctx, okey, block_size));
+    MD_CHK(mbedtls_md_update(ctx, output, hash_size));
+    MD_CHK(mbedtls_md_finish(ctx, output));
+
+    /* Done, get ready for next time */
+    MD_CHK(mbedtls_md_hmac_reset(ctx));
+
+#undef MD_CHK
+
+cleanup:
+    mbedtls_md_free(&aux);
+    return ret;
+}
+
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
+
 static uint32_t ssl_get_hs_total_len(mbedtls_ssl_context const *ssl);
 
 /*
@@ -1690,11 +1918,11 @@
         padlen = data[rec->data_len - 1];
 
         if (auth_done == 1) {
-            const size_t mask = mbedtls_ct_size_mask_ge(
+            const mbedtls_ct_condition_t ge = mbedtls_ct_bool_ge(
                 rec->data_len,
                 padlen + 1);
-            correct &= mask;
-            padlen  &= mask;
+            correct = mbedtls_ct_size_if0(ge, correct);
+            padlen  = mbedtls_ct_size_if0(ge, padlen);
         } else {
 #if defined(MBEDTLS_SSL_DEBUG_ALL)
             if (rec->data_len < transform->maclen + padlen + 1) {
@@ -1706,12 +1934,11 @@
                                           padlen + 1));
             }
 #endif
-
-            const size_t mask = mbedtls_ct_size_mask_ge(
+            const mbedtls_ct_condition_t ge = mbedtls_ct_bool_ge(
                 rec->data_len,
                 transform->maclen + padlen + 1);
-            correct &= mask;
-            padlen  &= mask;
+            correct = mbedtls_ct_size_if0(ge, correct);
+            padlen  = mbedtls_ct_size_if0(ge, padlen);
         }
 
         padlen++;
@@ -1740,19 +1967,20 @@
             /* pad_count += (idx >= padding_idx) &&
              *              (check[idx] == padlen - 1);
              */
-            const size_t mask = mbedtls_ct_size_mask_ge(idx, padding_idx);
-            const size_t equal = mbedtls_ct_size_bool_eq(check[idx],
-                                                         padlen - 1);
-            pad_count += mask & equal;
+            const mbedtls_ct_condition_t a = mbedtls_ct_bool_ge(idx, padding_idx);
+            size_t increment = mbedtls_ct_size_if0(a, 1);
+            const mbedtls_ct_condition_t b = mbedtls_ct_bool_eq(check[idx], padlen - 1);
+            increment = mbedtls_ct_size_if0(b, increment);
+            pad_count += increment;
         }
-        correct &= mbedtls_ct_size_bool_eq(pad_count, padlen);
+        correct = mbedtls_ct_size_if0(mbedtls_ct_bool_eq(pad_count, padlen), padlen);
 
 #if defined(MBEDTLS_SSL_DEBUG_ALL)
         if (padlen > 0 && correct == 0) {
             MBEDTLS_SSL_DEBUG_MSG(1, ("bad padding byte detected"));
         }
 #endif
-        padlen &= mbedtls_ct_size_mask(correct);
+        padlen = mbedtls_ct_size_if0(mbedtls_ct_bool(correct), padlen);
 
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c
index 5060277..006ce17 100644
--- a/library/ssl_tls12_server.c
+++ b/library/ssl_tls12_server.c
@@ -3500,9 +3500,8 @@
     unsigned char *pms = ssl->handshake->premaster + pms_offset;
     unsigned char ver[2];
     unsigned char fake_pms[48], peer_pms[48];
-    unsigned char mask;
-    size_t i, peer_pmslen;
-    unsigned int diff;
+    size_t peer_pmslen;
+    mbedtls_ct_condition_t diff;
 
     /* In case of a failure in decryption, the decryption may write less than
      * 2 bytes of output, but we always read the first two bytes. It doesn't
@@ -3531,13 +3530,10 @@
     /* Avoid data-dependent branches while checking for invalid
      * padding, to protect against timing-based Bleichenbacher-type
      * attacks. */
-    diff  = (unsigned int) ret;
-    diff |= peer_pmslen ^ 48;
-    diff |= peer_pms[0] ^ ver[0];
-    diff |= peer_pms[1] ^ ver[1];
-
-    /* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */
-    mask = mbedtls_ct_uint_mask(diff);
+    diff = mbedtls_ct_bool(ret);
+    diff = mbedtls_ct_bool_or(diff, mbedtls_ct_bool_ne(peer_pmslen, 48));
+    diff = mbedtls_ct_bool_or(diff, mbedtls_ct_bool_ne(peer_pms[0], ver[0]));
+    diff = mbedtls_ct_bool_or(diff, mbedtls_ct_bool_ne(peer_pms[1], ver[1]));
 
     /*
      * Protection against Bleichenbacher's attack: invalid PKCS#1 v1.5 padding
@@ -3556,7 +3552,7 @@
     }
 
 #if defined(MBEDTLS_SSL_DEBUG_ALL)
-    if (diff != 0) {
+    if (diff != MBEDTLS_CT_FALSE) {
         MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message"));
     }
 #endif
@@ -3570,9 +3566,7 @@
 
     /* Set pms to either the true or the fake PMS, without
      * data-dependent branches. */
-    for (i = 0; i < ssl->handshake->pmslen; i++) {
-        pms[i] = (mask & fake_pms[i]) | ((~mask) & peer_pms[i]);
-    }
+    mbedtls_ct_memcpy_if(diff, pms, fake_pms, peer_pms, ssl->handshake->pmslen);
 
     return 0;
 }
diff --git a/tests/suites/test_suite_base64.data b/tests/suites/test_suite_base64.data
index 5556668..3999e73 100644
--- a/tests/suites/test_suite_base64.data
+++ b/tests/suites/test_suite_base64.data
@@ -1,27 +1,3 @@
-mask_of_range empty (1..0)
-mask_of_range:1:0
-
-mask_of_range empty (255..0)
-mask_of_range:255:0
-
-mask_of_range empty (42..7)
-mask_of_range:42:7
-
-mask_of_range 0..0
-mask_of_range:0:0
-
-mask_of_range 42..42
-mask_of_range:42:42
-
-mask_of_range 255..255
-mask_of_range:255:255
-
-mask_of_range 0..255
-mask_of_range:0:255
-
-mask_of_range 'A'..'Z'
-mask_of_range:65:90
-
 enc_char (all digits)
 enc_chars:
 
diff --git a/tests/suites/test_suite_base64.function b/tests/suites/test_suite_base64.function
index ce6bd42..e351ad8 100644
--- a/tests/suites/test_suite_base64.function
+++ b/tests/suites/test_suite_base64.function
@@ -1,7 +1,7 @@
 /* BEGIN_HEADER */
 #include "mbedtls/base64.h"
+#include "base64_internal.h"
 #include "constant_time_internal.h"
-#include "constant_time_invasive.h"
 #include <test/constant_flow.h>
 
 #if defined(MBEDTLS_TEST_HOOKS)
@@ -17,26 +17,6 @@
  */
 
 /* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
-void mask_of_range(int low_arg, int high_arg)
-{
-    unsigned char low = low_arg, high = high_arg;
-    unsigned c;
-    for (c = 0; c <= 0xff; c++) {
-        mbedtls_test_set_step(c);
-        TEST_CF_SECRET(&c, sizeof(c));
-        unsigned char m = mbedtls_ct_uchar_mask_of_range(low, high, c);
-        TEST_CF_PUBLIC(&c, sizeof(c));
-        TEST_CF_PUBLIC(&m, sizeof(m));
-        if (low <= c && c <= high) {
-            TEST_EQUAL(m, 0xff);
-        } else {
-            TEST_EQUAL(m, 0);
-        }
-    }
-}
-/* END_CASE */
-
-/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
 void enc_chars()
 {
     for (unsigned value = 0; value < 64; value++) {
diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function
index 7f858e5..caa7e04 100644
--- a/tests/suites/test_suite_bignum.function
+++ b/tests/suites/test_suite_bignum.function
@@ -438,7 +438,7 @@
 
     TEST_ASSERT(mbedtls_mpi_lt_mpi_ct(&X, &Y, &ret) == input_err);
     if (input_err == 0) {
-        TEST_ASSERT(ret == input_uret);
+        TEST_EQUAL(ret, input_uret);
     }
 
 exit:
diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function
index 81a3a45..7ac03d0 100644
--- a/tests/suites/test_suite_bignum_core.function
+++ b/tests/suites/test_suite_bignum_core.function
@@ -358,7 +358,7 @@
     TEST_CF_SECRET(Y, X_limbs * sizeof(mbedtls_mpi_uint));
 
     ret = mbedtls_mpi_core_lt_ct(X, Y, X_limbs);
-    TEST_EQUAL(ret, exp_ret);
+    TEST_EQUAL(!!ret, exp_ret);
 
 exit:
     mbedtls_free(X);
@@ -384,25 +384,25 @@
 
     TEST_CF_SECRET(A, A_limbs * sizeof(*A));
 
-    TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(0, A, A_limbs), 1);
-    TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0], A, A_limbs), 1);
+    TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(0, A, A_limbs), 1);
+    TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0], A, A_limbs), 1);
 
     if (is_large) {
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
-                                                A, A_limbs), 1);
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
-                                                A, A_limbs), 1);
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
-                                                A, A_limbs), 1);
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
+                                                  A, A_limbs), 1);
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
+                                                  A, A_limbs), 1);
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
+                                                  A, A_limbs), 1);
     } else {
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
-                                                A, A_limbs),
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
+                                                  A, A_limbs),
                    A[0] + 1 <= A[0]);
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
-                                                A, A_limbs),
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
+                                                  A, A_limbs),
                    (mbedtls_mpi_uint) (-1) >> 1 <= A[0]);
-        TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
-                                                A, A_limbs),
+        TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
+                                                  A, A_limbs),
                    (mbedtls_mpi_uint) (-1) <= A[0]);
     }
 
@@ -447,7 +447,7 @@
     TEST_CF_SECRET(X, bytes);
     TEST_CF_SECRET(Y, bytes);
 
-    mbedtls_mpi_core_cond_assign(X, Y, copy_limbs, 1);
+    mbedtls_mpi_core_cond_assign(X, Y, copy_limbs, mbedtls_ct_bool(1));
 
     TEST_CF_PUBLIC(X, bytes);
     TEST_CF_PUBLIC(Y, bytes);
@@ -515,7 +515,7 @@
     TEST_CF_SECRET(X, bytes);
     TEST_CF_SECRET(Y, bytes);
 
-    mbedtls_mpi_core_cond_swap(X, Y, copy_limbs, 1);
+    mbedtls_mpi_core_cond_swap(X, Y, copy_limbs, mbedtls_ct_bool(1));
 
     TEST_CF_PUBLIC(X, bytes);
     TEST_CF_PUBLIC(Y, bytes);
diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function
index e4db3d7..51ff4d8 100644
--- a/tests/suites/test_suite_bignum_random.function
+++ b/tests/suites/test_suite_bignum_random.function
@@ -134,7 +134,7 @@
 
     if (expected_ret == 0) {
         TEST_EQUAL(0, mbedtls_mpi_core_lt_ct(result, lower_bound, limbs));
-        TEST_EQUAL(1, mbedtls_mpi_core_lt_ct(result, upper_bound, limbs));
+        TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result, upper_bound, limbs));
     }
 
 exit:
@@ -429,8 +429,7 @@
          * size as the modulus, otherwise it's a mistake in the test data. */
         TEST_EQUAL(result_limbs, N.limbs);
         /* Sanity check: check that the result is in range */
-        TEST_EQUAL(mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs),
-                   1);
+        TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs));
         /* Check result >= min (changes result) */
         TEST_EQUAL(mbedtls_mpi_core_sub_int(result_digits, result_digits, min,
                                             result_limbs),
@@ -444,8 +443,7 @@
                                               mbedtls_test_rnd_std_rand, NULL),
                    expected_ret);
         if (expected_ret == 0) {
-            TEST_EQUAL(mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs),
-                       1);
+            TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs));
             TEST_EQUAL(mbedtls_mpi_core_sub_int(result_digits, result.p, min,
                                                 result_limbs),
                        0);
diff --git a/tests/suites/test_suite_constant_time.data b/tests/suites/test_suite_constant_time.data
index 91a25fa..1b0b964 100644
--- a/tests/suites/test_suite_constant_time.data
+++ b/tests/suites/test_suite_constant_time.data
@@ -1,14 +1,14 @@
 # these are the numbers we'd get with an empty plaintext and truncated HMAC
 Constant-flow memcpy from offset: small
-ssl_cf_memcpy_offset:0:5:10
+mbedtls_ct_memcpy_offset:0:5:10
 
 # we could get this with 255-bytes plaintext and untruncated SHA-256
 Constant-flow memcpy from offset: medium
-ssl_cf_memcpy_offset:0:255:32
+mbedtls_ct_memcpy_offset:0:255:32
 
 # we could get this with 255-bytes plaintext and untruncated SHA-384
 Constant-flow memcpy from offset: large
-ssl_cf_memcpy_offset:100:339:48
+mbedtls_ct_memcpy_offset:100:339:48
 
 mbedtls_ct_memcmp NULL
 mbedtls_ct_memcmp_null
@@ -91,47 +91,611 @@
 mbedtls_ct_memcmp len 17 offset 3
 mbedtls_ct_memcmp:-1:17:3
 
-mbedtls_ct_memcpy_if_eq len 1 offset 0
-mbedtls_ct_memcpy_if_eq:1:1:0
+mbedtls_ct_memcpy_if len 1 offset 0
+mbedtls_ct_memcpy_if:1:1:0
 
-mbedtls_ct_memcpy_if_eq len 1 offset 1
-mbedtls_ct_memcpy_if_eq:1:1:1
+mbedtls_ct_memcpy_if len 1 offset 1
+mbedtls_ct_memcpy_if:1:1:1
 
-mbedtls_ct_memcpy_if_eq len 4 offset 0
-mbedtls_ct_memcpy_if_eq:1:1:0
+mbedtls_ct_memcpy_if len 4 offset 0
+mbedtls_ct_memcpy_if:1:1:0
 
-mbedtls_ct_memcpy_if_eq len 4 offset 1
-mbedtls_ct_memcpy_if_eq:1:1:1
+mbedtls_ct_memcpy_if len 4 offset 1
+mbedtls_ct_memcpy_if:1:1:1
 
-mbedtls_ct_memcpy_if_eq len 4 offset 2
-mbedtls_ct_memcpy_if_eq:1:1:2
+mbedtls_ct_memcpy_if len 4 offset 2
+mbedtls_ct_memcpy_if:1:1:2
 
-mbedtls_ct_memcpy_if_eq len 4 offset 3
-mbedtls_ct_memcpy_if_eq:1:1:3
+mbedtls_ct_memcpy_if len 4 offset 3
+mbedtls_ct_memcpy_if:1:1:3
 
-mbedtls_ct_memcpy_if_eq len 15 offset 0
-mbedtls_ct_memcpy_if_eq:1:15:0
+mbedtls_ct_memcpy_if len 15 offset 0
+mbedtls_ct_memcpy_if:1:15:0
 
-mbedtls_ct_memcpy_if_eq len 15 offset 1
-mbedtls_ct_memcpy_if_eq:1:15:1
+mbedtls_ct_memcpy_if len 15 offset 1
+mbedtls_ct_memcpy_if:1:15:1
 
-mbedtls_ct_memcpy_if_eq len 16 offset 0
-mbedtls_ct_memcpy_if_eq:1:16:0
+mbedtls_ct_memcpy_if len 16 offset 0
+mbedtls_ct_memcpy_if:1:16:0
 
-mbedtls_ct_memcpy_if_eq len 16 offset 1
-mbedtls_ct_memcpy_if_eq:1:16:1
+mbedtls_ct_memcpy_if len 16 offset 1
+mbedtls_ct_memcpy_if:1:16:1
 
-mbedtls_ct_memcpy_if_eq len 17 offset 0
-mbedtls_ct_memcpy_if_eq:1:17:0
+mbedtls_ct_memcpy_if len 17 offset 0
+mbedtls_ct_memcpy_if:1:17:0
 
-mbedtls_ct_memcpy_if_eq len 17 offset 1
-mbedtls_ct_memcpy_if_eq:1:17:1
+mbedtls_ct_memcpy_if len 17 offset 1
+mbedtls_ct_memcpy_if:1:17:1
 
-mbedtls_ct_memcpy_if_eq len 0 not eq
-mbedtls_ct_memcpy_if_eq:0:17:0
+mbedtls_ct_memcpy_if len 0 not eq
+mbedtls_ct_memcpy_if:0:17:0
 
-mbedtls_ct_memcpy_if_eq len 5 offset 1 not eq
-mbedtls_ct_memcpy_if_eq:0:5:1
+mbedtls_ct_memcpy_if len 5 offset 1 not eq
+mbedtls_ct_memcpy_if:0:5:1
 
-mbedtls_ct_memcpy_if_eq len 17 offset 3 not eq
-mbedtls_ct_memcpy_if_eq:0:17:3
+mbedtls_ct_memcpy_if len 17 offset 3 not eq
+mbedtls_ct_memcpy_if:0:17:3
+
+mbedtls_ct_bool 0
+mbedtls_ct_bool:"0x0"
+
+mbedtls_ct_bool 1
+mbedtls_ct_bool:"0x1"
+
+mbedtls_ct_bool 4
+mbedtls_ct_bool:"0x4"
+
+mbedtls_ct_bool 0xfffffff
+mbedtls_ct_bool:"0xfffffff"
+
+mbedtls_ct_bool 0x7fffffff
+mbedtls_ct_bool:"0x7fffffff"
+
+mbedtls_ct_bool 0xfffffffe
+mbedtls_ct_bool:"0xfffffffe"
+
+mbedtls_ct_bool 0xffffffff
+mbedtls_ct_bool:"0xffffffff"
+
+mbedtls_ct_bool 0x0fffffffffffffff
+mbedtls_ct_bool:"0x0fffffffffffffff"
+
+mbedtls_ct_bool 0x7fffffffffffffff
+mbedtls_ct_bool:"0x7fffffffffffffff"
+
+mbedtls_ct_bool 0xffffffffffffffff
+mbedtls_ct_bool:"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x0 0x0
+mbedtls_ct_bool_xxx:"0x0":"0x0"
+
+mbedtls_ct_bool_xxx 0x0 0x1
+mbedtls_ct_bool_xxx:"0x0":"0x1"
+
+mbedtls_ct_bool_xxx 0x0 0x7fffffff
+mbedtls_ct_bool_xxx:"0x0":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0x0 0xffffffff
+mbedtls_ct_bool_xxx:"0x0":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0x0 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0x0":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x0 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0x0":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x1 0x0
+mbedtls_ct_bool_xxx:"0x1":"0x0"
+
+mbedtls_ct_bool_xxx 0x1 0x1
+mbedtls_ct_bool_xxx:"0x1":"0x1"
+
+mbedtls_ct_bool_xxx 0x1 0x7fffffff
+mbedtls_ct_bool_xxx:"0x1":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0x1 0xffffffff
+mbedtls_ct_bool_xxx:"0x1":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0x1 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0x1":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x1 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0x1":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0x0
+mbedtls_ct_bool_xxx:"0x7fffffff":"0x0"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0x1
+mbedtls_ct_bool_xxx:"0x7fffffff":"0x1"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0x7fffffff
+mbedtls_ct_bool_xxx:"0x7fffffff":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0xffffffff
+mbedtls_ct_bool_xxx:"0x7fffffff":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0x7fffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffff 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0x7fffffff":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffff 0x0
+mbedtls_ct_bool_xxx:"0xffffffff":"0x0"
+
+mbedtls_ct_bool_xxx 0xffffffff 0x1
+mbedtls_ct_bool_xxx:"0xffffffff":"0x1"
+
+mbedtls_ct_bool_xxx 0xffffffff 0x7fffffff
+mbedtls_ct_bool_xxx:"0xffffffff":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0xffffffff 0xffffffff
+mbedtls_ct_bool_xxx:"0xffffffff":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffff 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0xffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffff 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0xffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x0
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x0"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x1
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x1"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x7fffffff
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0xffffffff
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0x7fffffffffffffff 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0x0
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x0"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0x1
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x1"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0x7fffffff
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0xffffffff
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0xffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_bool_xxx 0xffffffffffffffff 0xffffffffffffffff
+mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_bool_xxx 138 256
+mbedtls_ct_bool_xxx:"138":"256"
+
+mbedtls_ct_bool_xxx 256 138
+mbedtls_ct_bool_xxx:"256":"138"
+
+mbedtls_ct_bool_xxx 6 6
+mbedtls_ct_bool_xxx:"0x6":"0x6"
+
+mbedtls_ct_uchar_in_range_if 0 0 0
+mbedtls_ct_uchar_in_range_if:0:0:0
+
+mbedtls_ct_uchar_in_range_if 0 0 100
+mbedtls_ct_uchar_in_range_if:0:0:100
+
+mbedtls_ct_uchar_in_range_if 0 0 255
+mbedtls_ct_uchar_in_range_if:0:0:255
+
+mbedtls_ct_uchar_in_range_if 0 65 0
+mbedtls_ct_uchar_in_range_if:0:65:0
+
+mbedtls_ct_uchar_in_range_if 0 65 100
+mbedtls_ct_uchar_in_range_if:0:65:100
+
+mbedtls_ct_uchar_in_range_if 0 65 255
+mbedtls_ct_uchar_in_range_if:0:65:255
+
+mbedtls_ct_uchar_in_range_if 0 90 0
+mbedtls_ct_uchar_in_range_if:0:90:0
+
+mbedtls_ct_uchar_in_range_if 0 90 100
+mbedtls_ct_uchar_in_range_if:0:90:100
+
+mbedtls_ct_uchar_in_range_if 0 90 255
+mbedtls_ct_uchar_in_range_if:0:90:255
+
+mbedtls_ct_uchar_in_range_if 0 255 0
+mbedtls_ct_uchar_in_range_if:0:255:0
+
+mbedtls_ct_uchar_in_range_if 0 255 100
+mbedtls_ct_uchar_in_range_if:0:255:100
+
+mbedtls_ct_uchar_in_range_if 0 255 255
+mbedtls_ct_uchar_in_range_if:0:255:255
+
+mbedtls_ct_uchar_in_range_if 65 0 0
+mbedtls_ct_uchar_in_range_if:65:0:0
+
+mbedtls_ct_uchar_in_range_if 65 0 100
+mbedtls_ct_uchar_in_range_if:65:0:100
+
+mbedtls_ct_uchar_in_range_if 65 0 255
+mbedtls_ct_uchar_in_range_if:65:0:255
+
+mbedtls_ct_uchar_in_range_if 65 65 0
+mbedtls_ct_uchar_in_range_if:65:65:0
+
+mbedtls_ct_uchar_in_range_if 65 65 100
+mbedtls_ct_uchar_in_range_if:65:65:100
+
+mbedtls_ct_uchar_in_range_if 65 65 255
+mbedtls_ct_uchar_in_range_if:65:65:255
+
+mbedtls_ct_uchar_in_range_if 65 90 0
+mbedtls_ct_uchar_in_range_if:65:90:0
+
+mbedtls_ct_uchar_in_range_if 65 90 100
+mbedtls_ct_uchar_in_range_if:65:90:100
+
+mbedtls_ct_uchar_in_range_if 65 90 255
+mbedtls_ct_uchar_in_range_if:65:90:255
+
+mbedtls_ct_uchar_in_range_if 65 255 0
+mbedtls_ct_uchar_in_range_if:65:255:0
+
+mbedtls_ct_uchar_in_range_if 65 255 100
+mbedtls_ct_uchar_in_range_if:65:255:100
+
+mbedtls_ct_uchar_in_range_if 65 255 255
+mbedtls_ct_uchar_in_range_if:65:255:255
+
+mbedtls_ct_uchar_in_range_if 90 0 0
+mbedtls_ct_uchar_in_range_if:90:0:0
+
+mbedtls_ct_uchar_in_range_if 90 0 100
+mbedtls_ct_uchar_in_range_if:90:0:100
+
+mbedtls_ct_uchar_in_range_if 90 0 255
+mbedtls_ct_uchar_in_range_if:90:0:255
+
+mbedtls_ct_uchar_in_range_if 90 65 0
+mbedtls_ct_uchar_in_range_if:90:65:0
+
+mbedtls_ct_uchar_in_range_if 90 65 100
+mbedtls_ct_uchar_in_range_if:90:65:100
+
+mbedtls_ct_uchar_in_range_if 90 65 255
+mbedtls_ct_uchar_in_range_if:90:65:255
+
+mbedtls_ct_uchar_in_range_if 90 90 0
+mbedtls_ct_uchar_in_range_if:90:90:0
+
+mbedtls_ct_uchar_in_range_if 90 90 100
+mbedtls_ct_uchar_in_range_if:90:90:100
+
+mbedtls_ct_uchar_in_range_if 90 90 255
+mbedtls_ct_uchar_in_range_if:90:90:255
+
+mbedtls_ct_uchar_in_range_if 90 255 0
+mbedtls_ct_uchar_in_range_if:90:255:0
+
+mbedtls_ct_uchar_in_range_if 90 255 100
+mbedtls_ct_uchar_in_range_if:90:255:100
+
+mbedtls_ct_uchar_in_range_if 90 255 255
+mbedtls_ct_uchar_in_range_if:90:255:255
+
+mbedtls_ct_uchar_in_range_if 255 0 0
+mbedtls_ct_uchar_in_range_if:255:0:0
+
+mbedtls_ct_uchar_in_range_if 255 0 100
+mbedtls_ct_uchar_in_range_if:255:0:100
+
+mbedtls_ct_uchar_in_range_if 255 0 255
+mbedtls_ct_uchar_in_range_if:255:0:255
+
+mbedtls_ct_uchar_in_range_if 255 65 0
+mbedtls_ct_uchar_in_range_if:255:65:0
+
+mbedtls_ct_uchar_in_range_if 255 65 100
+mbedtls_ct_uchar_in_range_if:255:65:100
+
+mbedtls_ct_uchar_in_range_if 255 65 255
+mbedtls_ct_uchar_in_range_if:255:65:255
+
+mbedtls_ct_uchar_in_range_if 255 90 0
+mbedtls_ct_uchar_in_range_if:255:90:0
+
+mbedtls_ct_uchar_in_range_if 255 90 100
+mbedtls_ct_uchar_in_range_if:255:90:100
+
+mbedtls_ct_uchar_in_range_if 255 90 255
+mbedtls_ct_uchar_in_range_if:255:90:255
+
+mbedtls_ct_uchar_in_range_if 255 255 0
+mbedtls_ct_uchar_in_range_if:255:255:0
+
+mbedtls_ct_uchar_in_range_if 255 255 100
+mbedtls_ct_uchar_in_range_if:255:255:100
+
+mbedtls_ct_uchar_in_range_if 255 255 255
+mbedtls_ct_uchar_in_range_if:255:255:255
+
+mbedtls_ct_if 0x0 0x0 0x0
+mbedtls_ct_if:"0x0":"0x0":"0x0"
+
+mbedtls_ct_if 0x0 0x0 0x1
+mbedtls_ct_if:"0x0":"0x0":"0x1"
+
+mbedtls_ct_if 0x0 0x0 0x7fffffff
+mbedtls_ct_if:"0x0":"0x0":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0x0 0xffffffff
+mbedtls_ct_if:"0x0":"0x0":"0xffffffff"
+
+mbedtls_ct_if 0x0 0x0 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0x0":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0x0 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0x0":"0xffffffffffffffff"
+
+mbedtls_ct_if 0x0 0x1 0x0
+mbedtls_ct_if:"0x0":"0x1":"0x0"
+
+mbedtls_ct_if 0x0 0x1 0x1
+mbedtls_ct_if:"0x0":"0x1":"0x1"
+
+mbedtls_ct_if 0x0 0x1 0x7fffffff
+mbedtls_ct_if:"0x0":"0x1":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0x1 0xffffffff
+mbedtls_ct_if:"0x0":"0x1":"0xffffffff"
+
+mbedtls_ct_if 0x0 0x1 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0x1":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0x1 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0x1":"0xffffffffffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffff 0x0
+mbedtls_ct_if:"0x0":"0x7fffffff":"0x0"
+
+mbedtls_ct_if 0x0 0x7fffffff 0x1
+mbedtls_ct_if:"0x0":"0x7fffffff":"0x1"
+
+mbedtls_ct_if 0x0 0x7fffffff 0x7fffffff
+mbedtls_ct_if:"0x0":"0x7fffffff":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0x7fffffff 0xffffffff
+mbedtls_ct_if:"0x0":"0x7fffffff":"0xffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0x7fffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffff 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0x7fffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0x0 0xffffffff 0x0
+mbedtls_ct_if:"0x0":"0xffffffff":"0x0"
+
+mbedtls_ct_if 0x0 0xffffffff 0x1
+mbedtls_ct_if:"0x0":"0xffffffff":"0x1"
+
+mbedtls_ct_if 0x0 0xffffffff 0x7fffffff
+mbedtls_ct_if:"0x0":"0xffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0xffffffff 0xffffffff
+mbedtls_ct_if:"0x0":"0xffffffff":"0xffffffff"
+
+mbedtls_ct_if 0x0 0xffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0xffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0xffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0xffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0x0
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x0"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0x1
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x1"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0x7fffffff
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0xffffffff
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0xffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0x7fffffffffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0x0
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x0"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0x1
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x1"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0x7fffffff
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0xffffffff
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0xffffffff"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0x0 0xffffffffffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x0 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x1 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x0
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x0"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x1
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x1"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x7fffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x7fffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0xffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0xffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x7fffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x7fffffffffffffff"
+
+mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0xffffffffffffffff
+mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0xffffffffffffffff"
+
+mbedtls_ct_zeroize_if 0x0 0
+mbedtls_ct_zeroize_if:"0x0":0
+
+mbedtls_ct_zeroize_if 0x0 1
+mbedtls_ct_zeroize_if:"0x0":1
+
+mbedtls_ct_zeroize_if 0x0 1024
+mbedtls_ct_zeroize_if:"0x0":1024
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 0
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":0
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 1
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":1
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 4
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":4
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 5
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":5
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 7
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":7
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 8
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":8
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 9
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":9
+
+mbedtls_ct_zeroize_if 0xffffffffffffffff 1024
+mbedtls_ct_zeroize_if:"0xffffffffffffffff":1024
+
+mbedtls_ct_memmove_left 0 0
+mbedtls_ct_memmove_left:0:0
+
+mbedtls_ct_memmove_left 1 0
+mbedtls_ct_memmove_left:1:0
+
+mbedtls_ct_memmove_left 1 1
+mbedtls_ct_memmove_left:1:1
+
+mbedtls_ct_memmove_left 16 0
+mbedtls_ct_memmove_left:16:0
+
+mbedtls_ct_memmove_left 16 1
+mbedtls_ct_memmove_left:16:1
+
+mbedtls_ct_memmove_left 16 4
+mbedtls_ct_memmove_left:16:4
+
+mbedtls_ct_memmove_left 16 15
+mbedtls_ct_memmove_left:16:15
+
+mbedtls_ct_memmove_left 16 16
+mbedtls_ct_memmove_left:16:16
diff --git a/tests/suites/test_suite_constant_time.function b/tests/suites/test_suite_constant_time.function
index a2bf396..cce5b84 100644
--- a/tests/suites/test_suite_constant_time.function
+++ b/tests/suites/test_suite_constant_time.function
@@ -8,9 +8,15 @@
  * under MSan or Valgrind will detect a non-constant-time implementation.
  */
 
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <mbedtls/bignum.h>
 #include <mbedtls/constant_time.h>
 #include <constant_time_internal.h>
-#include <constant_time_invasive.h>
 
 #include <test/constant_flow.h>
 /* END_HEADER */
@@ -26,15 +32,152 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mbedtls_ct_bool(char *input)
+{
+    mbedtls_ct_uint_t v = (mbedtls_ct_uint_t) strtoull(input, NULL, 16);
+    TEST_ASSERT(errno == 0);
+
+    mbedtls_ct_condition_t expected = (v != 0) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_CF_SECRET(&v, sizeof(v));
+    TEST_EQUAL(mbedtls_ct_bool(v), expected);
+    TEST_CF_PUBLIC(&v, sizeof(v));
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mbedtls_ct_bool_xxx(char *x_str, char *y_str)
+{
+    mbedtls_ct_uint_t x = strtoull(x_str, NULL, 0);
+    mbedtls_ct_uint_t y = strtoull(y_str, NULL, 0);
+
+    mbedtls_ct_uint_t x1 = x;
+    mbedtls_ct_uint_t y1 = y;
+
+    TEST_CF_SECRET(&x, sizeof(x));
+    TEST_CF_SECRET(&y, sizeof(y));
+
+    mbedtls_ct_condition_t expected = x1 ? MBEDTLS_CT_FALSE : MBEDTLS_CT_TRUE;
+    TEST_EQUAL(mbedtls_ct_bool_not(mbedtls_ct_bool(x)), expected);
+
+    expected = x1 != y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_ne(x, y), expected);
+
+    expected = x1 == y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_eq(x, y), expected);
+
+    expected = x1 > y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_gt(x, y), expected);
+
+    expected = x1 < y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_lt(x, y), expected);
+
+    expected = x1 >= y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_ge(x, y), expected);
+
+    expected = x1 <= y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_le(x, y), expected);
+
+    expected = mbedtls_ct_bool(x) ^ mbedtls_ct_bool(y) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_xor(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
+
+    expected = mbedtls_ct_bool(x) & mbedtls_ct_bool(y) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_and(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
+
+    expected = mbedtls_ct_bool(x) | mbedtls_ct_bool(y) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
+    TEST_EQUAL(mbedtls_ct_bool_or(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
+
+    TEST_CF_PUBLIC(&x, sizeof(x));
+    TEST_CF_PUBLIC(&y, sizeof(y));
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_BASE64_C */
+void mbedtls_ct_uchar_in_range_if(int li, int hi, int ti)
+{
+    unsigned char l = li, h = hi, t = ti;
+
+    for (unsigned x = 0; x <= 255; x++) {
+        unsigned char expected = (x >= l) && (x <= h) ? t : 0;
+
+        TEST_CF_SECRET(&x, sizeof(x));
+        TEST_CF_SECRET(&l, sizeof(l));
+        TEST_CF_SECRET(&h, sizeof(h));
+        TEST_CF_SECRET(&t, sizeof(t));
+
+        TEST_EQUAL(mbedtls_ct_uchar_in_range_if(l, h, (unsigned char) x, t), expected);
+
+        TEST_CF_PUBLIC(&x, sizeof(x));
+        TEST_CF_PUBLIC(&l, sizeof(l));
+        TEST_CF_PUBLIC(&h, sizeof(h));
+        TEST_CF_PUBLIC(&t, sizeof(t));
+    }
+}
+/* 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));
+    mbedtls_ct_uint_t t = (mbedtls_ct_uint_t) strtoull(t_str, NULL, 16);
+    mbedtls_ct_uint_t f = (mbedtls_ct_uint_t) strtoull(f_str, NULL, 16);
+
+    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_if(c, t, f), expected);
+    TEST_EQUAL(mbedtls_ct_size_if(c, t, f), (size_t) expected);
+    TEST_EQUAL(mbedtls_ct_uint_if(c, t, f), (unsigned) expected);
+#if defined(MBEDTLS_BIGNUM_C)
+    TEST_EQUAL(mbedtls_ct_mpi_uint_if(c, t, f), (mbedtls_mpi_uint) expected);
+#endif
+
+    TEST_EQUAL(mbedtls_ct_uint_if0(c, t), (unsigned) expected0);
+#if defined(MBEDTLS_BIGNUM_C)
+    TEST_EQUAL(mbedtls_ct_mpi_uint_if0(c, t), (mbedtls_mpi_uint) expected0);
+#endif
+
+    TEST_CF_PUBLIC(&c, sizeof(c));
+    TEST_CF_PUBLIC(&t, sizeof(t));
+    TEST_CF_PUBLIC(&f, sizeof(f));
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_PKCS1_V15:MBEDTLS_RSA_C:!MBEDTLS_RSA_ALT */
+void mbedtls_ct_zeroize_if(char *c_str, int len)
+{
+    uint8_t *buf = NULL;
+    mbedtls_ct_condition_t c = mbedtls_ct_bool(strtoull(c_str, NULL, 16));
+
+    ASSERT_ALLOC(buf, len);
+    for (size_t i = 0; i < (size_t) len; i++) {
+        buf[i] = 1;
+    }
+
+    TEST_CF_SECRET(&c, sizeof(c));
+    TEST_CF_SECRET(buf, len);
+    mbedtls_ct_zeroize_if(c, buf, len);
+    TEST_CF_PUBLIC(&c, sizeof(c));
+    TEST_CF_PUBLIC(buf, len);
+
+    for (size_t i = 0; i < (size_t) len; i++) {
+        TEST_EQUAL(buf[i], c != 0 ? 0 : 1);
+    }
+exit:
+    mbedtls_free(buf);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mbedtls_ct_memcmp(int same, int size, int offset)
 {
     uint8_t *a = NULL, *b = NULL;
     ASSERT_ALLOC(a, size + offset);
     ASSERT_ALLOC(b, size + offset);
 
-    TEST_CF_SECRET(a + offset, size);
-    TEST_CF_SECRET(b + offset, size);
-
     /* Construct data that matches, if same == -1, otherwise
      * same gives the number of bytes (after the initial offset)
      * that will match; after that it will differ.
@@ -49,9 +192,15 @@
     }
 
     int reference = memcmp(a + offset, b + offset, size);
+
+    TEST_CF_SECRET(a, size + offset);
+    TEST_CF_SECRET(b, size + offset);
+
     int actual = mbedtls_ct_memcmp(a + offset, b + offset, size);
-    TEST_CF_PUBLIC(a + offset, size);
-    TEST_CF_PUBLIC(b + offset, size);
+
+    TEST_CF_PUBLIC(a, size + offset);
+    TEST_CF_PUBLIC(b, size + offset);
+    TEST_CF_PUBLIC(&actual, sizeof(actual));
 
     if (same == -1 || same >= size) {
         TEST_ASSERT(reference == 0);
@@ -66,59 +215,140 @@
 }
 /* END_CASE */
 
-/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-void mbedtls_ct_memcpy_if_eq(int eq, int size, int offset)
+/* BEGIN_CASE */
+void mbedtls_ct_memcpy_if(int eq, int size, int offset)
 {
-    uint8_t *src = NULL, *result = NULL, *expected = NULL;
+    uint8_t *src = NULL, *src2 = NULL, *result = NULL, *expected = NULL;
     ASSERT_ALLOC(src, size + offset);
+    ASSERT_ALLOC(src2, size + offset);
     ASSERT_ALLOC(result, size + offset);
     ASSERT_ALLOC(expected, size + offset);
 
+    /* Apply offset to result only */
     for (int i = 0; i < size + offset; i++) {
-        src[i]    = 1;
-        result[i] = 0xff;
+        src[i]      = 1;
+        result[i]   = 0xff;
         expected[i] = eq ? 1 : 0xff;
     }
 
-    int one, secret_eq;
-    TEST_CF_SECRET(&one, sizeof(one));
-    TEST_CF_SECRET(&secret_eq,  sizeof(secret_eq));
-    one = 1;
-    secret_eq = eq;
+    int secret_eq = eq;
+    TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
+    TEST_CF_SECRET(src, size + offset);
+    TEST_CF_SECRET(result, size + offset);
 
-    mbedtls_ct_memcpy_if_eq(result + offset, src, size, secret_eq, one);
+    mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result + offset, src, NULL, size);
 
-    TEST_CF_PUBLIC(&one, sizeof(one));
     TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
+    TEST_CF_PUBLIC(src, size + offset);
+    TEST_CF_PUBLIC(result, size + offset);
 
     ASSERT_COMPARE(expected, size, result + offset, size);
 
+
+    /* Apply offset to src only */
     for (int i = 0; i < size + offset; i++) {
         src[i]    = 1;
         result[i] = 0xff;
         expected[i] = eq ? 1 : 0xff;
     }
 
-    TEST_CF_SECRET(&one, sizeof(one));
-    TEST_CF_SECRET(&secret_eq,  sizeof(secret_eq));
-    one = 1;
-    secret_eq = eq;
+    TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
+    TEST_CF_SECRET(src, size + offset);
+    TEST_CF_SECRET(result, size + offset);
 
-    mbedtls_ct_memcpy_if_eq(result, src + offset, size, secret_eq, one);
+    mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result, src + offset, NULL, size);
 
-    TEST_CF_PUBLIC(&one, sizeof(one));
     TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
+    TEST_CF_PUBLIC(src, size + offset);
+    TEST_CF_PUBLIC(result, size + offset);
 
     ASSERT_COMPARE(expected, size, result, size);
+
+
+    /* Apply offset to src and src2 */
+    for (int i = 0; i < size + offset; i++) {
+        src[i]      = 1;
+        src2[i]     = 2;
+        result[i]   = 0xff;
+        expected[i] = eq ? 1 : 2;
+    }
+
+    TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
+    TEST_CF_SECRET(src, size + offset);
+    TEST_CF_SECRET(src2, size + offset);
+    TEST_CF_SECRET(result, size + offset);
+
+    mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result, src + offset, src2 + offset, size);
+
+    TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
+    TEST_CF_PUBLIC(src, size + offset);
+    TEST_CF_SECRET(src2, size + offset);
+    TEST_CF_PUBLIC(result, size + offset);
+
+    ASSERT_COMPARE(expected, size, result, size);
+
+
+    /* result == src == dest */
+    for (int i = 0; i < size + offset; i++) {
+        src[i]      = 2;
+        expected[i] = 2;
+    }
+
+    TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
+    TEST_CF_SECRET(src, size + offset);
+    TEST_CF_SECRET(result, size + offset);
+
+    mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), src + offset, src + offset, src + offset,
+                         size);
+
+    TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
+    TEST_CF_PUBLIC(src, size + offset);
+    TEST_CF_PUBLIC(result, size + offset);
+
+    ASSERT_COMPARE(expected, size, src + offset, size);
 exit:
     mbedtls_free(src);
+    mbedtls_free(src2);
     mbedtls_free(result);
     mbedtls_free(expected);
 }
 /* END_CASE */
 
-/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
-void ssl_cf_memcpy_offset(int offset_min, int offset_max, int len)
+/* BEGIN_CASE depends_on:MBEDTLS_PKCS1_V15:MBEDTLS_RSA_C:!MBEDTLS_RSA_ALT */
+void mbedtls_ct_memmove_left(int len, int offset)
+{
+    size_t l = (size_t) len;
+    size_t o = (size_t) offset;
+
+    uint8_t *buf = NULL, *buf_expected = NULL;
+    ASSERT_ALLOC(buf, l);
+    ASSERT_ALLOC(buf_expected, l);
+
+    for (size_t i = 0; i < l; i++) {
+        buf[i] = (uint8_t) i;
+        buf_expected[i] = buf[i];
+    }
+
+    //Note: Marking o as secret causes false positives from Memsan
+    //TEST_CF_SECRET(&o, sizeof(o));
+    TEST_CF_SECRET(buf, l);
+    mbedtls_ct_memmove_left(buf, l, o);
+    //TEST_CF_PUBLIC(&o, sizeof(o));
+    TEST_CF_PUBLIC(buf, l);
+
+    if (l > 0) {
+        memmove(buf_expected, buf_expected + o, l - o);
+        memset(buf_expected + (l - o), 0, o);
+        TEST_ASSERT(memcmp(buf, buf_expected, l) == 0);
+    }
+exit:
+    mbedtls_free(buf);
+    mbedtls_free(buf_expected);
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_MAC */
+void mbedtls_ct_memcpy_offset(int offset_min, int offset_max, int len)
 {
     unsigned char *dst = NULL;
     unsigned char *src = NULL;
@@ -135,9 +365,12 @@
         mbedtls_test_set_step((int) secret);
 
         TEST_CF_SECRET(&secret, sizeof(secret));
+        TEST_CF_SECRET(src, len);
+        TEST_CF_SECRET(dst, len);
         mbedtls_ct_memcpy_offset(dst, src, secret,
                                  offset_min, offset_max, len);
         TEST_CF_PUBLIC(&secret, sizeof(secret));
+        TEST_CF_PUBLIC(src, len);
         TEST_CF_PUBLIC(dst, len);
 
         ASSERT_COMPARE(dst, len, src + secret, len);
diff --git a/tests/suites/test_suite_constant_time_hmac.function b/tests/suites/test_suite_constant_time_hmac.function
index 9ee372b..902acfa 100644
--- a/tests/suites/test_suite_constant_time_hmac.function
+++ b/tests/suites/test_suite_constant_time_hmac.function
@@ -8,7 +8,7 @@
 #include <test/constant_flow.h>
 /* END_HEADER */
 
-/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
+/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_MAC:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
 void ssl_cf_hmac(int hash)
 {
     /*
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 2658a43..77c79e1 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -1622,7 +1622,7 @@
     TEST_EQUAL(0, mbedtls_mpi_mod_random(&rX, 1, &m,
                                          mbedtls_test_rnd_std_rand, NULL));
 
-    TEST_ASSERT(mbedtls_mpi_core_lt_ct(rX.p, m.p, limbs) == 1);
+    TEST_ASSERT(mbedtls_mpi_core_lt_ct(rX.p, m.p, limbs) == MBEDTLS_CT_TRUE);
 
 exit:
     mbedtls_mpi_mod_modulus_free(&m);