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