From fa2faef48f4ed13d7701368ed2332170531e7c1b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 16 Jul 2003 13:47:14 +0000 Subject: [PATCH] * Makefile.am: Add sc-copykeys program. * sc-copykeys.c: New. * app-openpgp.c (app_openpgp_storekey): New. (app_openpgp_cardinfo): New. (count_bits): New. (store_fpr): And use it here to get the actual length in bit. --- scd/ChangeLog | 9 + scd/Makefile.am | 26 +- scd/app-common.h | 18 ++ scd/app-openpgp.c | 256 +++++++++++++++- scd/sc-copykeys.c | 735 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1031 insertions(+), 13 deletions(-) create mode 100644 scd/sc-copykeys.c diff --git a/scd/ChangeLog b/scd/ChangeLog index c7327f0b2..b5c49e96a 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,12 @@ +2003-07-15 Werner Koch + + * Makefile.am: Add sc-copykeys program. + * sc-copykeys.c: New. + * app-openpgp.c (app_openpgp_storekey): New. + (app_openpgp_cardinfo): New. + (count_bits): New. + (store_fpr): And use it here to get the actual length in bit. + 2003-07-03 Werner Koch * app-openpgp.c (do_setattr): Add setting of the URL. diff --git a/scd/Makefile.am b/scd/Makefile.am index 833a4cdf9..93bd8c73d 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -21,7 +21,7 @@ localedir = $(datadir)/locale INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" -bin_PROGRAMS = scdaemon sc-investigate +bin_PROGRAMS = scdaemon sc-investigate sc-copykeys AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) @@ -43,15 +43,27 @@ sc_investigate_SOURCES = \ sc-investigate.c scdaemon.h \ apdu.c apdu.h \ iso7816.c iso7816.h \ + app.c app-common.h \ app-openpgp.c \ atr.c atr.h -sc_investigate_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ - $(LIBGCRYPT_LIBS) -lgpg-error -ldl - - - - +sc_investigate_LDADD = \ + ../jnlib/libjnlib.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) -lgpg-error -ldl + + +sc_copykeys_SOURCES = \ + sc-copykeys.c scdaemon.h \ + apdu.c apdu.h \ + iso7816.c iso7816.h \ + app.c app-common.h \ + app-openpgp.c \ + atr.c atr.h + +sc_copykeys_LDADD = \ + ../jnlib/libjnlib.a ../common/libcommon.a \ + ../common/libsimple-pwquery.a \ + $(LIBGCRYPT_LIBS) -lgpg-error -ldl diff --git a/scd/app-common.h b/scd/app-common.h index 00d09828c..1527338dd 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -83,6 +83,24 @@ int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, /*-- app-openpgp.c --*/ int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); +int app_openpgp_cardinfo (APP app, + char **serialno, + char **disp_name, + char **pubkey_url, + unsigned char **fpr1, + unsigned char **fpr2, + unsigned char **fpr3); +int app_openpgp_storekey (APP app, int keyno, + unsigned char *template, size_t template_len, + time_t created_at, + const unsigned char *m, size_t mlen, + const unsigned char *e, size_t elen, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); +int app_openpgp_readkey (APP app, int keyno, + unsigned char **m, size_t *mlen, + unsigned char **e, size_t *elen); + #endif /*GNUPG_SCD_APP_COMMON_H*/ diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index b546ed569..1f9ec56f0 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -341,6 +341,25 @@ dump_all_do (int slot) } } + +/* Count the number of bits, assuming the A represents an unsigned big + integer of length LEN bytes. */ +static unsigned int +count_bits (const unsigned char *a, size_t len) +{ + unsigned int n = len * 8; + int i; + + for (; len && !*a; len--, a++, n -=8) + ; + if (len) + { + for (i=7; i && !(*a & (1<> 8; *p++ = timestamp; *p++ = 1; /* RSA */ - *p++ = (mlen*8) >> 8; /* (we want number of bits here) */ - *p++ = (mlen*8); + nbits = count_bits (m, mlen); + *p++ = nbits >> 8; + *p++ = nbits; memcpy (p, m, mlen); p += mlen; - *p++ = (elen*8) >> 8; - *p++ = (elen*8); + nbits = count_bits (e, elen); + *p++ = nbits >> 8; + *p++ = nbits; memcpy (p, e, elen); p += elen; + log_printhex ("fprbuf:", buffer, n+3); gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3); xfree (buffer); @@ -603,7 +630,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, } xfree (buffer); buffer = NULL; -#if 1 +#if 0 log_info ("please wait while key is being generated ...\n"); start_at = time (NULL); rc = iso7816_generate_keypair @@ -627,6 +654,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); if (!keydata) { + rc = gpg_error (GPG_ERR_CARD); log_error ("response does not contain the public key data\n"); goto leave; } @@ -634,6 +662,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); if (!m) { + rc = gpg_error (GPG_ERR_CARD); log_error ("response does not contain the RSA modulus\n"); goto leave; } @@ -643,6 +672,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); if (!e) { + rc = gpg_error (GPG_ERR_CARD); log_error ("response does not contain the RSA public exponent\n"); goto leave; } @@ -996,4 +1026,218 @@ leave: +/* This function is a hack to retrieve essential information about the + card to be displayed by simple tools. It mostly resembles what the + LEARN command returns. All parameters return allocated strings or + buffers or NULL if the data object is not available. All returned + values are sanitized. */ +int +app_openpgp_cardinfo (APP app, + char **serialno, + char **disp_name, + char **pubkey_url, + unsigned char **fpr1, + unsigned char **fpr2, + unsigned char **fpr3) +{ + int rc; + void *relptr; + unsigned char *value; + size_t valuelen; + if (serialno) + { + time_t dummy; + + *serialno = NULL; + rc = app_get_serial_and_stamp (app, serialno, &dummy); + if (rc) + { + log_error ("error getting serial number: %s\n", gpg_strerror (rc)); + return rc; + } + } + + if (disp_name) + { + *disp_name = NULL; + relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); + if (relptr) + { + *disp_name = make_printable_string (value, valuelen, 0); + xfree (relptr); + } + } + + if (pubkey_url) + { + *pubkey_url = NULL; + relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen); + if (relptr) + { + *pubkey_url = make_printable_string (value, valuelen, 0); + xfree (relptr); + } + } + + if (fpr1) + *fpr1 = NULL; + if (fpr2) + *fpr2 = NULL; + if (fpr3) + *fpr3 = NULL; + relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); + if (relptr && valuelen >= 60) + { + if (fpr1) + { + *fpr1 = xmalloc (20); + memcpy (*fpr1, value + 0, 20); + } + if (fpr2) + { + *fpr2 = xmalloc (20); + memcpy (*fpr2, value + 20, 20); + } + if (fpr3) + { + *fpr3 = xmalloc (20); + memcpy (*fpr3, value + 40, 20); + } + } + xfree (relptr); + + return 0; +} + + + +/* This function is currently only used by the sc-copykeys program to + store a key on the smartcard. APP ist the application handle, + KEYNO is the number of the key and PINCB, PINCB_ARG are used to ask + for the SO PIN. TEMPLATE and TEMPLATE_LEN describe a buffer with + the key template to store. CREATED_AT is the timestamp used to + create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the + RSA public exponent. This function silently overwrites an existing + key.*/ +int +app_openpgp_storekey (APP app, int keyno, + unsigned char *template, size_t template_len, + time_t created_at, + const unsigned char *m, size_t mlen, + const unsigned char *e, size_t elen, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + unsigned char fprbuf[20]; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + { + char *pinvalue; + rc = pincb (pincb_arg, "Admin PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + return rc; + } + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + } + if (rc) + { + log_error ("verify CHV3 failed: rc=%04X\n", rc); + goto leave; + } + + rc = iso7816_put_data (app->slot, 0xE9 + keyno, template, template_len); + if (rc) + { + log_error ("failed to store the key: rc=%04X\n", rc); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + + log_printhex ("RSA n:", m, mlen); + log_printhex ("RSA e:", e, elen); + + rc = store_fpr (app->slot, keyno, (u32)created_at, + m, mlen, e, elen, fprbuf); + + leave: + return rc; +} + + +/* Utility function for external tools: Read the public RSA key at + KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */ +int +app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, + unsigned char **e, size_t *elen) +{ + int rc; + const unsigned char *keydata, *a; + unsigned char *buffer; + size_t buflen, keydatalen, alen; + + *m = NULL; + *e = NULL; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + rc = iso7816_read_public_key(app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", + 2, + &buffer, &buflen); + if (rc) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("reading key failed\n"); + goto leave; + } + + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + if (!keydata) + { + log_error ("response does not contain the public key data\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + + a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0); + if (!a) + { + log_error ("response does not contain the RSA modulus\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + *mlen = alen; + *m = xmalloc (alen); + memcpy (*m, a, alen); + + a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0); + if (!e) + { + log_error ("response does not contain the RSA public exponent\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + *elen = alen; + *e = xmalloc (alen); + memcpy (*e, a, alen); + + leave: + xfree (buffer); + if (rc) + { + xfree (*m); *m = NULL; + xfree (*e); *e = NULL; + } + return rc; +} diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c new file mode 100644 index 000000000..d9a009b33 --- /dev/null +++ b/scd/sc-copykeys.c @@ -0,0 +1,735 @@ +/* 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, + oDebug, + oDebugAll, + +aTest }; + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, "@Options:\n " }, + + { oVerbose, "verbose", 0, "verbose" }, + { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"}, + { 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; + int reader_port = 32768; /* First USB reader. */ + 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; + 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, &appbuf.serialno, &appbuf.serialnolen); + 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; + n = 4; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &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; + n = 64; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &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; + n = 64; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &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) */ + n = 128; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, &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); +} + +