chiark / gitweb /
rand/dsarand.c: Return the old number of passes from `DSARAND_PASSES'.
[catacomb] / rand / dsarand.c
1 /* -*-c-*-
2  *
3  * Random number generator for DSA
4  *
5  * (c) 1999 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 <stdarg.h>
31 #include <string.h>
32
33 #include <mLib/alloc.h>
34 #include <mLib/bits.h>
35 #include <mLib/sub.h>
36
37 #include "dsarand.h"
38 #include "grand.h"
39 #include "sha.h"
40
41 /*----- Main code ---------------------------------------------------------*/
42
43 /* --- @STEP@ --- *
44  *
45  * Arguments:   @dsarand *d@ = pointer to context
46  *
47  * Use:         Increments the buffer by one, interpreting it as a big-endian
48  *              integer.  Carries outside the integer are discarded.
49  */
50
51 #define STEP(d) do {                                                    \
52   dsarand *_d = (d);                                                    \
53   octet *_p = _d->p;                                                    \
54   octet *_q = _p + _d->sz;                                              \
55   unsigned _c = 1;                                                      \
56   while (_c && _q > _p) {                                               \
57     _c += *--_q;                                                        \
58     *_q = U8(_c);                                                       \
59     _c >>= 8;                                                           \
60   }                                                                     \
61 } while (0)
62
63 /* --- @dsarand_init@ --- *
64  *
65  * Arguments:   @dsarand *d@ = pointer to context
66  *              @const void *p@ = pointer to seed buffer
67  *              @size_t sz@ = size of the buffer
68  *
69  * Returns:     ---
70  *
71  * Use:         Initializes a DSA random number generator.
72  */
73
74 void dsarand_init(dsarand *d, const void *p, size_t sz)
75 {
76   d->p = xmalloc(sz);
77   d->sz = sz;
78   d->passes = 1;
79   if (p)
80     memcpy(d->p, p, sz);
81 }
82
83 /* --- @dsarand_reseed@ --- *
84  *
85  * Arguments:   @dsarand *d@ = pointer to context
86  *              @const void *p@ = pointer to seed buffer
87  *              @size_t sz@ = size of the buffer
88  *
89  * Returns:     ---
90  *
91  * Use:         Initializes a DSA random number generator.
92  */
93
94 void dsarand_reseed(dsarand *d, const void *p, size_t sz)
95 {
96   xfree(d->p);
97   d->p = xmalloc(sz);
98   d->sz = sz;
99   d->passes = 1;
100   if (p)
101     memcpy(d->p, p, sz);
102 }
103
104 /* --- @dsarand_destroy@ --- *
105  *
106  * Arguments:   @dsarand *d@ = pointer to context
107  *
108  * Returns:     ---
109  *
110  * Use:         Disposes of a DSA random number generation context.
111  */
112
113 void dsarand_destroy(dsarand *d)
114 {
115   xfree(d->p);
116 }
117
118 /* --- @dsarand_fill@ --- *
119  *
120  * Arguments:   @dsarand *d@ = pointer to context
121  *              @void *p@ = pointer to output buffer
122  *              @size_t sz@ = size of output buffer
123  *
124  * Returns:     ---
125  *
126  * Use:         Fills an output buffer with pseudorandom data.
127  *
128  *              Let %$p$% be the numerical value of the input buffer, and let
129  *              %$b$% be the number of bytes required.  Let
130  *              %$z = \lceil b / 20 \rceil$% be the number of SHA outputs
131  *              required.  Then the output of pass %$n$% is
132  *
133  *                %$P_n = \sum_{0 \le i < z} 2^{160i} SHA(p + nz + i)$%
134  *                                                      %${} \bmod 2^{8b}$%
135  *
136  *              and the actual result in the output buffer is the XOR of all
137  *              of the output passes.
138  *
139  *              The DSA procedure for choosing @q@ involves two passes with
140  *              %$z = 1$%; the procedure for choosing @p@ involves one pass
141  *              with larger %$z$%.  This generalization of the DSA generation
142  *              procedure is my own invention but it seems relatively sound.
143  */
144
145 void dsarand_fill(dsarand *d, void *p, size_t sz)
146 {
147   octet *q = p;
148   unsigned n = d->passes;
149
150   /* --- Write out the first pass --- *
151    *
152    * This can write directly to the output buffer, so it's done differently
153    * from the latter passes.
154    */
155
156   {
157     size_t o = sz;
158
159     while (o) {
160       sha_ctx h;
161
162       /* --- Hash the input buffer --- */
163
164       sha_init(&h);
165       sha_hash(&h, d->p, d->sz);
166
167       /* --- If enough space, extract the hash output directly --- */
168
169       if (o >= SHA_HASHSZ) {
170         o -= SHA_HASHSZ;
171         sha_done(&h, q + o);
172       }
173
174       /* --- Otherwise take the hash result out of line and copy it --- */
175
176       else {
177         octet hash[SHA_HASHSZ];
178         sha_done(&h, hash);
179         memcpy(q, hash + (SHA_HASHSZ - o), o);
180         o = 0;
181       }
182
183       /* --- Step the input buffer --- */
184
185       STEP(d);
186     }
187
188     /* --- Another pass has been done --- */
189
190     n--;
191   }
192
193   /* --- Write out subsequent passes --- *
194    *
195    * The hash output has to be done offline, so this is slightly easier.
196    */
197
198   while (n) {
199     size_t o = sz;
200
201     while (o) {
202       sha_ctx h;
203       octet hash[SHA_HASHSZ];
204       size_t n;
205       octet *pp, *qq;
206
207       /* --- Hash the input buffer --- */
208
209       sha_init(&h);
210       sha_hash(&h, d->p, d->sz);
211       sha_done(&h, hash);
212
213       /* --- Work out how much output is wanted --- */
214
215       n = SHA_HASHSZ;
216       if (n > o)
217         n = o;
218       o -= n;
219
220       /* --- XOR the data out --- */
221
222       for (pp = hash + (SHA_HASHSZ - n), qq = q + o;
223            pp < hash + SHA_HASHSZ; pp++, qq++)
224         *qq ^= *pp;
225
226       /* --- Step the input buffer --- */
227
228       STEP(d);
229     }
230
231     /* --- Another pass is done --- */
232
233     n--;
234   }
235 }
236
237 /*----- Generic pseudorandom-number generator interface -------------------*/
238
239 static const grand_ops gops;
240
241 typedef struct gctx {
242   grand r;
243   dsarand d;
244 } gctx;
245
246 static void gdestroy(grand *r)
247 {
248   gctx *g = (gctx *)r;
249   dsarand_destroy(&g->d);
250   DESTROY(g);
251 }
252
253 static int gmisc(grand *r, unsigned op, ...)
254 {
255   gctx *g = (gctx *)r;
256   va_list ap;
257   int rc = 0;
258   va_start(ap, op);
259
260   switch (op) {
261     case GRAND_CHECK:
262       switch (va_arg(ap, unsigned)) {
263         case GRAND_CHECK:
264         case GRAND_SEEDBLOCK:
265         case GRAND_SEEDRAND:
266         case DSARAND_PASSES:
267         case DSARAND_SEEDSZ:
268         case DSARAND_GETSEED:
269           rc = 1;
270           break;
271         default:
272           rc = 0;
273           break;
274       }
275       break;
276     case GRAND_SEEDBLOCK: {
277       const void *p = va_arg(ap, const void *);
278       size_t sz = va_arg(ap, size_t);
279       dsarand_reseed(&g->d, p, sz);
280     } break;
281     case GRAND_SEEDRAND: {
282       grand *rr = va_arg(ap, grand *);
283       rr->ops->fill(rr, g->d.p, g->d.sz);
284     } break;
285     case DSARAND_PASSES: {
286       unsigned n = va_arg(ap, unsigned);
287       rc = g->d.passes;
288       if (n > 0) g->d.passes = n;
289     } break;
290     case DSARAND_SEEDSZ:
291       rc = g->d.sz;
292       break;
293     case DSARAND_GETSEED:
294       memcpy(va_arg(ap, void *), g->d.p, g->d.sz);
295       break;
296     default:
297       GRAND_BADOP;
298       break;
299   }
300
301   va_end(ap);
302   return (rc);
303 }
304
305 static void gfill(grand *r, void *p, size_t sz)
306 {
307   gctx *g = (gctx *)r;
308   dsarand_fill(&g->d, p, sz);
309 }
310
311 static const grand_ops gops = {
312   "dsarand",
313   0, 0,
314   gmisc, gdestroy,
315   grand_defaultword, grand_defaultbyte, grand_defaultword,
316   grand_defaultrange, gfill
317 };
318
319 /* --- @dsarand_create@ --- *
320  *
321  * Arguments:   @const void *p@ = pointer to seed buffer
322  *              @size_t sz@ = size of seed buffer
323  *
324  * Returns:     Pointer to a generic generator.
325  *
326  * Use:         Constructs a generic generator interface over a Catacomb
327  *              entropy pool generator.
328  */
329
330 grand *dsarand_create(const void *p, size_t sz)
331 {
332   gctx *g = CREATE(gctx);
333   g->r.ops = &gops;
334   dsarand_init(&g->d, p, sz);
335   return (&g->r);
336 }
337
338 /*----- That's all, folks -------------------------------------------------*/