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
24e861b700e17e8afe68[...]f1
25f7a7ca5367c661f8e6[...]61
2610001
27
28# After an empty line another input block may follow.
297861b700e17e8afe68[...]f3
30e7a7ca5367c661f8e6[...]71
313
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.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.  */
78static int verbose;
79
80/* Prefix output with labels.  */
81static int with_labels;
82
83/* Do not suppress leading zeroes.  */
84static 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.  */
88static int openpgp_mode;
89
90
91/* Print a error message and exit the process with an error code.  */
92static void
93die (const char *format, ...)
94{
95  va_list arg_ptr;
96
97  va_start (arg_ptr, format);
98  fputs (PGM ": ", stderr);
99  vfprintf (stderr, format, arg_ptr);
100  va_end (arg_ptr);
101  exit (1);
102}
103
104
105static char *
106read_textline (FILE *fp)
107{
108  char line[4096];
109  char *p;
110  int any = 0;
111
112  /* Read line but skip over initial empty lines.  */
113  do
114    {
115      do
116        {
117          if (!fgets (line, sizeof line, fp))
118            {
119              if (feof (fp))
120                return NULL;
121              die ("error reading input line: %s\n", strerror (errno));
122            }
123          p = strchr (line, '\n');
124          if (p)
125            *p = 0;
126          p = line + (*line? (strlen (line)-1):0);
127          for ( ;p > line; p--)
128            if (my_isascii (*p) && isspace (*p))
129              *p = 0;
130        }
131      while (!any && !*line);
132      any = 1;
133    }
134  while (*line == '#');  /* Always skip comment lines.  */
135  if (verbose > 1)
136    fprintf (stderr, PGM ": received line: %s\n", line);
137  return gcry_xstrdup (line);
138}
139
140
141static gcry_mpi_t
142read_hexmpi_line (FILE *fp, int *got_eof)
143{
144  gpg_error_t err;
145  gcry_mpi_t a;
146  char *line;
147
148  *got_eof = 0;
149  line = read_textline (fp);
150  if (!line)
151    {
152      *got_eof = 1;
153      return NULL;
154    }
155  err = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL);
156  gcry_free (line);
157  if (err)
158    a = NULL;
159  return a;
160}
161
162
163static int
164skip_to_empty_line (FILE *fp)
165{
166  char line[256];
167  char *p;
168
169  do
170    {
171      if (!fgets (line, sizeof line, fp))
172        {
173          if (feof (fp))
174            return -1;
175          die ("error reading input line: %s\n", strerror (errno));
176        }
177      p = strchr (line, '\n');
178      if (p)
179        *p =0;
180    }
181  while (*line);
182  return 0;
183}
184
185
186/* Print an MPI on a line.  */
187static void
188print_mpi_line (const char *label, gcry_mpi_t a)
189{
190  unsigned char *buf, *p;
191  gcry_error_t err;
192  int writerr = 0;
193
194  if (with_labels && label)
195    printf ("%s = ", label);
196
197  err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a);
198  if (err)
199    die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err));
200
201  p = buf;
202  if (!keep_lz && p[0] == '0' && p[1] == '0' && p[2])
203    p += 2;
204
205  printf ("%s\n", p);
206  if (ferror (stdout))
207    writerr++;
208  if (!writerr && fflush (stdout) == EOF)
209    writerr++;
210  if (writerr)
211    die ("writing output failed: %s\n", strerror (errno));
212  gcry_free (buf);
213}
214
215
216/* Compute and print missing RSA parameters.  */
217static void
218compute_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  rsa_n = gcry_mpi_new (0);
224  rsa_d = gcry_mpi_new (0);
225  rsa_pm1 = gcry_mpi_new (0);
226  rsa_qm1 = gcry_mpi_new (0);
227  rsa_u = gcry_mpi_new (0);
228
229  phi = gcry_mpi_new (0);
230  tmp_f = gcry_mpi_new (0);
231  tmp_g = gcry_mpi_new (0);
232
233  /* Check that p < q; if not swap p and q.  */
234  if (openpgp_mode && gcry_mpi_cmp (rsa_p, rsa_q) > 0)
235    {
236      fprintf (stderr, PGM ": swapping p and q\n");
237      gcry_mpi_swap (rsa_p, rsa_q);
238    }
239
240  gcry_mpi_mul (rsa_n, rsa_p, rsa_q);
241
242
243  /* Compute the Euler totient:  phi = (p-1)(q-1)  */
244  gcry_mpi_sub_ui (rsa_pm1, rsa_p, 1);
245  gcry_mpi_sub_ui (rsa_qm1, rsa_q, 1);
246  gcry_mpi_mul (phi, rsa_pm1, rsa_qm1);
247
248  if (!gcry_mpi_gcd (tmp_g, rsa_e, phi))
249    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  gcry_mpi_gcd (tmp_g, rsa_pm1, rsa_qm1);
253  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  gcry_mpi_invm (rsa_d, rsa_e, tmp_f);
257
258  /* Compute the CRT helpers: d mod (p-1), d mod (q-1)   */
259  gcry_mpi_mod (rsa_pm1, rsa_d, rsa_pm1);
260  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  if (openpgp_mode)
265    gcry_mpi_invm (rsa_u, rsa_p, rsa_q);
266  else
267    gcry_mpi_invm (rsa_u, rsa_q, rsa_p);
268
269  gcry_mpi_release (phi);
270  gcry_mpi_release (tmp_f);
271  gcry_mpi_release (tmp_g);
272
273  /* Print everything.  */
274  print_mpi_line ("n", rsa_n);
275  print_mpi_line ("e", rsa_e);
276  if (openpgp_mode)
277    print_mpi_line ("d", rsa_d);
278  print_mpi_line ("p", rsa_p);
279  print_mpi_line ("q", rsa_q);
280  if (openpgp_mode)
281    print_mpi_line ("u", rsa_u);
282  else
283    {
284      print_mpi_line ("dmp1", rsa_pm1);
285      print_mpi_line ("dmq1", rsa_qm1);
286      print_mpi_line ("iqmp", rsa_u);
287    }
288
289  gcry_mpi_release (rsa_n);
290  gcry_mpi_release (rsa_d);
291  gcry_mpi_release (rsa_pm1);
292  gcry_mpi_release (rsa_qm1);
293  gcry_mpi_release (rsa_u);
294}
295
296
297
298static void
299usage (int show_help)
300{
301  if (!show_help)
302    {
303      fputs ("usage: " PGM
304             " [OPTION] [FILE] (try --help for more information)\n", stderr);
305      exit (2);
306    }
307  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  exit (0);
320}
321
322
323int
324main (int argc, char **argv)
325{
326  int last_argc = -1;
327  FILE *input;
328  gcry_mpi_t  rsa_p, rsa_q, rsa_e;
329  int got_eof;
330  int any = 0;
331
332  if (argc)
333    { argc--; argv++; }
334
335  while (argc && last_argc != argc )
336    {
337      last_argc = argc;
338      if (!strcmp (*argv, "--"))
339        {
340          argc--; argv++;
341          break;
342        }
343      else if (!strcmp (*argv, "--help"))
344        {
345          usage (1);
346        }
347      else if (!strcmp (*argv, "--version"))
348        {
349          fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout);
350          printf ("libgcrypt %s\n", gcry_check_version (NULL));
351          exit (0);
352        }
353      else if (!strcmp (*argv, "--verbose"))
354        {
355          verbose++;
356          argc--; argv++;
357        }
358      else if (!strcmp (*argv, "--labels"))
359        {
360          with_labels = 1;
361          argc--; argv++;
362        }
363      else if (!strcmp (*argv, "--keep-lz"))
364        {
365          keep_lz = 1;
366          argc--; argv++;
367        }
368      else if (!strcmp (*argv, "--openpgp"))
369        {
370          openpgp_mode = 1;
371          argc--; argv++;
372        }
373    }
374
375  if (argc > 1)
376    usage (0);
377
378#if !defined (HAVE_W32_SYSTEM) && !defined (_WIN32)
379  signal (SIGPIPE, SIG_IGN);
380#endif
381
382  if (argc == 1 && strcmp (argv[0], "-"))
383    {
384      input = fopen (argv[0], "r");
385      if (!input)
386        die ("can't open `%s': %s\n", argv[0], strerror (errno));
387    }
388  else
389    input = stdin;
390
391  gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose);
392  if (!gcry_check_version ("1.4.0"))
393    die ("Libgcrypt is not sufficient enough\n");
394  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
395  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
396
397  do
398    {
399      rsa_p = read_hexmpi_line (input, &got_eof);
400      if (!rsa_p && got_eof)
401        break;
402      if (!rsa_p)
403        die ("RSA parameter 'p' missing or not properly hex encoded\n");
404      rsa_q = read_hexmpi_line (input, &got_eof);
405      if (!rsa_q)
406        die ("RSA parameter 'q' missing or not properly hex encoded\n");
407      rsa_e = read_hexmpi_line (input, &got_eof);
408      if (!rsa_e)
409        die ("RSA parameter 'e' missing or not properly hex encoded\n");
410      got_eof = skip_to_empty_line (input);
411
412      if (any)
413        putchar ('\n');
414
415      compute_missing (rsa_p, rsa_q, rsa_e);
416
417      gcry_mpi_release (rsa_p);
418      gcry_mpi_release (rsa_q);
419      gcry_mpi_release (rsa_e);
420
421      any = 1;
422    }
423  while (!got_eof);
424
425  return 0;
426}
427