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