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 : #include "t-common.h"
65 :
66 :
67 : /* Prefix output with labels. */
68 : static int with_labels;
69 :
70 : /* Do not suppress leading zeroes. */
71 : static int keep_lz;
72 :
73 : /* Create parameters as specified by OpenPGP (rfc4880). That is we
74 : don't store dmp1 and dmp1 but d and make sure that p is less than q. */
75 : static int openpgp_mode;
76 :
77 :
78 : static char *
79 0 : read_textline (FILE *fp)
80 : {
81 : char line[4096];
82 : char *p;
83 0 : int any = 0;
84 :
85 : /* Read line but skip over initial empty lines. */
86 : do
87 : {
88 : do
89 : {
90 0 : if (!fgets (line, sizeof line, fp))
91 : {
92 0 : if (feof (fp))
93 0 : return NULL;
94 0 : die ("error reading input line: %s\n", strerror (errno));
95 : }
96 0 : p = strchr (line, '\n');
97 0 : if (p)
98 0 : *p = 0;
99 0 : p = line + (*line? (strlen (line)-1):0);
100 0 : for ( ;p > line; p--)
101 0 : if (my_isascii (*p) && isspace (*p))
102 0 : *p = 0;
103 : }
104 0 : while (!any && !*line);
105 0 : any = 1;
106 : }
107 0 : while (*line == '#'); /* Always skip comment lines. */
108 0 : if (verbose > 1)
109 0 : fprintf (stderr, PGM ": received line: %s\n", line);
110 0 : return gcry_xstrdup (line);
111 : }
112 :
113 :
114 : static gcry_mpi_t
115 0 : read_hexmpi_line (FILE *fp, int *got_eof)
116 : {
117 : gpg_error_t err;
118 : gcry_mpi_t a;
119 : char *line;
120 :
121 0 : *got_eof = 0;
122 0 : line = read_textline (fp);
123 0 : if (!line)
124 : {
125 0 : *got_eof = 1;
126 0 : return NULL;
127 : }
128 0 : err = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL);
129 0 : gcry_free (line);
130 0 : if (err)
131 0 : a = NULL;
132 0 : return a;
133 : }
134 :
135 :
136 : static int
137 0 : skip_to_empty_line (FILE *fp)
138 : {
139 : char line[256];
140 : char *p;
141 :
142 : do
143 : {
144 0 : if (!fgets (line, sizeof line, fp))
145 : {
146 0 : if (feof (fp))
147 0 : return -1;
148 0 : die ("error reading input line: %s\n", strerror (errno));
149 : }
150 0 : p = strchr (line, '\n');
151 0 : if (p)
152 0 : *p =0;
153 : }
154 0 : while (*line);
155 0 : return 0;
156 : }
157 :
158 :
159 : /* Print an MPI on a line. */
160 : static void
161 0 : print_mpi_line (const char *label, gcry_mpi_t a)
162 : {
163 : unsigned char *buf, *p;
164 : gcry_error_t err;
165 0 : int writerr = 0;
166 :
167 0 : if (with_labels && label)
168 0 : printf ("%s = ", label);
169 :
170 0 : err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a);
171 0 : if (err)
172 0 : die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err));
173 :
174 0 : p = buf;
175 0 : if (!keep_lz && p[0] == '0' && p[1] == '0' && p[2])
176 0 : p += 2;
177 :
178 0 : printf ("%s\n", p);
179 0 : if (ferror (stdout))
180 0 : writerr++;
181 0 : if (!writerr && fflush (stdout) == EOF)
182 0 : writerr++;
183 0 : if (writerr)
184 0 : die ("writing output failed: %s\n", strerror (errno));
185 0 : gcry_free (buf);
186 0 : }
187 :
188 :
189 : /* Compute and print missing RSA parameters. */
190 : static void
191 0 : compute_missing (gcry_mpi_t rsa_p, gcry_mpi_t rsa_q, gcry_mpi_t rsa_e)
192 : {
193 : gcry_mpi_t rsa_n, rsa_d, rsa_pm1, rsa_qm1, rsa_u;
194 : gcry_mpi_t phi, tmp_g, tmp_f;
195 :
196 0 : rsa_n = gcry_mpi_new (0);
197 0 : rsa_d = gcry_mpi_new (0);
198 0 : rsa_pm1 = gcry_mpi_new (0);
199 0 : rsa_qm1 = gcry_mpi_new (0);
200 0 : rsa_u = gcry_mpi_new (0);
201 :
202 0 : phi = gcry_mpi_new (0);
203 0 : tmp_f = gcry_mpi_new (0);
204 0 : tmp_g = gcry_mpi_new (0);
205 :
206 : /* Check that p < q; if not swap p and q. */
207 0 : if (openpgp_mode && gcry_mpi_cmp (rsa_p, rsa_q) > 0)
208 : {
209 0 : fprintf (stderr, PGM ": swapping p and q\n");
210 0 : gcry_mpi_swap (rsa_p, rsa_q);
211 : }
212 :
213 0 : gcry_mpi_mul (rsa_n, rsa_p, rsa_q);
214 :
215 :
216 : /* Compute the Euler totient: phi = (p-1)(q-1) */
217 0 : gcry_mpi_sub_ui (rsa_pm1, rsa_p, 1);
218 0 : gcry_mpi_sub_ui (rsa_qm1, rsa_q, 1);
219 0 : gcry_mpi_mul (phi, rsa_pm1, rsa_qm1);
220 :
221 0 : if (!gcry_mpi_gcd (tmp_g, rsa_e, phi))
222 0 : die ("parameter 'e' does match 'p' and 'q'\n");
223 :
224 : /* Compute: f = lcm(p-1,q-1) = phi / gcd(p-1,q-1) */
225 0 : gcry_mpi_gcd (tmp_g, rsa_pm1, rsa_qm1);
226 0 : gcry_mpi_div (tmp_f, NULL, phi, tmp_g, -1);
227 :
228 : /* Compute the secret key: d = e^{-1} mod lcm(p-1,q-1) */
229 0 : gcry_mpi_invm (rsa_d, rsa_e, tmp_f);
230 :
231 : /* Compute the CRT helpers: d mod (p-1), d mod (q-1) */
232 0 : gcry_mpi_mod (rsa_pm1, rsa_d, rsa_pm1);
233 0 : gcry_mpi_mod (rsa_qm1, rsa_d, rsa_qm1);
234 :
235 : /* Compute the CRT value: OpenPGP: u = p^{-1} mod q
236 : Standard: iqmp = q^{-1} mod p */
237 0 : if (openpgp_mode)
238 0 : gcry_mpi_invm (rsa_u, rsa_p, rsa_q);
239 : else
240 0 : gcry_mpi_invm (rsa_u, rsa_q, rsa_p);
241 :
242 0 : gcry_mpi_release (phi);
243 0 : gcry_mpi_release (tmp_f);
244 0 : gcry_mpi_release (tmp_g);
245 :
246 : /* Print everything. */
247 0 : print_mpi_line ("n", rsa_n);
248 0 : print_mpi_line ("e", rsa_e);
249 0 : if (openpgp_mode)
250 0 : print_mpi_line ("d", rsa_d);
251 0 : print_mpi_line ("p", rsa_p);
252 0 : print_mpi_line ("q", rsa_q);
253 0 : if (openpgp_mode)
254 0 : print_mpi_line ("u", rsa_u);
255 : else
256 : {
257 0 : print_mpi_line ("dmp1", rsa_pm1);
258 0 : print_mpi_line ("dmq1", rsa_qm1);
259 0 : print_mpi_line ("iqmp", rsa_u);
260 : }
261 :
262 0 : gcry_mpi_release (rsa_n);
263 0 : gcry_mpi_release (rsa_d);
264 0 : gcry_mpi_release (rsa_pm1);
265 0 : gcry_mpi_release (rsa_qm1);
266 0 : gcry_mpi_release (rsa_u);
267 0 : }
268 :
269 :
270 :
271 : static void
272 0 : usage (int show_help)
273 : {
274 0 : if (!show_help)
275 : {
276 0 : fputs ("usage: " PGM
277 : " [OPTION] [FILE] (try --help for more information)\n", stderr);
278 0 : exit (2);
279 : }
280 0 : fputs
281 : ("Usage: " PGM " [OPTIONS] [FILE]\n"
282 : "Take RSA parameters p, n, e and compute missing parameters.\n"
283 : "OPTIONS:\n"
284 : " --openpgp Compute as specified by RFC4880\n"
285 : " --labels Prefix output with labels\n"
286 : " --keep-lz Keep all leading zeroes in the output\n"
287 : " --verbose Print additional information\n"
288 : " --version Print version information\n"
289 : " --help Print this text\n"
290 : "With no FILE, or if FILE is -, read standard input.\n"
291 : "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout);
292 0 : exit (0);
293 : }
294 :
295 :
296 : int
297 0 : main (int argc, char **argv)
298 : {
299 0 : int last_argc = -1;
300 : FILE *input;
301 : gcry_mpi_t rsa_p, rsa_q, rsa_e;
302 : int got_eof;
303 0 : int any = 0;
304 :
305 0 : if (argc)
306 0 : { argc--; argv++; }
307 :
308 0 : while (argc && last_argc != argc )
309 : {
310 0 : last_argc = argc;
311 0 : if (!strcmp (*argv, "--"))
312 : {
313 0 : argc--; argv++;
314 0 : break;
315 : }
316 0 : else if (!strcmp (*argv, "--help"))
317 : {
318 0 : usage (1);
319 : }
320 0 : else if (!strcmp (*argv, "--version"))
321 : {
322 0 : fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout);
323 0 : printf ("libgcrypt %s\n", gcry_check_version (NULL));
324 0 : exit (0);
325 : }
326 0 : else if (!strcmp (*argv, "--verbose"))
327 : {
328 0 : verbose++;
329 0 : argc--; argv++;
330 : }
331 0 : else if (!strcmp (*argv, "--labels"))
332 : {
333 0 : with_labels = 1;
334 0 : argc--; argv++;
335 : }
336 0 : else if (!strcmp (*argv, "--keep-lz"))
337 : {
338 0 : keep_lz = 1;
339 0 : argc--; argv++;
340 : }
341 0 : else if (!strcmp (*argv, "--openpgp"))
342 : {
343 0 : openpgp_mode = 1;
344 0 : argc--; argv++;
345 : }
346 : }
347 :
348 0 : if (argc > 1)
349 0 : usage (0);
350 :
351 : #if !defined (HAVE_W32_SYSTEM) && !defined (_WIN32)
352 0 : signal (SIGPIPE, SIG_IGN);
353 : #endif
354 :
355 0 : if (argc == 1 && strcmp (argv[0], "-"))
356 : {
357 0 : input = fopen (argv[0], "r");
358 0 : if (!input)
359 0 : die ("can't open `%s': %s\n", argv[0], strerror (errno));
360 : }
361 : else
362 0 : input = stdin;
363 :
364 0 : xgcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose);
365 0 : if (!gcry_check_version ("1.4.0"))
366 0 : die ("Libgcrypt is not sufficient enough\n");
367 0 : xgcry_control (GCRYCTL_DISABLE_SECMEM, 0);
368 0 : xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
369 :
370 : do
371 : {
372 0 : rsa_p = read_hexmpi_line (input, &got_eof);
373 0 : if (!rsa_p && got_eof)
374 0 : break;
375 0 : if (!rsa_p)
376 0 : die ("RSA parameter 'p' missing or not properly hex encoded\n");
377 0 : rsa_q = read_hexmpi_line (input, &got_eof);
378 0 : if (!rsa_q)
379 0 : die ("RSA parameter 'q' missing or not properly hex encoded\n");
380 0 : rsa_e = read_hexmpi_line (input, &got_eof);
381 0 : if (!rsa_e)
382 0 : die ("RSA parameter 'e' missing or not properly hex encoded\n");
383 0 : got_eof = skip_to_empty_line (input);
384 :
385 0 : if (any)
386 0 : putchar ('\n');
387 :
388 0 : compute_missing (rsa_p, rsa_q, rsa_e);
389 :
390 0 : gcry_mpi_release (rsa_p);
391 0 : gcry_mpi_release (rsa_q);
392 0 : gcry_mpi_release (rsa_e);
393 :
394 0 : any = 1;
395 : }
396 0 : while (!got_eof);
397 :
398 0 : return 0;
399 : }
|