myl7/fss 1.2.0
Function secret sharing (FSS) primitives including distributed point/comparison function (DPF/DCF)
Loading...
Searching...
No Matches
half_tree_dpf.cuh
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
17#pragma once
18#include <cuda_runtime.h>
19#include <type_traits>
20#include <cstddef>
21#include <cassert>
22#include <omp.h>
23#include <fss/group.cuh>
24#include <fss/prg.cuh>
25#include <fss/util.cuh>
26
27namespace fss {
28
39template <int in_bits, typename Group, typename Prg, typename In = uint, int par_depth = -1>
40 requires((std::is_unsigned_v<In> || std::is_same_v<In, __uint128_t>) && in_bits <= sizeof(In) * 8 &&
43public:
44 Prg prg;
45 int4 hash_key;
46
53 struct __align__(32) Cw {
54 int4 s;
55 bool extra;
56 };
57 static_assert(sizeof(Cw) == 32);
58
68 __host__ __device__ void Gen(Cw cws[], int4 &ocw, const int4 s0s[2], In a, int4 b_buf) {
69 b_buf = util::SetLsb(b_buf, false);
70
71 // Initialize: node0 has t=0, node1 has t=1
72 int4 node0 = util::SetLsb(s0s[0], false);
73 int4 node1 = util::SetLsb(s0s[1], true);
74 int4 delta = util::Xor(node0, node1); // LSB = 0^1 = 1
75
76 // Levels 1 to n-1 (index i = 0 to in_bits-2)
77 for (int i = 0; i < in_bits - 1; ++i) {
78 int4 h0 = prg.Gen(util::Xor(hash_key, node0))[0];
79 int4 h1 = prg.Gen(util::Xor(hash_key, node1))[0];
80
81 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
82
83 // CW = h0 ^ h1 ^ (!a_bit ? delta : 0)
84 // When a_bit=0 (go left): non-alpha is right, CW = h0^h1^delta makes right0=right1
85 // When a_bit=1 (go right): non-alpha is left, CW = h0^h1 makes left0=left1
86 int4 cw = util::Xor(h0, h1);
87 if (!a_bit) cw = util::Xor(cw, delta);
88
89 cws[i] = {cw, false};
90
91 bool t0 = util::GetLsb(node0);
92 bool t1 = util::GetLsb(node1);
93
94 // node_b = h_b ^ (a_bit ? node_b : 0) ^ (t_b ? cw : 0)
95 int4 zero4 = {0, 0, 0, 0};
96 int4 ab_mask0 = a_bit ? node0 : zero4;
97 int4 ab_mask1 = a_bit ? node1 : zero4;
98 int4 t0_mask = t0 ? cw : zero4;
99 int4 t1_mask = t1 ? cw : zero4;
100
101 node0 = util::Xor(util::Xor(h0, ab_mask0), t0_mask);
102 node1 = util::Xor(util::Xor(h1, ab_mask1), t1_mask);
103
104 // delta = node0 ^ node1 for next level
105 delta = util::Xor(node0, node1);
106 }
107
108 // Level n (last level, index i = in_bits-1)
109 {
110 bool a_n = (a >> 0) & 1; // last bit of alpha
111 bool t0 = util::GetLsb(node0);
112 bool t1 = util::GetLsb(node1);
113
114 // Hash with sigma in {0, 1}
115 int4 h0_0 = prg.Gen(util::Xor(hash_key, util::SetLsb(node0, false)))[0];
116 int4 h0_1 = prg.Gen(util::Xor(hash_key, util::SetLsb(node0, true)))[0];
117 int4 h1_0 = prg.Gen(util::Xor(hash_key, util::SetLsb(node1, false)))[0];
118 int4 h1_1 = prg.Gen(util::Xor(hash_key, util::SetLsb(node1, true)))[0];
119
120 // Extract high (s) and low (t) parts
121 int4 high0_0 = util::SetLsb(h0_0, false);
122 bool low0_0 = util::GetLsb(h0_0);
123 int4 high0_1 = util::SetLsb(h0_1, false);
124 bool low0_1 = util::GetLsb(h0_1);
125 int4 high1_0 = util::SetLsb(h1_0, false);
126 bool low1_0 = util::GetLsb(h1_0);
127 int4 high1_1 = util::SetLsb(h1_1, false);
128 bool low1_1 = util::GetLsb(h1_1);
129
130 // HCW corrects the non-alpha direction so both parties converge.
131 // HCW = high{!a_n}_0 ^ high{!a_n}_1
132 int4 HCW;
133 if (a_n) HCW = util::Xor(high0_0, high1_0);
134 else HCW = util::Xor(high0_1, high1_1);
135
136 // LCW ensures:
137 // Alpha direction (sigma=a_n): low0 ^ low1 = 1 (exactly one adds ocw)
138 // Non-alpha direction (sigma=!a_n): low0 ^ low1 = 0 (cancels)
139 // LCW_0 = low{0}_0 ^ low{0}_1 ^ !a_n
140 // LCW_1 = low{1}_0 ^ low{1}_1 ^ a_n
141 bool LCW_0 = low0_0 ^ low1_0 ^ !a_n;
142 bool LCW_1 = low0_1 ^ low1_1 ^ a_n;
143
144 // Store CW_n. Both 16B halves written so the full 32B slot gets unmasked
145 // stores: {s, LCW_1} alone leaves padding undefined, NVCC emits a masked
146 // 256-bit store, and the partial-sector write forces a write-allocate
147 // round trip on GPU (measured ~2x Gen time for the same pattern).
148 int4 *cw_slot = reinterpret_cast<int4 *>(&cws[in_bits - 1]);
149 cw_slot[0] = util::SetLsb(HCW, LCW_0);
150 cw_slot[1] = make_int4(LCW_1, 0, 0, 0);
151
152 // Compute leaf for each party
153 // leaf_b = (a_n ? high{1}_b||low{1}_b : high{0}_b||low{0}_b)
154 int4 leaf0, leaf1;
155 if (a_n) {
156 leaf0 = util::SetLsb(high0_1, low0_1);
157 leaf1 = util::SetLsb(high1_1, low1_1);
158 } else {
159 leaf0 = util::SetLsb(high0_0, low0_0);
160 leaf1 = util::SetLsb(high1_0, low1_0);
161 }
162
163 // Apply CW correction: if t_b: leaf_b ^= SetLsb(HCW, lcw_an)
164 bool lcw_an = a_n ? LCW_1 : LCW_0;
165 int4 leaf_cw = util::SetLsb(HCW, lcw_an);
166 if (t0) leaf0 = util::Xor(leaf0, leaf_cw);
167 if (t1) leaf1 = util::Xor(leaf1, leaf_cw);
168
169 // Output CW: v_cw = Group::From(b_buf) + (-Group::From(SetLsb(leaf0,false))) + Group::From(SetLsb(leaf1,false))
170 auto v_cw =
171 Group::From(b_buf) + (-Group::From(util::SetLsb(leaf0, false))) + Group::From(util::SetLsb(leaf1, false));
172 if (util::GetLsb(leaf1)) v_cw = -v_cw;
173 ocw = v_cw.Into();
174 }
175 }
176
187 __host__ __device__ int4 Eval(bool b, int4 s0, const Cw cws[], int4 ocw, In x) {
188 int4 node = util::SetLsb(s0, b);
189
190 // Levels 1 to n-1 (index i = 0 to in_bits-2)
191 for (int i = 0; i < in_bits - 1; ++i) {
192 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
193 bool t = util::GetLsb(node);
194
195 int4 h = prg.Gen(util::Xor(hash_key, node))[0];
196
197 int4 zero4 = {0, 0, 0, 0};
198 int4 xb_mask = x_bit ? node : zero4;
199 int4 t_mask = t ? cws[i].s : zero4;
200
201 node = util::Xor(util::Xor(h, xb_mask), t_mask);
202 }
203
204 // Level n (last level)
205 {
206 bool x_n = (x >> 0) & 1;
207 bool t = util::GetLsb(node);
208
209 int4 h = prg.Gen(util::Xor(hash_key, util::SetLsb(node, x_n)))[0];
210
211 // Unpack CW_n
212 int4 hcw = util::SetLsb(cws[in_bits - 1].s, false);
213 bool lcw_xn;
214 if (x_n) lcw_xn = cws[in_bits - 1].extra;
215 else lcw_xn = util::GetLsb(cws[in_bits - 1].s);
216
217 int4 high = util::SetLsb(h, false);
218 bool low = util::GetLsb(h);
219
220 if (t) {
221 high = util::Xor(high, hcw);
222 low = low ^ lcw_xn;
223 }
224
225 auto y = Group::From(high);
226 if (low) y = y + Group::From(ocw);
227 if (b) y = -y;
228
229 return y.Into();
230 }
231 }
232
246 void EvalAll(bool b, int4 s0, const Cw cws[], int4 ocw, int4 ys[]) {
247 int4 node = util::SetLsb(s0, b);
248
249 assert(in_bits < sizeof(size_t) * 8);
250
251 int par_depth_ = util::ResolveParDepth(par_depth);
252
253 if constexpr (in_bits == 1) {
254 // Only level n (last level), no tree traversal
255#pragma omp parallel
256#pragma omp single
257 EvalLastLevel(b, node, cws, ocw, ys);
258 return;
259 }
260
261 // Phase 1: tree traversal for levels 1..n-1, stores nodes at level n-1
262 // We use ys[] as scratch space for intermediate nodes.
263 // After phase 1, ys[0..2^(in_bits-1)-1] hold the level n-1 nodes (packed s||t).
264 size_t num_leaves = 1ULL << (in_bits - 1);
265
266 // Recursive tree traversal
267#pragma omp parallel
268#pragma omp single
269 EvalTree(node, cws, ys, 0, num_leaves, 0, par_depth_);
270
271 // Phase 2: level n + output conversion
272 int4 hcw = util::SetLsb(cws[in_bits - 1].s, false);
273 bool lcw_0 = util::GetLsb(cws[in_bits - 1].s);
274 bool lcw_1 = cws[in_bits - 1].extra;
275 auto ocw_group = Group::From(ocw);
276
277 // Iterate backward to avoid overwriting unprocessed parent nodes.
278 for (size_t j = num_leaves; j-- > 0;) {
279 ConvertLastLevel(b, ys[j], hcw, lcw_0, lcw_1, ocw_group, ys[2 * j], ys[2 * j + 1]);
280 }
281 }
282
283private:
284 void EvalTree(int4 node, const Cw cws[], int4 ys[], size_t l, size_t r, int i, int par_depth_) {
285 // i is the level index (0-based), we traverse levels 0..in_bits-2
286 // At level in_bits-1, we store the node
287 if (i == in_bits - 1) {
288 assert(l + 1 == r);
289 ys[l] = node;
290 return;
291 }
292
293 bool t = util::GetLsb(node);
294 int4 h = prg.Gen(util::Xor(hash_key, node))[0];
295
296 int4 zero4 = {0, 0, 0, 0};
297 int4 t_mask = t ? cws[i].s : zero4;
298
299 // Left child: left = H_S(parent) ^ (t ? cw : 0)
300 int4 left = util::Xor(h, t_mask);
301 // Right child: right = left ^ parent
302 int4 right = util::Xor(left, node);
303
304 size_t mid = (l + r) / 2;
305
306 if (i < par_depth_) {
307#pragma omp task
308 EvalTree(left, cws, ys, l, mid, i + 1, par_depth_);
309#pragma omp task
310 EvalTree(right, cws, ys, mid, r, i + 1, par_depth_);
311#pragma omp taskwait
312 } else {
313 EvalTree(left, cws, ys, l, mid, i + 1, par_depth_);
314 EvalTree(right, cws, ys, mid, r, i + 1, par_depth_);
315 }
316 }
317
318 void EvalLastLevel(bool b, int4 node, const Cw cws[], int4 ocw, int4 ys[]) {
319 int4 hcw = util::SetLsb(cws[0].s, false);
320 bool lcw_0 = util::GetLsb(cws[0].s);
321 bool lcw_1 = cws[0].extra;
322 ConvertLastLevel(b, node, hcw, lcw_0, lcw_1, Group::From(ocw), ys[0], ys[1]);
323 }
324
325 void ConvertLastLevel(
326 bool b, int4 parent, int4 hcw, bool lcw_0, bool lcw_1, Group ocw_group, int4 &y0_out, int4 &y1_out) {
327 bool t_parent = util::GetLsb(parent);
328
329 int4 h0 = prg.Gen(util::Xor(hash_key, util::SetLsb(parent, false)))[0];
330 int4 h1 = prg.Gen(util::Xor(hash_key, util::SetLsb(parent, true)))[0];
331
332 int4 high0 = util::SetLsb(h0, false);
333 bool low0 = util::GetLsb(h0);
334 int4 high1 = util::SetLsb(h1, false);
335 bool low1 = util::GetLsb(h1);
336
337 if (t_parent) {
338 high0 = util::Xor(high0, hcw);
339 low0 = low0 ^ lcw_0;
340 high1 = util::Xor(high1, hcw);
341 low1 = low1 ^ lcw_1;
342 }
343
344 auto y0 = Group::From(high0);
345 if (low0) y0 = y0 + ocw_group;
346 if (b) y0 = -y0;
347
348 auto y1 = Group::From(high1);
349 if (low1) y1 = y1 + ocw_group;
350 if (b) y1 = -y1;
351
352 y0_out = y0.Into();
353 y1_out = y1.Into();
354 }
355};
356
357} // namespace fss
2-party DPF scheme using the Half-Tree construction.
Definition half_tree_dpf.cuh:42
void EvalAll(bool b, int4 s0, const Cw cws[], int4 ocw, int4 ys[])
Full domain evaluation method.
Definition half_tree_dpf.cuh:246
int4 Eval(bool b, int4 s0, const Cw cws[], int4 ocw, In x)
Evaluation method.
Definition half_tree_dpf.cuh:187
void Gen(Cw cws[], int4 &ocw, const int4 s0s[2], In a, int4 b_buf)
Key generation method.
Definition half_tree_dpf.cuh:68
Group interface.
Definition group.cuh:40
Pseudorandom generator (PRG) interface.
Definition prg.cuh:21
Correction word.
Definition half_tree_dpf.cuh:53