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