/* sc-copykeys.c - A tool to store keys on a smartcard. * Copyright (C) 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #define JNLIB_NEED_LOG_LOGV #include "scdaemon.h" #include #include "../common/ttyio.h" #include "../common/simple-pwquery.h" #include "apdu.h" /* for open_reader */ #include "atr.h" #include "app-common.h" #define _(a) (a) enum cmd_and_opt_values { oVerbose = 'v', oReaderPort = 500, octapiDriver, oDebug, oDebugAll, aTest }; static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, "@Options:\n " }, { oVerbose, "verbose", 0, "verbose" }, { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"}, { octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"}, { oDebug, "debug" ,4|16, "set debugging flags"}, { oDebugAll, "debug-all" ,0, "enable full debugging"}, {0} }; static void copykeys (APP app, const char *fname); static const char * my_strusage (int level) { const char *p; switch (level) { case 11: p = "sc-copykeys (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); break; case 1: case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n"); break; case 41: p = _("Syntax: sc-copykeys [options] " "file-with-key\n" "Copy keys to a smartcards\n"); break; default: p = NULL; } return p; } /* Used by gcry for logging */ static void my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) { /* translate the log levels */ switch (level) { case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; default: level = JNLIB_LOG_ERROR; break; } log_logv (level, fmt, arg_ptr); } int main (int argc, char **argv ) { ARGPARSE_ARGS pargs; int slot, rc; const char *reader_port = NULL; struct app_ctx_s appbuf; memset (&appbuf, 0, sizeof appbuf); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); log_set_prefix ("sc-copykeys", 1); /* check that the libraries are suitable. Do it here because the option parsing may need services of the library */ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) { log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); } gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */ /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/ pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ while (arg_parse (&pargs, opts) ) { switch (pargs.r_opt) { case oVerbose: opt.verbose++; break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; case oReaderPort: reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; default : pargs.err = 2; break; } } if (log_get_errorcount(0)) exit(2); if (argc != 1) usage (1); slot = apdu_open_reader (reader_port); if (slot == -1) exit (1); /* FIXME: Use select_application. */ appbuf.slot = slot; rc = app_select_openpgp (&appbuf); if (rc) { log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); exit (1); } appbuf.initialized = 1; log_info ("openpgp application selected\n"); copykeys (&appbuf, *argv); return 0; } void send_status_info (CTRL ctrl, const char *keyword, ...) { /* DUMMY */ } static char * read_file (const char *fname, size_t *r_length) { FILE *fp; struct stat st; char *buf; size_t buflen; fp = fname? fopen (fname, "rb") : stdin; if (!fp) { log_error ("can't open `%s': %s\n", fname? fname: "[stdin]", strerror (errno)); return NULL; } if (fstat (fileno(fp), &st)) { log_error ("can't stat `%s': %s\n", fname? fname: "[stdin]", strerror (errno)); if (fname) fclose (fp); return NULL; } buflen = st.st_size; buf = xmalloc (buflen+1); if (fread (buf, buflen, 1, fp) != 1) { log_error ("error reading `%s': %s\n", fname? fname: "[stdin]", strerror (errno)); if (fname) fclose (fp); xfree (buf); return NULL; } if (fname) fclose (fp); *r_length = buflen; return buf; } static gcry_sexp_t read_key (const char *fname) { char *buf; size_t buflen; gcry_sexp_t private; int rc; buf = read_file (fname, &buflen); if (!buf) return NULL; rc = gcry_sexp_new (&private, buf, buflen, 1); if (rc) { log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc)); return NULL; } xfree (buf); return private; } static gcry_mpi_t * sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created) { gcry_sexp_t list, l2; const char *name; const char *s; size_t n; int i, idx; const char *elems; gcry_mpi_t *array; *created = 0; list = gcry_sexp_find_token (sexp, "private-key", 0 ); if(!list) return NULL; /* quick hack to get the creation time. */ l2 = gcry_sexp_find_token (list, "created", 0); if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n))) { char *tmp = xmalloc (n+1); memcpy (tmp, name, n); tmp[n] = 0; *created = strtoul (tmp, NULL, 10); xfree (tmp); } gcry_sexp_release (l2); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if(!name || n != 3 || memcmp (name, "rsa", 3)) { gcry_sexp_release (list); return NULL; } /* Parameter names used with RSA. */ elems = "nedpqu"; array = xcalloc (strlen(elems) + 1, sizeof *array); for (idx=0, s=elems; *s; s++, idx++ ) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { for (i=0; i 32) { log_error ("public exponent too large (more than 32 bits)\n"); goto failure; } nbits = gcry_mpi_get_nbits (rsa_p); if (nbits != 512) { log_error ("length of first RSA prime is not 512\n"); goto failure; } nbits = gcry_mpi_get_nbits (rsa_q); if (nbits != 512) { log_error ("length of second RSA prime is not 512\n"); goto failure; } nbits = gcry_mpi_get_nbits (rsa_n); if (nbits != 1024) { log_error ("length of RSA modulus is not 1024\n"); goto failure; } keyno = query_card (app); if (!keyno) goto failure; /* Build the private key template as described in section 4.3.3.6 of the specs. 0xC0 public exponent 0xC1 prime p 0xC2 prime q */ template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64); *tp++ = 0xC0; *tp++ = 4; rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 4, &n, rsa_e); if (rc) { log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); goto failure; } assert (n <= 4); memcpy (e, tp, n); elen = n; if (n != 4) { memmove (tp+4-n, tp, 4-n); memset (tp, 0, 4-n); } tp += 4; *tp++ = 0xC1; *tp++ = 64; rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_p); if (rc) { log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); goto failure; } assert (n == 64); tp += 64; *tp++ = 0xC2; *tp++ = 64; rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_q); if (rc) { log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); goto failure; } assert (n == 64); tp += 64; assert (tp - template == 138); /* (we need the modulus to calculate the fingerprint) */ rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, 128, &n, rsa_n); if (rc) { log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); goto failure; } assert (n == 128); mlen = 128; rc = app_openpgp_storekey (app, keyno, template, tp - template, created_at, m, mlen, e, elen, pincb, NULL); if (rc) { log_error ("error storing key: %s\n", gpg_strerror (rc)); goto failure; } log_info ("key successfully stored\n"); { unsigned char *mm, *ee; size_t mmlen, eelen; int i; rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen); if (rc) { log_error ("error reading key back: %s\n", gpg_strerror (rc)); goto failure; } /* Strip leading zeroes. */ for (i=0; i < mmlen && !mm[i]; i++) ; mmlen -= i; memmove (mm, mm+i, mmlen); for (i=0; i < eelen && !ee[i]; i++) ; eelen -= i; memmove (ee, ee+i, eelen); if (eelen != elen || mmlen != mlen) { log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n", (unsigned int)mlen, (unsigned int)mmlen, (unsigned int)elen, (unsigned int)eelen); xfree (mm); xfree (ee); goto failure; } if (memcmp (m, mm, mlen)) { log_error ("key parameter n mismatch\n"); log_printhex ("original n: ", m, mlen); log_printhex (" copied n: ", mm, mlen); xfree (mm); xfree (ee); goto failure; } if (memcmp (e, ee, elen)) { log_error ("key parameter e mismatch\n"); log_printhex ("original e: ", e, elen); log_printhex (" copied e: ", ee, elen); xfree (mm); xfree (ee); goto failure; } xfree (mm); xfree (ee); } gcry_mpi_release (rsa_e); gcry_mpi_release (rsa_p); gcry_mpi_release (rsa_q); gcry_mpi_release (rsa_n); return; failure: gcry_mpi_release (rsa_e); gcry_mpi_release (rsa_p); gcry_mpi_release (rsa_q); gcry_mpi_release (rsa_n); exit (1); }