Generate precomputed tables as sources in `precomps/'.
[u/mdw/catacomb] / misc / gfshare.c
1 /* -*-c-*-
2 *
3 * Secret sharing over %$\gf{2^8}$%
4 *
5 * (c) 2000 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <assert.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <mLib/alloc.h>
36 #include <mLib/bits.h>
37
38 #include "arena.h"
39 #include "gfshare.h"
40 #include "grand.h"
41
42 /*----- Static variables --------------------------------------------------*/
43
44 extern const octet gfshare_log[256], gfshare_exp[510];
45
46 /*----- Main code ---------------------------------------------------------*/
47
48 /* --- @gfshare_create@ --- *
49 *
50 * Arguments: @gfshare *s@ = pointer to share context to initialize
51 * @unsigned t@ = threshold for the system
52 * @size_t sz@ = size of the secret
53 *
54 * Returns: ---
55 *
56 * Use: Initializes a sharing context.
57 */
58
59 void gfshare_create(gfshare *s, unsigned t, size_t sz)
60 {
61 s->t = t;
62 s->i = 0;
63 s->sz = sz;
64 s->v = 0;
65 }
66
67 /* --- @gfshare_destroy@ --- *
68 *
69 * Arguments: @gfshare *s@ = pointer to share context to destroy
70 *
71 * Returns: ---
72 *
73 * Use: Disposes of a sharing context. The allocations for the
74 * individual shares and the vector @v@ are freed; the secret is
75 * left alone.
76 */
77
78 void gfshare_destroy(gfshare *s)
79 {
80 if (s->v)
81 XS_FREE(s->v);
82 }
83
84 /* --- @gfshare_mkshares@ --- *
85 *
86 * Arguments: @gfshare *s@ = pointer to share context to fill in
87 * @grand *r@ = pointer to random number source
88 * @const void *buf@ = pointer to the secret to share
89 *
90 * Returns: ---
91 *
92 * Use: Initializes a sharing context to be able to create shares.
93 * The context structure is expected to be mostly filled in. In
94 * particular, @t@ must be initialized. If @v@ is zero, a
95 * vector of appropriate size is allocated. You should use the
96 * macro @GFSHARE_INIT@ or @gfshare_create@ to construct sharing
97 * contexts.
98 */
99
100 void gfshare_mkshares(gfshare *s, grand *r, const void *buf)
101 {
102 s->v = XS_ALLOC(s->sz * s->t);
103 r->ops->fill(r, s->v, s->sz * (s->t - 1));
104 memcpy(s->v + s->sz * (s->t - 1), buf, s->sz);
105 }
106
107 /* --- @gfshare_get@ --- *
108 *
109 * Arguments: @gfshare *s@ = pointer to share conext
110 * @unsigned x@ = share index to fetch
111 * @void *buf@ = pointer to output buffer
112 *
113 * Returns: ---
114 *
115 * Use: Extracts a share from the system. You may extract up to 255
116 * shares from the system. Shares are indexed from 0.
117 */
118
119 void gfshare_get(gfshare *s, unsigned x, void *buf)
120 {
121 unsigned i;
122 const octet *p = s->v;
123 unsigned ilog = gfshare_log[x + 1];
124
125 /* --- Evaluate the polynomial at %$x = i + 1$% --- */
126
127 memcpy(buf, p, s->sz);
128 p += s->sz;
129
130 for (i = 1; i < s->t; i++) {
131 octet *q = buf;
132 unsigned k;
133 for (k = 0; k < s->sz; k++) {
134 unsigned qq = *q;
135 if (qq)
136 qq = gfshare_exp[ilog + gfshare_log[qq]];
137 *q++ = qq ^ *p++;
138 }
139 }
140 }
141
142 /* --- @gfshare_addedp@ --- *
143 *
144 * Arguments: @gfshare *s@ = pointer to sharing context
145 * @unsigned x@ = which share number to check
146 *
147 * Returns: Nonzero if share @x@ has been added already, zero if it
148 * hasn't.
149 */
150
151 int gfshare_addedp(gfshare *s, unsigned x)
152 {
153 unsigned i;
154
155 for (i = 0; i < s->i; i++) {
156 if (GFSHARE_INDEX(s, i) == x + 1)
157 return (1);
158 }
159 return (0);
160 }
161
162 /* --- @gfshare_add@ --- *
163 *
164 * Arguments: @gfshare *s@ = pointer to sharing context
165 * @unsigned x@ = which share number this is
166 * @const void *y@ = the share value
167 *
168 * Returns: Number of shares required before recovery may be performed.
169 *
170 * Use: Adds a share to the context. The context must have been
171 * initialized with the correct threshold @t@.
172 */
173
174 unsigned gfshare_add(gfshare *s, unsigned x, const void *y)
175 {
176 octet *p;
177
178 assert(((void)"Share context is full", s->i < s->t));
179 assert(((void)"Share already present", !gfshare_addedp(s, x)));
180
181 /* --- If no vector has been allocated, create one --- */
182
183 if (!s->v) {
184 s->v = XS_ALLOC(s->t * (s->sz + 1));
185 s->i = 0;
186 }
187
188 /* --- Store the share in the vector --- */
189
190 p = &GFSHARE_INDEX(s, s->i);
191 *p++ = x + 1;
192 memcpy(p, y, s->sz);
193 s->i++;
194
195 /* --- Done --- */
196
197 return (s->t - s->i);
198 }
199
200 /* --- @gfshare_combine@ --- *
201 *
202 * Arguments: @gfshare *s@ = pointer to share context
203 * @void *buf@ = pointer to output buffer for the secret
204 *
205 * Returns: ---
206 *
207 * Use: Reconstructs a secret, given enough shares.
208 */
209
210 void gfshare_combine(gfshare *s, void *buf)
211 {
212 unsigned i, j;
213 unsigned xi, xj;
214
215 /* --- Sanity checking --- */
216
217 assert(((void)"Not enough shares yet", s->i == s->t));
218
219 /* --- Grind through the shares --- */
220
221 memset(buf, 0, s->sz);
222
223 for (i = 0; i < s->t; i++) {
224 octet *p = buf;
225 octet *q = &GFSHARE_INDEX(s, i);
226 unsigned c = 0, ci = 0;
227
228 /* --- Compute the magic coefficient --- */
229
230 xi = *q++;
231 for (j = 0; j < s->t; j++) {
232 if (i == j)
233 continue;
234 xj = GFSHARE_INDEX(s, j);
235 c += gfshare_log[xj];
236 if (c >= 0xff)
237 c -= 0xff;
238 ci += gfshare_log[xi ^ xj];
239 if (ci >= 0xff)
240 ci -= 0xff;
241 }
242 if (ci > c)
243 c += 0xff;
244 c -= ci;
245
246 /* --- Work out another layer of the secret --- */
247
248 p = buf;
249 for (j = 0; j < s->sz; j++) {
250 if (*q)
251 *p ^= gfshare_exp[c + gfshare_log[*q]];
252 p++, q++;
253 }
254 }
255 }
256
257 /*----- Test rig ----------------------------------------------------------*/
258
259 #ifdef TEST_RIG
260
261 #include "fibrand.h"
262
263 static int verify(grand *r)
264 {
265 unsigned n = r->ops->range(r, 16) + 8;
266 unsigned t = r->ops->range(r, n - 1) + 1;
267 unsigned len = r->ops->range(r, 32) + 1;
268
269 octet *v = xmalloc(t * len);
270 unsigned *p = xmalloc(n * sizeof(unsigned));
271 octet *sec = xmalloc(len), *sbuf = xmalloc(len);
272 gfshare s;
273 unsigned i;
274
275 int ok = 1;
276
277 for (i = 0; i < n; i++)
278 p[i] = i;
279 for (i = 0; i < t; i++) {
280 unsigned long j = r->ops->range(r, n - i);
281 unsigned x = p[i];
282 p[i] = p[i + j];
283 p[i + j] = x;
284 }
285
286 r->ops->fill(r, sec, len);
287
288 gfshare_create(&s, t, len);
289
290 gfshare_mkshares(&s, r, sec);
291 for (i = 0; i < t; i++)
292 gfshare_get(&s, p[i], v + (i * len));
293 gfshare_destroy(&s);
294
295 gfshare_create(&s, t, len);
296 for (i = 0; i < t; i++)
297 gfshare_add(&s, p[i], v + (i * len));
298 gfshare_combine(&s, sbuf);
299 gfshare_destroy(&s);
300
301 if (memcmp(sec, sbuf, len) != 0) {
302 ok = 0;
303 fprintf(stderr, "\nbad recombination of shares\n");
304 };
305
306 xfree(sec);
307 xfree(sbuf);
308
309 xfree(v);
310 xfree(p);
311
312 return (ok);
313 }
314
315 int main(void)
316 {
317 grand *r = fibrand_create(0);
318 unsigned i;
319 int ok = 1;
320
321 fputs("gfshare: ", stdout);
322 for (i = 0; i < 40; i++) {
323 if (!verify(r))
324 ok = 0;
325 fputc('.', stdout);
326 fflush(stdout);
327 }
328
329 if (ok)
330 fputs(" ok\n", stdout);
331 else
332 fputs(" failed\n", stdout);
333 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
334 }
335
336 #endif
337
338 /*----- That's all, folks -------------------------------------------------*/