/* rsacvt.c - A debug tool to convert RSA formats. Copyright (C) 2009 Free Software Foundation, Inc. This file is part of Libgcrypt. Libgcrypt is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. Libgcrypt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ /* Input data format: ======= # A hash denotes a comment line e861b700e17e8afe68[...]f1 f7a7ca5367c661f8e6[...]61 10001 # After an empty line another input block may follow. 7861b700e17e8afe68[...]f3 e7a7ca5367c661f8e6[...]71 3 ========= */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # include /* We need setmode(). */ #else # include #endif #include #include #ifdef _GCRYPT_IN_LIBGCRYPT # include "../src/gcrypt-int.h" #else # include # define PACKAGE_BUGREPORT "devnull@example.org" # define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]" #endif #define PGM "rsacvt" #define my_isascii(c) (!((c) & 0x80)) #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) /* Verbose mode flag. */ static int verbose; /* Prefix output with labels. */ static int with_labels; /* Do not suppress leading zeroes. */ static int keep_lz; /* Create parameters as specified by OpenPGP (rfc4880). That is we don't store dmp1 and dmp1 but d and make sure that p is less than q. */ static int openpgp_mode; /* Print a error message and exit the process with an error code. */ static void die (const char *format, ...) { va_list arg_ptr; va_start (arg_ptr, format); fputs (PGM ": ", stderr); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); exit (1); } static char * read_textline (FILE *fp) { char line[4096]; char *p; int any = 0; /* Read line but skip over initial empty lines. */ do { do { if (!fgets (line, sizeof line, fp)) { if (feof (fp)) return NULL; die ("error reading input line: %s\n", strerror (errno)); } p = strchr (line, '\n'); if (p) *p = 0; p = line + (*line? (strlen (line)-1):0); for ( ;p > line; p--) if (my_isascii (*p) && isspace (*p)) *p = 0; } while (!any && !*line); any = 1; } while (*line == '#'); /* Always skip comment lines. */ if (verbose > 1) fprintf (stderr, PGM ": received line: %s\n", line); return gcry_xstrdup (line); } static gcry_mpi_t read_hexmpi_line (FILE *fp, int *got_eof) { gpg_error_t err; gcry_mpi_t a; char *line; *got_eof = 0; line = read_textline (fp); if (!line) { *got_eof = 1; return NULL; } err = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL); gcry_free (line); if (err) a = NULL; return a; } static int skip_to_empty_line (FILE *fp) { char line[256]; char *p; do { if (!fgets (line, sizeof line, fp)) { if (feof (fp)) return -1; die ("error reading input line: %s\n", strerror (errno)); } p = strchr (line, '\n'); if (p) *p =0; } while (*line); return 0; } /* Print an MPI on a line. */ static void print_mpi_line (const char *label, gcry_mpi_t a) { unsigned char *buf, *p; gcry_error_t err; int writerr = 0; if (with_labels && label) printf ("%s = ", label); err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a); if (err) die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); p = buf; if (!keep_lz && p[0] == '0' && p[1] == '0' && p[2]) p += 2; printf ("%s\n", p); if (ferror (stdout)) writerr++; if (!writerr && fflush (stdout) == EOF) writerr++; if (writerr) die ("writing output failed: %s\n", strerror (errno)); gcry_free (buf); } /* Compute and print missing RSA parameters. */ static void compute_missing (gcry_mpi_t rsa_p, gcry_mpi_t rsa_q, gcry_mpi_t rsa_e) { gcry_mpi_t rsa_n, rsa_d, rsa_pm1, rsa_qm1, rsa_u; gcry_mpi_t phi, tmp_g, tmp_f; rsa_n = gcry_mpi_new (0); rsa_d = gcry_mpi_new (0); rsa_pm1 = gcry_mpi_new (0); rsa_qm1 = gcry_mpi_new (0); rsa_u = gcry_mpi_new (0); phi = gcry_mpi_new (0); tmp_f = gcry_mpi_new (0); tmp_g = gcry_mpi_new (0); /* Check that p < q; if not swap p and q. */ if (openpgp_mode && gcry_mpi_cmp (rsa_p, rsa_q) > 0) { fprintf (stderr, PGM ": swapping p and q\n"); gcry_mpi_swap (rsa_p, rsa_q); } gcry_mpi_mul (rsa_n, rsa_p, rsa_q); /* Compute the Euler totient: phi = (p-1)(q-1) */ gcry_mpi_sub_ui (rsa_pm1, rsa_p, 1); gcry_mpi_sub_ui (rsa_qm1, rsa_q, 1); gcry_mpi_mul (phi, rsa_pm1, rsa_qm1); if (!gcry_mpi_gcd (tmp_g, rsa_e, phi)) die ("parameter 'e' does match 'p' and 'q'\n"); /* Compute: f = lcm(p-1,q-1) = phi / gcd(p-1,q-1) */ gcry_mpi_gcd (tmp_g, rsa_pm1, rsa_qm1); gcry_mpi_div (tmp_f, NULL, phi, tmp_g, -1); /* Compute the secret key: d = e^{-1} mod lcm(p-1,q-1) */ gcry_mpi_invm (rsa_d, rsa_e, tmp_f); /* Compute the CRT helpers: d mod (p-1), d mod (q-1) */ gcry_mpi_mod (rsa_pm1, rsa_d, rsa_pm1); gcry_mpi_mod (rsa_qm1, rsa_d, rsa_qm1); /* Compute the CRT value: OpenPGP: u = p^{-1} mod q Standard: iqmp = q^{-1} mod p */ if (openpgp_mode) gcry_mpi_invm (rsa_u, rsa_p, rsa_q); else gcry_mpi_invm (rsa_u, rsa_q, rsa_p); gcry_mpi_release (phi); gcry_mpi_release (tmp_f); gcry_mpi_release (tmp_g); /* Print everything. */ print_mpi_line ("n", rsa_n); print_mpi_line ("e", rsa_e); if (openpgp_mode) print_mpi_line ("d", rsa_d); print_mpi_line ("p", rsa_p); print_mpi_line ("q", rsa_q); if (openpgp_mode) print_mpi_line ("u", rsa_u); else { print_mpi_line ("dmp1", rsa_pm1); print_mpi_line ("dmq1", rsa_qm1); print_mpi_line ("iqmp", rsa_u); } gcry_mpi_release (rsa_n); gcry_mpi_release (rsa_d); gcry_mpi_release (rsa_pm1); gcry_mpi_release (rsa_qm1); gcry_mpi_release (rsa_u); } static void usage (int show_help) { if (!show_help) { fputs ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n", stderr); exit (2); } fputs ("Usage: " PGM " [OPTIONS] [FILE]\n" "Take RSA parameters p, n, e and compute missing parameters.\n" "OPTIONS:\n" " --openpgp Compute as specified by RFC4880\n" " --labels Prefix output with labels\n" " --keep-lz Keep all leading zeroes in the output\n" " --verbose Print additional information\n" " --version Print version information\n" " --help Print this text\n" "With no FILE, or if FILE is -, read standard input.\n" "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout); exit (0); } int main (int argc, char **argv) { int last_argc = -1; FILE *input; gcry_mpi_t rsa_p, rsa_q, rsa_e; int got_eof; int any = 0; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { usage (1); } else if (!strcmp (*argv, "--version")) { fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout); printf ("libgcrypt %s\n", gcry_check_version (NULL)); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose++; argc--; argv++; } else if (!strcmp (*argv, "--labels")) { with_labels = 1; argc--; argv++; } else if (!strcmp (*argv, "--keep-lz")) { keep_lz = 1; argc--; argv++; } else if (!strcmp (*argv, "--openpgp")) { openpgp_mode = 1; argc--; argv++; } } if (argc > 1) usage (0); #if !defined (HAVE_W32_SYSTEM) && !defined (_WIN32) signal (SIGPIPE, SIG_IGN); #endif if (argc == 1 && strcmp (argv[0], "-")) { input = fopen (argv[0], "r"); if (!input) die ("can't open `%s': %s\n", argv[0], strerror (errno)); } else input = stdin; gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose); if (!gcry_check_version ("1.4.0")) die ("Libgcrypt is not sufficient enough\n"); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); do { rsa_p = read_hexmpi_line (input, &got_eof); if (!rsa_p && got_eof) break; if (!rsa_p) die ("RSA parameter 'p' missing or not properly hex encoded\n"); rsa_q = read_hexmpi_line (input, &got_eof); if (!rsa_q) die ("RSA parameter 'q' missing or not properly hex encoded\n"); rsa_e = read_hexmpi_line (input, &got_eof); if (!rsa_e) die ("RSA parameter 'e' missing or not properly hex encoded\n"); got_eof = skip_to_empty_line (input); if (any) putchar ('\n'); compute_missing (rsa_p, rsa_q, rsa_e); gcry_mpi_release (rsa_p); gcry_mpi_release (rsa_q); gcry_mpi_release (rsa_e); any = 1; } while (!got_eof); return 0; }