diff options
Diffstat (limited to 'scd/app-openpgp.c')
-rw-r--r-- | scd/app-openpgp.c | 247 |
1 files changed, 243 insertions, 4 deletions
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index fca0a98b7..8d146ba6a 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -90,6 +90,7 @@ static struct { }; +/* One cache item for DOs. */ struct cache_s { struct cache_s *next; int tag; @@ -97,8 +98,20 @@ struct cache_s { unsigned char data[1]; }; + +/* Object with application (i.e. OpenPGP card) specific data. */ struct app_local_s { + /* A linked list with cached DOs. */ struct cache_s *cache; + + /* Keep track of the public keys. */ + struct + { + int read_done; /* True if we have at least tried to read them. */ + gcry_sexp_t key; /* Might be NULL if key is not available. */ + } pk[3]; + + /* Keep track of card capabilities. */ struct { unsigned int get_challenge:1; @@ -106,6 +119,8 @@ struct app_local_s { unsigned int change_force_chv:1; unsigned int private_dos:1; } extcap; + + /* Flags used to control the application. */ struct { unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */ @@ -114,10 +129,16 @@ struct app_local_s { }; + +/***** Local prototypes *****/ static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); -static unsigned long get_sig_counter (APP app); +static unsigned long get_sig_counter (app_t app); + + + + /* Deconstructor. */ static void do_deinit (app_t app) @@ -125,12 +146,19 @@ do_deinit (app_t app) if (app && app->app_local) { struct cache_s *c, *c2; + int i; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } + + for (i=0; i < DIM (app->app_local->pk); i++) + { + gcry_sexp_release (app->app_local->pk[i].key); + app->app_local->pk[i].read_done = 0; + } xfree (app->app_local); app->app_local = NULL; } @@ -736,6 +764,156 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } +/* Get the public key for KEYNO and store it as an S-expresion with + the APP handle. On error that field gets cleared. If we already + know about the public key we will just return. Note that this does + not mean a key is available; this is soley indicated by the + presence of the app->app_local->pk[KEYNO-1].key field. + + Note that GnuPG 1.x does not need this and it would be too time + consuming to send it just for the fun of it. */ +#if GNUPG_MAJOR_VERSION > 1 +static gpg_error_t +get_public_key (app_t app, int keyno) +{ + gpg_error_t err = 0; + unsigned char *buffer; + const unsigned char *keydata, *m, *e; + size_t buflen, keydatalen, mlen, elen; + gcry_sexp_t sexp; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + /* Already cached? */ + if (app->app_local->pk[keyno].read_done) + return 0; + + gcry_sexp_release (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + + if (app->card_version > 0x0100) + { + /* We may simply read the public key out of these cards. */ + err = iso7816_read_public_key (app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", + 2, + &buffer, &buflen); + if (err) + { + log_error (_("reading public key failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); + if (!keydata) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the public key data\n")); + goto leave; + } + + m = find_tlv (keydata, keydatalen, 0x0081, &mlen); + if (!m) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the RSA modulus\n")); + goto leave; + } + + e = find_tlv (keydata, keydatalen, 0x0082, &elen); + if (!e) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the RSA public exponent\n")); + goto leave; + } + + err = gcry_sexp_build (&sexp, NULL, + "(public-key (rsa (n %b) (e %b)))", + (int)mlen, m,(int)elen, e); + + if (err) + { + log_error ("error formatting the key into an S-expression: %s\n", + gpg_strerror (err)); + goto leave; + } + app->app_local->pk[keyno].key = sexp; + + } + else + { + /* Due to a design problem in v1.0 cards we can't get the public + key out of these cards without doing a verify on CHV3. + Clearly that is not an option and thus we try to locate the + key using an external helper. */ + + buffer = NULL; + /* FIXME */ + + } + + leave: + /* Set a flag to indicate that we tried to read the key. */ + app->app_local->pk[keyno].read_done = 1; + + xfree (buffer); + return 0; +} +#endif /* GNUPG_MAJOR_VERSION > 1 */ + + + +/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3]. + This is used by the LEARN command. */ +static gpg_error_t +send_keypair_info (app_t app, ctrl_t ctrl, int keyno) +{ + gpg_error_t err = 0; + /* Note that GnuPG 1.x does not need this and it would be too time + consuming to send it just for the fun of it. */ +#if GNUPG_MAJOR_VERSION > 1 + gcry_sexp_t sexp; + unsigned char grip[20]; + char gripstr[41]; + char idbuf[50]; + int i; + + err = get_public_key (app, keyno); + if (err) + goto leave; + + assert (keyno >= 1 && keyno <= 3); + sexp = app->app_local->pk[keyno-1].key; + if (!sexp) + goto leave; /* No such key. */ + + if (!gcry_pk_get_keygrip (sexp, grip)) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + for (i=0; i < 20; i++) + sprintf (gripstr+i*2, "%02X", grip[i]); + + sprintf (idbuf, "OPENPGP.%d", keyno); + send_status_info (ctrl, "KEYPAIRINFO", + gripstr, 40, + idbuf, strlen (idbuf), + NULL, (size_t)0); + + leave: +#endif /* GNUPG_MAJOR_VERSION > 1 */ + + return err; +} + + +/* Handle the LEARN command for OpenPGP. */ static int do_learn_status (app_t app, ctrl_t ctrl) { @@ -760,11 +938,63 @@ do_learn_status (app_t app, ctrl_t ctrl) if (app->did_chv3) do_getattr (app, ctrl, "PRIVATE-DO-4"); } + send_keypair_info (app, ctrl, 1); + send_keypair_info (app, ctrl, 2); + send_keypair_info (app, ctrl, 3); + return 0; +} + + +/* Handle the READKEY command for OpenPGP. On success a canonical + encoded S-expression with the public key will get stored at PK and + its length (for assertions) at PKLEN; the caller must release that + buffer. On error PK and PKLEN are not changed and an error code is + returned. */ +static int +do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) +{ + gpg_error_t err; + int keyno; + size_t n; + unsigned char *buf; + gcry_sexp_t sexp; + + if (!strcmp (keyid, "OPENPGP.1")) + keyno = 1; + else if (!strcmp (keyid, "OPENPGP.2")) + keyno = 2; + else if (!strcmp (keyid, "OPENPGP.3")) + keyno = 3; + else + return gpg_error (GPG_ERR_INV_ID); + err = get_public_key (app, keyno); + if (err) + return err; + + sexp = app->app_local->pk[keyno-1].key; + if (!sexp) + return gpg_error (GPG_ERR_NO_PUBKEY); + + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!n) + return gpg_error (GPG_ERR_BUG); + buf = xtrymalloc (n); + if (!buf) + return gpg_error_from_errno (errno); + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, n); + if (!n) + { + xfree (buf); + return gpg_error (GPG_ERR_BUG); + } + *pk = buf; + *pklen = n; return 0; } + /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ static int @@ -1082,6 +1312,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, generation. This _might_ help a card to gather more entropy. */ flush_cache (app); + /* Obviously we need to remove the cached public key. */ + gcry_sexp_release (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].read_done = 0; + + /* Check whether a key already exists. */ rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); if (rc) { @@ -1109,11 +1344,12 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, else log_info (_("generating new key\n")); - + + /* 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 @@ -1682,7 +1918,7 @@ app_select_openpgp (app_t app) app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; - app->fnc.readcert = NULL; + app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.genkey = do_genkey; @@ -1818,6 +2054,9 @@ app_openpgp_storekey (app_t app, int keyno, flush_cache (app); + gcry_sexp_release (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].read_done = 0; + rc = iso7816_put_data (app->slot, (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, template, template_len); |