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