#include #include #include #include #include #include #include "dpf.hpp" #include "dpf/dpf3_ds.hpp" #include "dpf/prg_aes_ccr.hpp" namespace { using dpf::fp61; fp61 open3(fp61 a, fp61 b, fp61 c) { return dpf::shamir3::reconstruct( dpf::shamir3::share{1, a}, dpf::shamir3::share{2, b}, dpf::shamir3::share{3, c}); } /// F_DPF3CMP: complementary DCF halves (party 1 = k0, party 2 = k1). fp61 open_cmp(std::uint64_t k0_half, std::uint64_t k1_half) { return dpf::reconstruct_cmp_halves(k0_half, k1_half); } template void expect_point(const K1 & k1, const K2 & k2, const K3 & k3, Input alpha, fp61 beta) { for (unsigned x = 0; x < 256; ++x) { const fp61 got = open3(dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x)), dpf::eval_point(k3, static_cast(x))); if (static_cast(x) == alpha) EXPECT_EQ(got, beta) << "x=" << x; else EXPECT_EQ(got.raw(), 0u) << "x=" << x; } } } // namespace TEST(Shamir3, ShareAndReconstruct) { const fp61 secret{123456789u}; const auto s = dpf::shamir3::share_secret(secret); EXPECT_EQ(dpf::shamir3::reconstruct(s[0], s[1]), secret); EXPECT_EQ(dpf::shamir3::reconstruct(s[0], s[2]), secret); EXPECT_EQ(dpf::shamir3::reconstruct(s[1], s[2]), secret); EXPECT_EQ(dpf::shamir3::reconstruct(s[0], s[1], s[2]), secret); } TEST(Shamir3, PartyScaleRoundTrip) { const fp61 v{0x123456789abcdefull & ((1ull << 61) - 1)}; const auto s = dpf::shamir3::share_secret(v); for (const auto & sh : s) { const fp61 uns = dpf::shamir3::unscale(sh); const fp61 back = dpf::shamir3::party_scale( dpf::shamir3::share{sh.party, uns}); EXPECT_EQ(back, sh.value); } } TEST(Dpf3, PointFullDomain) { using Input = std::uint8_t; const Input alpha = 42; const fp61 beta{99}; auto [k1, k2, k3] = dpf::make_dpf3(alpha, beta); expect_point(k1, k2, k3, alpha, beta); // Single key is not the clear point function. EXPECT_NE(dpf::eval_point(k1, alpha), beta); EXPECT_NE(dpf::eval_point(k2, alpha), beta); EXPECT_NE(dpf::eval_point(k3, alpha), beta); } TEST(Dpf3, HalfTree) { using Input = std::uint8_t; using Interior = dpf::prg::aes128_ccr; using Exterior = dpf::prg::aes128; const Input alpha = 7; const fp61 beta{5}; auto [k1, k2, k3] = dpf::make_dpf3(alpha, beta); expect_point(k1, k2, k3, alpha, beta); } TEST(Dpf3, VerifiableProof) { using Input = std::uint8_t; const Input alpha = 11; const fp61 beta{3}; auto [k1, k2, k3] = dpf::make_dpf3(alpha, beta, dpf::verifiable{}); auto p1 = dpf::prove_dpf3(k1, alpha); auto p2 = dpf::prove_dpf3(k2, alpha); auto p3 = dpf::prove_dpf3(k3, alpha); EXPECT_TRUE(dpf::verify_dpf3(p1, p2, p3)); using arr = typename std::decay_t::correction_seeds_array; for (auto & cs : const_cast(k1.a.dpf_key.correction_seeds())) cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1)); auto bad = dpf::prove_dpf3(k1, alpha); EXPECT_FALSE(dpf::verify_dpf3(bad, p2, p3)); } TEST(Dpf3, ExtractableWeightOne) { using Input = std::uint8_t; const Input alpha = 20; const fp61 beta{7}; auto [k1, k2, k3] = dpf::make_dpf3(alpha, beta, dpf::extractable{}); EXPECT_TRUE(k1.extractable); std::vector s1(256), s2(256), s3(256), rs(256); for (unsigned x = 0; x < 256; ++x) { s1[x] = dpf::eval_point(k1, static_cast(x)); s2[x] = dpf::eval_point(k2, static_cast(x)); s3[x] = dpf::eval_point(k3, static_cast(x)); rs[x] = dpf::uniform_sample(); } EXPECT_TRUE(dpf::sketch_verify3(k1, k2, k3, s1, s2, s3, rs)); s1[21] = s1[21] + beta; s2[21] = s2[21] + beta; s3[21] = s3[21] + beta; EXPECT_FALSE(dpf::sketch_verify3(k1, k2, k3, s1, s2, s3, rs)); } TEST(Dpf3, ExtractableGateRejectsPlainKeys) { using Input = std::uint8_t; auto [k1, k2, k3] = dpf::make_dpf3(Input{1}, fp61{1}); std::vector z(1, fp61{}); std::vector rs(1, fp61{1}); EXPECT_THROW(dpf::sketch_verify3(k1, k2, k3, z, z, z, rs), std::invalid_argument); } TEST(Dpf3, VerifiableExtractableProofAndSketch) { using Input = std::uint8_t; const Input alpha = 33; const fp61 beta{6}; auto [k1, k2, k3] = dpf::make_dpf3(alpha, beta, dpf::verifiable{}, dpf::extractable{}); std::vector s1(256), s2(256), s3(256), rs(256); for (unsigned x = 0; x < 256; ++x) { s1[x] = dpf::eval_point(k1, static_cast(x)); s2[x] = dpf::eval_point(k2, static_cast(x)); s3[x] = dpf::eval_point(k3, static_cast(x)); rs[x] = dpf::uniform_sample(); } EXPECT_TRUE(dpf::verify_dpf3(k1, k2, k3, dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha), s1, s2, s3, rs)); } TEST(Dpf3, UpdatableInPlaceKeepsAlpha) { using Input = std::uint8_t; const Input alpha = 9; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{5}, dpf::updatable{}); EXPECT_TRUE(k1.updatable && k2.updatable && k3.updatable); expect_point(k1, k2, k3, alpha, fp61{5}); dpf::update_payload(k1, k2, k3, alpha, fp61{8}); expect_point(k1, k2, k3, alpha, fp61{8}); dpf::update_payload(k1, k2, k3, alpha, fp61{0}); expect_point(k1, k2, k3, alpha, fp61{0}); dpf::update_payload(k1, k2, k3, alpha, fp61{100}); expect_point(k1, k2, k3, alpha, fp61{100}); } TEST(Dpf3, NonUpdatableRejectsUpdate) { using Input = std::uint8_t; auto [k1, k2, k3] = dpf::make_dpf3(Input{3}, fp61{1}); EXPECT_FALSE(k1.updatable); EXPECT_THROW(dpf::update_payload(k1, k2, k3, Input{3}, fp61{2}), std::invalid_argument); } TEST(Dpf3, RemakeFreshTrees) { using Input = std::uint8_t; const Input alpha = 15; auto [k1, k2, k3] = dpf::remake_dpf3(alpha, fp61{44}); expect_point(k1, k2, k3, alpha, fp61{44}); auto [v1, v2, v3] = dpf::remake_dpf3(alpha, fp61{2}, dpf::verifiable{}); EXPECT_TRUE(v1.verifiable); expect_point(v1, v2, v3, alpha, fp61{2}); } TEST(Dpf3, VerifiableUpdatableProofSurvivesUpdate) { using Input = std::uint8_t; const Input alpha = 33; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{6}, dpf::verifiable{}, dpf::updatable{}); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); dpf::update_payload(k1, k2, k3, alpha, fp61{90}); expect_point(k1, k2, k3, alpha, fp61{90}); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); } TEST(Dpf3, MultipointFullDomain) { using Input = std::uint8_t; const std::vector alphas{1, 2, 9, 40}; const std::vector betas{fp61{7}, fp61{11}, fp61{3}, fp61{4}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas); for (unsigned x = 0; x < 256; ++x) { fp61 want{}; for (std::size_t i = 0; i < alphas.size(); ++i) if (alphas[i] == static_cast(x)) want = betas[i]; const fp61 got = open3( dpf::eval_multipoint(k1, static_cast(x)), dpf::eval_multipoint(k2, static_cast(x)), dpf::eval_multipoint(k3, static_cast(x))); EXPECT_EQ(got, want) << "x=" << x; } } TEST(Dpf3, MultipointUpdatableInPlace) { using Input = std::uint8_t; const std::vector alphas{1, 2, 9, 40}; const std::vector betas{fp61{7}, fp61{11}, fp61{3}, fp61{4}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas, dpf::updatable{}); EXPECT_TRUE(k1.updatable); const auto sigma = k1.sigma; const auto m = k1.bucket_count; const std::vector betas2{fp61{1}, fp61{2}, fp61{3}, fp61{4}}; dpf::update_payload(k1, k2, k3, alphas, betas2); EXPECT_EQ(0, std::memcmp(&k1.sigma, &sigma, sizeof(sigma))); EXPECT_EQ(k1.bucket_count, m); for (unsigned x = 0; x < 256; ++x) { fp61 want{}; for (std::size_t i = 0; i < alphas.size(); ++i) if (alphas[i] == static_cast(x)) want = betas2[i]; const fp61 got = open3( dpf::eval_multipoint(k1, static_cast(x)), dpf::eval_multipoint(k2, static_cast(x)), dpf::eval_multipoint(k3, static_cast(x))); EXPECT_EQ(got, want) << "x=" << x; } } TEST(Dpf3, MultipointNonUpdatableRejects) { using Input = std::uint8_t; const std::vector alphas{3, 5}; const std::vector betas{fp61{1}, fp61{1}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas); EXPECT_THROW(dpf::update_payload(k1, k2, k3, alphas, betas), std::invalid_argument); } TEST(Dpf3, MultipointSeedFlip) { using Input = std::uint8_t; const std::vector alphas{3, 5, 7}; const std::vector betas{fp61{1}, fp61{1}, fp61{1}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas, dpf::verifiable{}); ASSERT_FALSE(k1.buckets.empty()); auto flip = [](auto & plus) { using arr = typename std::decay_t::correction_seeds_array; for (auto & cs : const_cast(plus.dpf_key.correction_seeds())) cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1)); }; for (auto & bucket : k1.buckets) { flip(bucket.a); flip(bucket.b); } bool rejected = false; for (std::size_t i = 0; i < k1.buckets.size(); ++i) { auto p1 = dpf::prove_dpf3(k1.buckets[i], Input{0}); auto p2 = dpf::prove_dpf3(k2.buckets[i], Input{0}); auto p3 = dpf::prove_dpf3(k3.buckets[i], Input{0}); if (!dpf::verify_dpf3(p1, p2, p3)) rejected = true; } EXPECT_TRUE(rejected); } TEST(Dpf3, ComparisonFullDomain) { using Input = std::uint8_t; const Input thresh = 100; const uint64_t beta = 17; auto [k1, k2, k3] = dpf::make_dpf3_cmp(thresh, beta); for (unsigned x = 0; x < 256; ++x) { const bool hot = static_cast(x) < thresh; const auto s1 = dpf::eval_point(k1, static_cast(x)); const auto s2 = dpf::eval_point(k2, static_cast(x)); const auto s3 = dpf::eval_point(k3, static_cast(x)); EXPECT_EQ(open_cmp(s1, s2).raw(), hot ? beta : 0u) << "x=" << x; EXPECT_EQ(open_cmp(s3, s2).raw(), hot ? beta : 0u) << "x=" << x; EXPECT_EQ(s1, s3) << "x=" << x; // both hold k0 if (hot) EXPECT_NE(s1, beta); } } TEST(Dpf3, BlockedComparison) { using Input = std::uint8_t; auto [k1, k2, k3] = dpf::make_dpf3_cmp_blocked<4>(Input{50}, 9u); EXPECT_EQ(open_cmp(dpf::eval_point(k1, Input{10}), dpf::eval_point(k2, Input{10})) .raw(), 9u); EXPECT_EQ(open_cmp(dpf::eval_point(k1, Input{200}), dpf::eval_point(k2, Input{200})) .raw(), 0u); } TEST(Dpf3, IntervalFullDomain) { using Input = std::uint8_t; const Input r = 10, p = 20, q = 40; const uint64_t beta = 5; auto [k1, k2, k3] = dpf::make_dpf3_ic(r, p, q, beta); auto two = dpf::make_dpf(r, dpf::ic(p, q, beta)); for (unsigned x = 0; x < 256; ++x) { const auto want = dpf::reconstruct( dpf::eval_point(dpf::ic, two.first, static_cast(x)), dpf::eval_point(dpf::ic, two.second, static_cast(x))); const fp61 got = open_cmp( dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), static_cast(want)) << "x=" << x; } } TEST(Dpf3, ComparisonPayloadUpdateInPlace) { using Input = std::uint8_t; const Input thresh = 80; auto [k1, k2, k3] = dpf::make_dpf3_cmp(thresh, 3u); dpf::update_payload_cmp(k1, k2, k3, 3u, 11u); for (unsigned x = 0; x < 256; ++x) { const bool hot = static_cast(x) < thresh; const fp61 got = open_cmp( dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), hot ? 11u : 0u) << "x=" << x; } dpf::update_payload_cmp(k1, k2, k3, 11u, 0u); for (unsigned x = 0; x < 256; ++x) { const fp61 got = open_cmp( dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), 0u) << "x=" << x; } } TEST(Dpf3, ComparisonRemake) { using Input = std::uint8_t; auto [k1, k2, k3] = dpf::remake_dpf3_cmp(Input{50}, 7u, 0u); EXPECT_EQ(open_cmp(dpf::eval_point(k1, Input{10}), dpf::eval_point(k2, Input{10})) .raw(), 7u); } TEST(Dpf3, DoernerShelatMatchesDealer) { using Input = std::uint8_t; const Input alpha = 0x2a; const Input x0 = 0x13; const Input x1 = static_cast(alpha ^ x0); const fp61 beta{77}; auto [d1, d2, d3] = dpf::make_dpf3(alpha, beta); auto [s1, s2, s3] = dpf::make_dpf3_doerner_shelat(x0, x1, beta); expect_point(d1, d2, d3, alpha, beta); expect_point(s1, s2, s3, alpha, beta); EXPECT_EQ(dpf::detail::dpf3_impl::open_xor_point(x0, x1), alpha); } TEST(Dpf3, DoernerShelatVerifiable) { using Input = std::uint8_t; const Input alpha = 19; const Input x0 = 7; const Input x1 = static_cast(alpha ^ x0); auto [k1, k2, k3] = dpf::make_dpf3_doerner_shelat(x0, x1, fp61{4}, dpf::verifiable{}); EXPECT_TRUE(k1.verifiable); expect_point(k1, k2, k3, alpha, fp61{4}); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); } TEST(Dpf3, DoernerShelatUpdatable) { using Input = std::uint8_t; const Input alpha = 8; const Input x0 = 1; const Input x1 = static_cast(alpha ^ x0); auto [k1, k2, k3] = dpf::make_dpf3_doerner_shelat(x0, x1, fp61{2}, dpf::updatable{}); EXPECT_TRUE(k1.updatable); EXPECT_FALSE(k1.verifiable); dpf::update_payload(k1, k2, k3, alpha, fp61{55}); expect_point(k1, k2, k3, alpha, fp61{55}); } TEST(Dpf3, DoernerShelatVerifiableUpdatableTagParity) { using Input = std::uint8_t; const Input alpha = 14; const Input x0 = 2; const Input x1 = static_cast(alpha ^ x0); auto [k1, k2, k3] = dpf::make_dpf3_doerner_shelat(x0, x1, fp61{3}, dpf::verifiable{}, dpf::updatable{}); EXPECT_TRUE(k1.verifiable && k1.updatable); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); dpf::update_payload(k1, k2, k3, alpha, fp61{11}); expect_point(k1, k2, k3, alpha, fp61{11}); } TEST(Dpf3, DoernerShelatExtractable) { using Input = std::uint8_t; const Input alpha = 12; const Input x0 = 3; const Input x1 = static_cast(alpha ^ x0); auto [k1, k2, k3] = dpf::make_dpf3_doerner_shelat(x0, x1, fp61{1}, dpf::extractable{}); EXPECT_TRUE(k1.extractable && k2.extractable && k3.extractable); expect_point(k1, k2, k3, alpha, fp61{1}); std::vector s1(256), s2(256), s3(256), rs(256); for (unsigned x = 0; x < 256; ++x) { s1[x] = dpf::eval_point(k1, static_cast(x)); s2[x] = dpf::eval_point(k2, static_cast(x)); s3[x] = dpf::eval_point(k3, static_cast(x)); rs[x] = dpf::uniform_sample(); } EXPECT_TRUE(dpf::sketch_verify3(k1, k2, k3, s1, s2, s3, rs)); } TEST(Dpf3, RemakeTagParity) { using Input = std::uint8_t; auto [u1, u2, u3] = dpf::remake_dpf3(Input{1}, fp61{2}, dpf::updatable{}); EXPECT_TRUE(u1.updatable); expect_point(u1, u2, u3, Input{1}, fp61{2}); auto [e1, e2, e3] = dpf::remake_dpf3(Input{2}, fp61{3}, dpf::verifiable{}, dpf::extractable{}); EXPECT_TRUE(e1.verifiable && e1.extractable); expect_point(e1, e2, e3, Input{2}, fp61{3}); } TEST(Dpf3, MultipointVerifiableUpdatable) { using Input = std::uint8_t; const std::vector alphas{4, 8, 16}; const std::vector betas{fp61{1}, fp61{2}, fp61{3}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas, dpf::verifiable{}, dpf::updatable{}); EXPECT_TRUE(k1.verifiable && k1.updatable); const std::vector betas2{fp61{9}, fp61{8}, fp61{7}}; dpf::update_payload(k1, k2, k3, alphas, betas2); for (std::size_t i = 0; i < alphas.size(); ++i) { const fp61 got = open3(dpf::eval_multipoint(k1, alphas[i]), dpf::eval_multipoint(k2, alphas[i]), dpf::eval_multipoint(k3, alphas[i])); EXPECT_EQ(got, betas2[i]); } } TEST(Dpf3, AsSharePartyIndex) { using Input = std::uint8_t; auto [k1, k2, k3] = dpf::make_dpf3(Input{0}, fp61{1}); const fp61 y{}; EXPECT_EQ(dpf::as_share(k1, y).party, 1); EXPECT_EQ(dpf::as_share(k2, y).party, 2); EXPECT_EQ(dpf::as_share(k3, y).party, 3); } TEST(Shamir3, InconsistentSharesThrow) { const auto s = dpf::shamir3::share_secret(fp61{42}); auto bad = s[2]; bad.value = bad.value + fp61{1}; EXPECT_THROW(dpf::shamir3::reconstruct(s[0], s[1], bad), std::runtime_error); } TEST(Dpf3, ComparisonPredicatesLeqGtGeq) { using Input = std::uint8_t; const Input thresh = 100; const uint64_t beta = 4; auto check = [&](auto keys, auto pred) { auto [k1, k2, k3] = keys; for (unsigned x = 0; x < 256; ++x) { const bool hot = pred(static_cast(x)); const fp61 got = open_cmp(dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), hot ? beta : 0u) << "x=" << x; } }; check(dpf::make_dpf3_cmp(thresh, dpf::leq(beta)), [&](Input x) { return x <= thresh; }); check(dpf::make_dpf3_cmp(thresh, dpf::gt(beta)), [&](Input x) { return x > thresh; }); check(dpf::make_dpf3_cmp(thresh, dpf::geq(beta)), [&](Input x) { return x >= thresh; }); } TEST(Dpf3, ComparisonUpdateFp61NotUint64Wrap) { // Historical bug: `(0 - 11) & ~0ull` is not −11 mod p. Updating 11 → 0 // must clear the hot lane, not leave a wraparound residue. using Input = std::uint8_t; const Input thresh = 60; auto [k1, k2, k3] = dpf::make_dpf3_cmp(thresh, 11u); dpf::update_payload_cmp(k1, k2, k3, 11u, 0u); for (unsigned x = 0; x < 256; ++x) { const fp61 got = open_cmp(dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), 0u) << "x=" << x; } } TEST(Dpf3, PublicPiIdenticalAfterUpdate) { using Input = std::uint8_t; const Input alpha = 21; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{3}, dpf::updatable{}); EXPECT_EQ(k1.a.offset, k2.a.offset); EXPECT_EQ(k2.a.offset, k3.a.offset); EXPECT_EQ(k1.b.offset, k2.b.offset); EXPECT_EQ(k2.b.offset, k3.b.offset); dpf::update_payload(k1, k2, k3, alpha, fp61{70}); EXPECT_EQ(k1.a.offset, k2.a.offset); EXPECT_EQ(k2.a.offset, k3.a.offset); EXPECT_EQ(k1.b.offset, k2.b.offset); EXPECT_EQ(k2.b.offset, k3.b.offset); expect_point(k1, k2, k3, alpha, fp61{70}); } TEST(Dpf3, ProveRejectsClearedVerifiableFlag) { using Input = std::uint8_t; const Input alpha = 5; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{1}, dpf::verifiable{}); k1.verifiable = false; // outer flag gate (inners remain V) EXPECT_THROW(dpf::prove_dpf3(k1, alpha), std::invalid_argument); (void)k2; (void)k3; } TEST(Dpf3, VerifyExtractableRejectsNonExtractable) { using Input = std::uint8_t; const Input alpha = 8; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{2}, dpf::verifiable{}); std::vector s1(8), s2(8), s3(8), rs(8); for (unsigned x = 0; x < 8; ++x) { s1[x] = dpf::eval_point(k1, static_cast(x)); s2[x] = dpf::eval_point(k2, static_cast(x)); s3[x] = dpf::eval_point(k3, static_cast(x)); rs[x] = fp61{1}; } EXPECT_THROW( dpf::verify_dpf3(k1, k2, k3, dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha), s1, s2, s3, rs), std::invalid_argument); } TEST(Dpf3, IntervalBoundaryRelativeToR) { // Interval is relative to public shift `r`; x=r+p and x=r+q−1 are the // classic edges that mis-scored in the IC example (absolute vs relative). using Input = std::uint8_t; const Input r = 10, p = 20, q = 40; const uint64_t beta = 5; auto [k1, k2, k3] = dpf::make_dpf3_ic(r, p, q, beta); auto two = dpf::make_dpf(r, dpf::ic(p, q, beta)); for (Input x : {Input{29}, Input{30}, Input{35}, Input{49}, Input{50}}) { const auto want = dpf::reconstruct( dpf::eval_point(dpf::ic, two.first, x), dpf::eval_point(dpf::ic, two.second, x)); const fp61 got = open_cmp(dpf::eval_point(k1, x), dpf::eval_point(k2, x)); EXPECT_EQ(got.raw(), static_cast(want)) << "x=" << unsigned(x); } } TEST(Dpf3, MultipointRemakeFreshSigma) { using Input = std::uint8_t; const std::vector alphas{2, 4, 8}; const std::vector betas{fp61{1}, fp61{2}, fp61{3}}; auto [a1, a2, a3] = dpf::make_multipoint3(alphas, betas, dpf::updatable{}); const auto sigma0 = a1.sigma; auto [b1, b2, b3] = dpf::remake_multipoint3(alphas, betas); EXPECT_NE(0, std::memcmp(&b1.sigma, &sigma0, sizeof(sigma0))); for (std::size_t i = 0; i < alphas.size(); ++i) { EXPECT_EQ(open3(dpf::eval_multipoint(b1, alphas[i]), dpf::eval_multipoint(b2, alphas[i]), dpf::eval_multipoint(b3, alphas[i])), betas[i]); } (void)a2; (void)a3; } TEST(Dpf3, HalfTreeUpdatableUpdate) { using Input = std::uint8_t; using Interior = dpf::prg::aes128_ccr; using Exterior = dpf::prg::aes128; const Input alpha = 55; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{8}, dpf::updatable{}); dpf::update_payload(k1, k2, k3, alpha, fp61{12}); expect_point(k1, k2, k3, alpha, fp61{12}); } TEST(Dpf3, UpdatePreservesCorrectionSeeds) { using Input = std::uint8_t; const Input alpha = 17; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{4}, dpf::verifiable{}, dpf::updatable{}); const auto seeds = k1.a.dpf_key.correction_seeds(); const auto root = k1.a.dpf_key.root(); dpf::update_payload(k1, k2, k3, alpha, fp61{19}); EXPECT_EQ(0, std::memcmp(seeds.data(), k1.a.dpf_key.correction_seeds().data(), sizeof(seeds))); EXPECT_EQ(0, std::memcmp(&root, &k1.a.dpf_key.root(), sizeof(root))); expect_point(k1, k2, k3, alpha, fp61{19}); auto [r1, r2, r3] = dpf::remake_dpf3(alpha, fp61{19}, dpf::verifiable{}, dpf::updatable{}); EXPECT_NE(0, std::memcmp(seeds.data(), r1.a.dpf_key.correction_seeds().data(), sizeof(seeds))); (void)r2; (void)r3; } TEST(Dpf3, DoernerShelatVuProofAfterUpdate) { using Input = std::uint8_t; const Input alpha = 22; const Input x0 = 6; const Input x1 = static_cast(alpha ^ x0); auto [k1, k2, k3] = dpf::make_dpf3_doerner_shelat(x0, x1, fp61{5}, dpf::verifiable{}, dpf::updatable{}); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); dpf::update_payload(k1, k2, k3, alpha, fp61{41}); expect_point(k1, k2, k3, alpha, fp61{41}); EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1, alpha), dpf::prove_dpf3(k2, alpha), dpf::prove_dpf3(k3, alpha))); } TEST(Dpf3, MultipointVuProofAfterUpdate) { using Input = std::uint8_t; const std::vector alphas{4, 8, 16}; const std::vector betas{fp61{1}, fp61{2}, fp61{3}}; auto [k1, k2, k3] = dpf::make_multipoint3(alphas, betas, dpf::verifiable{}, dpf::updatable{}); const auto sigma = k1.sigma; const std::vector betas2{fp61{9}, fp61{8}, fp61{7}}; dpf::update_payload(k1, k2, k3, alphas, betas2); EXPECT_EQ(0, std::memcmp(&k1.sigma, &sigma, sizeof(sigma))); bool any = false; for (std::size_t i = 0; i < k1.buckets.size(); ++i) { any = true; EXPECT_TRUE(dpf::verify_dpf3(dpf::prove_dpf3(k1.buckets[i], Input{0}), dpf::prove_dpf3(k2.buckets[i], Input{0}), dpf::prove_dpf3(k3.buckets[i], Input{0}))); } EXPECT_TRUE(any); } TEST(Dpf3, BlockedComparisonFullDomain) { using Input = std::uint8_t; const Input thresh = 50; const uint64_t beta = 9; auto [k1, k2, k3] = dpf::make_dpf3_cmp_blocked<4>(thresh, beta); for (unsigned x = 0; x < 256; ++x) { const bool hot = static_cast(x) < thresh; const fp61 got = open_cmp(dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), hot ? beta : 0u) << "x=" << x; } } TEST(Dpf3, TagsAnyOrderIncludingExtractableUpdatable) { using Input = std::uint8_t; const Input alpha = 17; auto [k1, k2, k3] = dpf::make_dpf3(alpha, fp61{4}, dpf::extractable{}, dpf::updatable{}, dpf::verifiable{}); EXPECT_TRUE(k1.verifiable && k1.extractable && k1.updatable); EXPECT_TRUE(k2.extractable && k3.updatable); expect_point(k1, k2, k3, alpha, fp61{4}); dpf::update_payload(k1, k2, k3, alpha, fp61{11}); expect_point(k1, k2, k3, alpha, fp61{11}); } TEST(Dpf3, ComparisonSpecWrappersMatchPlainPredicate) { using Input = std::uint8_t; const Input thresh = 40; const uint64_t beta = 3; auto check = [&](auto keys) { auto [k1, k2, k3] = keys; for (unsigned x = 0; x < 256; ++x) { const bool hot = static_cast(x) > thresh; const fp61 got = open_cmp(dpf::eval_point(k1, static_cast(x)), dpf::eval_point(k2, static_cast(x))); EXPECT_EQ(got.raw(), hot ? beta : 0u) << "x=" << x; } }; check(dpf::make_dpf3_cmp(thresh, dpf::idcf(dpf::gt(beta)))); check(dpf::make_dpf3_cmp(thresh, dpf::block_width<4>(dpf::gt(beta)))); }