#include #include #include "dpf.hpp" #include #include #include #include namespace { 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); const In last = static_cast(16); for (In x = 0; x < last; ++x) { const Out got = open_at(k0, k1, x); EXPECT_EQ(got, x == alpha ? beta : Out{}) << static_cast(x); } const Out on = open_at(k0, k1, alpha); EXPECT_EQ(on, beta); } } // namespace TEST(Field64, ArithmeticMatchesThePrime) { constexpr auto p = dpf::field64::mod; EXPECT_EQ((dpf::field64{p - 1} + dpf::field64{p - 1}).raw(), p - 2); EXPECT_EQ((dpf::field64{p - 1} + dpf::field64{1}).raw(), 0u); EXPECT_EQ((dpf::field64{1} - dpf::field64{2}).raw(), p - 1); EXPECT_EQ((-dpf::field64{0}).raw(), 0u); EXPECT_EQ((-dpf::field64{1}).raw(), p - 1); EXPECT_EQ(dpf::field64{-1}.raw(), p - 1); EXPECT_EQ((dpf::field64{p - 1} * dpf::field64{p - 1}).raw(), 1u); EXPECT_EQ((dpf::field64{0x0123456789abcdefull} * dpf::field64{0xfedcba9876543210ull}).raw(), 0xcfaeafd136c7bbaeull); EXPECT_EQ((dpf::field64{std::uint64_t{1} << 32} * dpf::field64{std::uint64_t{1} << 32}).raw(), 0xffffffffull); const dpf::field64 a{1000}; const dpf::field64 b{p - 5}; const dpf::field64 c{17}; EXPECT_EQ((a + b) * c, a * c + b * c); EXPECT_TRUE(std::is_trivially_copyable_v); EXPECT_TRUE(std::is_standard_layout_v); } TEST(Field128, ArithmeticMatchesThePrime) { const dpf::field128 almost{static_cast( (static_cast(dpf::field128::mod_hi) << 64) | dpf::field128::mod_lo) - 1}; EXPECT_EQ(almost + dpf::field128{1}, dpf::field128{0}); EXPECT_EQ(almost * almost, dpf::field128{1}); EXPECT_EQ(-almost, dpf::field128{1}); EXPECT_EQ(dpf::field128{-1}, almost); EXPECT_EQ(dpf::field128{1} - dpf::field128{2}, almost); EXPECT_TRUE(std::is_trivially_copyable_v); EXPECT_TRUE(std::is_standard_layout_v); } TEST(Field128, WideProduct) { const unsigned __int128 a = (static_cast(0x123456789abcdef0ull) << 64) | 0x123456789abcdefull; const unsigned __int128 b = (static_cast(0xfedcba9876543210ull) << 64) | 0xfedcba9876543210ull; const auto got = dpf::field128{a} * dpf::field128{b}; EXPECT_EQ(got.hi(), 0xb0a8b1cbf73350c9ull); EXPECT_EQ(got.lo(), 0xf0123456789abe97ull); } TEST(P256, GeneratorAndDoubling) { const auto g = dpf::p256::generator(); EXPECT_EQ(dpf::p256{1}, g); EXPECT_EQ(dpf::p256{0}, dpf::p256{}); EXPECT_TRUE(dpf::p256{}.is_identity()); EXPECT_EQ(g + dpf::p256{}, g); EXPECT_EQ(g - g, dpf::p256{}); EXPECT_EQ(-dpf::p256{1}, dpf::p256{-1}); const auto two = g + g; EXPECT_EQ(two, dpf::p256{2}); EXPECT_EQ(two - g, g); const unsigned char expect[] = { 0x03, 0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e, 0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3, 0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35, 0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78, }; EXPECT_EQ(std::memcmp(two.bytes(), expect, 33), 0); EXPECT_EQ(dpf::p256::from_compressed(expect), two); const unsigned char bad[33] = {0x04}; EXPECT_THROW(dpf::p256::from_compressed(bad), std::invalid_argument); EXPECT_TRUE(std::is_trivially_copyable_v); EXPECT_TRUE(std::is_standard_layout_v); } 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; } } TEST(FieldOutput, ComparisonPayload) { const auto f = dpf::field64{5}; expect_cmp(std::uint8_t{10}, f, dpf::lt(f), false); expect_cmp(std::uint8_t{10}, f, dpf::leq(f), true); const auto w = dpf::field128{9}; expect_cmp(std::uint8_t{10}, w, dpf::lt(w), false); expect_cmp(std::uint8_t{4}, dpf::p256{1}, dpf::lt(dpf::p256{1}), false); } TEST(FieldOutput, IdcfPrefixUsesTheSameGroup) { const auto beta = dpf::field64{3}; const std::uint8_t alpha = 0x2a; auto [k0, k1] = dpf::make_dpf(alpha, dpf::idcf(dpf::lt(beta))); auto [a0, a1] = dpf::make_dpf(alpha, dpf::lt_at<4>(beta)); for (int x = 0; x < 32; ++x) { const auto q = static_cast(x); const auto full0 = dpf::eval_point(dpf::cmp, k0, q); const auto full1 = dpf::eval_point(dpf::cmp, k1, q); EXPECT_EQ(dpf::reconstruct(full0, full1), x < static_cast(alpha) ? beta : dpf::field64{}); const auto p0 = dpf::eval_point<4, dpf::field64>(dpf::cmp_prefix<4>, k0, q); const auto p1 = dpf::eval_point<4, dpf::field64>(dpf::cmp_prefix<4>, k1, q); const auto n0 = dpf::eval_point(dpf::cmp, a0, q); const auto n1 = dpf::eval_point(dpf::cmp, a1, q); EXPECT_EQ(dpf::reconstruct(p0, p1), dpf::reconstruct(n0, n1)) << x; } } TEST(FieldOutput, PointPayloadRoundTrip) { expect_point_payload(std::uint8_t{0x2a}, dpf::field64{99}); expect_point_payload(std::uint8_t{0x11}, dpf::field128{7}); expect_point_payload(std::uint8_t{0x2a}, dpf::p256{1}); } TEST(FieldOutput, Fp61PayloadNearModulus) { const dpf::fp61 beta{dpf::fp61_mod - 1}; expect_point_payload(std::uint8_t{0x2a}, beta); expect_point_payload(std::uint8_t{0x05}, dpf::fp61{dpf::fp61_mod - 7}); } TEST(FieldOutput, Fp61NegativeComparisonDelta) { // δ = −1 ≡ p−1 must survive comparison (not collapse to 0 via a 61-bit mask). expect_cmp(std::uint8_t{10}, dpf::fp61{dpf::fp61_mod - 1}, dpf::lt(dpf::fp61{dpf::fp61_mod - 1}), false); expect_cmp(std::uint8_t{10}, dpf::fp61{dpf::fp61_mod - 1}, dpf::leq(dpf::fp61{dpf::fp61_mod - 1}), true); } TEST(FieldOutput, P256MalformedCompressedRejected) { unsigned char bad[33] = {0x02}; bad[32] = 1; // x = 1 is not on P-256 EXPECT_THROW(dpf::p256::from_compressed(bad), std::invalid_argument); dpf::p256 bogus{}; unsigned char raw[sizeof(dpf::p256)]{}; std::memcpy(raw, &bogus, sizeof(raw)); std::memcpy(raw, bad, 33); std::memcpy(&bogus, raw, sizeof(raw)); EXPECT_THROW(bogus + dpf::p256{}, std::invalid_argument); EXPECT_THROW(-bogus, std::invalid_argument); } TEST(FieldOutput, P256ScalarWideComparisonPayload) { const dpf::p256_scalar beta{17}; expect_cmp(std::uint8_t{10}, beta, dpf::lt(beta), false); expect_cmp(std::uint8_t{10}, beta, dpf::leq(beta), true); } TEST(FieldOutput, Field128NoncanonicalNegationAndEquality) { dpf::field128 a{}; const std::uint64_t w[2] = { dpf::field128::mod_lo, dpf::field128::mod_hi}; std::memcpy(&a, w, sizeof(w)); EXPECT_EQ(dpf::field128::canonicalize(a), dpf::field128{}); EXPECT_EQ(-a, dpf::field128{}); EXPECT_EQ(a, dpf::field128{}); }