Line data Source code
1 : /* rsacvt.c - A debug tool to convert RSA formats.
2 : Copyright (C) 2009 Free Software Foundation, Inc.
3 :
4 : This file is part of Libgcrypt.
5 :
6 : Libgcrypt is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU Lesser General Public License as
8 : published by the Free Software Foundation; either version 2.1 of
9 : the License, or (at your option) any later version.
10 :
11 : Libgcrypt is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public
17 : License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* Input data format:
21 :
22 : =======
23 : # A hash denotes a comment line
24 : e861b700e17e8afe68[...]f1
25 : f7a7ca5367c661f8e6[...]61
26 : 10001
27 :
28 : # After an empty line another input block may follow.
29 : 7861b700e17e8afe68[...]f3
30 : e7a7ca5367c661f8e6[...]71
31 : 3
32 : =========
33 :
34 : */
35 :
36 :
37 : #ifdef HAVE_CONFIG_H
38 : #include <config.h>
39 : #endif
40 : #include <stdio.h>
41 : #include <stdlib.h>
42 : #include <string.h>
43 : #include <stdarg.h>
44 : #include <errno.h>
45 : #include <ctype.h>
46 : #ifdef HAVE_W32_SYSTEM
47 : # include <fcntl.h> /* We need setmode(). */
48 : #else
49 : # include <signal.h>
50 : #endif
51 : #include <assert.h>
52 : #include <unistd.h>
53 :
54 : #ifdef _GCRYPT_IN_LIBGCRYPT
55 : # include "../src/gcrypt-int.h"
56 : #else
57 : # include <gcrypt.h>
58 : # define PACKAGE_BUGREPORT "devnull@example.org"
59 : # define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]"
60 : #endif
61 :
62 :
63 : #define PGM "rsacvt"
64 :
65 : #define my_isascii(c) (!((c) & 0x80))
66 : #define digitp(p) (*(p) >= '0' && *(p) <= '9')
67 : #define hexdigitp(a) (digitp (a) \
68 : || (*(a) >= 'A' && *(a) <= 'F') \
69 : || (*(a) >= 'a' && *(a) <= 'f'))
70 : #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
71 : *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
72 : #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
73 : #define DIM(v) (sizeof(v)/sizeof((v)[0]))
74 : #define DIMof(type,member) DIM(((type *)0)->member)
75 :
76 :
77 : /* Verbose mode flag. */
78 : static int verbose;
79 :
80 : /* Prefix output with labels. */
81 : static int with_labels;
82 :
83 : /* Do not suppress leading zeroes. */
84 : static int keep_lz;
85 :
86 : /* Create parameters as specified by OpenPGP (rfc4880). That is we
87 : don't store dmp1 and dmp1 but d and make sure that p is less than q. */
88 : static int openpgp_mode;
89 :
90 :
91 : /* Print a error message and exit the process with an error code. */
92 : static void
93 0 : die (const char *format, ...)
94 : {
95 : va_list arg_ptr;
96 :
97 0 : va_start (arg_ptr, format);
98 0 : fputs (PGM ": ", stderr);
99 0 : vfprintf (stderr, format, arg_ptr);
100 0 : va_end (arg_ptr);
101 0 : exit (1);
102 : }
103 :
104 :
105 : static char *
106 0 : read_textline (FILE *fp)
107 : {
108 : char line[4096];
109 : char *p;
110 0 : int any = 0;
111 :
112 : /* Read line but skip over initial empty lines. */
113 : do
114 : {
115 : do
116 : {
117 0 : if (!fgets (line, sizeof line, fp))
118 : {
119 0 : if (feof (fp))
120 0 : return NULL;
121 0 : die ("error reading input line: %s\n", strerror (errno));
122 : }
123 0 : p = strchr (line, '\n');
124 0 : if (p)
125 0 : *p = 0;
126 0 : p = line + (*line? (strlen (line)-1):0);
127 0 : for ( ;p > line; p--)
128 0 : if (my_isascii (*p) && isspace (*p))
129 0 : *p = 0;
130 : }
131 0 : while (!any && !*line);
132 0 : any = 1;
133 : }
134 0 : while (*line == '#'); /* Always skip comment lines. */
135 0 : if (verbose > 1)
136 0 : fprintf (stderr, PGM ": received line: %s\n", line);
137 0 : return gcry_xstrdup (line);
138 : }
139 :
140 :
141 : static gcry_mpi_t
142 0 : read_hexmpi_line (FILE *fp, int *got_eof)
143 : {
144 : gpg_error_t err;
145 : gcry_mpi_t a;
146 : char *line;
147 :
148 0 : *got_eof = 0;
149 0 : line = read_textline (fp);
150 0 : if (!line)
151 : {
152 0 : *got_eof = 1;
153 0 : return NULL;
154 : }
155 0 : err = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL);
156 0 : gcry_free (line);
157 0 : if (err)
158 0 : a = NULL;
159 0 : return a;
160 : }
161 :
162 :
163 : static int
164 0 : skip_to_empty_line (FILE *fp)
165 : {
166 : char line[256];
167 : char *p;
168 :
169 : do
170 : {
171 0 : if (!fgets (line, sizeof line, fp))
172 : {
173 0 : if (feof (fp))
174 0 : return -1;
175 0 : die ("error reading input line: %s\n", strerror (errno));
176 : }
177 0 : p = strchr (line, '\n');
178 0 : if (p)
179 0 : *p =0;
180 : }
181 0 : while (*line);
182 0 : return 0;
183 : }
184 :
185 :
186 : /* Print an MPI on a line. */
187 : static void
188 0 : print_mpi_line (const char *label, gcry_mpi_t a)
189 : {
190 : unsigned char *buf, *p;
191 : gcry_error_t err;
192 0 : int writerr = 0;
193 :
194 0 : if (with_labels && label)
195 0 : printf ("%s = ", label);
196 :
197 0 : err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a);
198 0 : if (err)
199 0 : die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err));
200 :
201 0 : p = buf;
202 0 : if (!keep_lz && p[0] == '0' && p[1] == '0' && p[2])
203 0 : p += 2;
204 :
205 0 : printf ("%s\n", p);
206 0 : if (ferror (stdout))
207 0 : writerr++;
208 0 : if (!writerr && fflush (stdout) == EOF)
209 0 : writerr++;
210 0 : if (writerr)
211 0 : die ("writing output failed: %s\n", strerror (errno));
212 0 : gcry_free (buf);
213 0 : }
214 :
215 :
216 : /* Compute and print missing RSA parameters. */
217 : static void
218 0 : compute_missing (gcry_mpi_t rsa_p, gcry_mpi_t rsa_q, gcry_mpi_t rsa_e)
219 : {
220 : gcry_mpi_t rsa_n, rsa_d, rsa_pm1, rsa_qm1, rsa_u;
221 : gcry_mpi_t phi, tmp_g, tmp_f;
222 :
223 0 : rsa_n = gcry_mpi_new (0);
224 0 : rsa_d = gcry_mpi_new (0);
225 0 : rsa_pm1 = gcry_mpi_new (0);
226 0 : rsa_qm1 = gcry_mpi_new (0);
227 0 : rsa_u = gcry_mpi_new (0);
228 :
229 0 : phi = gcry_mpi_new (0);
230 0 : tmp_f = gcry_mpi_new (0);
231 0 : tmp_g = gcry_mpi_new (0);
232 :
233 : /* Check that p < q; if not swap p and q. */
234 0 : if (openpgp_mode && gcry_mpi_cmp (rsa_p, rsa_q) > 0)
235 : {
236 0 : fprintf (stderr, PGM ": swapping p and q\n");
237 0 : gcry_mpi_swap (rsa_p, rsa_q);
238 : }
239 :
240 0 : gcry_mpi_mul (rsa_n, rsa_p, rsa_q);
241 :
242 :
243 : /* Compute the Euler totient: phi = (p-1)(q-1) */
244 0 : gcry_mpi_sub_ui (rsa_pm1, rsa_p, 1);
245 0 : gcry_mpi_sub_ui (rsa_qm1, rsa_q, 1);
246 0 : gcry_mpi_mul (phi, rsa_pm1, rsa_qm1);
247 :
248 0 : if (!gcry_mpi_gcd (tmp_g, rsa_e, phi))
249 0 : die ("parameter 'e' does match 'p' and 'q'\n");
250 :
251 : /* Compute: f = lcm(p-1,q-1) = phi / gcd(p-1,q-1) */
252 0 : gcry_mpi_gcd (tmp_g, rsa_pm1, rsa_qm1);
253 0 : gcry_mpi_div (tmp_f, NULL, phi, tmp_g, -1);
254 :
255 : /* Compute the secret key: d = e^{-1} mod lcm(p-1,q-1) */
256 0 : gcry_mpi_invm (rsa_d, rsa_e, tmp_f);
257 :
258 : /* Compute the CRT helpers: d mod (p-1), d mod (q-1) */
259 0 : gcry_mpi_mod (rsa_pm1, rsa_d, rsa_pm1);
260 0 : gcry_mpi_mod (rsa_qm1, rsa_d, rsa_qm1);
261 :
262 : /* Compute the CRT value: OpenPGP: u = p^{-1} mod q
263 : Standard: iqmp = q^{-1} mod p */
264 0 : if (openpgp_mode)
265 0 : gcry_mpi_invm (rsa_u, rsa_p, rsa_q);
266 : else
267 0 : gcry_mpi_invm (rsa_u, rsa_q, rsa_p);
268 :
269 0 : gcry_mpi_release (phi);
270 0 : gcry_mpi_release (tmp_f);
271 0 : gcry_mpi_release (tmp_g);
272 :
273 : /* Print everything. */
274 0 : print_mpi_line ("n", rsa_n);
275 0 : print_mpi_line ("e", rsa_e);
276 0 : if (openpgp_mode)
277 0 : print_mpi_line ("d", rsa_d);
278 0 : print_mpi_line ("p", rsa_p);
279 0 : print_mpi_line ("q", rsa_q);
280 0 : if (openpgp_mode)
281 0 : print_mpi_line ("u", rsa_u);
282 : else
283 : {
284 0 : print_mpi_line ("dmp1", rsa_pm1);
285 0 : print_mpi_line ("dmq1", rsa_qm1);
286 0 : print_mpi_line ("iqmp", rsa_u);
287 : }
288 :
289 0 : gcry_mpi_release (rsa_n);
290 0 : gcry_mpi_release (rsa_d);
291 0 : gcry_mpi_release (rsa_pm1);
292 0 : gcry_mpi_release (rsa_qm1);
293 0 : gcry_mpi_release (rsa_u);
294 0 : }
295 :
296 :
297 :
298 : static void
299 0 : usage (int show_help)
300 : {
301 0 : if (!show_help)
302 : {
303 0 : fputs ("usage: " PGM
304 : " [OPTION] [FILE] (try --help for more information)\n", stderr);
305 0 : exit (2);
306 : }
307 0 : fputs
308 : ("Usage: " PGM " [OPTIONS] [FILE]\n"
309 : "Take RSA parameters p, n, e and compute missing parameters.\n"
310 : "OPTIONS:\n"
311 : " --openpgp Compute as specified by RFC4880\n"
312 : " --labels Prefix output with labels\n"
313 : " --keep-lz Keep all leading zeroes in the output\n"
314 : " --verbose Print additional information\n"
315 : " --version Print version information\n"
316 : " --help Print this text\n"
317 : "With no FILE, or if FILE is -, read standard input.\n"
318 : "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout);
319 0 : exit (0);
320 : }
321 :
322 :
323 : int
324 0 : main (int argc, char **argv)
325 : {
326 0 : int last_argc = -1;
327 : FILE *input;
328 : gcry_mpi_t rsa_p, rsa_q, rsa_e;
329 : int got_eof;
330 0 : int any = 0;
331 :
332 0 : if (argc)
333 0 : { argc--; argv++; }
334 :
335 0 : while (argc && last_argc != argc )
336 : {
337 0 : last_argc = argc;
338 0 : if (!strcmp (*argv, "--"))
339 : {
340 0 : argc--; argv++;
341 0 : break;
342 : }
343 0 : else if (!strcmp (*argv, "--help"))
344 : {
345 0 : usage (1);
346 : }
347 0 : else if (!strcmp (*argv, "--version"))
348 : {
349 0 : fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout);
350 0 : printf ("libgcrypt %s\n", gcry_check_version (NULL));
351 0 : exit (0);
352 : }
353 0 : else if (!strcmp (*argv, "--verbose"))
354 : {
355 0 : verbose++;
356 0 : argc--; argv++;
357 : }
358 0 : else if (!strcmp (*argv, "--labels"))
359 : {
360 0 : with_labels = 1;
361 0 : argc--; argv++;
362 : }
363 0 : else if (!strcmp (*argv, "--keep-lz"))
364 : {
365 0 : keep_lz = 1;
366 0 : argc--; argv++;
367 : }
368 0 : else if (!strcmp (*argv, "--openpgp"))
369 : {
370 0 : openpgp_mode = 1;
371 0 : argc--; argv++;
372 : }
373 : }
374 :
375 0 : if (argc > 1)
376 0 : usage (0);
377 :
378 : #if !defined (HAVE_W32_SYSTEM) && !defined (_WIN32)
379 0 : signal (SIGPIPE, SIG_IGN);
380 : #endif
381 :
382 0 : if (argc == 1 && strcmp (argv[0], "-"))
383 : {
384 0 : input = fopen (argv[0], "r");
385 0 : if (!input)
386 0 : die ("can't open `%s': %s\n", argv[0], strerror (errno));
387 : }
388 : else
389 0 : input = stdin;
390 :
391 0 : gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose);
392 0 : if (!gcry_check_version ("1.4.0"))
393 0 : die ("Libgcrypt is not sufficient enough\n");
394 0 : gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
395 0 : gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
396 :
397 : do
398 : {
399 0 : rsa_p = read_hexmpi_line (input, &got_eof);
400 0 : if (!rsa_p && got_eof)
401 0 : break;
402 0 : if (!rsa_p)
403 0 : die ("RSA parameter 'p' missing or not properly hex encoded\n");
404 0 : rsa_q = read_hexmpi_line (input, &got_eof);
405 0 : if (!rsa_q)
406 0 : die ("RSA parameter 'q' missing or not properly hex encoded\n");
407 0 : rsa_e = read_hexmpi_line (input, &got_eof);
408 0 : if (!rsa_e)
409 0 : die ("RSA parameter 'e' missing or not properly hex encoded\n");
410 0 : got_eof = skip_to_empty_line (input);
411 :
412 0 : if (any)
413 0 : putchar ('\n');
414 :
415 0 : compute_missing (rsa_p, rsa_q, rsa_e);
416 :
417 0 : gcry_mpi_release (rsa_p);
418 0 : gcry_mpi_release (rsa_q);
419 0 : gcry_mpi_release (rsa_e);
420 :
421 0 : any = 1;
422 : }
423 0 : while (!got_eof);
424 :
425 0 : return 0;
426 : }
|