.gitignore: Ignore `auto-version' script.
[u/mdw/catacomb] / bbs-rand.c
CommitLineData
2c52abe6 1/* -*-c-*-
2 *
2cc3d1d0 3 * $Id$
2c52abe6 4 *
5 * Blum-Blum-Shub secure random number generator
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
45c0fd36 10/*----- Licensing notice --------------------------------------------------*
2c52abe6 11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
45c0fd36 18 *
2c52abe6 19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
45c0fd36 23 *
2c52abe6 24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
2c52abe6 30/*----- Header files ------------------------------------------------------*/
31
32#include <stdarg.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <mLib/bits.h>
37#include <mLib/sub.h>
38
fbdfd34e 39#include "arena.h"
2c52abe6 40#include "bbs.h"
41#include "grand.h"
42#include "mp.h"
43#include "mpbarrett.h"
44#include "mpint.h"
4ab1268f 45#include "mprand.h"
fbdfd34e 46#include "paranoia.h"
2c52abe6 47
48/*----- Main code ---------------------------------------------------------*/
49
50/* --- @bbs_create@ --- *
51 *
52 * Arguments: @bbs *b@ = pointer to BBS generator state to initialize
53 * @mp *m@ = modulus (must be a Blum integer)
54 * @mp *x@ = initial seed for generator
55 *
56 * Returns: ---
57 *
58 * Use: Initializes a BBS generator. The generator is stepped once
59 * after initialization, as for @bbs_seed@.
60 */
61
62void bbs_create(bbs *b, mp *m, mp *x)
63{
64 mpw kw;
65 mp k;
66
67 mpbarrett_create(&b->mb, m);
68 kw = mp_bits(m) - 1;
69 mp_build(&k, &kw, &kw + 1);
70 b->k = mp_bits(&k) - 1;
71 b->x = 0;
72 bbs_seed(b, x);
73}
74
75/* --- @bbs_destroy@ --- *
76 *
77 * Arguments: @bbs *b@ = pointer to BBS generator state
78 *
79 * Returns: ---
80 *
81 * Use: Destroys a generator state when it's no longer wanted.
82 */
83
84void bbs_destroy(bbs *b)
85{
86 mp_drop(b->x);
87 mpbarrett_destroy(&b->mb);
88}
89
90/* --- @bbs_step@ --- *
91 *
92 * Arguments: @bbs *b@ = pointer to BBS generator state
93 *
94 * Returns: ---
95 *
96 * Use: Steps the generator once. This isn't too useful in client
97 * code.
98 */
99
100void bbs_step(bbs *b)
101{
102 mp *x = b->x;
103 x = mp_sqr(x, x);
104 x = mpbarrett_reduce(&b->mb, x, x);
105 b->x = x;
106 b->b = b->k;
107 b->r = b->x->v[0];
108}
109
110/* --- @bbs_set@ --- *
111 *
112 * Arguments: @bbs *b@ = pointer to BBS generator state
113 * @mp *x@ = new residue to set
114 *
115 * Returns: ---
116 *
117 * Use: Sets a new quadratic residue. The generator is stepped once.
118 */
119
120void bbs_set(bbs *b, mp *x)
121{
f1140c41 122 mp_drop(b->x);
2c52abe6 123 b->x = MP_COPY(x);
124 bbs_step(b);
125}
126
127/* --- @bbs_seed@ --- *
128 *
129 * Arguments: @bbs *b@ = pointer to BBS generator state
130 * @mp *x@ = new seed to set
131 *
132 * Returns ---
133 *
134 * Use: Sets a new seed. The generator is stepped until the residue
135 * has clearly wrapped around.
136 */
137
138void bbs_seed(bbs *b, mp *x)
139{
140 mp *y;
141 x = MP_COPY(x);
142 for (;;) {
143 y = mp_sqr(MP_NEW, x);
144 y = mpbarrett_reduce(&b->mb, y, y);
145 if (MP_CMP(y, <, x))
146 break;
147 mp_drop(x);
148 x = y;
149 }
150 mp_drop(x);
151 bbs_set(b, y);
152 mp_drop(y);
153}
154
155/* --- @bbs_bits@ --- *
156 *
157 * Arguments: @bbs *b@ = pointer to BBS generator state
158 * @unsigned bits@ = number of bits wanted
159 *
160 * Returns: Bits extracted from the BBS generator.
161 *
162 * Use: Extracts a requested number of bits from the BBS generator.
163 */
164
165uint32 bbs_bits(bbs *b, unsigned bits)
166{
167 uint32 x = 0;
168 mpw m;
169
170 /* --- Keep turning the handle until there's enough in the reservoir --- */
171
172 while (bits >= b->b) {
173 bits -= b->b;
174 m = (1 << b->b) - 1;
175 x |= (b->r & m) << bits;
176 bbs_step(b);
177 }
178
179 /* --- Extract the last few bits needed --- */
180
181 if (bits) {
182 m = (1 << bits) - 1;
183 b->b -= bits;
184 x |= (b->r >> b->b) & m;
185 }
186
187 /* --- Done --- */
188
189 return (x);
190}
191
192/* --- @bbs_wrap@ --- *
193 *
194 * Arguments: @bbs *b@ = pointer to BBS generator state
195 *
196 * Returns: ---
197 *
198 * Use: Steps the generator if any of the reservoir bits are used.
199 * This can be used to `wrap up' after a Blum-Goldwasser
200 * encryption, for example, producing the final value to be sent
201 * along with the ciphertext.
202 *
203 * If a generator is seeded, %$b$% bits are extracted, and then
204 * @bbs_wrap@ is called, the generator will have been stepped
fbdfd34e 205 * %$\lceil b/k \rceil$% times.
2c52abe6 206 */
207
208void bbs_wrap(bbs *b)
209{
210 if (b->b < b->k)
211 bbs_step(b);
212}
213
214/*----- Generic random number generator interface -------------------------*/
215
216typedef struct gctx {
217 grand r;
218 bbs b;
219} gctx;
220
221static void gdestroy(grand *r)
222{
223 gctx *g = (gctx *)r;
224 bbs_destroy(&g->b);
fbdfd34e 225 BURN(*g);
226 S_DESTROY(g);
2c52abe6 227}
228
229static int gmisc(grand *r, unsigned op, ...)
230{
231 gctx *g = (gctx *)r;
232 va_list ap;
233 int rc = 0;
234 va_start(ap, op);
235
236 switch (op) {
237 case GRAND_CHECK:
238 switch (va_arg(ap, unsigned)) {
45c0fd36
MW
239 case GRAND_CHECK:
240 case GRAND_SEEDINT:
241 case GRAND_SEEDUINT32:
242 case GRAND_SEEDMP:
4ab1268f 243 case GRAND_SEEDRAND:
2c52abe6 244 case BBS_SET:
2cc3d1d0 245 case BBS_STEP:
246 case BBS_STEPSZ:
247 case BBS_BITS:
248 case BBS_WRAP:
249 case BBS_FF:
250 case BBS_FFN:
251 case BBS_REW:
252 case BBS_REWN:
253 case BBS_MOD:
254 case BBS_STATE:
45c0fd36
MW
255 rc = 1;
256 break;
257 default:
258 rc = 0;
259 break;
2c52abe6 260 }
261 break;
262 case GRAND_SEEDINT: {
263 mp *x = mp_fromuint(MP_NEW, va_arg(ap, unsigned));
264 bbs_seed(&g->b, x);
265 mp_drop(x);
266 } break;
267 case GRAND_SEEDUINT32: {
268 mp *x = mp_fromuint32(MP_NEW, va_arg(ap, uint32));
269 bbs_seed(&g->b, x);
270 mp_drop(x);
271 } break;
272 case GRAND_SEEDMP:
273 bbs_seed(&g->b, va_arg(ap, mp *));
274 break;
4ab1268f 275 case GRAND_SEEDRAND: {
276 grand *rr = va_arg(ap, grand *);
277 mp *m = mprand(MP_NEW, mp_bits(g->b.mb.m) - 1, rr, 0);
278 bbs_seed(&g->b, m);
279 mp_drop(m);
280 } break;
2c52abe6 281 case BBS_SET:
282 bbs_set(&g->b, va_arg(ap, mp *));
283 break;
2cc3d1d0 284 case BBS_STEP:
285 bbs_step(&g->b);
286 break;
287 case BBS_STEPSZ:
288 rc = g->b.k;
289 break;
290 case BBS_BITS: {
291 unsigned nb = va_arg(ap, unsigned);
292 uint32 *w = va_arg(ap, uint32 *);
293 *w = bbs_bits(&g->b, nb);
294 } break;
295 case BBS_WRAP:
296 bbs_wrap(&g->b);
297 break;
298 case BBS_FF: {
299 const bbs_priv *bp = va_arg(ap, const bbs_priv *);
300 mp *n = va_arg(ap, mp *);
301 bbs_ff(&g->b, bp, n);
302 } break;
303 case BBS_FFN: {
304 const bbs_priv *bp = va_arg(ap, const bbs_priv *);
305 unsigned long n = va_arg(ap, unsigned long);
306 bbs_ffn(&g->b, bp, n);
307 } break;
308 case BBS_REW: {
309 const bbs_priv *bp = va_arg(ap, const bbs_priv *);
310 mp *n = va_arg(ap, mp *);
311 bbs_rew(&g->b, bp, n);
312 } break;
313 case BBS_REWN: {
314 const bbs_priv *bp = va_arg(ap, const bbs_priv *);
315 unsigned long n = va_arg(ap, unsigned long);
316 bbs_rewn(&g->b, bp, n);
317 } break;
318 case BBS_MOD: {
319 mp **n = va_arg(ap, mp **);
320 if (*n) MP_DROP(*n);
321 *n = MP_COPY(g->b.mb.m);
322 } break;
323 case BBS_STATE: {
324 mp **n = va_arg(ap, mp **);
325 if (*n) MP_DROP(*n);
326 *n = MP_COPY(g->b.x);
327 } break;
2c52abe6 328 default:
329 GRAND_BADOP;
330 break;
331 }
332
333 va_end(ap);
334 return (rc);
335}
336
337static octet gbyte(grand *r)
338{
339 gctx *g = (gctx *)r;
340 return (bbs_bits(&g->b, 8));
341}
342
343static uint32 gword(grand *r)
344{
345 gctx *g = (gctx *)r;
346 return (bbs_bits(&g->b, 32));
347}
348
349static const grand_ops gops = {
350 "bbs",
fbdfd34e 351 GRAND_CRYPTO, 0,
2c52abe6 352 gmisc, gdestroy,
353 gword, gbyte, gword, grand_range, grand_fill
354};
355
356/* --- @bbs_rand@ --- *
357 *
45c0fd36 358 * Arguments: @mp *m@ = modulus
2c52abe6 359 * @mp *x@ = initial seed
360 *
45c0fd36 361 * Returns: Pointer to a generic generator.
2c52abe6 362 *
45c0fd36 363 * Use: Constructs a generic generator interface over a
2c52abe6 364 * Blum-Blum-Shub generator.
365 */
366
367grand *bbs_rand(mp *m, mp *x)
368{
fbdfd34e 369 gctx *g = S_CREATE(gctx);
2c52abe6 370 g->r.ops = &gops;
371 bbs_create(&g->b, m, x);
372 return (&g->r);
373}
374
375/*----- Test rig ----------------------------------------------------------*/
376
377#ifdef TEST_RIG
378
379static int verify(dstr *v)
380{
381 mp *n = *(mp **)v[0].buf;
382 mp *x = *(mp **)v[1].buf;
383 grand *b = bbs_rand(n, x);
384 dstr d = DSTR_INIT;
385 int ok = 1;
386
387 dstr_ensure(&d, v[2].len);
388 b->ops->fill(b, d.buf, v[2].len);
389 d.len = v[2].len;
390 if (memcmp(d.buf, v[2].buf, v[2].len) != 0) {
391 fputs("\n*** bbs failure\n", stderr);
392 fputs("n = ", stderr); mp_writefile(n, stderr, 10); fputc('\n', stderr);
393 fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
394 fputs("expected = ", stderr); type_hex.dump(&v[2], stderr);
395 fputc('\n', stderr);
396 fputs(" found = ", stderr); type_hex.dump(&d, stderr);
397 fputc('\n', stderr);
398 fprintf(stderr, "k = %u\n", ((gctx *)b)->b.k);
399 ok = 0;
400 }
401 b->ops->destroy(b);
402 mp_drop(x);
403 mp_drop(n);
404 dstr_destroy(&d);
405 assert(mparena_count(MPARENA_GLOBAL) == 0);
406 return (ok);
407}
408
409static test_chunk tests[] = {
410 { "bbs", verify, { &type_mp, &type_mp, &type_hex, 0 } },
411 { 0, 0, { 0 } }
412};
413
414int main(int argc, char *argv[])
415{
416 sub_init();
417 test_run(argc, argv, tests, SRCDIR "/tests/bbs");
418 return (0);
419}
420
421#endif
422
423/*----- That's all, folks -------------------------------------------------*/