diff options
Diffstat (limited to '')
-rw-r--r-- | g10/ChangeLog | 11 | ||||
-rw-r--r-- | g10/app-common.h | 10 | ||||
-rw-r--r-- | g10/app-openpgp.c | 351 | ||||
-rw-r--r-- | g10/cardglue.c | 98 | ||||
-rw-r--r-- | g10/cardglue.h | 17 | ||||
-rw-r--r-- | g10/keygen.c | 172 | ||||
-rw-r--r-- | g10/tlv.c | 73 | ||||
-rw-r--r-- | g10/tlv.h | 17 |
8 files changed, 554 insertions, 195 deletions
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 <[email protected]> + + * 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 <[email protected]> * 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 <length> public exponent + 0xC1 <length> prime p + 0xC2 <length> 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; - - /* Build the private key template as described in section 4.3.3.6 of - the OpenPGP card specs: - 0xC0 <length> public exponent - 0xC1 <length> prime p - 0xC2 <length> 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; - - *tp++ = 0xC1; - *tp++ = 64; - rc = copy_mpi (rsa_p, tp, 64, &n); - if (rc) - goto leave; - assert (n == 64); - tp += 64; + /* 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); - *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); + 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; + + sprintf (numbuf, ")(1:e%u:", rsa_e_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_e, rsa_e_len); + p += rsa_e_len; + + sprintf (numbuf, ")(1:p%u:", rsa_p_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_p, rsa_p_len); + p += rsa_p_len; + + 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_openpgp_storekey (keyno, - template, tp - template, - sk->timestamp, - m, mlen, - e, elen); + 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*/ @@ -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; +} + @@ -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 */ |