xref: /netbsd-src/usr.bin/moduli/qsafe/qsafe.c (revision 96cef9518936fd4efe47205412ab2e50a5f85a0d)
1 /* $NetBSD: qsafe.c,v 1.4 2018/02/06 19:32:49 christos Exp $ */
2 
3 /*-
4  * Copyright 1994 Phil Karn <karn@qualcomm.com>
5  * Copyright 1996-1998, 2003 William Allen206 Simpson <wsimpson@greendragon.com>
6  * Copyright 2000 Niels Provos <provos@citi.umich.edu>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * Test probable "safe" primes,
32  *
33  *  suitable for use as Diffie-Hellman moduli;
34  *  that is, where q = (p-1)/2 is also prime.
35  *
36  * This is the second of two steps.
37  * This step is processor intensive.
38  *
39  * 1996 May     William Allen Simpson
40  *              extracted from earlier code by Phil Karn, April 1994.
41  *              read large prime candidates list (q),
42  *              and check prime probability of (p).
43  * 1998 May     William Allen Simpson
44  *              parameterized.
45  *              optionally limit to a single generator.
46  * 2000 Dec     Niels Provos
47  *              convert from GMP to openssl BN.
48  * 2003 Jun     William Allen Simpson
49  *              change outfile definition slightly to match openssh mistake.
50  *              move common file i/o to own file for better documentation.
51  *              redo debugprint again.
52  */
53 
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <time.h>
58 #include <openssl/bn.h>
59 #include "qfile.h"
60 
61 /* define DEBUGPRINT     1 */
62 #define TRIAL_MINIMUM           (4)
63 
64 __dead static void     usage(void);
65 
66 /*
67  * perform a Miller-Rabin primality test
68  * on the list of candidates
69  * (checking both q and p)
70  * from standard input.
71  * The result is a list of so-call "safe" primes
72  * to standard output,
73  */
74 int
main(int argc,char * argv[])75 main(int argc, char *argv[])
76 {
77 	BIGNUM         *q, *p, *a;
78 	BN_CTX         *ctx;
79 	char           *cp;
80 	char           *lp;
81 	uint32_t        count_in = 0;
82 	uint32_t        count_out = 0;
83 	uint32_t        count_possible = 0;
84 	uint32_t        generator_known;
85 	uint32_t        generator_wanted = 0;
86 	uint32_t        in_tests;
87 	uint32_t        in_tries;
88 	uint32_t        in_type;
89 	uint32_t        in_size;
90 	int             trials;
91 	time_t          time_start;
92 	time_t          time_stop;
93 
94 	setprogname(argv[0]);
95 
96 	if (argc < 2) {
97 		usage();
98 	}
99 
100 	if ((trials = strtoul(argv[1], NULL, 10)) < TRIAL_MINIMUM) {
101 		trials = TRIAL_MINIMUM;
102 	}
103 
104 	if (argc > 2) {
105 		generator_wanted = strtoul(argv[2], NULL, 16);
106 	}
107 
108 	time(&time_start);
109 
110 	p = BN_new();
111 	q = BN_new();
112 	ctx = BN_CTX_new();
113 
114 	(void)fprintf(stderr,
115 		"%.24s Final %d Miller-Rabin trials (%x generator)\n",
116 		ctime(&time_start), trials, generator_wanted);
117 
118 	lp = (char *) malloc((unsigned long) QLINESIZE + 1);
119 
120 	while (fgets(lp, QLINESIZE, stdin) != NULL) {
121 		size_t          ll = strlen(lp);
122 
123 		count_in++;
124 
125 		if (ll < 14 || *lp == '!' || *lp == '#') {
126 #ifdef  DEBUGPRINT
127 			(void)fprintf(stderr, "%10lu: comment or short"
128 				      " line\n", count_in);
129 #endif
130 			continue;
131 		}
132 
133 		/* time */
134 		cp = &lp[14];	/* (skip) */
135 
136 		/* type */
137 		in_type = strtoul(cp, &cp, 10);
138 
139 		/* tests */
140 		in_tests = strtoul(cp, &cp, 10);
141 		if (in_tests & QTEST_COMPOSITE) {
142 #ifdef  DEBUGPRINT
143 			(void)fprintf(stderr, "%10lu: known composite\n",
144 				count_in);
145 #endif
146 			continue;
147 		}
148 
149 		/* tries */
150 		in_tries = (uint32_t) strtoul(cp, &cp, 10);
151 
152 		/* size (most significant bit) */
153 		in_size = (uint32_t) strtoul(cp, &cp, 10);
154 
155 		/* generator (hex) */
156 		generator_known = (uint32_t) strtoul(cp, &cp, 16);
157 
158 		/* Skip white space */
159 		cp += strspn(cp, " ");
160 
161 		/* modulus (hex) */
162 		switch (in_type) {
163 		case QTYPE_SOPHIE_GERMAINE:
164 #ifdef  DEBUGPRINT
165 			(void)fprintf(stderr, "%10lu: (%lu) "
166 				      "Sophie-Germaine\n", count_in,
167 				      in_type);
168 #endif
169 
170 			a = q;
171 			BN_hex2bn(&a, cp);
172 			/* p = 2*q + 1 */
173 			BN_lshift(p, q, 1);
174 			BN_add_word(p, 1UL);
175 			in_size += 1;
176 			generator_known = 0;
177 
178 			break;
179 
180 		default:
181 #ifdef  DEBUGPRINT
182 			(void)fprintf(stderr, "%10lu: (%lu)\n",
183 				      count_in,	in_type);
184 #endif
185 			a = p;
186 			BN_hex2bn(&a, cp);
187 			/* q = (p-1) / 2 */
188 			BN_rshift(q, p, 1);
189 
190 			break;
191 		}
192 
193 		/*
194 		 * due to earlier inconsistencies in interpretation, check the
195 		 * proposed bit size.
196 		 */
197 		if ((uint32_t)BN_num_bits(p) != (in_size + 1)) {
198 #ifdef  DEBUGPRINT
199 			(void)fprintf(stderr, "%10lu: bit size %ul "
200 				      "mismatch\n", count_in, in_size);
201 #endif
202 			continue;
203 		}
204 
205 		if (in_size < QSIZE_MINIMUM) {
206 #ifdef  DEBUGPRINT
207 			(void)fprintf(stderr, "%10lu: bit size %ul "
208 				      "too short\n", count_in, in_size);
209 #endif
210 			continue;
211 		}
212 
213 		if (in_tests & QTEST_MILLER_RABIN)
214 			in_tries += trials;
215 		else
216 			in_tries = trials;
217 
218 		/*
219 		 * guess unknown generator
220 		 */
221 		if (generator_known == 0) {
222 			if (BN_mod_word(p, 24UL) == 11)
223 				generator_known = 2;
224 			else if (BN_mod_word(p, 12UL) == 5)
225 				generator_known = 3;
226 			else {
227 				BN_ULONG        r = BN_mod_word(p, 10UL);
228 
229 				if (r == 3 || r == 7) {
230 					generator_known = 5;
231 				}
232 			}
233 		}
234 
235 		/*
236 		 * skip tests when desired generator doesn't match
237 		 */
238 		if (generator_wanted > 0 &&
239 		    generator_wanted != generator_known) {
240 #ifdef  DEBUGPRINT
241 			(void)fprintf(stderr,
242 				      "%10lu: generator %ld != %ld\n",
243 				count_in, generator_known, generator_wanted);
244 #endif
245 			continue;
246 		}
247 
248 		count_possible++;
249 
250 		/*
251 		 * The (1/4)^N performance bound on Miller-Rabin is extremely
252 		 * pessimistic, so don't spend a lot of time really verifying
253 		 * that q is prime until after we know that p is also prime. A
254 		 * single pass will weed out the vast majority of composite
255 		 * q's.
256 		 */
257 		if (BN_is_prime_ex(q, 1, ctx, NULL) <= 0) {
258 #ifdef  DEBUGPRINT
259 			(void)fprintf(stderr, "%10lu: q failed first "
260 				      "possible prime test\n", count_in);
261 #endif
262 			continue;
263 		}
264 
265 		/*
266 		 * q is possibly prime, so go ahead and really make sure that
267 		 * p is prime. If it is, then we can go back and do the same
268 		 * for q. If p is composite, chances are that will show up on
269 		 * the first Rabin-Miller iteration so it doesn't hurt to
270 		 * specify a high iteration count.
271 		 */
272 		if (!BN_is_prime_ex(p, trials, ctx, NULL)) {
273 #ifdef  DEBUGPRINT
274 			(void)fprintf(stderr, "%10lu: p is not prime\n",
275 				      count_in);
276 #endif
277 			continue;
278 		}
279 
280 #ifdef  DEBUGPRINT
281 		(void)fprintf(stderr, "%10lu: p is almost certainly "
282 			      "prime\n", count_in);
283 #endif
284 
285 		/* recheck q more rigorously */
286 		if (!BN_is_prime_ex(q, trials - 1, ctx, NULL)) {
287 #ifdef  DEBUGPRINT
288 			(void)fprintf(stderr, "%10lu: q is not prime\n",
289 				      count_in);
290 #endif
291 			continue;
292 		}
293 #ifdef  DEBUGPRINT
294 		fprintf(stderr, "%10lu: q is almost certainly prime\n",
295 			count_in);
296 #endif
297 		if (0 > qfileout(stdout,
298 				 QTYPE_SAFE,
299 				 (in_tests | QTEST_MILLER_RABIN),
300 				 in_tries,
301 				 in_size,
302 				 generator_known,
303 				 p)) {
304 			break;
305 		}
306 		count_out++;
307 #ifdef  DEBUGPRINT
308 		fflush(stderr);
309 		fflush(stdout);
310 #endif
311 	}
312 
313 	time(&time_stop);
314 	free(lp);
315 	BN_free(p);
316 	BN_free(q);
317 	BN_CTX_free(ctx);
318 	fflush(stdout);		/* fclose(stdout); */
319 	/* fclose(stdin); */
320 	(void)fprintf(stderr,
321 	   "%.24s Found %u safe primes of %u candidates in %lu seconds\n",
322 		ctime(&time_stop), count_out, count_possible,
323 		(long) (time_stop - time_start));
324 
325 	return (0);
326 }
327 
328 static void
usage(void)329 usage(void)
330 {
331 	(void)fprintf(stderr, "Usage: %s <trials> [generator]\n",
332 		      getprogname());
333 	exit(1);
334 }
335