diff --git a/src/lib/utils/concepts.h b/src/lib/utils/concepts.h index 90ce49ce7ff..595d186144f 100644 --- a/src/lib/utils/concepts.h +++ b/src/lib/utils/concepts.h @@ -33,6 +33,14 @@ struct is_strong_type> : std::true_type {}; template constexpr bool is_strong_type_v = is_strong_type...>::value; +template +struct all_same { + static constexpr bool value = (std::is_same_v && ...); +}; + +template +static constexpr bool all_same_v = all_same::value; + namespace ranges { /** @@ -205,6 +213,10 @@ concept contiguous_strong_type = strong_type && contiguous_container; template concept integral = std::is_integral_v; +// TODO: C++20 - replace with std::unsigned_integral +template +concept unsigned_integral = std::is_integral_v && std::is_unsigned_v; + } // namespace concepts } // namespace Botan diff --git a/src/lib/utils/ghash/ghash.cpp b/src/lib/utils/ghash/ghash.cpp index 38604afdbed..8c3b1ed6c2a 100644 --- a/src/lib/utils/ghash/ghash.cpp +++ b/src/lib/utils/ghash/ghash.cpp @@ -13,6 +13,8 @@ #include #include +#include + namespace Botan { std::string GHASH::provider() const { @@ -177,9 +179,12 @@ void GHASH::add_final_block(secure_vector& hash, size_t ad_len, size_t * stack buffer is fine here since the text len is public * and the length of the AD is probably not sensitive either. */ - uint8_t final_block[GCM_BS]; - store_be(final_block, 8 * ad_len, 8 * text_len); - ghash_update(hash, {final_block, GCM_BS}); + std::array final_block; + + const uint64_t ad_bits = 8 * ad_len; + const uint64_t text_bits = 8 * text_len; + store_be(final_block, ad_bits, text_bits); + ghash_update(hash, final_block); } void GHASH::final(std::span mac) { diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index f0e50a1c2e3..15bc7ff7d1b 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -9,6 +9,7 @@ #ifndef BOTAN_LOAD_STORE_H_ #define BOTAN_LOAD_STORE_H_ +#include #include #include #include @@ -82,6 +83,107 @@ inline constexpr uint64_t make_uint64( (static_cast(i6) << 8) | (static_cast(i7))); } +/** +* Load a big-endian unsigned integer +* @param in_range a fixed-length span with some bytes +* @return T loaded from in, as a big-endian value +*/ +template InR> +inline constexpr T load_be(InR&& in_range) { + ranges::assert_exact_byte_length(in_range); + std::span in{in_range}; + if constexpr(sizeof(T) == 1) { + return static_cast(in[0]); + } else { +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + return typecast_copy(in); +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + return reverse_bytes(typecast_copy(in)); +#else + return [&](std::index_sequence) { + return ((static_cast(in[i]) << ((sizeof(T) - i - 1) * 8)) | ...); + } + (std::make_index_sequence()); +#endif + } +} + +/** +* Load a little-endian unsigned integer +* @param in_range a fixed-length span with some bytes +* @return T loaded from in, as a little-endian value +*/ +template InR> +inline constexpr T load_le(InR&& in_range) { + ranges::assert_exact_byte_length(in_range); + std::span in{in_range}; + if constexpr(sizeof(T) == 1) { + return static_cast(in[0]); + } else { +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + return reverse_bytes(typecast_copy(in)); +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + return typecast_copy(in); +#else + return [&](std::index_sequence) { + return ((static_cast(in[i]) << (i * 8)) | ...); + } + (std::make_index_sequence()); +#endif + } +} + +namespace details { + +/** +* Given a statically sized range @p r of size 1, 2, 4, or 8, this finds the +* corresponding unsigned integer type to hold the value of @p r. Note, that +* this only maps the type, and does not perform any loading. +*/ +template InR> +consteval auto default_load_type(InR&& r) { + std::span s{r}; + static_assert(decltype(s)::extent != std::dynamic_extent, + "Cannot determine the output type based on a dynamically sized bytearray"); + constexpr size_t e = decltype(s)::extent; + + // clang-format off + using type = + std::conditional_t>>>; + // clang-format on + + static_assert( + !std::is_void_v, + "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8"); + + return type{}; +} + +} // namespace details + +/** +* Load a little-endian unsigned integer, auto-detect the output type +* @param in_range a statically-sized range with some bytes +* @return T loaded from in, as a little-endian value +*/ +template InR> +inline constexpr auto load_le(InR&& in_range) { + return load_le(std::forward(in_range)); +} + +/** +* Load a big-endian unsigned integer, auto-detect the output type +* @param in_range a statically-sized range with some bytes +* @return T loaded from in, as a big-endian value +*/ +template InR> +inline constexpr auto load_be(InR&& in_range) { + return load_be(std::forward(in_range)); +} + /** * Load a big-endian word * @param in a pointer to some bytes @@ -115,169 +217,87 @@ inline constexpr T load_le(const uint8_t in[], size_t off) { } /** -* Load a big-endian uint16_t +* Load a big-endian unsigned integer * @param in a pointer to some bytes * @param off an offset into the array -* @return off'th uint16_t of in, as a big-endian value +* @return off'th unsigned integer of in, as a big-endian value */ -template <> -inline constexpr uint16_t load_be(const uint8_t in[], size_t off) { - in += off * sizeof(uint16_t); - -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return typecast_copy(in); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#else - return make_uint16(in[0], in[1]); -#endif +template +inline constexpr T load_be(const uint8_t in[], size_t off) { + // asserts that *in points to the correct amount of memory + return load_be(std::span(in + off * sizeof(T), sizeof(T))); } /** -* Load a little-endian uint16_t +* Load a little-endian unsigned integer * @param in a pointer to some bytes * @param off an offset into the array -* @return off'th uint16_t of in, as a little-endian value +* @return off'th unsigned integer of in, as a little-endian value */ -template <> -inline constexpr uint16_t load_le(const uint8_t in[], size_t off) { - in += off * sizeof(uint16_t); - -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return typecast_copy(in); -#else - return make_uint16(in[1], in[0]); -#endif +template +inline constexpr T load_le(const uint8_t in[], size_t off) { + // asserts that *in points to the correct amount of memory + return load_le(std::span(in + off * sizeof(T), sizeof(T))); } /** -* Load a big-endian uint32_t -* @param in a pointer to some bytes -* @param off an offset into the array -* @return off'th uint32_t of in, as a big-endian value +* Load many big-endian unsigned integers +* @param in a fixed-length span to some bytes +* @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template <> -inline constexpr uint32_t load_be(const uint8_t in[], size_t off) { - in += off * sizeof(uint32_t); +template InR, concepts::unsigned_integral... Ts> + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void load_be(InR&& in, Ts&... outs) { + ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in); + auto load_one = [off = 0](auto i, T& o) mutable { + o = load_be(i.subspan(off).template first()); + off += sizeof(T); + }; -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return typecast_copy(in); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#else - return make_uint32(in[0], in[1], in[2], in[3]); -#endif + (load_one(std::span{in}, outs), ...); } /** -* Load a little-endian uint32_t -* @param in a pointer to some bytes -* @param off an offset into the array -* @return off'th uint32_t of in, as a little-endian value +* Load many little-endian unsigned integers +* @param in a fixed-length span to some bytes +* @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template <> -inline constexpr uint32_t load_le(const uint8_t in[], size_t off) { - in += off * sizeof(uint32_t); +template InR, concepts::unsigned_integral... Ts> + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void load_le(InR&& in, Ts&... outs) { + ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in); + auto load_one = [off = 0](auto i, T& o) mutable { + o = load_le(i.subspan(off).template first()); + off += sizeof(T); + }; -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return typecast_copy(in); -#else - return make_uint32(in[3], in[2], in[1], in[0]); -#endif + (load_one(std::span{in}, outs), ...); } /** -* Load a big-endian uint64_t -* @param in a pointer to some bytes -* @param off an offset into the array -* @return off'th uint64_t of in, as a big-endian value +* Load many big-endian unsigned integers +* @param in a pointer to some bytes +* @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template <> -inline constexpr uint64_t load_be(const uint8_t in[], size_t off) { - in += off * sizeof(uint64_t); - -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return typecast_copy(in); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#else - return make_uint64(in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]); -#endif +template + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void load_be(const uint8_t in[], Ts&... outs) { + constexpr auto bytes = (sizeof(outs) + ...); + // asserts that *in points to the correct amount of memory + load_be(std::span(in, bytes), outs...); } /** -* Load a little-endian uint64_t -* @param in a pointer to some bytes -* @param off an offset into the array -* @return off'th uint64_t of in, as a little-endian value +* Load many little-endian unsigned integers +* @param in a pointer to some bytes +* @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template <> -inline constexpr uint64_t load_le(const uint8_t in[], size_t off) { - in += off * sizeof(uint64_t); - -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return reverse_bytes(typecast_copy(in)); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return typecast_copy(in); -#else - return make_uint64(in[7], in[6], in[5], in[4], in[3], in[2], in[1], in[0]); -#endif -} - -/** -* Load two little-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -*/ -template -inline constexpr void load_le(const uint8_t in[], T& x0, T& x1) { - x0 = load_le(in, 0); - x1 = load_le(in, 1); -} - -/** -* Load four little-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -* @param x2 where the third word will be written -* @param x3 where the fourth word will be written -*/ -template -inline constexpr void load_le(const uint8_t in[], T& x0, T& x1, T& x2, T& x3) { - x0 = load_le(in, 0); - x1 = load_le(in, 1); - x2 = load_le(in, 2); - x3 = load_le(in, 3); -} - -/** -* Load eight little-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -* @param x2 where the third word will be written -* @param x3 where the fourth word will be written -* @param x4 where the fifth word will be written -* @param x5 where the sixth word will be written -* @param x6 where the seventh word will be written -* @param x7 where the eighth word will be written -*/ -template -inline constexpr void load_le(const uint8_t in[], T& x0, T& x1, T& x2, T& x3, T& x4, T& x5, T& x6, T& x7) { - x0 = load_le(in, 0); - x1 = load_le(in, 1); - x2 = load_le(in, 2); - x3 = load_le(in, 3); - x4 = load_le(in, 4); - x5 = load_le(in, 5); - x6 = load_le(in, 6); - x7 = load_le(in, 7); +template + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void load_le(const uint8_t in[], Ts&... outs) { + constexpr auto bytes = (sizeof(outs) + ...); + // asserts that *in points to the correct amount of memory + load_le(std::span(in, bytes), outs...); } /** @@ -304,58 +324,6 @@ inline constexpr void load_le(T out[], const uint8_t in[], size_t count) { } } -/** -* Load two big-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -*/ -template -inline constexpr void load_be(const uint8_t in[], T& x0, T& x1) { - x0 = load_be(in, 0); - x1 = load_be(in, 1); -} - -/** -* Load four big-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -* @param x2 where the third word will be written -* @param x3 where the fourth word will be written -*/ -template -inline constexpr void load_be(const uint8_t in[], T& x0, T& x1, T& x2, T& x3) { - x0 = load_be(in, 0); - x1 = load_be(in, 1); - x2 = load_be(in, 2); - x3 = load_be(in, 3); -} - -/** -* Load eight big-endian words -* @param in a pointer to some bytes -* @param x0 where the first word will be written -* @param x1 where the second word will be written -* @param x2 where the third word will be written -* @param x3 where the fourth word will be written -* @param x4 where the fifth word will be written -* @param x5 where the sixth word will be written -* @param x6 where the seventh word will be written -* @param x7 where the eighth word will be written -*/ -template -inline constexpr void load_be(const uint8_t in[], T& x0, T& x1, T& x2, T& x3, T& x4, T& x5, T& x6, T& x7) { - x0 = load_be(in, 0); - x1 = load_be(in, 1); - x2 = load_be(in, 2); - x3 = load_be(in, 3); - x4 = load_be(in, 4); - x5 = load_be(in, 5); - x6 = load_be(in, 6); - x7 = load_be(in, 7); -} - /** * Load a variable number of big-endian words * @param out the output array of words @@ -382,219 +350,157 @@ inline constexpr void load_be(T out[], const uint8_t in[], size_t count) { } /** -* Store a big-endian uint16_t -* @param in the input uint16_t -* @param out the byte array to write to +* Store a big-endian unsigned integer +* @param in the input unsigned integer +* @param out_range the fixed-length span to write to */ -inline constexpr void store_be(uint16_t in, uint8_t out[2]) { +template OutR> +inline constexpr void store_be(T in, OutR&& out_range) { + ranges::assert_exact_byte_length(out_range); + std::span out{out_range}; + if constexpr(sizeof(T) == 1) { + out[0] = static_cast(in); + } else { #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, in); + typecast_copy(out, in); #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, reverse_bytes(in)); + typecast_copy(out, reverse_bytes(in)); #else - out[0] = get_byte<0>(in); - out[1] = get_byte<1>(in); + [&](std::index_sequence) { + ((out[i] = get_byte(in)), ...); + } + (std::make_index_sequence()); #endif + } } /** -* Store a little-endian uint16_t -* @param in the input uint16_t -* @param out the byte array to write to +* Store a little-endian unsigned integer +* @param in the input unsigned integer +* @param out_range the fixed-length span to write to */ -inline constexpr void store_le(uint16_t in, uint8_t out[2]) { +template OutR> +inline constexpr void store_le(T in, OutR&& out_range) { + ranges::assert_exact_byte_length(out_range); + std::span out{out_range}; + if constexpr(sizeof(T) == 1) { + out[0] = static_cast(in); + } else { #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, reverse_bytes(in)); + typecast_copy(out, reverse_bytes(in)); #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, in); + typecast_copy(out, in); #else - out[0] = get_byte<1>(in); - out[1] = get_byte<0>(in); + [&](std::index_sequence) { + ((out[i] = get_byte(in)), ...); + } + (std::make_index_sequence()); #endif + } } /** -* Store a big-endian uint32_t -* @param in the input uint32_t +* Store a big-endian unsigned integer +* @param in the input unsigned integer * @param out the byte array to write to */ -inline constexpr void store_be(uint32_t in, uint8_t out[4]) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, in); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, reverse_bytes(in)); -#else - out[0] = get_byte<0>(in); - out[1] = get_byte<1>(in); - out[2] = get_byte<2>(in); - out[3] = get_byte<3>(in); -#endif +template +inline constexpr void store_be(T in, uint8_t out[sizeof(T)]) { + store_be(in, std::span(out, sizeof(T))); } /** -* Store a little-endian uint32_t -* @param in the input uint32_t +* Store a little-endian unsigned integer +* @param in the input unsigned integer * @param out the byte array to write to */ -inline constexpr void store_le(uint32_t in, uint8_t out[4]) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, reverse_bytes(in)); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, in); -#else - out[0] = get_byte<3>(in); - out[1] = get_byte<2>(in); - out[2] = get_byte<1>(in); - out[3] = get_byte<0>(in); -#endif +template +inline constexpr void store_le(T in, uint8_t out[sizeof(T)]) { + store_le(in, std::span(out, sizeof(T))); } /** -* Store a big-endian uint64_t -* @param in the input uint64_t -* @param out the byte array to write to +* Store many big-endian unsigned integers +* @param out a fixed-length span to some bytes +* @param ins a arbitrary-length parameter list of unsigned integers to be stored */ -inline constexpr void store_be(uint64_t in, uint8_t out[8]) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, in); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, reverse_bytes(in)); -#else - out[0] = get_byte<0>(in); - out[1] = get_byte<1>(in); - out[2] = get_byte<2>(in); - out[3] = get_byte<3>(in); - out[4] = get_byte<4>(in); - out[5] = get_byte<5>(in); - out[6] = get_byte<6>(in); - out[7] = get_byte<7>(in); -#endif -} +template OutR, concepts::unsigned_integral... Ts> + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void store_be(OutR&& out, Ts... ins) { + ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out); + auto store_one = [off = 0](auto o, T i) mutable { + store_be(i, o.subspan(off).template first()); + off += sizeof(T); + }; -/** -* Store a little-endian uint64_t -* @param in the input uint64_t -* @param out the byte array to write to -*/ -inline constexpr void store_le(uint64_t in, uint8_t out[8]) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - typecast_copy(out, reverse_bytes(in)); -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - typecast_copy(out, in); -#else - out[0] = get_byte<7>(in); - out[1] = get_byte<6>(in); - out[2] = get_byte<5>(in); - out[3] = get_byte<4>(in); - out[4] = get_byte<3>(in); - out[5] = get_byte<2>(in); - out[6] = get_byte<1>(in); - out[7] = get_byte<0>(in); -#endif + (store_one(std::span{out}, ins), ...); } /** -* Store two little-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word +* Store many little-endian unsigned integers +* @param out a fixed-length span to some bytes +* @param ins a arbitrary-length parameter list of unsigned integers to be stored */ -template -inline constexpr void store_le(uint8_t out[], T x0, T x1) { - store_le(x0, out + (0 * sizeof(T))); - store_le(x1, out + (1 * sizeof(T))); -} +template OutR, concepts::unsigned_integral... Ts> + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void store_le(OutR&& out, Ts... ins) { + ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out); + auto store_one = [off = 0](auto o, T i) mutable { + store_le(i, o.subspan(off).template first()); + off += sizeof(T); + }; -/** -* Store two big-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -*/ -template -inline constexpr void store_be(uint8_t out[], T x0, T x1) { - store_be(x0, out + (0 * sizeof(T))); - store_be(x1, out + (1 * sizeof(T))); + (store_one(std::span{out}, ins), ...); } /** -* Store four little-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -* @param x2 the third word -* @param x3 the fourth word +* Store many big-endian unsigned integers +* @param ins a pointer to some bytes to be written +* @param out a arbitrary-length parameter list of unsigned integers to be stored */ -template -inline constexpr void store_le(uint8_t out[], T x0, T x1, T x2, T x3) { - store_le(x0, out + (0 * sizeof(T))); - store_le(x1, out + (1 * sizeof(T))); - store_le(x2, out + (2 * sizeof(T))); - store_le(x3, out + (3 * sizeof(T))); +template + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void store_be(uint8_t out[], Ts... ins) { + constexpr auto bytes = (sizeof(ins) + ...); + // asserts that *out points to the correct amount of memory + store_be(std::span(out, bytes), ins...); } /** -* Store four big-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -* @param x2 the third word -* @param x3 the fourth word +* Store many little-endian unsigned integers +* @param ins a pointer to some bytes to be written +* @param out a arbitrary-length parameter list of unsigned integers to be stored */ -template -inline constexpr void store_be(uint8_t out[], T x0, T x1, T x2, T x3) { - store_be(x0, out + (0 * sizeof(T))); - store_be(x1, out + (1 * sizeof(T))); - store_be(x2, out + (2 * sizeof(T))); - store_be(x3, out + (3 * sizeof(T))); +template + requires(sizeof...(Ts) > 0) && all_same_v +inline constexpr void store_le(uint8_t out[], Ts... ins) { + constexpr auto bytes = (sizeof(ins) + ...); + // asserts that *out points to the correct amount of memory + store_le(std::span(out, bytes), ins...); } /** -* Store eight little-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -* @param x2 the third word -* @param x3 the fourth word -* @param x4 the fifth word -* @param x5 the sixth word -* @param x6 the seventh word -* @param x7 the eighth word +* Store a big-endian unsigned integer +* @param in the input unsigned integer +* @return a byte array holding the integer value in big-endian byte order */ -template -inline constexpr void store_le(uint8_t out[], T x0, T x1, T x2, T x3, T x4, T x5, T x6, T x7) { - store_le(x0, out + (0 * sizeof(T))); - store_le(x1, out + (1 * sizeof(T))); - store_le(x2, out + (2 * sizeof(T))); - store_le(x3, out + (3 * sizeof(T))); - store_le(x4, out + (4 * sizeof(T))); - store_le(x5, out + (5 * sizeof(T))); - store_le(x6, out + (6 * sizeof(T))); - store_le(x7, out + (7 * sizeof(T))); +template +inline constexpr auto store_be(T in) { + std::array out; + store_be(in, out); + return out; } /** -* Store eight big-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -* @param x2 the third word -* @param x3 the fourth word -* @param x4 the fifth word -* @param x5 the sixth word -* @param x6 the seventh word -* @param x7 the eighth word +* Store a little-endian unsigned integer +* @param in the input unsigned integer +* @return a byte array holding the integer value in little-endian byte order */ -template -inline constexpr void store_be(uint8_t out[], T x0, T x1, T x2, T x3, T x4, T x5, T x6, T x7) { - store_be(x0, out + (0 * sizeof(T))); - store_be(x1, out + (1 * sizeof(T))); - store_be(x2, out + (2 * sizeof(T))); - store_be(x3, out + (3 * sizeof(T))); - store_be(x4, out + (4 * sizeof(T))); - store_be(x5, out + (5 * sizeof(T))); - store_be(x6, out + (6 * sizeof(T))); - store_be(x7, out + (7 * sizeof(T))); +template +inline constexpr auto store_le(T in) { + std::array out; + store_le(in, out); + return out; } template diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index ae056009d83..604bd782fb1 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -169,6 +169,44 @@ class Utility_Function_Tests final : public Text_Based_Test { result.test_is_eq(out[7], 0xAB); } + std::array outarr; + uint16_t i0, i1, i2, i3; + Botan::store_be(in64, outarr); + + Botan::load_be(outarr, i0, i1, i2, i3); + result.test_is_eq(i0, 0xABCD); + result.test_is_eq(i1, 0xEF01); + result.test_is_eq(i2, 0x2345); + result.test_is_eq(i3, 0x6789); + + Botan::load_le(std::span{outarr}.first<6>(), i0, i1, i2); + result.test_is_eq(i0, 0xCDAB); + result.test_is_eq(i1, 0x01EF); + result.test_is_eq(i2, 0x4523); + result.test_is_eq(i3, 0x6789); // remains unchanged + + Botan::store_le(in64, outarr); + + Botan::load_le(outarr, i0, i1, i2, i3); + result.test_is_eq(i0, 0x6789); + result.test_is_eq(i1, 0x2345); + result.test_is_eq(i2, 0xEF01); + result.test_is_eq(i3, 0xABCD); + + Botan::load_be(std::span{outarr}.first<6>(), i0, i1, i2); + result.test_is_eq(i0, 0x8967); + result.test_is_eq(i1, 0x4523); + result.test_is_eq(i2, 0x01EF); + result.test_is_eq(i3, 0xABCD); // remains unchanged + + result.test_is_eq(in16, Botan::load_be(Botan::store_be(in16))); + result.test_is_eq(in32, Botan::load_be(Botan::store_be(in32))); + result.test_is_eq(in64, Botan::load_be(Botan::store_be(in64))); + + result.test_is_eq(in16, Botan::load_le(Botan::store_le(in16))); + result.test_is_eq(in32, Botan::load_le(Botan::store_le(in32))); + result.test_is_eq(in64, Botan::load_le(Botan::store_le(in64))); + return result; } };