From be2aa37dbf0da2c953143c328a1aed4bf210cc87 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 21 May 2005 14:04:32 +0000 Subject: [PATCH] * cardglue.c (send_status_info): Make CTRL optional. (agent_scd_writekey, inq_writekey_parms): New. (agent_openpgp_storekey): Removed. * cardglue.h: Add a few more error code mappings. * keygen.c (copy_mpi): Removed. (save_unprotected_key_to_card): Changed to use agent_scd_writekey. * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer version in gnupg 1.9 CVS. --- NEWS | 17 +++ g10/ChangeLog | 11 ++ g10/app-common.h | 10 ++ g10/app-openpgp.c | 351 +++++++++++++++++++++++++++++++++++++++++----- g10/cardglue.c | 98 ++++++++----- g10/cardglue.h | 17 ++- g10/keygen.c | 168 +++++++--------------- g10/tlv.c | 73 ++++++++++ g10/tlv.h | 17 +++ include/ChangeLog | 4 + include/util.h | 21 ++- kbx/keybox-blob.c | 6 +- mpi/ChangeLog | 9 ++ mpi/mpi-scan.c | 13 +- mpi/mpicoder.c | 29 ++-- util/ChangeLog | 4 + util/Makefile.am | 2 +- util/membuf.c | 83 +++++++++++ 18 files changed, 724 insertions(+), 209 deletions(-) create mode 100644 util/membuf.c diff --git a/NEWS b/NEWS index 6a58be674..edcc648b7 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,23 @@ Noteworthy changes in version 1.4.2 ------------------------------------------------ + * New command "verify" in the card-edit menu to display + the Private-DO-3. The Admin command has been enhanced to take + the optional arguments "on", "off" and "verify". The latter may + be used to verify the ADmin Pin without modifying data; this + allows displayin the Private-DO-4 with the "list" command. + + * Rewrote large parts of the card code to optionally make use of a + running gpg-agent. If --use-agent is beeing used and a + gpg-agent with enabled scdaemon is active, gpg will now divert + all card operations to that daemon. This is required because + bot, scdaemon and gpg require exclusive access to the card + reader. By delegating the work to scdaemon, both can peacefully + coexist and scdaemon is able to control the use of the reader. + Note that this requires at least gnupg 1.9.17. + + * Fixed a couple of problems with the card reader. + Noteworthy changes in version 1.4.1 (2005-03-15) ------------------------------------------------ diff --git a/g10/ChangeLog b/g10/ChangeLog index 8f05b7e5b..fc36891dd 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,14 @@ +2005-05-21 Werner Koch + + * cardglue.c (send_status_info): Make CTRL optional. + (agent_scd_writekey, inq_writekey_parms): New. + (agent_openpgp_storekey): Removed. + * cardglue.h: Add a few more error code mappings. + * keygen.c (copy_mpi): Removed. + (save_unprotected_key_to_card): Changed to use agent_scd_writekey. + * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer + version in gnupg 1.9 CVS. + 2005-05-20 Werner Koch * ccid-driver.c (ccid_transceive): Arghhh. The seqno is another diff --git a/g10/app-common.h b/g10/app-common.h index f1058dda1..c2c302395 100644 --- a/g10/app-common.h +++ b/g10/app-common.h @@ -86,6 +86,11 @@ struct app_ctx_s { void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); + gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, + const char *certid, unsigned int flags, + gpg_error_t (*pincb)(void*,const char *,char **), + void *pincb_arg, + const unsigned char *pk, size_t pklen); gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), @@ -148,6 +153,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr, void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); +gpg_error_t app_writekey (app_t app, ctrl_t ctrl, + const char *keyidstr, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen); gpg_error_t app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c index 6f4778ba5..1165ec683 100644 --- a/g10/app-openpgp.c +++ b/g10/app-openpgp.c @@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp, n = 6 + 2 + mlen + 2 + elen; p = buffer = xtrymalloc (3 + n); if (!buffer) - return gpg_error (gpg_err_code_from_errno (errno)); + return gpg_error_from_errno (errno); *p++ = 0x99; /* ctb */ *p++ = n >> 8; /* 2 byte length header */ @@ -1527,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, } +/* Check whether a key already exists. KEYIDX is the index of the key + (0..2). If FORCE is TRUE a diagnositivc will be printed but no + error returned if the key already exists. */ +static gpg_error_t +does_key_exist (app_t app, int keyidx, int force) +{ + const unsigned char *fpr; + unsigned char *buffer; + size_t buflen, n; + int i; + + assert (keyidx >=0 && keyidx <= 2); + + if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen)) + { + log_error (_("error reading application data\n")); + return gpg_error (GPG_ERR_GENERAL); + } + fpr = find_tlv (buffer, buflen, 0x00C5, &n); + if (!fpr || n < 60) + { + log_error (_("error reading fingerprint DO\n")); + xfree (buffer); + return gpg_error (GPG_ERR_GENERAL); + } + fpr += 20*keyidx; + for (i=0; i < 20 && !fpr[i]; i++) + ; + xfree (buffer); + if (i!=20 && !force) + { + log_error (_("key already exists\n")); + return gpg_error (GPG_ERR_EEXIST); + } + else if (i!=20) + log_info (_("existing key will be replaced\n")); + else + log_info (_("generating new key\n")); + return 0; +} + + + +/* Handle the WRITEKEY command for OpenPGP. This function expects a + canonical encoded S-expression with the secret key in KEYDATA and + its length (for assertions) in KEYDATALEN. KEYID needs to be the + usual keyid which for OpenPGP is the string "OPENPGP.n" with + n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall + get overwritten. PINCB and PINCB_ARG are the usual arguments for + the pinentry callback. */ +static gpg_error_t +do_writekey (app_t app, ctrl_t ctrl, + const char *keyid, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen) +{ + gpg_error_t err; + int force = (flags & 1); + int keyno; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *rsa_n = NULL; + const unsigned char *rsa_e = NULL; + const unsigned char *rsa_p = NULL; + const unsigned char *rsa_q = NULL; + size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; + unsigned int nbits; + unsigned char *template = NULL; + unsigned char *tp; + size_t template_len; + unsigned char fprbuf[20]; + u32 created_at = 0; + + if (!strcmp (keyid, "OPENPGP.1")) + keyno = 0; + else if (!strcmp (keyid, "OPENPGP.2")) + keyno = 1; + else if (!strcmp (keyid, "OPENPGP.3")) + keyno = 2; + else + return gpg_error (GPG_ERR_INV_ID); + + err = does_key_exist (app, keyno, force); + if (err) + return err; + + + /* + Parse the S-expression + */ + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen)) + { + if (!tok) + ; + else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen)) + log_info ("protected-private-key passed to writekey\n"); + else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) + log_info ("shadowed-private-key passed to writekey\n"); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + { + err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + goto leave; + } + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; + case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; + case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; + case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + { + err = gpg_error (GPG_ERR_DUP_VALUE); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && mpi) + { + /* Strip off leading zero bytes and save. */ + for (;toklen && !*tok; toklen--, tok++) + ; + *mpi = tok; + *mpi_len = toklen; + } + } + /* Skip until end of list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + goto leave; + } + /* Parse other attributes. */ + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen)) + { + if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen))) + goto leave; + if (tok) + { + for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9'; + tok++, toklen--) + created_at = created_at*10 + (*tok - '0'); + } + } + /* Skip until end of list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + goto leave; + } + + + /* Check that we have all parameters and that they match the card + description. */ + if (!created_at) + { + log_error (_("creation timestamp missing\n")); + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0; + if (nbits != 1024) + { + log_error (_("RSA modulus missing or not of size %d bits\n"), 1024); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0; + if (nbits < 2 || nbits > 32) + { + log_error (_("RSA public exponent missing or largerr than %d bits\n"), + 32); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0; + if (nbits != 512) + { + log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0; + if (nbits != 512) + { + log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + + /* Build the private key template as described in section 4.3.3.6 of + the OpenPGP card specs: + 0xC0 public exponent + 0xC1 prime p + 0xC2 prime q + */ + assert (rsa_e_len <= 4); + template_len = (1 + 1 + 4 + + 1 + 1 + rsa_p_len + + 1 + 1 + rsa_q_len); + template = tp = xtrymalloc_secure (template_len); + if (!template) + { + err = gpg_error_from_errno (errno); + goto leave; + } + *tp++ = 0xC0; + *tp++ = 4; + memcpy (tp, rsa_e, rsa_e_len); + if (rsa_e_len < 4) + { + /* Right justify E. */ + memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len); + memset (tp, 0, 4-rsa_e_len); + } + tp += 4; + + *tp++ = 0xC1; + *tp++ = rsa_p_len; + memcpy (tp, rsa_p, rsa_p_len); + tp += rsa_p_len; + + *tp++ = 0xC2; + *tp++ = rsa_q_len; + memcpy (tp, rsa_q, rsa_q_len); + tp += rsa_q_len; + + assert (tp - template == template_len); + + + /* Obviously we need to remove the cached public key. */ + xfree (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + app->app_local->pk[keyno].keylen = 0; + app->app_local->pk[keyno].read_done = 0; + + /* Prepare for storing the key. */ + err = verify_chv3 (app, pincb, pincb_arg); + if (err) + goto leave; + + /* Store the key. */ + err = iso7816_put_data (app->slot, + (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, + template, template_len); + if (err) + { + log_error (_("failed to store the key: %s\n"), gpg_strerror (err)); + goto leave; + } + + err = store_fpr (app->slot, keyno, created_at, + rsa_n, rsa_n_len, rsa_e, rsa_e_len, + fprbuf, app->card_version); + if (err) + goto leave; + + + leave: + xfree (template); + return err; +} + /* Handle the GENKEY command. */ static gpg_error_t @@ -1535,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, void *pincb_arg) { int rc; - int i; char numbuf[30]; unsigned char fprbuf[20]; - const unsigned char *fpr; const unsigned char *keydata, *m, *e; - unsigned char *buffer; - size_t buflen, keydatalen, n, mlen, elen; + unsigned char *buffer = NULL; + size_t buflen, keydatalen, mlen, elen; time_t created_at; int keyno = atoi (keynostr); int force = (flags & 1); @@ -1562,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, app->app_local->pk[keyno].read_done = 0; /* Check whether a key already exists. */ - rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); + rc = does_key_exist (app, keyno, force); if (rc) - { - log_error (_("error reading application data\n")); - return gpg_error (GPG_ERR_GENERAL); - } - fpr = find_tlv (buffer, buflen, 0x00C5, &n); - if (!fpr || n != 60) - { - rc = gpg_error (GPG_ERR_GENERAL); - log_error (_("error reading fingerprint DO\n")); - goto leave; - } - fpr += 20*keyno; - for (i=0; i < 20 && !fpr[i]; i++) - ; - if (i!=20 && !force) - { - rc = gpg_error (GPG_ERR_EEXIST); - log_error (_("key already exists\n")); - goto leave; - } - else if (i!=20) - log_info (_("existing key will be replaced\n")); - else - log_info (_("generating new key\n")); + return rc; - /* Prepare for key generation by verifying the ADmin PIN. */ rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; - xfree (buffer); buffer = NULL; - #if 1 log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); @@ -2220,6 +2504,7 @@ app_select_openpgp (app_t app) app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; + app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; app->fnc.auth = do_auth; diff --git a/g10/cardglue.c b/g10/cardglue.c index 940ec64a3..01d8b4292 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -63,6 +63,15 @@ struct pincb_parm_s }; +struct writekey_parm_s +{ + assuan_context_t ctx; + const unsigned char *keydata; + size_t keydatalen; +}; + + + static char *default_reader_port; static app_t current_app; @@ -100,7 +109,7 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen, buffers. The variable elements are pairs of (char *, size_t), terminated with a (NULL, 0). */ void -send_status_info (CTRL ctrl, const char *keyword, ...) +send_status_info (ctrl_t ctrl, const char *keyword, ...) { va_list arg_ptr; const unsigned char *value; @@ -140,7 +149,8 @@ send_status_info (CTRL ctrl, const char *keyword, ...) } } *p = 0; - ctrl->status_cb (ctrl->status_cb_arg, buf); + if (ctrl && ctrl->status_cb) + ctrl->status_cb (ctrl->status_cb_arg, buf); va_end (arg_ptr); } @@ -970,6 +980,59 @@ agent_scd_setattr (const char *name, } +/* Handle a KEYDATA inquiry. Note, we only send the data, + assuan_transact takes care of flushing and writing the end */ +static assuan_error_t +inq_writekey_parms (void *opaque, const char *keyword) +{ + struct writekey_parm_s *parm = opaque; + + return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen); +} + + +/* Send a WRITEKEY command to the SCdaemon. */ +int +agent_scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen) +{ + app_t app; + int rc; + char line[ASSUAN_LINELENGTH]; + app = current_app? current_app : open_card (); + if (!app) + return gpg_error (GPG_ERR_CARD); + + if (app->assuan_ctx) + { + struct writekey_parm_s parms; + + snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno); + line[DIM(line)-1] = 0; + parms.ctx = app->assuan_ctx; + parms.keydata = keydata; + parms.keydatalen = keydatalen; + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, + inq_writekey_parms, &parms, + NULL, NULL), + "SCD WRITEKEY"); + } + else + { + snprintf (line, DIM(line)-1, "OPENPGP.%d", keyno); + line[DIM(line)-1] = 0; + rc = app->fnc.writekey (app, NULL, line, 0x0001, + pin_cb, NULL, + keydata, keydatalen); + } + + if (rc) + write_status (STATUS_SC_OP_FAILURE); + return rc; +} + + + static assuan_error_t genkey_status_cb (void *opaque, const char *line) { @@ -1281,37 +1344,6 @@ agent_scd_checkpin (const char *serialnobuf) } -/* Wrapper to call the store key helper function of app-openpgp.c. */ -int -agent_openpgp_storekey (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) -{ - app_t app; - int rc; - - app = current_app? current_app : open_card (); - if (!app) - return gpg_error (GPG_ERR_CARD); - - if (app->assuan_ctx) - { - rc = gpg_error (GPG_ERR_CARD); - } - else - { - rc = app_openpgp_storekey (app, keyno, template, template_len, - created_at, m, mlen, e, elen, - pin_cb, NULL); - } - - if (rc) - write_status (STATUS_SC_OP_FAILURE); - return rc; -} - void agent_clear_pin_cache (const char *sn) diff --git a/g10/cardglue.h b/g10/cardglue.h index 068a1aacf..a679dbb95 100644 --- a/g10/cardglue.h +++ b/g10/cardglue.h @@ -81,6 +81,7 @@ typedef struct ctrl_ctx_s *ctrl_t; #define GPG_ERR_GENERAL G10ERR_GENERAL #define GPG_ERR_BAD_PIN G10ERR_BAD_PASS +#define GPG_ERR_BAD_KEy G10ERR_BAD_KEY #define GPG_ERR_CARD G10ERR_GENERAL #define GPG_ERR_EEXIST G10ERR_FILE_EXISTS #define GPG_ERR_ENOMEM G10ERR_RESOURCE_LIMIT @@ -105,6 +106,10 @@ typedef struct ctrl_ctx_s *ctrl_t; #define GPG_ERR_EOF (-1) #define GPG_ERR_CARD_NOT_PRESENT G10ERR_NO_CARD #define GPG_ERR_CARD_RESET G10ERR_GENERAL +#define GPG_ERR_WRONG_PUBKEY_ALGO G10ERR_PUBKEY_ALGO +#define GPG_ERR_UNKNOWN_SEXP G10ERR_INV_ARG +#define GPG_ERR_DUP_VALUE G10ERR_INV_ARG +#define GPG_ERR_BAD_SECKEY G10ERR_BAD_SECKEY #define GPG_ERR_EBUSY G10ERR_GENERAL #define GPG_ERR_ENOENT G10ERR_OPEN_FILE @@ -129,6 +134,7 @@ typedef int gpg_err_code_t; #define xtrymalloc(n) xmalloc((n)) #define xtrycalloc(n,m) xcalloc((n),(m)) #define xtryrealloc(n,m) xrealloc((n),(m)) +#define xtrymalloc_secure(n) xmalloc_secure((n)) #define out_of_core() (-1) #define gnupg_get_time() make_timestamp () @@ -168,6 +174,10 @@ int agent_scd_getattr (const char *name, struct agent_card_info_s *info); int agent_scd_setattr (const char *name, const unsigned char *value, size_t valuelen); +/* Send a WRITEKEY command to the SCdaemon. */ +int agent_scd_writekey (int keyno, + const unsigned char *keydata, size_t keydatalen); + /* Send a GENKEY command to the SCdaemon. */ int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force); @@ -187,13 +197,6 @@ int agent_scd_change_pin (int chvno); /* Send a CHECKPIN command. */ int agent_scd_checkpin (const char *serialnobuf); -/* Call the store key utility command. */ -int agent_openpgp_storekey (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); - /* Clear a cached PIN. */ void agent_clear_pin_cache (const char *sn); diff --git a/g10/keygen.c b/g10/keygen.c index c4b9dab8f..b5408a6aa 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -128,42 +128,6 @@ static int gen_card_key_with_backup (int algo, int keyno, int is_primary, const char *backup_dir); -#if GNUPG_MAJOR_VERSION == 1 -#define GET_NBITS(a) mpi_get_nbits (a) -#else -#define GET_NBITS(a) gcry_mpi_get_nbits (a) -#endif - -#ifdef ENABLE_CARD_SUPPORT -static int -copy_mpi (MPI a, unsigned char *buffer, size_t len, size_t *ncopied) -{ - int rc; -#if GNUPG_MAJOR_VERSION == 1 - unsigned char *tmp; - unsigned int n; - - tmp = mpi_get_secure_buffer (a, &n, NULL); - if (n > len) - rc = G10ERR_GENERAL; - else - { - rc = 0; - memcpy (buffer, tmp, n); - *ncopied = n; - } - xfree (tmp); -#else /* GNUPG_MAJOR_VERSION != 1 */ - rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, len, ncopied, a); -#endif /* GNUPG_MAJOR_VERSION != 1 */ - if (rc) - log_error ("mpi_copy failed: %s\n", gpg_strerror (rc)); - return rc; -} -#endif /* ENABLE_CARD_SUPPORT */ - - - static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { @@ -3527,104 +3491,68 @@ int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno) { int rc; - size_t n; - MPI rsa_n, rsa_e, rsa_p, rsa_q; - unsigned int nbits; - unsigned char *template = NULL; - unsigned char *tp; - unsigned char m[128], e[4]; - size_t mlen, elen; + unsigned char *rsa_n = NULL; + unsigned char *rsa_e = NULL; + unsigned char *rsa_p = NULL; + unsigned char *rsa_q = NULL; + unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; + unsigned char *sexp = NULL; + unsigned char *p; + char numbuf[55], numbuf2[50]; assert (is_RSA (sk->pubkey_algo)); assert (!sk->is_protected); - rc = -1; - /* Some basic checks on the key parameters. */ - rsa_n = sk->skey[0]; - rsa_e = sk->skey[1]; - rsa_p = sk->skey[3]; - rsa_q = sk->skey[4]; - - nbits = GET_NBITS (rsa_n); - if (nbits != 1024) + /* Copy the parameters into straight buffers. */ + rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL); + rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL); + rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL); + rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL); + if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { - log_error (_("length of RSA modulus is not %d\n"), 1024); - goto leave; - } - nbits = GET_NBITS (rsa_e); - if (nbits < 2 || nbits > 32) - { - log_error (_("public exponent too large (more than 32 bits)\n")); - goto leave; - } - nbits = GET_NBITS (rsa_p); - if (nbits != 512) - { - log_error (_("length of an RSA prime is not %d\n"), 512); - goto leave; - } - nbits = GET_NBITS (rsa_q); - if (nbits != 512) - { - log_error (_("length of an RSA prime is not %d\n"), 512); + rc = G10ERR_INV_ARG; goto leave; } - - /* We need the modulus later to calculate the fingerprint. */ - rc = copy_mpi (rsa_n, m, 128, &n); - if (rc) - goto leave; - assert (n == 128); - mlen = 128; + /* Put the key into an S-expression. */ + sexp = p = xmalloc_secure (30 + + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len + + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20); - /* Build the private key template as described in section 4.3.3.6 of - the OpenPGP card specs: - 0xC0 public exponent - 0xC1 prime p - 0xC2 prime q - */ - template = tp = xmalloc_secure (1+2 + 1+1+4 + 1+1+(512/8) + 1+1+(512/8)); - *tp++ = 0xC0; - *tp++ = 4; - rc = copy_mpi (rsa_e, tp, 4, &n); - if (rc) - goto leave; - assert (n <= 4); - memcpy (e, tp, n); /* Save a copy of the exponent for later use. */ - elen = n; - if (n != 4) - { - memmove (tp+4-n, tp, 4-n); - memset (tp, 0, 4-n); - } - tp += 4; + p = stpcpy (p,"(11:private-key(3:rsa(1:n"); + sprintf (numbuf, "%u:", rsa_n_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_n, rsa_n_len); + p += rsa_n_len; - *tp++ = 0xC1; - *tp++ = 64; - rc = copy_mpi (rsa_p, tp, 64, &n); - if (rc) - goto leave; - assert (n == 64); - tp += 64; + sprintf (numbuf, ")(1:e%u:", rsa_e_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_e, rsa_e_len); + p += rsa_e_len; - *tp++ = 0xC2; - *tp++ = 64; - rc = copy_mpi (rsa_q, tp, 64, &n); - if (rc) - goto leave; - assert (n == 64); - tp += 64; - assert (tp - template == 138); + sprintf (numbuf, ")(1:p%u:", rsa_p_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_p, rsa_p_len); + p += rsa_p_len; - rc = agent_openpgp_storekey (keyno, - template, tp - template, - sk->timestamp, - m, mlen, - e, elen); + sprintf (numbuf, ")(1:q%u:", rsa_q_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_q, rsa_q_len); + p += rsa_q_len; + + p = stpcpy (p,"))(10:created-at"); + sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp); + sprintf (numbuf, "%d:", strlen (numbuf2)); + p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); + + rc = agent_scd_writekey (keyno, sexp, p - sexp); leave: - xfree (template); + xfree (sexp); + xfree (rsa_n); + xfree (rsa_e); + xfree (rsa_p); + xfree (rsa_q); return rc; } #endif /*ENABLE_CARD_SUPPORT*/ diff --git a/g10/tlv.c b/g10/tlv.c index 3a81ea6d9..b5dcd4021 100644 --- a/g10/tlv.c +++ b/g10/tlv.c @@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size, *size = length; return 0; } + + +/* FIXME: The following function should not go into this file but for + now it is easier to keep it here. */ + +/* Return the next token of an canconical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the orginal buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error a error code + is returned and all pointers should are not guaranteed to point to + a meanigful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return: + + depth = 0; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth) + process_token (tok, toklen); + if (err) + handle_error (); + */ +gpg_error_t +parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen) +{ + const unsigned char *s; + size_t n, vlen; + + s = *buf; + n = *buflen; + *tok = NULL; + *toklen = 0; + if (!n) + return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0; + if (*s == '(') + { + s++; n--; + (*depth)++; + *buf = s; + *buflen = n; + return 0; + } + if (*s == ')') + { + if (!*depth) + return gpg_error (GPG_ERR_INV_SEXP); + *toklen = 1; + s++; n--; + (*depth)--; + *buf = s; + *buflen = n; + return 0; + } + for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) + vlen = vlen*10 + (*s - '0'); + if (!n || *s != ':') + return gpg_error (GPG_ERR_INV_SEXP); + s++; n--; + if (vlen > n) + return gpg_error (GPG_ERR_INV_SEXP); + *tok = s; + *toklen = vlen; + s += vlen; + n -= vlen; + *buf = s; + *buflen = n; + return 0; +} + diff --git a/g10/tlv.h b/g10/tlv.h index 628580431..f587dd9df 100644 --- a/g10/tlv.h +++ b/g10/tlv.h @@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, +/* Return the next token of an canconical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the orginal buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error a error code + is returned and all pointers should are not guaranteed to point to + a meanigful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. */ +gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen); + + + #endif /* SCD_TLV_H */ diff --git a/include/ChangeLog b/include/ChangeLog index 428105045..e17b96df1 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2005-05-19 Werner Koch + + * util.h: Add definitions for membuf functions. + 2005-05-05 David Shaw * util.h: Remove add_days_to_timestamp as unused. diff --git a/include/util.h b/include/util.h index 97943afa9..b32d771af 100644 --- a/include/util.h +++ b/include/util.h @@ -222,6 +222,24 @@ int strncasecmp (const char *, const char *b, size_t n); #define memmove(d, s, n) bcopy((s), (d), (n)) #endif +/*-- membuf.c --*/ +/* The definition of the structure is private, we only need it here, + so it can be allocated on the stack. */ +struct private_membuf_s { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + +typedef struct private_membuf_s membuf_t; + +void init_membuf (membuf_t *mb, int initiallen); +void put_membuf (membuf_t *mb, const void *buf, size_t len); +void *get_membuf (membuf_t *mb, size_t *len); + + + #if defined (_WIN32) /*-- w32reg.c --*/ char *read_w32_registry_string( const char *root, @@ -232,7 +250,8 @@ int write_w32_registry_string(const char *root, const char *dir, /*-- strgutil.c --*/ int vasprintf (char **result, const char *format, va_list args); int asprintf (char **buf, const char *fmt, ...); -#endif +#endif /*_WIN32*/ + /**** other missing stuff ****/ #ifndef HAVE_ATEXIT /* For SunOS */ diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 5ad1d2610..8a85e0270 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -57,7 +57,7 @@ X.509 specific are noted like [X.509: xxx] b20 The keys fingerprint (fingerprints are always 20 bytes, MD5 left padded with zeroes) u32 offset to the n-th key's keyID (a keyID is always 8 byte) - or 0 if not known which is the case opnly for X509. + or 0 if not known which is the case only for X509. u16 special key flags bit 0 = u16 reserved @@ -72,7 +72,7 @@ X.509 specific are noted like [X.509: xxx] bit 0 = byte validity byte reserved - [For X509, the first user ID is the ISsuer, the second the subject + [For X509, the first user ID is the issuer, the second the subject and the others are subjectAltNames] u16 number of signatures u16 size of signature information (4) @@ -99,7 +99,7 @@ X.509 specific are noted like [X.509: xxx] b16 MD5 checksum (useful for KS syncronisation), we might also want to use a mac here. - b4 resevered + b4 reserved */ diff --git a/mpi/ChangeLog b/mpi/ChangeLog index 00e0e3e6a..775f437b4 100644 --- a/mpi/ChangeLog +++ b/mpi/ChangeLog @@ -1,3 +1,12 @@ +2005-05-06 Werner Koch + + * mpi-scan.c (mpi_putbyte, mpi_getbyte): Removed. Not used. + +2005-04-21 Werner Koch + + * mpicoder.c (mpi_read): Changed error detection to always return + an error while maintaining the actual number of bytes read. + 2005-03-11 Werner Koch * Makefile.am (ASFLAGS): Renamed to AM_CCASFLAGS and added the diff --git a/mpi/mpi-scan.c b/mpi/mpi-scan.c index 615b9464f..fe093adea 100644 --- a/mpi/mpi-scan.c +++ b/mpi/mpi-scan.c @@ -18,8 +18,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#include -#include +#include +#include #include #include "mpi-internal.h" #include "longlong.h" @@ -31,6 +31,7 @@ * * FIXME: This code is VERY ugly! */ +#if 0 /* Code is not used */ int mpi_getbyte( MPI a, unsigned idx ) { @@ -48,14 +49,19 @@ mpi_getbyte( MPI a, unsigned idx ) } return -1; } +#endif /* Code is not used */ /**************** * Put a value at position IDX into A. idx counts from lsb to msb */ +/* FIXME: There is a problem with the long constants which should have +a LL prefix or better the macros we use at other places. */ +#if 0 /* Code is not used */ void mpi_putbyte( MPI a, unsigned idx, int xc ) { + int i, j; unsigned n; mpi_ptr_t ap; @@ -104,12 +110,13 @@ mpi_putbyte( MPI a, unsigned idx, int xc ) } abort(); /* index out of range */ } +#endif /* Code is not used */ /**************** * Count the number of zerobits at the low end of A */ -unsigned +unsigned int mpi_trailing_zeros( MPI a ) { unsigned n, count = 0; diff --git a/mpi/mpicoder.c b/mpi/mpicoder.c index 4d4e45553..ab913861d 100644 --- a/mpi/mpicoder.c +++ b/mpi/mpicoder.c @@ -1,5 +1,5 @@ /* mpicoder.c - Coder for the external representation of MPIs - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -74,20 +74,23 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure) #endif { int c, i, j; + unsigned int nmax = *ret_nread; unsigned nbits, nbytes, nlimbs, nread=0; mpi_limb_t a; MPI val = MPI_NULL; if( (c = iobuf_get(inp)) == -1 ) goto leave; - nread++; + if (++nread >= nmax) + goto overflow; nbits = c << 8; if( (c = iobuf_get(inp)) == -1 ) goto leave; - nread++; + if (++nread >= nmax) + goto overflow; nbits |= c; if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); + log_error("mpi too large for this implementation (%u bits)\n", nbits); goto leave; } @@ -108,6 +111,15 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure) for( ; j > 0; j-- ) { a = 0; for(; i < BYTES_PER_MPI_LIMB; i++ ) { + if (nread >= nmax) { +#ifdef M_DEBUG + mpi_debug_free (val); +#else + mpi_free (val); +#endif + val = NULL; + goto overflow; + } a <<= 8; a |= iobuf_get(inp) & 0xff; nread++; } @@ -116,10 +128,11 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure) } leave: - if( nread > *ret_nread ) - log_bug("mpi crosses packet border\n"); - else - *ret_nread = nread; + *ret_nread = nread; + return val; + overflow: + log_error ("mpi larger than indicated length (%u bytes)\n", nmax); + *ret_nread = nread; return val; } diff --git a/util/ChangeLog b/util/ChangeLog index e16df58ee..f1b36ff49 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,7 @@ +2005-05-19 Werner Koch + + * membuf.c: New. Taken from gnupg 1.9. + 2005-05-05 David Shaw * miscutil.c (add_days_to_timestamp): Remove as unused. diff --git a/util/Makefile.am b/util/Makefile.am index cb241da3f..d54aa7a97 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -39,7 +39,7 @@ endif libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \ dotlock.c http.c srv.h srv.c simple-gettext.c \ - w32reg.c $(assuan_source) + membuf.c w32reg.c $(assuan_source) libutil_a_DEPENDENCIES = @LIBOBJS@ @REGEX_O@ # LIBOBJS is for the replacement functions diff --git a/util/membuf.c b/util/membuf.c new file mode 100644 index 000000000..ea69ee953 --- /dev/null +++ b/util/membuf.c @@ -0,0 +1,83 @@ +/* membuf.c - A simple implementation of a dynamic buffer + * Copyright (C) 2001, 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 "util.h" + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +void +init_membuf (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xmalloc (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + + +void +put_membuf (membuf_t *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xrealloc (mb->buf, mb->size); + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + + +void * +get_membuf (membuf_t *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return p; +}