/**
 *  Constant-time functions
 *
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
 */

#ifndef TF_PSA_CRYPTO_CONSTANT_TIME_INTERNAL_H
#define TF_PSA_CRYPTO_CONSTANT_TIME_INTERNAL_H

#include <stdint.h>
#include <stddef.h>

#include "tf_psa_crypto_common.h"

/*
 * bignum.h may define MBEDTLS_HAVE_INT64 that is used in the definition of
 * mbedtls_ct_*_t types below.
 */
#include "mbedtls/private/bignum.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_<type>_<operation>.
 *   They operate over <type> and return mbedtls_ct_condition_t.
 *   All arguments are considered secret.
 *   example: bool x = y | z          =>    x = mbedtls_ct_bool_or(y, z)
 *   example: bool x = y == z         =>    x = mbedtls_ct_uint_eq(y, z)
 *
 * - conditional data selection
 *   These are all named mbedtls_ct_<type>_if and mbedtls_ct_<type>_if_else_0
 *   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_if_else_0(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 must be treated as opaque and only created and
 * manipulated via the functions in this header. The compiler should never
 * be able to prove anything about its value at compile-time.
 *
 * 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, it uses 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).
 *
 * For readability, the static inline definitions are separated out into
 * constant_time_impl.h.
 */

#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) mbedtls_ct_compiler_opaque(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_SIZE_64
#define MBEDTLS_CT_TRUE  ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(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_SIZE_32
#define MBEDTLS_CT_TRUE  ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT32_MAX))
#endif
#define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(0))

/* ============================================================================
 * Boolean operations
 */

/** Convert a number into a mbedtls_ct_condition_t.
 *
 * \param x Number to convert.
 *
 * \return MBEDTLS_CT_TRUE if \p x != 0, or MBEDTLS_CT_FALSE if \p x == 0
 *
 */
static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x);

/** Boolean "not 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_uint_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);

/** Boolean "equals" 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_uint_eq(mbedtls_ct_uint_t x,
                                                        mbedtls_ct_uint_t y);

/** Boolean "less than" 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_uint_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);

/** Boolean "greater than" 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_uint_gt(mbedtls_ct_uint_t x,
                                                        mbedtls_ct_uint_t y);

/** Boolean "greater 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_uint_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_uint_le(mbedtls_ct_uint_t x,
                                                        mbedtls_ct_uint_t y);

/** Boolean not-equals 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_uint_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_ne(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 == 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 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);

/** Choose between two mbedtls_ct_condition_t 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 mbedtls_ct_condition_t mbedtls_ct_bool_if(mbedtls_ct_condition_t condition,
                                                        mbedtls_ct_condition_t if1,
                                                        mbedtls_ct_condition_t if0);

/** Choose between an unsigned value and 0.
 *
 * Functionally equivalent to:
 *
 * condition ? if1 : 0.
 *
 * Functionally equivalent to mbedtls_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.
 */
static inline unsigned mbedtls_ct_uint_if_else_0(mbedtls_ct_condition_t condition, unsigned if1);

/** Choose between an mbedtls_ct_condition_t and 0.
 *
 * Functionally equivalent to:
 *
 * condition ? if1 : 0.
 *
 * Functionally equivalent to mbedtls_ct_bool_if(condition, if1, 0) but
 * results in smaller code size.
 *
 * \param condition     Condition to test.
 * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
 *
 * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0.
 */
static inline mbedtls_ct_condition_t mbedtls_ct_bool_if_else_0(mbedtls_ct_condition_t condition,
                                                               mbedtls_ct_condition_t if1);

/** Choose between a size_t value and 0.
 *
 * Functionally equivalent to:
 *
 * condition ? if1 : 0.
 *
 * Functionally equivalent to mbedtls_ct_size_if(condition, if1, 0) but
 * results in smaller code size.
 *
 * \param condition     Condition to test.
 * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
 *
 * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0.
 */
static inline size_t mbedtls_ct_size_if_else_0(mbedtls_ct_condition_t condition, size_t if1);

/** Constant-flow char selection
 *
 * \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
 *
 * \return      \p t if \p low <= \p c <= \p high, 0 otherwise.
 */
static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low,
                                                         unsigned char high,
                                                         unsigned char c,
                                                         unsigned char t);

/** Choose between two error values. The values must be in the range [-32767..0].
 *
 * Functionally equivalent to:
 *
 * condition ? if1 : if0.
 *
 * \param condition     Condition to test.
 * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
 * \param if0           Value to use if \p condition == MBEDTLS_CT_FALSE.
 *
 * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0.
 */
static inline int mbedtls_ct_error_if(mbedtls_ct_condition_t condition, int if1, int if0);

/** Choose between an error value and 0. The error value must be in the range [-32767..0].
 *
 * Functionally equivalent to:
 *
 * condition ? if1 : 0.
 *
 * Functionally equivalent to mbedtls_ct_error_if(condition, if1, 0) but
 * results in smaller code size.
 *
 * \param condition     Condition to test.
 * \param if1           Value to use if \p condition == MBEDTLS_CT_TRUE.
 *
 * \return  \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0.
 */
static inline int mbedtls_ct_error_if_else_0(mbedtls_ct_condition_t condition, int if1);

/* ============================================================================
 * Block memory operations
 */

#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C)

/** Conditionally set a block of memory to zero.
 *
 * 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) */

/** 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 determine if this parameter is 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 + \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
 *                      function's behavior is well-defined regardless of the
 *                      contents of the buffers. This may result in false
 *                      positives from static or dynamic analyzers, especially
 *                      if \p dest is not initialized.
 *
 * \param dest          Secret. The destination buffer. This must point to a writable
 *                      buffer of at least \p len bytes.
 * \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        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.
 * \param offset_max    The maximal value of \p offset.
 * \param len           The number of bytes to copy.
 */
void mbedtls_ct_memcpy_offset(unsigned char *dest,
                              const unsigned char *src,
                              size_t offset,
                              size_t offset_min,
                              size_t offset_max,
                              size_t len);

/* 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);
 */

#if defined(MBEDTLS_NIST_KW_C)

/** Constant-time buffer comparison without branches.
 *
 * Similar to mbedtls_ct_memcmp, except that the result only depends on part of
 * the input data - differences in the head or tail are ignored. Functionally equivalent to:
 *
 * memcmp(a + skip_head, b + skip_head, size - skip_head - skip_tail)
 *
 * Time taken depends on \p n, but not on \p skip_head or \p skip_tail .
 *
 * Behaviour is undefined if ( \p skip_head + \p skip_tail) > \p n.
 *
 * \param a         Secret. Pointer to the first buffer, containing at least \p n bytes. May not be NULL.
 * \param b         Secret. Pointer to the second buffer, containing at least \p n bytes. May not be NULL.
 * \param n         The number of bytes to examine (total size of the buffers).
 * \param skip_head Secret. The number of bytes to treat as non-significant at the start of the buffer.
 *                  These bytes will still be read.
 * \param skip_tail Secret. The number of bytes to treat as non-significant at the end of the buffer.
 *                  These bytes will still be read.
 *
 * \return          Zero if the contents of the two buffers are the same, otherwise non-zero.
 */
int mbedtls_ct_memcmp_partial(const void *a,
                              const void *b,
                              size_t n,
                              size_t skip_head,
                              size_t skip_tail);

#endif

/* Include the implementation of static inline functions above. */
#include "constant_time_impl.h"

#endif /* TF_PSA_CRYPTO_CONSTANT_TIME_INTERNAL_H */
