Renamed from `rsa-decrypt', since the name was no longer appropriate.
[u/mdw/catacomb] / bbs-rand.c
1 /* -*-c-*-
2 *
3 * $Id: bbs-rand.c,v 1.3 2000/06/17 10:45:21 mdw Exp $
4 *
5 * Blum-Blum-Shub secure random number generator
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
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.
18 *
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.
23 *
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
30 /*----- Revision history --------------------------------------------------*
31 *
32 * $Log: bbs-rand.c,v $
33 * Revision 1.3 2000/06/17 10:45:21 mdw
34 * Typesetting fixes. Advertise random number generator strength. Use
35 * secure arena for memory allocation.
36 *
37 * Revision 1.2 1999/12/13 15:34:01 mdw
38 * Add support for seeding from a generic pseudorandom source.
39 *
40 * Revision 1.1 1999/12/10 23:14:59 mdw
41 * Blum-Blum-Shub generator, and Blum-Goldwasser encryption.
42 *
43 */
44
45 /*----- Header files ------------------------------------------------------*/
46
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include <mLib/bits.h>
52 #include <mLib/sub.h>
53
54 #include "arena.h"
55 #include "bbs.h"
56 #include "grand.h"
57 #include "mp.h"
58 #include "mpbarrett.h"
59 #include "mpint.h"
60 #include "mprand.h"
61 #include "paranoia.h"
62
63 /*----- Main code ---------------------------------------------------------*/
64
65 /* --- @bbs_create@ --- *
66 *
67 * Arguments: @bbs *b@ = pointer to BBS generator state to initialize
68 * @mp *m@ = modulus (must be a Blum integer)
69 * @mp *x@ = initial seed for generator
70 *
71 * Returns: ---
72 *
73 * Use: Initializes a BBS generator. The generator is stepped once
74 * after initialization, as for @bbs_seed@.
75 */
76
77 void bbs_create(bbs *b, mp *m, mp *x)
78 {
79 mpw kw;
80 mp k;
81
82 mpbarrett_create(&b->mb, m);
83 kw = mp_bits(m) - 1;
84 mp_build(&k, &kw, &kw + 1);
85 b->k = mp_bits(&k) - 1;
86 b->x = 0;
87 bbs_seed(b, x);
88 }
89
90 /* --- @bbs_destroy@ --- *
91 *
92 * Arguments: @bbs *b@ = pointer to BBS generator state
93 *
94 * Returns: ---
95 *
96 * Use: Destroys a generator state when it's no longer wanted.
97 */
98
99 void bbs_destroy(bbs *b)
100 {
101 mp_drop(b->x);
102 mpbarrett_destroy(&b->mb);
103 }
104
105 /* --- @bbs_step@ --- *
106 *
107 * Arguments: @bbs *b@ = pointer to BBS generator state
108 *
109 * Returns: ---
110 *
111 * Use: Steps the generator once. This isn't too useful in client
112 * code.
113 */
114
115 void bbs_step(bbs *b)
116 {
117 mp *x = b->x;
118 x = mp_sqr(x, x);
119 x = mpbarrett_reduce(&b->mb, x, x);
120 b->x = x;
121 b->b = b->k;
122 b->r = b->x->v[0];
123 }
124
125 /* --- @bbs_set@ --- *
126 *
127 * Arguments: @bbs *b@ = pointer to BBS generator state
128 * @mp *x@ = new residue to set
129 *
130 * Returns: ---
131 *
132 * Use: Sets a new quadratic residue. The generator is stepped once.
133 */
134
135 void bbs_set(bbs *b, mp *x)
136 {
137 if (b->x)
138 mp_drop(b->x);
139 b->x = MP_COPY(x);
140 bbs_step(b);
141 }
142
143 /* --- @bbs_seed@ --- *
144 *
145 * Arguments: @bbs *b@ = pointer to BBS generator state
146 * @mp *x@ = new seed to set
147 *
148 * Returns ---
149 *
150 * Use: Sets a new seed. The generator is stepped until the residue
151 * has clearly wrapped around.
152 */
153
154 void bbs_seed(bbs *b, mp *x)
155 {
156 mp *y;
157 x = MP_COPY(x);
158 for (;;) {
159 y = mp_sqr(MP_NEW, x);
160 y = mpbarrett_reduce(&b->mb, y, y);
161 if (MP_CMP(y, <, x))
162 break;
163 mp_drop(x);
164 x = y;
165 }
166 mp_drop(x);
167 bbs_set(b, y);
168 mp_drop(y);
169 }
170
171 /* --- @bbs_bits@ --- *
172 *
173 * Arguments: @bbs *b@ = pointer to BBS generator state
174 * @unsigned bits@ = number of bits wanted
175 *
176 * Returns: Bits extracted from the BBS generator.
177 *
178 * Use: Extracts a requested number of bits from the BBS generator.
179 */
180
181 uint32 bbs_bits(bbs *b, unsigned bits)
182 {
183 uint32 x = 0;
184 mpw m;
185
186 /* --- Keep turning the handle until there's enough in the reservoir --- */
187
188 while (bits >= b->b) {
189 bits -= b->b;
190 m = (1 << b->b) - 1;
191 x |= (b->r & m) << bits;
192 bbs_step(b);
193 }
194
195 /* --- Extract the last few bits needed --- */
196
197 if (bits) {
198 m = (1 << bits) - 1;
199 b->b -= bits;
200 x |= (b->r >> b->b) & m;
201 }
202
203 /* --- Done --- */
204
205 return (x);
206 }
207
208 /* --- @bbs_wrap@ --- *
209 *
210 * Arguments: @bbs *b@ = pointer to BBS generator state
211 *
212 * Returns: ---
213 *
214 * Use: Steps the generator if any of the reservoir bits are used.
215 * This can be used to `wrap up' after a Blum-Goldwasser
216 * encryption, for example, producing the final value to be sent
217 * along with the ciphertext.
218 *
219 * If a generator is seeded, %$b$% bits are extracted, and then
220 * @bbs_wrap@ is called, the generator will have been stepped
221 * %$\lceil b/k \rceil$% times.
222 */
223
224 void bbs_wrap(bbs *b)
225 {
226 if (b->b < b->k)
227 bbs_step(b);
228 }
229
230 /*----- Generic random number generator interface -------------------------*/
231
232 typedef struct gctx {
233 grand r;
234 bbs b;
235 } gctx;
236
237 static void gdestroy(grand *r)
238 {
239 gctx *g = (gctx *)r;
240 bbs_destroy(&g->b);
241 BURN(*g);
242 S_DESTROY(g);
243 }
244
245 static int gmisc(grand *r, unsigned op, ...)
246 {
247 gctx *g = (gctx *)r;
248 va_list ap;
249 int rc = 0;
250 va_start(ap, op);
251
252 switch (op) {
253 case GRAND_CHECK:
254 switch (va_arg(ap, unsigned)) {
255 case GRAND_CHECK:
256 case GRAND_SEEDINT:
257 case GRAND_SEEDUINT32:
258 case GRAND_SEEDMP:
259 case GRAND_SEEDRAND:
260 case BBS_SET:
261 rc = 1;
262 break;
263 default:
264 rc = 0;
265 break;
266 }
267 break;
268 case GRAND_SEEDINT: {
269 mp *x = mp_fromuint(MP_NEW, va_arg(ap, unsigned));
270 bbs_seed(&g->b, x);
271 mp_drop(x);
272 } break;
273 case GRAND_SEEDUINT32: {
274 mp *x = mp_fromuint32(MP_NEW, va_arg(ap, uint32));
275 bbs_seed(&g->b, x);
276 mp_drop(x);
277 } break;
278 case GRAND_SEEDMP:
279 bbs_seed(&g->b, va_arg(ap, mp *));
280 break;
281 case GRAND_SEEDRAND: {
282 grand *rr = va_arg(ap, grand *);
283 mp *m = mprand(MP_NEW, mp_bits(g->b.mb.m) - 1, rr, 0);
284 bbs_seed(&g->b, m);
285 mp_drop(m);
286 } break;
287 case BBS_SET:
288 bbs_set(&g->b, va_arg(ap, mp *));
289 break;
290 default:
291 GRAND_BADOP;
292 break;
293 }
294
295 va_end(ap);
296 return (rc);
297 }
298
299 static octet gbyte(grand *r)
300 {
301 gctx *g = (gctx *)r;
302 return (bbs_bits(&g->b, 8));
303 }
304
305 static uint32 gword(grand *r)
306 {
307 gctx *g = (gctx *)r;
308 return (bbs_bits(&g->b, 32));
309 }
310
311 static const grand_ops gops = {
312 "bbs",
313 GRAND_CRYPTO, 0,
314 gmisc, gdestroy,
315 gword, gbyte, gword, grand_range, grand_fill
316 };
317
318 /* --- @bbs_rand@ --- *
319 *
320 * Arguments: @mp *m@ = modulus
321 * @mp *x@ = initial seed
322 *
323 * Returns: Pointer to a generic generator.
324 *
325 * Use: Constructs a generic generator interface over a
326 * Blum-Blum-Shub generator.
327 */
328
329 grand *bbs_rand(mp *m, mp *x)
330 {
331 gctx *g = S_CREATE(gctx);
332 g->r.ops = &gops;
333 bbs_create(&g->b, m, x);
334 return (&g->r);
335 }
336
337 /*----- Test rig ----------------------------------------------------------*/
338
339 #ifdef TEST_RIG
340
341 static int verify(dstr *v)
342 {
343 mp *n = *(mp **)v[0].buf;
344 mp *x = *(mp **)v[1].buf;
345 grand *b = bbs_rand(n, x);
346 dstr d = DSTR_INIT;
347 int ok = 1;
348
349 dstr_ensure(&d, v[2].len);
350 b->ops->fill(b, d.buf, v[2].len);
351 d.len = v[2].len;
352 if (memcmp(d.buf, v[2].buf, v[2].len) != 0) {
353 fputs("\n*** bbs failure\n", stderr);
354 fputs("n = ", stderr); mp_writefile(n, stderr, 10); fputc('\n', stderr);
355 fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
356 fputs("expected = ", stderr); type_hex.dump(&v[2], stderr);
357 fputc('\n', stderr);
358 fputs(" found = ", stderr); type_hex.dump(&d, stderr);
359 fputc('\n', stderr);
360 fprintf(stderr, "k = %u\n", ((gctx *)b)->b.k);
361 ok = 0;
362 }
363 b->ops->destroy(b);
364 mp_drop(x);
365 mp_drop(n);
366 dstr_destroy(&d);
367 assert(mparena_count(MPARENA_GLOBAL) == 0);
368 return (ok);
369 }
370
371 static test_chunk tests[] = {
372 { "bbs", verify, { &type_mp, &type_mp, &type_hex, 0 } },
373 { 0, 0, { 0 } }
374 };
375
376 int main(int argc, char *argv[])
377 {
378 sub_init();
379 test_run(argc, argv, tests, SRCDIR "/tests/bbs");
380 return (0);
381 }
382
383 #endif
384
385 /*----- That's all, folks -------------------------------------------------*/