* 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

@ -1,3 +1,14 @@
2005-02-22 Werner Koch <wk@g10code.com>
* 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.
2005-01-26 Werner Koch <wk@g10code.com>
* ccid-driver.c (parse_ccid_descriptor): Need the CSM workaround
@ -18,7 +29,7 @@
side effect of the retrieval of the the C4 DO from the 6E DO the
cached fingerprint will get updated to the old value and later
when signing the generated key the checking of the fingerprint
fails becuase it won't match the new one. Thanks to Moritz for
fails because it won't match the new one. Thanks to Moritz for
analyzing this problem.
(verify_chv3): Removed the CHV status reread logic because we
won't cache the C4 DO anymore.
@ -934,7 +945,8 @@
* scdaemon.c scdaemon.h, command.c: New. Based on the code from
the gpg-agent.
Copyright 2002 Free Software Foundation, Inc.
Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without

View File

@ -1,5 +1,5 @@
/* app-common.h - Common declarations for all card applications
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -49,6 +49,8 @@ struct app_ctx_s {
int (*learn_status) (app_t app, ctrl_t ctrl);
int (*readcert) (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int (*readkey) (app_t app, const char *certid,
unsigned char **pk, size_t *pklen);
int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
int (*setattr) (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
@ -109,6 +111,8 @@ int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
int app_write_learn_status (app_t app, ctrl_t ctrl);
int app_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int app_readkey (app_t app, const char *keyid,
unsigned char **pk, size_t *pklen);
int app_getattr (app_t app, ctrl_t ctrl, const char *name);
int app_setattr (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),

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

View File

@ -263,6 +263,32 @@ app_readcert (app_t app, const char *certid,
}
/* Read the key with ID KEYID. 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 NULL will be stored at PK and PKLEN and an error
code returned.
This function might not be supported by all applications. */
int
app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
{
if (pk)
*pk = NULL;
if (pklen)
*pklen = 0;
if (!app || !keyid || !pk || !pklen)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.readkey)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
return app->fnc.readkey (app, keyid, pk, pklen);
}
/* Perform a GETATTR operation. */
int
app_getattr (APP app, CTRL ctrl, const char *name)

View File

@ -1,5 +1,5 @@
/* command.c - SCdaemon command handler
* Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -494,9 +494,9 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
Return the public key for the given cert or key ID as an standard
S-Expression. */
static int
cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
cmd_readkey (assuan_context_t ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *cert = NULL;
size_t ncert, n;
@ -509,9 +509,31 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
line = xstrdup (line); /* Need a copy of the line. */
if (ctrl->app_ctx)
{
rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
if (rc)
log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
unsigned char *pk;
size_t pklen;
/* If the application supports the READKEY function we use that.
Otherwise we use the old way by extracting it from the
certificate. */
rc = app_readkey (ctrl->app_ctx, line, &pk, &pklen);
if (!rc)
{ /* Yeah, got that key - send it back. */
rc = assuan_send_data (ctx, pk, pklen);
xfree (pk);
rc = map_assuan_err (rc);
xfree (line);
line = NULL;
goto leave;
}
if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION)
log_error ("app_readkey failed: %s\n", gpg_strerror (rc));
else
{
rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
if (rc)
log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
}
}
else
{