/// @file dpf/prg_lowmc.hpp /// @brief Fixed-key LowMC PRG. Same Matyas–Meyer–Oseas stretch as `aes128`. /// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors) /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see [LICENSE.md](@ref license) for details. #ifndef LIBDPF_INCLUDE_DPF_PRG_LOWMC_HPP__ #define LIBDPF_INCLUDE_DPF_PRG_LOWMC_HPP__ #include #include #include #include #include #include "hedley/hedley.h" #include "simde/simde/x86/avx2.h" #include "portable-snippets/exact-int/exact-int.h" #include "lowmc/LowMC.h" #include "lowmc/LowMC.cpp" #include "dpf/prg_count.hpp" namespace dpf { namespace prg { /// @brief LowMCv3, 128-bit block and key, 10 S-boxes, 32 rounds, all-zero key. /// @details `eval(seed, pos)` is `E(seed ⊕ pos) ⊕ seed`, with `pos` in the low lane. struct lowmc128 final { using block_type = simde__m128i; HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static block_type eval(block_type seed, psnip_uint32_t pos) noexcept { note_eval(1, primitive::lowmc); block_type in = simde_mm_xor_si128(seed, simde_mm_set_epi64x(0, pos)); return simde_mm_xor_si128(permute(in), seed); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static auto eval01(block_type seed) noexcept { HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") return std::array{eval(seed, 0), eval(seed, 1)}; HEDLEY_PRAGMA(GCC diagnostic pop) } HEDLEY_ALWAYS_INLINE static void eval(block_type seed, block_type * HEDLEY_RESTRICT output, psnip_uint32_t count, psnip_uint32_t pos = 0) { if (count > 1 && pos > static_cast(~static_cast(0)) - (count - 1u)) { throw std::invalid_argument("prg lane index is out of range"); } for (psnip_uint32_t i = 0; i < count; ++i) { output[i] = eval(seed, pos + i); } } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_NON_NULL(1, 2, 3) static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds, block_type * HEDLEY_RESTRICT left, block_type * HEDLEY_RESTRICT right) noexcept { for (std::size_t i = 0; i < 4; ++i) { auto kids = eval01(seeds[i]); left[i] = kids[0]; right[i] = kids[1]; } } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_NON_NULL(1, 2) static void eval_x4(const block_type * HEDLEY_RESTRICT seeds, block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept { for (std::size_t i = 0; i < 4; ++i) { output[i] = eval(seeds[i], pos); } } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_NON_NULL(1, 2) static void eval_x8(const block_type * HEDLEY_RESTRICT seeds, block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept { for (std::size_t i = 0; i < 8; ++i) { output[i] = eval(seeds[i], pos); } } /// @brief Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`). /// @tparam T value type /// @tparam Party party index, `0` or `1` /// @param seed the PRG seed /// @param pos the 0-based index /// @return Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`) /// @see `prg.hpp` template HEDLEY_NO_THROW static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept; private: HEDLEY_NO_THROW static lowmc::block to_block(block_type x) noexcept { std::uint64_t lane[2]; std::memcpy(lane, &x, sizeof(lane)); lowmc::block b; for (unsigned i = 0; i < 64; ++i) { b[i] = (lane[0] >> i) & 1ull; b[i + 64] = (lane[1] >> i) & 1ull; } return b; } HEDLEY_NO_THROW static block_type from_block(const lowmc::block & b) noexcept { std::uint64_t lane[2] = {0, 0}; for (unsigned i = 0; i < 64; ++i) { lane[0] |= static_cast(b[i]) << i; lane[1] |= static_cast(b[i + 64]) << i; } block_type x; std::memcpy(&x, lane, sizeof(x)); return x; } HEDLEY_NO_THROW static block_type permute(block_type x) noexcept { static lowmc::LowMC cipher; return from_block(cipher.encrypt(to_block(x))); } }; // struct lowmc128 } // namespace prg } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_PRG_LOWMC_HPP__