14#ifndef BOTAN_CT_UTILS_H_
15#define BOTAN_CT_UTILS_H_
17#include <botan/concepts.h>
18#include <botan/secmem.h>
19#include <botan/internal/bit_ops.h>
20#include <botan/internal/stl_util.h>
21#include <botan/internal/target_info.h>
27#if defined(BOTAN_HAS_VALGRIND)
28 #include <valgrind/memcheck.h>
54constexpr inline void poison(
const T* p,
size_t n) {
55#if defined(BOTAN_HAS_VALGRIND)
56 if(!std::is_constant_evaluated()) {
57 VALGRIND_MAKE_MEM_UNDEFINED(p, n *
sizeof(T));
65constexpr inline void unpoison(
const T* p,
size_t n) {
66#if defined(BOTAN_HAS_VALGRIND)
67 if(!std::is_constant_evaluated()) {
68 VALGRIND_MAKE_MEM_DEFINED(p, n *
sizeof(T));
84#if defined(BOTAN_HAS_VALGRIND)
85 return RUNNING_ON_VALGRIND;
104template <std::
integral T>
109template <std::
integral T>
117template <ranges::spanable_range R>
118 requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>> && (!custom_poisonable<R>)
121 poison(s.data(), s.size());
124template <ranges::spanable_range R>
125 requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>> && (!custom_unpoisonable<R>)
135template <custom_poisonable T>
137 x._const_time_poison();
140template <custom_unpoisonable T>
142 x._const_time_unpoison();
150constexpr void poison(
const std::optional<T>& x) {
158constexpr void unpoison(
const std::optional<T>& x) {
177template <std::ranges::range R>
180 for(
const auto& v : r) {
185template <std::ranges::range R>
186 requires unpoisonable<std::ranges::range_value_t<R>>
188 for(
const auto& v : r) {
197template <poisonable... Ts>
198 requires(
sizeof...(Ts) > 0)
203template <unpoisonable... Ts>
204 requires(
sizeof...(Ts) > 0)
218template <
typename... Ts>
219 requires(
sizeof...(Ts) > 0) && (poisonable<Ts> && ...) && (unpoisonable<Ts> && ...)
229template <poisonable T>
231 requires(std::is_rvalue_reference_v<
decltype(v)>)
234 return std::forward<T>(v);
240template <unpoisonable T>
242 requires(std::is_rvalue_reference_v<
decltype(v)>)
245 return std::forward<T>(v);
275template <std::
unsigned_
integral T>
276 requires(!std::same_as<bool, T>)
278 if(std::is_constant_evaluated()) {
281#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
289 asm(
"" :
"+r"(x) : );
291#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
312 template <
typename T>
313 requires std::unsigned_integral<T> && (!std::same_as<bool, T>)
357 constexpr bool as_bool()
const {
return m_value != 0; }
369 constexpr explicit Choice(uint32_t v) : m_value(v) {}
390 static_assert(std::is_unsigned_v<T> && !std::is_same_v<bool, T>,
391 "Only unsigned integer types are supported by CT::Mask");
402 template <
typename U>
404 static_assert(
sizeof(U) >
sizeof(T),
"sizes ok");
431 if constexpr(
sizeof(T) <=
sizeof(uint32_t)) {
456 template <
typename U>
458 static_assert(
sizeof(U) <
sizeof(T),
"sizes ok");
479 T u = x ^ ((x ^ y) | ((x - y) ^ x));
501 const T v_lt_l = v ^ ((v ^ l) | ((v - l) ^ v));
502 const T v_gt_u = u ^ ((u ^ v) | ((u - v) ^ u));
510 for(
auto a : accepted) {
511 const T diff = a ^ v;
593 constexpr void select_n(T output[],
const T x[],
const T y[],
size_t len)
const {
594 const T mask =
value();
595 for(
size_t i = 0; i != len; ++i) {
596 output[i] =
choose(mask, x[i], y[i]);
604 for(
size_t i = 0; i != elems; ++i) {
612 template <
typename U>
614 requires(
sizeof(U) <=
sizeof(T))
617 U t0 = cnd.select(y, x);
618 U t1 = cnd.select(x, y);
648 if constexpr(
sizeof(T) >=
sizeof(uint32_t)) {
665 constexpr explicit Mask(T m) : m_mask(m) {}
681 constexpr Option(T v,
Choice valid) : m_has_value(valid), m_value(std::move(v)) {}
688 requires std::default_initializable<T>
701 template <std::invocable<const T&> F>
703 return {f(m_value), m_has_value};
719 other.conditional_assign(m_has_value, m_value);
728 requires std::unsigned_integral<T>
731 return mask.select(m_value, other);
739 if(m_has_value.as_bool()) {
761 mask.
select_n(dest, if_set, if_unset, elems);
779 mask.select_n(dest, src, dest, elems);
791 mask.select_n(dest, src, dest, elems);
798 swap.conditional_swap(x, y);
803 uintptr_t xp =
reinterpret_cast<uintptr_t
>(x);
804 uintptr_t yp =
reinterpret_cast<uintptr_t
>(y);
808 x =
reinterpret_cast<T
>(xp);
809 y =
reinterpret_cast<T
>(yp);
815 for(
size_t i = 0; i != len; ++i) {
827 if(std::is_constant_evaluated()) {
830 for(
size_t i = 0; i != len; ++i) {
831 difference = difference | (x[i] ^ y[i]);
836 volatile T difference = 0;
838 for(
size_t i = 0; i != len; ++i) {
839 difference = difference | (x[i] ^ y[i]);
854 if(x.size() != y.size()) {
858 return is_equal(x.data(), y.data(), x.size());
891 std::span<uint8_t> output,
892 std::span<const uint8_t> input,
#define BOTAN_STATE_CHECK(expr)
constexpr Choice operator==(const Choice &other) const
static constexpr Choice yes()
static constexpr Choice from_int(T v)
constexpr Choice & operator=(const Choice &other) noexcept=default
constexpr Choice(const Choice &other)=default
constexpr Choice operator!=(const Choice &other) const
constexpr Choice operator||(const Choice &other) const
constexpr Choice operator!() const
static constexpr Choice no()
constexpr bool as_bool() const
constexpr Choice(Choice &&other)=default
constexpr ~Choice()=default
constexpr Choice operator&&(const Choice &other) const
constexpr uint32_t value() const
Return the masked value.
constexpr Choice & operator=(Choice &&other) noexcept=default
static constexpr Choice from_mask(uint32_t v)
static constexpr Mask< T > expand_bit(T v, size_t bit)
Mask< T > select_mask(Mask< T > x, Mask< T > y) const
void conditional_swap(U &x, U &y) const
constexpr T value() const
constexpr void if_set_zero_out(T buf[], size_t elems)
static constexpr Mask< T > is_lte(T x, T y)
static constexpr Mask< T > is_gte(T x, T y)
constexpr T if_not_set_return(T x) const
constexpr Mask(Mask< U > o)
static constexpr Mask< T > set()
friend Mask< T > operator|(Mask< T > x, Mask< T > y)
Mask< T > & operator^=(Mask< T > o)
Mask(Mask< T > &&other)=default
constexpr T unpoisoned_value() const
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
friend Mask< T > operator^(Mask< T > x, Mask< T > y)
constexpr T if_set_return(T x) const
Mask< T > & operator=(const Mask< T > &other)=default
Mask< T > & operator=(Mask< T > &&other)=default
constexpr void _const_time_poison() const
Mask(const Mask< T > &other)=default
static constexpr Mask< T > expand(Mask< U > m)
Mask< T > & operator&=(Mask< T > o)
constexpr T select_and_unpoison(T x, T y) const
static constexpr Mask< T > expand(T v)
constexpr T select(T x, T y) const
static constexpr Mask< T > expand_top_bit(T v)
static constexpr Mask< T > is_within_range(T v, T l, T u)
constexpr CT::Choice as_choice() const
static constexpr Mask< T > from_choice(Choice c)
static constexpr Mask< T > is_equal(T x, T y)
Mask< T > & operator|=(Mask< T > o)
constexpr Mask< T > operator~() const
static constexpr Mask< T > expand_bool(bool v)
static constexpr Mask< T > is_gt(T x, T y)
constexpr bool as_bool() const
static constexpr Mask< T > is_lt(T x, T y)
friend Mask< T > operator&(Mask< T > x, Mask< T > y)
constexpr void _const_time_unpoison() const
static constexpr Mask< T > is_any_of(T v, std::initializer_list< T > accepted)
static constexpr Mask< T > is_zero(T x)
static constexpr Mask< T > cleared()
constexpr Option(T v)
Construct a set option with the provided value.
constexpr Option(T v, Choice valid)
Construct an Option which contains the specified value, and is set or not.
constexpr auto transform(F f) const -> Option< std::remove_cvref_t< std::invoke_result_t< F, const T & > > >
constexpr Choice has_value() const
Return true if this Option contains a value.
constexpr CT::Option< T > operator&&(CT::Choice also)
Return a new CT::Option that is set if also is set as well.
constexpr const T & value() const
Either returns the value or throws an exception.
constexpr Option()
Construct an unset option with a default inner value.
constexpr std::optional< T > as_optional_vartime() const
constexpr T value_or(T other) const
constexpr T value_or(T other) const
Helper class to create a RAII-style cleanup callback.
secure_vector< uint8_t > strip_leading_zeros(std::span< const uint8_t > input)
constexpr void conditional_swap_ptr(bool cnd, T &x, T &y)
decltype(auto) driveby_unpoison(T &&v)
constexpr void conditional_swap(bool cnd, T &x, T &y)
constexpr void poison_range(const R &r)
constexpr void poison_all(const Ts &... ts)
constexpr T value_barrier(T x)
decltype(auto) driveby_poison(T &&v)
constexpr Mask< T > conditional_copy_mem(Mask< T > mask, T *dest, const T *if_set, const T *if_unset, size_t elems)
constexpr auto scoped_poison(const Ts &... xs)
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
constexpr void unpoison_all(const Ts &... ts)
BOTAN_TEST_API CT::Option< size_t > copy_output(CT::Choice accept, std::span< uint8_t > output, std::span< const uint8_t > input, size_t offset)
size_t count_leading_zero_bytes(std::span< const uint8_t > input)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
constexpr void unpoison_range(const R &r)
constexpr void unpoison(const T *p, size_t n)
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
constexpr Mask< T > conditional_assign_mem(T cnd, T *dest, const T *src, size_t elems)
constexpr void poison(const T *p, size_t n)
BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b)
std::vector< T, secure_allocator< T > > secure_vector
BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x)
BOTAN_FORCE_INLINE constexpr T expand_top_bit(T a)