From ac1a3524392ad891ce0bf2e3552bdd0d610e2cbf Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 19 Sep 2023 17:50:22 +0200 Subject: [PATCH 1/7] add concepts::unsigned_integral and all_same_v<> --- src/lib/utils/concepts.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 From f1e187bf908c467fc83537a70acc9582a58c3d09 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 19 Sep 2023 17:51:21 +0200 Subject: [PATCH 2/7] range-based store_{le/be} and load_{le/be} --- src/lib/utils/loadstor.h | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index f0e50a1c2e3..af7f10991cf 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,56 @@ 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 + } +} + /** * Load a big-endian word * @param in a pointer to some bytes @@ -381,6 +432,56 @@ inline constexpr void load_be(T out[], const uint8_t in[], size_t count) { } } +/** +* Store a big-endian unsigned integer +* @param in the input unsigned integer +* @param out_range the fixed-length span to write to +*/ +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); +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + typecast_copy(out, reverse_bytes(in)); +#else + [&](std::index_sequence) { + ((out[i] = get_byte(in)), ...); + } + (std::make_index_sequence()); +#endif + } +} + +/** +* Store a little-endian unsigned integer +* @param in the input unsigned integer +* @param out_range the fixed-length span to write to +*/ +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)); +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + typecast_copy(out, in); +#else + [&](std::index_sequence) { + ((out[i] = get_byte(in)), ...); + } + (std::make_index_sequence()); +#endif + } +} + /** * Store a big-endian uint16_t * @param in the input uint16_t From 6888c8b717dbede36ab493f78f19620d069da284 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 19 Sep 2023 17:45:12 +0200 Subject: [PATCH 3/7] range-based multi-target store_{le/be} and load_{le/be} --- src/lib/utils/loadstor.h | 68 ++++++++++++++++++++++++++++++++++++++++ src/tests/test_utils.cpp | 30 ++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index af7f10991cf..39f730788a6 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -279,6 +279,40 @@ inline constexpr uint64_t load_le(const uint8_t in[], size_t off) { #endif } +/** +* 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 InR, concepts::unsigned_integral... Ts> + requires 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); + }; + + (load_one(std::span{in}, outs), ...); +} + +/** +* 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 InR, concepts::unsigned_integral... Ts> + requires 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); + }; + + (load_one(std::span{in}, outs), ...); +} + /** * Load two little-endian words * @param in a pointer to some bytes @@ -594,6 +628,40 @@ inline constexpr void store_le(uint64_t in, uint8_t out[8]) { #endif } +/** +* 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 +*/ +template OutR, concepts::unsigned_integral... Ts> + requires 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_one(std::span{out}, ins), ...); +} + +/** +* 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 OutR, concepts::unsigned_integral... Ts> + requires 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_one(std::span{out}, ins), ...); +} + /** * Store two little-endian words * @param out the output byte array diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index ae056009d83..5c8fa7b6c93 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -169,6 +169,36 @@ 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 + return result; } }; From b6c203bc5e1e1f8c9361dd5843a466e9061ed6f6 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 19 Sep 2023 16:07:41 +0200 Subject: [PATCH 4/7] load_{be,le} legacy adapters --- src/lib/utils/loadstor.h | 226 ++++----------------------------------- 1 file changed, 22 insertions(+), 204 deletions(-) diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index 39f730788a6..0bd21ed8152 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -166,117 +166,27 @@ inline constexpr T load_le(const uint8_t in[], size_t off) { } /** -* Load a big-endian uint16_t -* @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 -*/ -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 -} - -/** -* Load a little-endian uint16_t -* @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 -*/ -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 -} - -/** -* 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 -*/ -template <> -inline constexpr uint32_t load_be(const uint8_t in[], size_t off) { - in += off * sizeof(uint32_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 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 -*/ -template <> -inline constexpr uint32_t load_le(const uint8_t in[], size_t off) { - in += off * sizeof(uint32_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 a big-endian uint64_t +* Load a big-endian unsigned integer * @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 +* @return off'th unsigned integer of in, as a big-endian value */ -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 +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 uint64_t +* Load a little-endian unsigned integer * @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 +* @return off'th unsigned integer of in, as a little-endian value */ -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 +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))); } /** @@ -517,115 +427,23 @@ inline constexpr void store_le(T in, OutR&& out_range) { } /** -* Store a big-endian uint16_t -* @param in the input uint16_t -* @param out the byte array to write to -*/ -inline constexpr void store_be(uint16_t in, uint8_t out[2]) { -#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); -#endif -} - -/** -* Store a little-endian uint16_t -* @param in the input uint16_t -* @param out the byte array to write to -*/ -inline constexpr void store_le(uint16_t in, uint8_t out[2]) { -#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<1>(in); - out[1] = get_byte<0>(in); -#endif -} - -/** -* Store a big-endian uint32_t -* @param in the input uint32_t -* @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 -} - -/** -* Store a little-endian uint32_t -* @param in the input uint32_t -* @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 -} - -/** -* Store a big-endian uint64_t -* @param in the input uint64_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(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 +inline constexpr void store_be(T in, uint8_t out[sizeof(T)]) { + store_be(in, std::span(out, sizeof(T))); } /** -* Store a little-endian uint64_t -* @param in the input uint64_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(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 +template +inline constexpr void store_le(T in, uint8_t out[sizeof(T)]) { + store_le(in, std::span(out, sizeof(T))); } /** From 2208d23329783be59ec8dfe120a3a481bca539f0 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 19 Sep 2023 17:47:49 +0200 Subject: [PATCH 5/7] multi-target load_{be,le} legacy adapters --- src/lib/utils/ghash/ghash.cpp | 11 +- src/lib/utils/loadstor.h | 228 ++++++---------------------------- 2 files changed, 44 insertions(+), 195 deletions(-) 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 0bd21ed8152..c90463f4f74 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -224,55 +224,29 @@ inline constexpr void load_le(InR&& in, Ts&... outs) { } /** -* 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 +* 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 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); +template + requires 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 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 +* 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 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 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...); } /** @@ -299,58 +273,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 @@ -481,107 +403,29 @@ inline constexpr void store_le(OutR&& out, Ts... ins) { } /** -* Store two little-endian words -* @param out the output byte array -* @param x0 the first word -* @param x1 the second word -*/ -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))); -} - -/** -* 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 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 -*/ -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))); -} - -/** -* 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 -*/ -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))); -} - -/** -* 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 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, 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 + requires 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 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 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, 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 + requires 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...); } template From b4c057e6ab585b1fe632c718796cfff605ed5332 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 21 Nov 2023 12:16:10 +0100 Subject: [PATCH 6/7] Add auto-typed load_{le,be} and store_{le,be} These overloads auto-detect their required return type at compile-time given the input parameter type. --- src/lib/utils/loadstor.h | 91 ++++++++++++++++++++++++++++++++++++---- src/tests/test_utils.cpp | 8 ++++ 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index c90463f4f74..7f7279412d4 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -133,6 +133,57 @@ inline constexpr T load_le(InR&& in_range) { } } +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 @@ -195,7 +246,7 @@ inline constexpr T load_le(const uint8_t in[], size_t off) { * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ template InR, concepts::unsigned_integral... Ts> - requires all_same_v + 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 { @@ -212,7 +263,7 @@ inline constexpr void load_be(InR&& in, Ts&... outs) { * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ template InR, concepts::unsigned_integral... Ts> - requires all_same_v + 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 { @@ -229,7 +280,7 @@ inline constexpr void load_le(InR&& in, Ts&... outs) { * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ template - requires all_same_v + 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 @@ -242,7 +293,7 @@ inline constexpr void load_be(const uint8_t in[], Ts&... outs) { * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ template - requires all_same_v + 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 @@ -374,7 +425,7 @@ inline constexpr void store_le(T in, uint8_t out[sizeof(T)]) { * @param ins a arbitrary-length parameter list of unsigned integers to be stored */ template OutR, concepts::unsigned_integral... Ts> - requires all_same_v + 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 { @@ -391,7 +442,7 @@ inline constexpr void store_be(OutR&& out, Ts... ins) { * @param ins a arbitrary-length parameter list of unsigned integers to be stored */ template OutR, concepts::unsigned_integral... Ts> - requires all_same_v + 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 { @@ -408,7 +459,7 @@ inline constexpr void store_le(OutR&& out, Ts... ins) { * @param out a arbitrary-length parameter list of unsigned integers to be stored */ template - requires all_same_v + 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 @@ -421,13 +472,37 @@ inline constexpr void store_be(uint8_t out[], Ts... ins) { * @param out a arbitrary-length parameter list of unsigned integers to be stored */ template - requires all_same_v + 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 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 auto store_be(T in) { + std::array out; + store_be(in, out); + return out; +} + +/** +* 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 auto store_le(T in) { + std::array out; + store_le(in, out); + return out; +} + template void copy_out_be(uint8_t out[], size_t out_bytes, const T in[]) { while(out_bytes >= sizeof(T)) { diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index 5c8fa7b6c93..604bd782fb1 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -199,6 +199,14 @@ class Utility_Function_Tests final : public Text_Based_Test { 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; } }; From 5e52e4b83e86d27d347405ced3afd404fd136923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Wed, 22 Nov 2023 20:54:38 +0100 Subject: [PATCH 7/7] Update src/lib/utils/loadstor.h Co-authored-by: Fabian Albert <126883116+FAlbertDev@users.noreply.github.com> --- src/lib/utils/loadstor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index 7f7279412d4..15bc7ff7d1b 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -492,9 +492,9 @@ inline constexpr auto store_be(T in) { } /** -* Store a big-endian unsigned integer +* Store a little-endian unsigned integer * @param in the input unsigned integer -* @return a byte array holding the integer value in big-endian byte order +* @return a byte array holding the integer value in little-endian byte order */ template inline constexpr auto store_le(T in) {