myl7/fss 1.2.0
Function secret sharing (FSS) primitives including distributed point/comparison function (DPF/DCF)
Loading...
Searching...
No Matches
vdpf.cuh
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
35#pragma once
36#include <cuda_runtime.h>
37#include <cuda/std/array>
38#include <cuda/std/span>
39#include <cuda/std/tuple>
40#include <type_traits>
41#include <cstddef>
42#include <cassert>
43#include <omp.h>
44#include <fss/group.cuh>
45#include <fss/prg.cuh>
46#include <fss/hash.cuh>
47#include <fss/util.cuh>
48
49namespace fss {
50
63template <int in_bits, typename Group, typename Prg, typename XorHash, typename Hash, typename In = uint,
64 int par_depth = -1>
65 requires((std::is_unsigned_v<In> || std::is_same_v<In, __uint128_t>) && in_bits <= sizeof(In) * 8 &&
67class Vdpf {
68public:
69 Prg prg;
70 XorHash xor_hash;
71 Hash hash;
72
81 struct __align__(32) Cw {
82 int4 s;
83 bool tr;
84 };
85 // For only 1 and aligned memory access on GPU
86 static_assert(sizeof(Cw) == 32);
87
101 __host__ __device__ int Gen(
102 Cw cws[], cuda::std::array<int4, 4> &cs, int4 &ocw, cuda::std::span<const int4, 2> s0s, In a, int4 b_buf) {
103 int4 s0 = s0s[0];
104 s0 = util::SetLsb(s0, false);
105 int4 s1 = s0s[1];
106 s1 = util::SetLsb(s1, false);
107 bool t0 = false;
108 bool t1 = true;
109 b_buf = util::SetLsb(b_buf, false);
110
111 for (int i = 0; i < in_bits; ++i) {
112 auto [s0l, s0r] = prg.Gen(s0);
113 auto [s1l, s1r] = prg.Gen(s1);
114
115 bool t0l = util::GetLsb(s0l);
116 s0l = util::SetLsb(s0l, false);
117 bool t0r = util::GetLsb(s0r);
118 s0r = util::SetLsb(s0r, false);
119 bool t1l = util::GetLsb(s1l);
120 s1l = util::SetLsb(s1l, false);
121 bool t1r = util::GetLsb(s1r);
122 s1r = util::SetLsb(s1r, false);
123
124 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
125
126 int4 s_cw;
127 if (!a_bit) s_cw = util::Xor(s0r, s1r);
128 else s_cw = util::Xor(s0l, s1l);
129
130 bool tl_cw = t0l ^ t1l ^ a_bit ^ 1;
131 bool tr_cw = t0r ^ t1r ^ a_bit;
132
133 if (!a_bit) {
134 s0 = s0l;
135 if (t0) s0 = util::Xor(s0, s_cw);
136 s1 = s1l;
137 if (t1) s1 = util::Xor(s1, s_cw);
138
139 if (t0) t0 = t0l ^ tl_cw;
140 else t0 = t0l;
141 if (t1) t1 = t1l ^ tl_cw;
142 else t1 = t1l;
143 } else {
144 s0 = s0r;
145 if (t0) s0 = util::Xor(s0, s_cw);
146 s1 = s1r;
147 if (t1) s1 = util::Xor(s1, s_cw);
148
149 if (t0) t0 = t0r ^ tr_cw;
150 else t0 = t0r;
151 if (t1) t1 = t1r ^ tr_cw;
152 else t1 = t1r;
153 }
154
155 s_cw = util::SetLsb(s_cw, tl_cw);
156 // Both 16B halves written so the full 32B slot gets unmasked stores.
157 // {s_cw, tr_cw} alone leaves padding undefined: NVCC emits a masked
158 // 256-bit store, and the partial-sector write forces a write-allocate
159 // round trip per level on GPU (measured ~2x Gen time).
160 int4 *cw_slot = reinterpret_cast<int4 *>(&cws[i]);
161 cw_slot[0] = s_cw;
162 cw_slot[1] = make_int4(tr_cw, 0, 0, 0);
163 }
164
165 // Verification hash
166 int4 a_buf = util::Pack(a);
167
168 auto pi_tilde_0 = xor_hash.Hash(cuda::std::tuple<int4, const int4>{a_buf, s0});
169 auto pi_tilde_1 = xor_hash.Hash(cuda::std::tuple<int4, const int4>{a_buf, s1});
170 cs = util::Xor(cuda::std::span<const int4, 4>(pi_tilde_0), cuda::std::span<const int4, 4>(pi_tilde_1));
171
172 // Check retry condition
173 if (t0 == t1) return 1;
174
175 // Output correction word
176 auto v_cw = Group::From(b_buf) + (-Group::From(s0)) + Group::From(s1);
177 if (t1) v_cw = -v_cw;
178 ocw = v_cw.Into();
179
180 return 0;
181 }
182
195 __host__ __device__ cuda::std::array<int4, 4> Eval(
196 bool b, int4 s0, cuda::std::span<const Cw> cws, cuda::std::span<const int4, 4> cs, int4 ocw, In x, int4 &y) {
197 int4 s = s0;
198 s = util::SetLsb(s, false);
199 bool t = b;
200
201 for (int i = 0; i < in_bits; ++i) {
202 Cw cw = cws[i];
203 int4 s_cw = cw.s;
204 bool tl_cw = util::GetLsb(s_cw);
205 s_cw = util::SetLsb(s_cw, false);
206 bool tr_cw = cw.tr;
207
208 auto [sl, sr] = prg.Gen(s);
209
210 bool tl = util::GetLsb(sl);
211 sl = util::SetLsb(sl, false);
212 bool tr = util::GetLsb(sr);
213 sr = util::SetLsb(sr, false);
214
215 if (t) {
216 sl = util::Xor(sl, s_cw);
217 sr = util::Xor(sr, s_cw);
218 tl = tl ^ tl_cw;
219 tr = tr ^ tr_cw;
220 }
221
222 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
223
224 if (!x_bit) {
225 s = sl;
226 t = tl;
227 } else {
228 s = sr;
229 t = tr;
230 }
231 }
232
233 // Output share
234 auto g = Group::From(s);
235 assert((ocw.w & 1) == 0);
236 if (t) g = g + Group::From(ocw);
237 if (b) g = -g;
238 y = g.Into();
239
240 // Corrected verification hash
241 int4 x_buf = util::Pack(x);
242
243 auto pi_tilde = xor_hash.Hash(cuda::std::tuple<int4, const int4>{x_buf, s});
244 if (t) {
245 return util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs));
246 }
247 return pi_tilde;
248 }
249
259 void Prove(cuda::std::span<const cuda::std::array<int4, 4>> pi_tildes, cuda::std::span<const int4, 4> cs,
260 cuda::std::array<int4, 4> &pi) {
261 pi = {cs[0], cs[1], cs[2], cs[3]};
262 for (size_t i = 0; i < pi_tildes.size(); ++i) {
263 cuda::std::array<int4, 4> h_input =
264 util::Xor(cuda::std::span<const int4, 4>(pi), cuda::std::span<const int4, 4>(pi_tildes[i]));
265 auto h_out = hash.Hash(cuda::std::span<const int4, 4>(h_input));
266 pi[0] = util::Xor(pi[0], h_out[0]);
267 pi[1] = util::Xor(pi[1], h_out[1]);
268 }
269 }
270
276 __host__ __device__ static bool Verify(cuda::std::span<const int4, 4> pi0, cuda::std::span<const int4, 4> pi1) {
277 for (int i = 0; i < 4; ++i) {
278 if (pi0[i].x != pi1[i].x || pi0[i].y != pi1[i].y || pi0[i].z != pi1[i].z || pi0[i].w != pi1[i].w) return false;
279 }
280 return true;
281 }
282
302 void EvalAll(bool b, int4 s0, cuda::std::span<const Cw> cws, cuda::std::span<const int4, 4> cs, int4 ocw,
303 cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi) {
304 int4 st = s0;
305 bool t = b;
306 st = util::SetLsb(st, t);
307
308 assert(in_bits < sizeof(size_t) * 8);
309 size_t l = 0;
310 size_t r = 1ULL << in_bits;
311 int i = 0;
312
313 int par_depth_ = util::ResolveParDepth(par_depth);
314
315 // Phase 1: tree traversal, store (s, t) packed into ys temporarily
316#pragma omp parallel
317#pragma omp single
318 EvalTree(st, cws, ys, l, r, i, par_depth_);
319
320 // Phase 2: sequential output computation and proof accumulation
321 pi = {cs[0], cs[1], cs[2], cs[3]};
322 size_t n = 1ULL << in_bits;
323 assert((ocw.w & 1) == 0);
324 auto ocw_group = Group::From(ocw);
325 for (size_t j = 0; j < n; ++j) {
326 int4 sj = ys[j];
327 bool tj = util::GetLsb(sj);
328 sj = util::SetLsb(sj, false);
329
330 // Output share
331 auto g = Group::From(sj);
332 if (tj) g = g + ocw_group;
333 if (b) g = -g;
334 ys[j] = g.Into();
335
336 // Proof accumulation
337 int4 x_buf = util::Pack(static_cast<In>(j));
338
339 auto pi_tilde = xor_hash.Hash(cuda::std::tuple<int4, const int4>{x_buf, sj});
340 if (tj) {
341 pi_tilde = util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs));
342 }
343
344 cuda::std::array<int4, 4> h_input =
345 util::Xor(cuda::std::span<const int4, 4>(pi), cuda::std::span<const int4, 4>(pi_tilde));
346 auto h_out = hash.Hash(cuda::std::span<const int4, 4>(h_input));
347 pi[0] = util::Xor(pi[0], h_out[0]);
348 pi[1] = util::Xor(pi[1], h_out[1]);
349 }
350 }
351
352private:
353 void EvalTree(
354 int4 st, cuda::std::span<const Cw> cws, cuda::std::span<int4> ys, size_t l, size_t r, int i, int par_depth_) {
355 if (i == in_bits) {
356 assert(l + 1 == r);
357 ys[l] = st;
358 return;
359 }
360
361 bool t = util::GetLsb(st);
362 int4 s = st;
363 s = util::SetLsb(s, false);
364
365 Cw cw = cws[i];
366 int4 s_cw = cw.s;
367 bool tl_cw = util::GetLsb(s_cw);
368 s_cw = util::SetLsb(s_cw, false);
369 bool tr_cw = cw.tr;
370
371 auto [sl, sr] = prg.Gen(s);
372
373 bool tl = util::GetLsb(sl);
374 sl = util::SetLsb(sl, false);
375 bool tr = util::GetLsb(sr);
376 sr = util::SetLsb(sr, false);
377
378 if (t) {
379 sl = util::Xor(sl, s_cw);
380 sr = util::Xor(sr, s_cw);
381 tl = tl ^ tl_cw;
382 tr = tr ^ tr_cw;
383 }
384
385 int4 stl = sl;
386 stl = util::SetLsb(stl, tl);
387 int4 str = sr;
388 str = util::SetLsb(str, tr);
389
390 size_t mid = (l + r) / 2;
391
392 if (i < par_depth_) {
393#pragma omp task
394 EvalTree(stl, cws, ys, l, mid, i + 1, par_depth_);
395#pragma omp task
396 EvalTree(str, cws, ys, mid, r, i + 1, par_depth_);
397#pragma omp taskwait
398 } else {
399 EvalTree(stl, cws, ys, l, mid, i + 1, par_depth_);
400 EvalTree(str, cws, ys, mid, r, i + 1, par_depth_);
401 }
402 }
403};
404
405} // namespace fss
2-party VDPF scheme.
Definition vdpf.cuh:67
int Gen(Cw cws[], cuda::std::array< int4, 4 > &cs, int4 &ocw, cuda::std::span< const int4, 2 > s0s, In a, int4 b_buf)
Key generation method.
Definition vdpf.cuh:101
void Prove(cuda::std::span< const cuda::std::array< int4, 4 > > pi_tildes, cuda::std::span< const int4, 4 > cs, cuda::std::array< int4, 4 > &pi)
Proof accumulation method.
Definition vdpf.cuh:259
cuda::std::array< int4, 4 > Eval(bool b, int4 s0, cuda::std::span< const Cw > cws, cuda::std::span< const int4, 4 > cs, int4 ocw, In x, int4 &y)
Evaluation method.
Definition vdpf.cuh:195
static bool Verify(cuda::std::span< const int4, 4 > pi0, cuda::std::span< const int4, 4 > pi1)
Verification method.
Definition vdpf.cuh:276
void EvalAll(bool b, int4 s0, cuda::std::span< const Cw > cws, cuda::std::span< const int4, 4 > cs, int4 ocw, cuda::std::span< int4 > ys, cuda::std::array< int4, 4 > &pi)
Full domain evaluation method.
Definition vdpf.cuh:302
Group interface.
Definition group.cuh:40
Collision-resistant hash interface.
Definition hash.cuh:19
Pseudorandom generator (PRG) interface.
Definition prg.cuh:21
Collision-resistant and XOR-collision-resistant hash interface.
Definition hash.cuh:27
Correction word.
Definition vdpf.cuh:81