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