mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01: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:
parent
823eaefb0b
commit
8c77433de9
@ -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
|
||||
|
@ -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 **),
|
||||
|
@ -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);
|
||||
|
26
scd/app.c
26
scd/app.c
@ -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)
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user