Add an internal-representation no-op function.
[u/mdw/catacomb] / dsa-gen.c
1 /* -*-c-*-
2 *
3 * $Id: dsa-gen.c,v 1.9 2001/02/03 16:09:29 mdw Exp $
4 *
5 * Generate DSA shared parameters
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: dsa-gen.c,v $
33 * Revision 1.9 2001/02/03 16:09:29 mdw
34 * Allow the caller to fetch the parameter generation seed and counter.
35 *
36 * Revision 1.8 2000/10/08 12:12:47 mdw
37 * Use @MP_EQ@ instead of @MP_CMP@. Remove vestages of @primorial@.
38 *
39 * Revision 1.7 2000/08/15 21:45:05 mdw
40 * Use the new trial division equipment in pfilt. This gives a 10%
41 * performance improvement in dsa-gen.t.
42 *
43 * Revision 1.6 2000/07/29 10:00:14 mdw
44 * Rename `dsa_seed' to `dsa_gen' for consistency with other parameter-
45 * generation interfaces.
46 *
47 * Revision 1.5 2000/02/12 18:21:02 mdw
48 * Overhaul of key management (again).
49 *
50 * Revision 1.4 1999/12/22 15:52:44 mdw
51 * Reworking for new prime-search system.
52 *
53 * Revision 1.3 1999/12/10 23:18:38 mdw
54 * Change interface for suggested destinations.
55 *
56 * Revision 1.2 1999/11/20 22:23:48 mdw
57 * Allow event handler to abort the search process.
58 *
59 * Revision 1.1 1999/11/19 19:28:00 mdw
60 * Implementation of the Digital Signature Algorithm.
61 *
62 */
63
64 /*----- Header files ------------------------------------------------------*/
65
66 #include <stdio.h>
67 #include <stdlib.h>
68
69 #include "dsa.h"
70 #include "dsarand.h"
71 #include "fibrand.h"
72 #include "mp.h"
73 #include "mprand.h"
74 #include "pgen.h"
75 #include "prim.h"
76 #include "sha.h"
77
78 /*----- The DSA stepper ---------------------------------------------------*/
79
80 /* --- @next@ --- *
81 *
82 * Arguments: @pgen_event *ev@ = pointer to event block
83 * @dsa_stepctx *d@ = pointer to stepping context
84 *
85 * Returns: A @PGEN@ result code.
86 *
87 * Use: Steps the generator once, reads the result, and tests it.
88 */
89
90 static int next(pgen_event *ev, dsa_stepctx *d)
91 {
92 mp *m;
93 int rc;
94
95 /* --- Load the new candidate --- */
96
97 if (d->seedbuf)
98 d->r->ops->misc(d->r, DSARAND_GETSEED, d->seedbuf);
99 m = mprand(ev->m, d->bits, d->r, 0);
100
101 /* --- Force to be a multiple of @q@ --- */
102
103 if (d->q) {
104 mp *r = MP_NEW;
105 mp_div(0, &r, m, d->q);
106 m = mp_sub(m, m, r);
107 mp_drop(r);
108 }
109 m->v[0] |= d->or;
110 ev->m = m;
111
112 /* --- Do the trial division --- */
113
114 rc = pfilt_smallfactor(m);
115 d->count++;
116
117 /* --- Return the result --- */
118
119 return (rc);
120 }
121
122 /* --- @dsa_step@ --- */
123
124 int dsa_step(int rq, pgen_event *ev, void *p)
125 {
126 dsa_stepctx *d = p;
127
128 switch (rq) {
129 case PGEN_BEGIN:
130 case PGEN_TRY:
131 return (next(ev, d));
132 case PGEN_DONE:
133 return (PGEN_DONE);
134 }
135 return (PGEN_ABORT);
136 }
137
138 /*----- Glue code ---------------------------------------------------------*/
139
140 /* --- @dsa_gen@ --- *
141 *
142 * Arguments: @dsa_param *dp@ = where to store parameters
143 * @unsigned ql@ = length of @q@ in bits
144 * @unsigned pl@ = length of @p@ in bits
145 * @unsigned steps@ = number of steps to find @q@
146 * @const void *k@ = pointer to key material
147 * @size_t sz@ = size of key material
148 * @dsa_seed *ds@ = optional pointer for output seed information
149 * @pgen_proc *event@ = event handler function
150 * @void *ectx@ = argument for event handler
151 *
152 * Returns: @PGEN_DONE@ if everything worked ok; @PGEN_ABORT@ otherwise.
153 *
154 * Use: Generates the DSA shared parameters from a given seed value.
155 *
156 * The parameters are a prime %$q$%, relatively small, and a
157 * large prime %$p = kq + 1$% for some %$k$%, together with a
158 * generator %$g$% of the cyclic subgroup of order %$q$%. These
159 * are actually the same as the Diffie-Hellman parameter set,
160 * but the generation algorithm is different.
161 *
162 * The algorithm used is a compatible extension of the method
163 * described in the DSA standard, FIPS 186. The standard
164 * requires that %$q$% be 160 bits in size (i.e., @ql == 160@)
165 * and that the length of %$p$% be %$L = 512 + 64l$% for some
166 * %$l$%. Neither limitation applies to this implementation.
167 */
168
169 int dsa_gen(dsa_param *dp, unsigned ql, unsigned pl, unsigned steps,
170 const void *k, size_t sz, dsa_seed *ds,
171 pgen_proc *event, void *ectx)
172 {
173 dsa_stepctx s;
174 prim_ctx p;
175 int i;
176 rabin r;
177 mp *qc;
178
179 /* --- Initialize the stepping context --- */
180
181 s.r = dsarand_create(k, sz);
182
183 /* --- Find @q@ --- */
184
185 s.q = 0;
186 s.r->ops->misc(s.r, DSARAND_PASSES, 2);
187 s.bits = ql;
188 s.count = 0;
189 s.or = 1;
190 if (!ds)
191 s.seedbuf = 0;
192 else {
193 ds->sz = sz;
194 ds->p = s.seedbuf = xmalloc(sz);
195 }
196 if ((dp->q = pgen("q", MP_NEW, MP_NEW, event, ectx, steps, dsa_step, &s,
197 rabin_iters(ql), pgen_test, &r)) == 0)
198 goto fail_q;
199
200 /* --- Find @p@ --- */
201
202 s.count = ~0;
203 s.q = mp_lsl(MP_NEW, dp->q, 1);
204 s.r->ops->misc(s.r, DSARAND_PASSES, 1);
205 s.bits = pl;
206 s.seedbuf = 0;
207 if ((dp->p = pgen("p", MP_NEW, MP_NEW, event, ectx, 4096, dsa_step, &s,
208 rabin_iters(pl), pgen_test, &r)) == 0)
209 goto fail_p;
210 mp_drop(s.q);
211 if (ds)
212 ds->count = s.count;
213
214 /* --- Find @g@ --- *
215 *
216 * The division returns remainder 1. This doesn't matter.
217 */
218
219 mpmont_create(&p.mm, dp->p);
220 qc = MP_NEW; mp_div(&qc, 0, dp->p, dp->q);
221 i = 0;
222 p.exp = qc;
223 p.n = 0;
224 if ((dp->g = pgen("g", MP_NEW, MP_NEW, event, ectx, 0, prim_step, &i,
225 1, prim_test, &p)) == 0)
226 goto fail_g;
227
228 /* --- Done --- */
229
230 mp_drop(qc);
231 mpmont_destroy(&p.mm);
232 s.r->ops->destroy(s.r);
233 return (PGEN_DONE);
234
235 /* --- Tidy up when things go wrong --- */
236
237 fail_g:
238 mp_drop(qc);
239 mpmont_destroy(&p.mm);
240 fail_p:
241 mp_drop(dp->q);
242 mp_drop(s.q);
243 fail_q:
244 s.r->ops->destroy(s.r);
245 if (ds)
246 xfree(ds->p);
247 return (PGEN_ABORT);
248 }
249
250 /*----- Test rig ----------------------------------------------------------*/
251
252 #ifdef TEST_RIG
253
254 static int verify(dstr *v)
255 {
256 mp *q = *(mp **)v[4].buf;
257 mp *p = *(mp **)v[5].buf;
258 mp *g = *(mp **)v[6].buf;
259 dsa_param dp;
260 dsa_seed ds;
261 unsigned long l = *(unsigned long *)v[1].buf;
262 unsigned long n = *(unsigned long *)v[3].buf;
263 int ok = 1;
264 int rc;
265 keycheck kc;
266 keycheck_reportctx kcr;
267
268 rc = dsa_gen(&dp, 160, l, 16, v[0].buf, v[0].len, &ds, pgen_evspin, 0);
269 if (rc || ds.count != n || ds.sz != v[2].len ||
270 memcmp(ds.p, v[2].buf, v[2].len) != 0 ||
271 !MP_EQ(q, dp.q) || !MP_EQ(p, dp.p) || !MP_EQ(g, dp.g)) {
272 fputs("\n*** gen failed", stderr);
273 fputs("\nseed_in = ", stderr); type_hex.dump(&v[0], stderr);
274 fprintf(stderr, "\nl = %lu", l);
275 fputs("\nseed_out = ", stderr); type_hex.dump(&v[2], stderr);
276 fprintf(stderr, "\ncount = %lu", n);
277 fputs("\n q = ", stderr); mp_writefile(q, stderr, 16);
278 fputs("\n p = ", stderr); mp_writefile(p, stderr, 16);
279 fputs("\n g = ", stderr); mp_writefile(g, stderr, 16);
280 if (!rc) {
281 dstr d;
282 d.buf = ds.p; d.len = ds.sz;
283 fputs("\nds.seed = ", stderr); type_hex.dump(&d, stderr);
284 fprintf(stderr, "\nds.count = %u", ds.count);
285 fputs("\ndp.q = ", stderr); mp_writefile(dp.q, stderr, 16);
286 fputs("\ndp.p = ", stderr); mp_writefile(dp.p, stderr, 16);
287 fputs("\ndp.g = ", stderr); mp_writefile(dp.g, stderr, 16);
288 }
289 fputc('\n', stderr);
290 ok = 0;
291 }
292
293 kcr.fp = stderr;
294 kcr.sev = KCSEV_ERR;
295 keycheck_init(&kc, keycheck_stdreport, &kcr);
296 if (!rc)
297 dsa_checkparam(&kc, &dp, &ds);
298 if (!keycheck_allclear(&kc, KCSEV_ERR)) {
299 fputs("\n*** gen failed check", stderr);
300 fputs("\nseed_in = ", stderr); type_hex.dump(&v[0], stderr);
301 fprintf(stderr, "\nl = %lu", l);
302 fputs("\nseed_out = ", stderr); type_hex.dump(&v[2], stderr);
303 fprintf(stderr, "\ncount = %lu", n);
304 fputs("\n q = ", stderr); mp_writefile(q, stderr, 16);
305 fputs("\n p = ", stderr); mp_writefile(p, stderr, 16);
306 fputs("\n g = ", stderr); mp_writefile(g, stderr, 16);
307 fputc('\n', stderr);
308 ok = 0;
309 }
310
311 mp_drop(q); mp_drop(p); mp_drop(g);
312 if (!rc) {
313 mp_drop(dp.q); mp_drop(dp.p); mp_drop(dp.g); xfree(ds.p);
314 }
315 assert(mparena_count(MPARENA_GLOBAL) == 0);
316 return (ok);
317 }
318
319 static test_chunk tests[] = {
320 { "gen", verify,
321 { &type_hex, &type_ulong, &type_hex, &type_ulong,
322 &type_mp, &type_mp, &type_mp, 0 } },
323 { 0, 0, { 0 } }
324 };
325
326 int main(int argc, char *argv[])
327 {
328 sub_init();
329 test_run(argc, argv, tests, SRCDIR "/tests/dsa");
330 return (0);
331 }
332
333 #endif
334
335 /*----- That's all, folks -------------------------------------------------*/