myl7/fss 1.2.0
Function secret sharing (FSS) primitives including distributed point/comparison function (DPF/DCF)
Loading...
Searching...
No Matches
dpf.cuh
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
39#pragma once
40#include <cuda_runtime.h>
41#include <type_traits>
42#include <cstddef>
43#include <cassert>
44#include <omp.h>
45#include <fss/group.cuh>
46#include <fss/prg.cuh>
47#include <fss/util.cuh>
48
49namespace fss {
50
61template <int in_bits, typename Group, typename Prg, typename In = uint, int par_depth = -1>
62 requires((std::is_unsigned_v<In> || std::is_same_v<In, __uint128_t>) && in_bits <= sizeof(In) * 8 &&
64class Dpf {
65public:
66 Prg prg;
67
76 struct __align__(32) Cw {
77 int4 s;
78 bool tr;
79 };
80 // For only 1 and aligned memory access on GPU
81 static_assert(sizeof(Cw) == 32);
82
93 __host__ __device__ void Gen(Cw cws[], const int4 s0s[2], In a, int4 b_buf) {
94 int4 s0 = s0s[0];
95 s0 = util::SetLsb(s0, false);
96 int4 s1 = s0s[1];
97 s1 = util::SetLsb(s1, false);
98 bool t0 = false;
99 bool t1 = true;
100 b_buf = util::SetLsb(b_buf, false);
101
102 for (int i = 0; i < in_bits; ++i) {
103 auto [s0l, s0r] = prg.Gen(s0);
104 auto [s1l, s1r] = prg.Gen(s1);
105
106 bool t0l = util::GetLsb(s0l);
107 s0l = util::SetLsb(s0l, false);
108 bool t0r = util::GetLsb(s0r);
109 s0r = util::SetLsb(s0r, false);
110 bool t1l = util::GetLsb(s1l);
111 s1l = util::SetLsb(s1l, false);
112 bool t1r = util::GetLsb(s1r);
113 s1r = util::SetLsb(s1r, false);
114
115 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
116
117 int4 s_cw;
118 if (!a_bit) s_cw = util::Xor(s0r, s1r);
119 else s_cw = util::Xor(s0l, s1l);
120
121 bool tl_cw = t0l ^ t1l ^ a_bit ^ 1;
122 bool tr_cw = t0r ^ t1r ^ a_bit;
123
124 if (!a_bit) {
125 s0 = s0l;
126 if (t0) s0 = util::Xor(s0, s_cw);
127 s1 = s1l;
128 if (t1) s1 = util::Xor(s1, s_cw);
129
130 if (t0) t0 = t0l ^ tl_cw;
131 else t0 = t0l;
132 if (t1) t1 = t1l ^ tl_cw;
133 else t1 = t1l;
134 } else {
135 s0 = s0r;
136 if (t0) s0 = util::Xor(s0, s_cw);
137 s1 = s1r;
138 if (t1) s1 = util::Xor(s1, s_cw);
139
140 if (t0) t0 = t0r ^ tr_cw;
141 else t0 = t0r;
142 if (t1) t1 = t1r ^ tr_cw;
143 else t1 = t1r;
144 }
145
146 s_cw = util::SetLsb(s_cw, tl_cw);
147 // Write both 16B halves so the full 32B slot is covered by unmasked stores.
148 // {s_cw, tr_cw} alone leaves the padding undefined: NVCC emits a masked
149 // 256-bit store, and the partial-sector write forces a write-allocate
150 // round trip per level on GPU (measured ~2x Gen time).
151 int4 *cw_slot = reinterpret_cast<int4 *>(&cws[i]);
152 cw_slot[0] = s_cw;
153 cw_slot[1] = make_int4(tr_cw, 0, 0, 0);
154 }
155
156 auto v_cw_np1 = Group::From(b_buf) + (-Group::From(s0)) + Group::From(s1);
157 if (t1) v_cw_np1 = -v_cw_np1;
158 cws[in_bits] = {v_cw_np1.Into(), false};
159 }
160
170 __host__ __device__ int4 Eval(bool b, int4 s0, const Cw cws[], In x) {
171 int4 s = s0;
172 s = util::SetLsb(s, false);
173 bool t = b;
174
175 for (int i = 0; i < in_bits; ++i) {
176 Cw cw = cws[i];
177 int4 s_cw = cw.s;
178 bool tl_cw = util::GetLsb(s_cw);
179 s_cw = util::SetLsb(s_cw, false);
180 bool tr_cw = cw.tr;
181
182 auto [sl, sr] = prg.Gen(s);
183
184 bool tl = util::GetLsb(sl);
185 sl = util::SetLsb(sl, false);
186 bool tr = util::GetLsb(sr);
187 sr = util::SetLsb(sr, false);
188
189 if (t) {
190 sl = util::Xor(sl, s_cw);
191 sr = util::Xor(sr, s_cw);
192 tl = tl ^ tl_cw;
193 tr = tr ^ tr_cw;
194 }
195
196 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
197
198 if (!x_bit) {
199 s = sl;
200 t = tl;
201 } else {
202 s = sr;
203 t = tr;
204 }
205 }
206
207 auto y = Group::From(s);
208 int4 v_cw_np1 = cws[in_bits].s;
209 assert((v_cw_np1.w & 1) == 0);
210 if (t) y = y + Group::From(v_cw_np1);
211 if (b) y = -y;
212
213 return y.Into();
214 }
215
232 void EvalAll(bool b, int4 s0, const Cw cws[], int4 ys[]) {
233 int4 st = s0;
234 bool t = b;
235 st = util::SetLsb(st, t);
236
237 assert(in_bits < sizeof(size_t) * 8);
238 size_t l = 0;
239 size_t r = 1ULL << in_bits;
240 int i = 0;
241
242 int par_depth_ = util::ResolveParDepth(par_depth);
243
244#pragma omp parallel
245#pragma omp single
246 EvalTree(b, st, cws, ys, l, r, i, par_depth_);
247 }
248
249private:
250 void EvalTree(bool b, int4 st, const Cw cws[], int4 ys[], size_t l, size_t r, int i, int par_depth_) {
251 bool t = util::GetLsb(st);
252 int4 s = st;
253 s = util::SetLsb(s, false);
254
255 if (i == in_bits) {
256 auto y = Group::From(s);
257 int4 v_cw_np1 = cws[in_bits].s;
258 assert((v_cw_np1.w & 1) == 0);
259 if (t) y = y + Group::From(v_cw_np1);
260 if (b) y = -y;
261 assert(l + 1 == r);
262 ys[l] = y.Into();
263 return;
264 }
265
266 Cw cw = cws[i];
267 int4 s_cw = cw.s;
268 bool tl_cw = util::GetLsb(s_cw);
269 s_cw = util::SetLsb(s_cw, false);
270 bool tr_cw = cw.tr;
271
272 auto [sl, sr] = prg.Gen(s);
273
274 bool tl = util::GetLsb(sl);
275 sl = util::SetLsb(sl, false);
276 bool tr = util::GetLsb(sr);
277 sr = util::SetLsb(sr, false);
278
279 if (t) {
280 sl = util::Xor(sl, s_cw);
281 sr = util::Xor(sr, s_cw);
282 tl = tl ^ tl_cw;
283 tr = tr ^ tr_cw;
284 }
285
286 int4 stl = sl;
287 stl = util::SetLsb(stl, tl);
288 int4 str = sr;
289 str = util::SetLsb(str, tr);
290
291 size_t mid = (l + r) / 2;
292
293 if (i < par_depth_) {
294#pragma omp task
295 EvalTree(b, stl, cws, ys, l, mid, i + 1, par_depth_);
296#pragma omp task
297 EvalTree(b, str, cws, ys, mid, r, i + 1, par_depth_);
298#pragma omp taskwait
299 } else {
300 EvalTree(b, stl, cws, ys, l, mid, i + 1, par_depth_);
301 EvalTree(b, str, cws, ys, mid, r, i + 1, par_depth_);
302 }
303 }
304};
305
306} // namespace fss
2-party DPF scheme.
Definition dpf.cuh:64
int4 Eval(bool b, int4 s0, const Cw cws[], In x)
Evaluation method.
Definition dpf.cuh:170
void EvalAll(bool b, int4 s0, const Cw cws[], int4 ys[])
Full domain evaluation method.
Definition dpf.cuh:232
void Gen(Cw cws[], const int4 s0s[2], In a, int4 b_buf)
Key generation method.
Definition dpf.cuh:93
Group interface.
Definition group.cuh:40
Pseudorandom generator (PRG) interface.
Definition prg.cuh:21
Correction word.
Definition dpf.cuh:76