1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

* app-openpgp.c (app_local_s): New field PK.

(do_deinit, do_genkey, app_openpgp_storekey): Clear it.
(get_public_key, send_keypair_info): New.
(do_learn_status): Send KEYPAIR info

* app-common.h (app_ctx_t): Add function pointer READKEY.
* app.c (app_readkey): New.
* command.c (cmd_readkey): Use READKEY function if possible.
This commit is contained in:
Werner Koch 2005-02-22 17:29:07 +00:00
parent 823eaefb0b
commit 8c77433de9
5 changed files with 317 additions and 14 deletions

View file

@ -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);