#include #include "dpf.hpp" #include #include #include #include #include #include namespace { template F pow_elem(F base, unsigned long long exp) { F r{1}; while (exp != 0) { if ((exp & 1ull) != 0) r = r * base; exp >>= 1; if (exp != 0) base = base * base; } return r; } template void expect_field_laws() { constexpr unsigned bits = F::bits; EXPECT_EQ(F{0} + F{1}, F{1}); EXPECT_EQ(F{1} + F{1}, F{0}); EXPECT_EQ(-F{1}, F{1}); EXPECT_EQ(F{-1}, F{1}); EXPECT_EQ(F{1} * F{1}, F{1}); EXPECT_EQ(F{0} * F{5}, F{0}); EXPECT_TRUE(std::is_trivially_copyable_v); EXPECT_TRUE(std::is_standard_layout_v); EXPECT_TRUE(dpf::utils::has_characteristic_two_v); EXPECT_EQ(dpf::utils::bitlength_of_v, bits); EXPECT_EQ(dpf::utils::make_default_v, F{1}); if constexpr (bits > 1) { const F x{2}; F xpow{1}; for (unsigned i = 0; i < bits; ++i) xpow = xpow * x; if constexpr (bits == 2 || bits == 4) EXPECT_EQ(xpow, F{0x3}); else if constexpr (bits == 8) EXPECT_EQ(xpow, F{0x1b}); else if constexpr (bits == 16) EXPECT_EQ(xpow, F{0x2d}); else if constexpr (bits == 32) EXPECT_EQ(xpow.raw(), 0x90200001u); else { const auto tail = static_cast( (typename F::integral_type{1} << 63) | (typename F::integral_type{1} << 62) | (typename F::integral_type{1} << 53) | typename F::integral_type{1}); EXPECT_EQ(xpow.raw(), tail); } } const F a{0x13}; const F b{0x2a}; const F c{0x7}; EXPECT_EQ((a + b) * c, a * c + b * c); EXPECT_EQ((a * b) * c, a * (b * c)); if constexpr (bits <= 32) { if (a != F{0}) EXPECT_EQ(a * pow_elem(a, (1ull << bits) - 2ull), F{1}); } if constexpr (bits <= 8) { const unsigned lim = 1u << bits; for (unsigned i = 1; i < lim; ++i) EXPECT_EQ(F{i} * pow_elem(F{i}, (1ull << bits) - 2ull), F{1}) << i; } } template auto open_at(const Key0 & k0, const Key1 & k1, In x) { const auto y0 = *dpf::eval_point(k0, x); const auto y1 = *dpf::eval_point(k1, x); return dpf::reconstruct(y0, y1); } template void expect_point_payload(In alpha, Out beta) { auto [k0, k1] = dpf::make_dpf(alpha, beta); for (int x = 0; x < 32; ++x) { const In q = static_cast(x); const Out got = open_at(k0, k1, q); EXPECT_EQ(got, q == alpha ? beta : Out{}) << static_cast(x); } EXPECT_EQ(open_at(k0, k1, alpha), beta); } template void expect_cmp(std::uint8_t alpha, Out beta, Spec spec, bool leq) { auto [k0, k1] = dpf::make_dpf(alpha, spec); for (int x = 0; x < 24; ++x) { const auto q = static_cast(x); const auto y0 = dpf::eval_point(dpf::cmp, k0, q); const auto y1 = dpf::eval_point(dpf::cmp, k1, q); const bool hot = leq ? x <= static_cast(alpha) : x < static_cast(alpha); EXPECT_EQ(dpf::reconstruct(y0, y1), hot ? beta : Out{}) << x; } } } // namespace TEST(Gf2, FieldLaws) { expect_field_laws(); expect_field_laws(); expect_field_laws(); expect_field_laws(); expect_field_laws(); expect_field_laws(); expect_field_laws(); } TEST(Gf2, SubByteAdditionIsXor) { EXPECT_EQ(dpf::gf22{3} + dpf::gf22{1}, dpf::gf22{2}); EXPECT_EQ(dpf::gf24{0xf} + dpf::gf24{1}, dpf::gf24{0xe}); EXPECT_EQ(dpf::gf24{2} * dpf::gf24{2}, dpf::gf24{4}); EXPECT_EQ(dpf::gf22{2} * dpf::gf22{2}, dpf::gf22{3}); } TEST(Gf2, LeafScaleIsPerLane) { alignas(16) unsigned char bytes[16]; for (int i = 0; i < 16; ++i) bytes[i] = 0xe4; simde__m128i node; std::memcpy(&node, bytes, sizeof(node)); const auto scaled = dpf::multiply_leaf(node, dpf::gf24{2}); unsigned char out[16]; std::memcpy(out, &scaled, sizeof(out)); const auto lo = static_cast((dpf::gf24{0x4} * dpf::gf24{2}).raw()); const auto hi = static_cast((dpf::gf24{0xe} * dpf::gf24{2}).raw()); const auto expect = static_cast(lo | (hi << 4)); for (unsigned char b : out) EXPECT_EQ(b, expect); const auto summed = dpf::add_leaf(node, node); unsigned char z[16]; std::memcpy(z, &summed, sizeof(z)); for (unsigned char b : z) EXPECT_EQ(b, 0); } TEST(Gf2, ShufbScalarVectorMatchesFieldMul) { alignas(32) unsigned char bytes[33]; for (int i = 0; i < 33; ++i) bytes[i] = static_cast(i * 17 + 3); const unsigned scalars[] = {0u, 1u, 2u, 0x1bu, 0x80u, 0xffu, 0x2du, 0x8000u, 0xffffu}; for (unsigned s : scalars) { if (s > 0xffu) continue; simde__m128i n128; simde__m256i n256; std::memcpy(&n128, bytes, 16); std::memcpy(&n256, bytes, 32); const auto a128 = dpf::multiply_leaf(n128, dpf::gf28{s}); const auto a256 = dpf::multiply_leaf(n256, dpf::gf28{s}); unsigned char o128[16]; unsigned char o256[32]; std::memcpy(o128, &a128, 16); std::memcpy(o256, &a256, 32); for (int i = 0; i < 16; ++i) EXPECT_EQ(o128[i], (dpf::gf28{bytes[i]} * dpf::gf28{s}).raw()) << s << " " << i; for (int i = 0; i < 32; ++i) EXPECT_EQ(o256[i], (dpf::gf28{bytes[i]} * dpf::gf28{s}).raw()) << s << " " << i; unsigned char tail[33]; std::memcpy(tail, bytes, 33); dpf::gf2_detail::scale_gf28(tail, bytes, 33, static_cast(s)); for (int i = 0; i < 33; ++i) EXPECT_EQ(tail[i], (dpf::gf28{bytes[i]} * dpf::gf28{s}).raw()) << "tail " << i; } alignas(32) unsigned char lanes[32]; for (int i = 0; i < 16; ++i) { const auto lane = static_cast(i * 19 + 1); lanes[2 * i] = static_cast(lane); lanes[2 * i + 1] = static_cast(lane >> 8); } for (unsigned s : scalars) { simde__m256i node; std::memcpy(&node, lanes, 32); const auto scaled = dpf::multiply_leaf(node, dpf::gf216{s}); unsigned char got[32]; std::memcpy(got, &scaled, 32); for (int i = 0; i < 16; ++i) { const auto lane = static_cast(lanes[2 * i] | (lanes[2 * i + 1] << 8)); const auto prod = (dpf::gf216{lane} * dpf::gf216{s}).raw(); EXPECT_EQ(got[2 * i], static_cast(prod)) << s << " " << i; EXPECT_EQ(got[2 * i + 1], static_cast(prod >> 8)) << s << " " << i; } } } TEST(Gf2, PointPayload) { expect_point_payload(std::uint8_t{0x2a}, dpf::gf2{1}); expect_point_payload(std::uint8_t{0x11}, dpf::gf22{3}); expect_point_payload(std::uint8_t{0x05}, dpf::gf24{0xa}); expect_point_payload(std::uint8_t{0x2a}, dpf::gf28{0x1b}); expect_point_payload(std::uint8_t{0x07}, dpf::gf216{0x2d}); expect_point_payload(std::uint8_t{0x13}, dpf::gf232{0x90200001u}); expect_point_payload(std::uint8_t{0x04}, dpf::gf264{0x11}); } TEST(Gf2, ComparisonPayload) { expect_cmp(std::uint8_t{10}, dpf::gf24{0x7}, dpf::lt(dpf::gf24{0x7}), false); expect_cmp(std::uint8_t{10}, dpf::gf28{0x1b}, dpf::leq(dpf::gf28{0x1b}), true); expect_cmp(std::uint8_t{4}, dpf::gf264{0x53}, dpf::lt(dpf::gf264{0x53}), false); } template void expect_inv_exhaustive() { EXPECT_THROW(dpf::detail::shamir_field::inv(F{0}), std::invalid_argument); const unsigned long long n = 1ull << F::bits; for (unsigned long long i = 1; i < n; ++i) { const F a{static_cast(i)}; const F b = dpf::detail::shamir_field::inv(a); EXPECT_EQ(a * b, F{1}) << i; } } template void expect_shamir23(F secret, F slope) { const auto shares = dpf::shamir::deal(secret, std::array{{slope}}); EXPECT_EQ((dpf::shamir::reconstruct(std::get<0>(shares), std::get<1>(shares))), secret); EXPECT_EQ((dpf::shamir::reconstruct(std::get<1>(shares), std::get<2>(shares))), secret); EXPECT_EQ((dpf::shamir::reconstruct(std::get<2>(shares), std::get<0>(shares))), secret); EXPECT_EQ((dpf::shamir::reconstruct( std::get<0>(shares), std::get<1>(shares), std::get<2>(shares))), secret); } TEST(Gf2, InverseAndShamir) { expect_inv_exhaustive(); expect_inv_exhaustive(); expect_inv_exhaustive(); expect_inv_exhaustive(); const dpf::gf216 wide[] = {dpf::gf216{1}, dpf::gf216{2}, dpf::gf216{0x2d}, dpf::gf216{0xffff}}; for (auto a : wide) EXPECT_EQ(a * dpf::detail::shamir_field::inv(a), dpf::gf216{1}); const dpf::gf232 mid[] = {dpf::gf232{1}, dpf::gf232{2}, dpf::gf232{0x190200001u}}; for (auto a : mid) EXPECT_EQ(a * dpf::detail::shamir_field::inv(a), dpf::gf232{1}); const dpf::gf264 big[] = {dpf::gf264{1}, dpf::gf264{2}, dpf::gf264{~std::uint64_t{0}}}; for (auto a : big) EXPECT_EQ(a * dpf::detail::shamir_field::inv(a), dpf::gf264{1}); expect_shamir23(dpf::gf22{1}, dpf::gf22{2}); expect_shamir23(dpf::gf28{0x1b}, dpf::gf28{0x5a}); expect_shamir23(dpf::gf264{0x11}, dpf::gf264{0x53}); const auto shares = dpf::shamir::deal( dpf::gf28{0x1b}, std::array{{dpf::gf28{2}, dpf::gf28{9}}}); EXPECT_EQ((dpf::shamir::reconstruct( std::get<0>(shares), std::get<2>(shares), std::get<4>(shares))), dpf::gf28{0x1b}); // Point 2 is 0 in GF(2), so that party would hold the secret. const auto leaked = dpf::shamir::deal( dpf::gf2{1}, std::array{{dpf::gf2{1}}}); EXPECT_EQ(std::get<1>(leaked).raw(), dpf::gf2{1}.raw()); EXPECT_THROW((dpf::shamir::reconstruct(std::get<0>(leaked), std::get<1>(leaked))), std::invalid_argument); const auto one = dpf::shamir::deal(dpf::gf2{1}, std::array{}); EXPECT_EQ(dpf::shamir::reconstruct(std::get<0>(one)), dpf::gf2{1}); }