mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
gpg: Allow decryption using non-OpenPGP cards.
* g10/call-agent.c (struct getattr_one_parm_s): New. (getattr_one_status_cb): New. (agent_scd_getattr_one): New. * g10/pubkey-enc.c (get_it): Allow the standard leading zero byte from pkcs#1. * g10/getkey.c (enum_secret_keys): Move to... * g10/skclist.c (enum_secret_keys): here and handle non-OpenPGP cards. -- Signed-off-by: Werner Koch <wk@gnupg.org> (cherry picked from commit ec6a6779236a89d4784a6bb7de0def9cc0f9e8a4) This commit also incorporates "g10: Move enum_secret_keys to skclist.c." Which was started with commit 03a8de7def4195b9accde47c1dcb84279361936d on master about a year ago. Signed-off-by: Werner Koch <wk@gnupg.org> GnuPG-bug-id: 4681
This commit is contained in:
parent
23784f8bf0
commit
9a317557c5
@ -925,6 +925,86 @@ agent_keytocard (const char *hexgrip, int keyno, int force,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Object used with the agent_scd_getattr_one. */
|
||||
struct getattr_one_parm_s {
|
||||
const char *keyword; /* Keyword to look for. */
|
||||
char *data; /* Malloced and unescaped data. */
|
||||
gpg_error_t err; /* Error code or 0 on success. */
|
||||
};
|
||||
|
||||
|
||||
/* Callback for agent_scd_getattr_one. */
|
||||
static gpg_error_t
|
||||
getattr_one_status_cb (void *opaque, const char *line)
|
||||
{
|
||||
struct getattr_one_parm_s *parm = opaque;
|
||||
const char *s;
|
||||
|
||||
if (parm->data)
|
||||
return 0; /* We want only the first occurrence. */
|
||||
|
||||
if ((s=has_leading_keyword (line, parm->keyword)))
|
||||
{
|
||||
parm->data = percent_plus_unescape (s, 0xff);
|
||||
if (!parm->data)
|
||||
parm->err = gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Simplified version of agent_scd_getattr. This function returns
|
||||
* only the first occurance of the attribute NAME and stores it at
|
||||
* R_VALUE. A nul in the result is silennly replaced by 0xff. On
|
||||
* error NULL is stored at R_VALUE. */
|
||||
gpg_error_t
|
||||
agent_scd_getattr_one (const char *name, char **r_value)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
struct default_inq_parm_s inqparm;
|
||||
struct getattr_one_parm_s parm;
|
||||
|
||||
*r_value = NULL;
|
||||
|
||||
if (!*name)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
memset (&inqparm, 0, sizeof inqparm);
|
||||
inqparm.ctx = agent_ctx;
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
parm.keyword = name;
|
||||
|
||||
/* We assume that NAME does not need escaping. */
|
||||
if (12 + strlen (name) > DIM(line)-1)
|
||||
return gpg_error (GPG_ERR_TOO_LARGE);
|
||||
stpcpy (stpcpy (line, "SCD GETATTR "), name);
|
||||
|
||||
err = start_agent (NULL, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = assuan_transact (agent_ctx, line,
|
||||
NULL, NULL,
|
||||
default_inq_cb, &inqparm,
|
||||
getattr_one_status_cb, &parm);
|
||||
if (!err && parm.err)
|
||||
err = parm.err;
|
||||
else if (!err && !parm.data)
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
|
||||
if (!err)
|
||||
*r_value = parm.data;
|
||||
else
|
||||
xfree (parm.data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Call the agent to retrieve a data object. This function returns
|
||||
* the data in the same structure as used by the learn command. It is
|
||||
|
@ -94,6 +94,9 @@ int agent_scd_serialno (char **r_serialno, const char *demand);
|
||||
/* Send an APDU to the card. */
|
||||
gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw);
|
||||
|
||||
/* Get attribute NAME from the card and store at R_VALUE. */
|
||||
gpg_error_t agent_scd_getattr_one (const char *name, char **r_value);
|
||||
|
||||
/* Update INFO with the attribute NAME. */
|
||||
int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
|
||||
|
||||
|
174
g10/getkey.c
174
g10/getkey.c
@ -3774,180 +3774,6 @@ lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
|
||||
}
|
||||
|
||||
|
||||
/* Enumerate some secret keys (specifically, those specified with
|
||||
* --default-key and --try-secret-key). Use the following procedure:
|
||||
*
|
||||
* 1) Initialize a void pointer to NULL
|
||||
* 2) Pass a reference to this pointer to this function (content)
|
||||
* and provide space for the secret key (sk)
|
||||
* 3) Call this function as long as it does not return an error (or
|
||||
* until you are done). The error code GPG_ERR_EOF indicates the
|
||||
* end of the listing.
|
||||
* 4) Call this function a last time with SK set to NULL,
|
||||
* so that can free it's context.
|
||||
*
|
||||
* In pseudo-code:
|
||||
*
|
||||
* void *ctx = NULL;
|
||||
* PKT_public_key *sk = xmalloc_clear (sizeof (*sk));
|
||||
*
|
||||
* while ((err = enum_secret_keys (&ctx, sk)))
|
||||
* { // Process SK.
|
||||
* if (done)
|
||||
* break;
|
||||
* free_public_key (sk);
|
||||
* sk = xmalloc_clear (sizeof (*sk));
|
||||
* }
|
||||
*
|
||||
* // Release any resources used by CTX.
|
||||
* enum_secret_keys (&ctx, NULL);
|
||||
* free_public_key (sk);
|
||||
*
|
||||
* if (gpg_err_code (err) != GPG_ERR_EOF)
|
||||
* ; // An error occurred.
|
||||
*/
|
||||
gpg_error_t
|
||||
enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const char *name;
|
||||
kbnode_t keyblock;
|
||||
struct
|
||||
{
|
||||
int eof;
|
||||
int state;
|
||||
strlist_t sl;
|
||||
kbnode_t keyblock;
|
||||
kbnode_t node;
|
||||
getkey_ctx_t ctx;
|
||||
} *c = *context;
|
||||
|
||||
if (!c)
|
||||
{
|
||||
/* Make a new context. */
|
||||
c = xtrycalloc (1, sizeof *c);
|
||||
if (!c)
|
||||
return gpg_error_from_syserror ();
|
||||
*context = c;
|
||||
}
|
||||
|
||||
if (!sk)
|
||||
{
|
||||
/* Free the context. */
|
||||
release_kbnode (c->keyblock);
|
||||
getkey_end (ctrl, c->ctx);
|
||||
xfree (c);
|
||||
*context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c->eof)
|
||||
return gpg_error (GPG_ERR_EOF);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Loop until we have a keyblock. */
|
||||
while (!c->keyblock)
|
||||
{
|
||||
/* Loop over the list of secret keys. */
|
||||
do
|
||||
{
|
||||
name = NULL;
|
||||
keyblock = NULL;
|
||||
switch (c->state)
|
||||
{
|
||||
case 0: /* First try to use the --default-key. */
|
||||
name = parse_def_secret_key (ctrl);
|
||||
c->state = 1;
|
||||
break;
|
||||
|
||||
case 1: /* Init list of keys to try. */
|
||||
c->sl = opt.secret_keys_to_try;
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 2: /* Get next item from list. */
|
||||
if (c->sl)
|
||||
{
|
||||
name = c->sl->d;
|
||||
c->sl = c->sl->next;
|
||||
}
|
||||
else
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 3: /* Init search context to enum all secret keys. */
|
||||
err = getkey_bynames (ctrl, &c->ctx, NULL, NULL, 1,
|
||||
&keyblock);
|
||||
if (err)
|
||||
{
|
||||
release_kbnode (keyblock);
|
||||
keyblock = NULL;
|
||||
getkey_end (ctrl, c->ctx);
|
||||
c->ctx = NULL;
|
||||
}
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 4: /* Get next item from the context. */
|
||||
if (c->ctx)
|
||||
{
|
||||
err = getkey_next (ctrl, c->ctx, NULL, &keyblock);
|
||||
if (err)
|
||||
{
|
||||
release_kbnode (keyblock);
|
||||
keyblock = NULL;
|
||||
getkey_end (ctrl, c->ctx);
|
||||
c->ctx = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
default: /* No more names to check - stop. */
|
||||
c->eof = 1;
|
||||
return gpg_error (GPG_ERR_EOF);
|
||||
}
|
||||
}
|
||||
while ((!name || !*name) && !keyblock);
|
||||
|
||||
if (keyblock)
|
||||
c->node = c->keyblock = keyblock;
|
||||
else
|
||||
{
|
||||
err = getkey_byname (ctrl, NULL, NULL, name, 1, &c->keyblock);
|
||||
if (err)
|
||||
{
|
||||
/* getkey_byname might return a keyblock even in the
|
||||
error case - I have not checked. Thus better release
|
||||
it. */
|
||||
release_kbnode (c->keyblock);
|
||||
c->keyblock = NULL;
|
||||
}
|
||||
else
|
||||
c->node = c->keyblock;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the next key from the current keyblock. */
|
||||
for (; c->node; c->node = c->node->next)
|
||||
{
|
||||
if (c->node->pkt->pkttype == PKT_PUBLIC_KEY
|
||||
|| c->node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
||||
{
|
||||
copy_public_key (sk, c->node->pkt->pkt.public_key);
|
||||
c->node = c->node->next;
|
||||
return 0; /* Found. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispose the keyblock and continue. */
|
||||
release_kbnode (c->keyblock);
|
||||
c->keyblock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gpg_error_t
|
||||
get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk,
|
||||
const byte *fpr_card, size_t fpr_len)
|
||||
|
@ -305,6 +305,16 @@ get_it (ctrl_t ctrl,
|
||||
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* FIXME: Actually the leading zero is required but due to
|
||||
* the way we encode the output in libgcrypt as an MPI we
|
||||
* are not able to encode that leading zero. However, when
|
||||
* using a Smartcard we are doing it the right way and
|
||||
* therefore we have to skip the zero. This should be fixed
|
||||
* in gpg-agent of course. */
|
||||
if (!frame[n])
|
||||
n++;
|
||||
|
||||
if (frame[n] == 1 && frame[nframe - 1] == 2)
|
||||
{
|
||||
log_info (_("old encoding of the DEK is not supported\n"));
|
||||
|
307
g10/skclist.c
307
g10/skclist.c
@ -286,3 +286,310 @@ build_sk_list (ctrl_t ctrl,
|
||||
*ret_sk_list = sk_list;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Enumerate some secret keys (specifically, those specified with
|
||||
* --default-key and --try-secret-key). Use the following procedure:
|
||||
*
|
||||
* 1) Initialize a void pointer to NULL
|
||||
* 2) Pass a reference to this pointer to this function (content)
|
||||
* and provide space for the secret key (sk)
|
||||
* 3) Call this function as long as it does not return an error (or
|
||||
* until you are done). The error code GPG_ERR_EOF indicates the
|
||||
* end of the listing.
|
||||
* 4) Call this function a last time with SK set to NULL,
|
||||
* so that can free it's context.
|
||||
*
|
||||
* In pseudo-code:
|
||||
*
|
||||
* void *ctx = NULL;
|
||||
* PKT_public_key *sk = xmalloc_clear (sizeof (*sk));
|
||||
*
|
||||
* while ((err = enum_secret_keys (&ctx, sk)))
|
||||
* { // Process SK.
|
||||
* if (done)
|
||||
* break;
|
||||
* sk = xmalloc_clear (sizeof (*sk));
|
||||
* }
|
||||
*
|
||||
* // Release any resources used by CTX.
|
||||
* enum_secret_keys (&ctx, NULL);
|
||||
*
|
||||
* if (gpg_err_code (err) != GPG_ERR_EOF)
|
||||
* ; // An error occurred.
|
||||
*/
|
||||
gpg_error_t
|
||||
enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const char *name;
|
||||
kbnode_t keyblock;
|
||||
struct
|
||||
{
|
||||
int eof;
|
||||
int state;
|
||||
strlist_t sl;
|
||||
strlist_t card_list;
|
||||
char *serialno;
|
||||
char fpr2[2 * MAX_FINGERPRINT_LEN + 3 ];
|
||||
struct agent_card_info_s info;
|
||||
kbnode_t keyblock;
|
||||
kbnode_t node;
|
||||
getkey_ctx_t ctx;
|
||||
SK_LIST results;
|
||||
} *c = *context;
|
||||
|
||||
#if MAX_FINGERPRINT_LEN < KEYGRIP_LEN
|
||||
# error buffer too short for this configuration
|
||||
#endif
|
||||
|
||||
if (!c)
|
||||
{
|
||||
/* Make a new context. */
|
||||
c = xtrycalloc (1, sizeof *c);
|
||||
if (!c)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
free_public_key (sk);
|
||||
return err;
|
||||
}
|
||||
*context = c;
|
||||
}
|
||||
|
||||
if (!sk)
|
||||
{
|
||||
/* Free the context. */
|
||||
xfree (c->serialno);
|
||||
free_strlist (c->card_list);
|
||||
release_sk_list (c->results);
|
||||
release_kbnode (c->keyblock);
|
||||
getkey_end (ctrl, c->ctx);
|
||||
xfree (c);
|
||||
*context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c->eof)
|
||||
{
|
||||
free_public_key (sk);
|
||||
return gpg_error (GPG_ERR_EOF);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Loop until we have a keyblock. */
|
||||
while (!c->keyblock)
|
||||
{
|
||||
/* Loop over the list of secret keys. */
|
||||
do
|
||||
{
|
||||
char *serialno;
|
||||
|
||||
name = NULL;
|
||||
keyblock = NULL;
|
||||
switch (c->state)
|
||||
{
|
||||
case 0: /* First try to use the --default-key. */
|
||||
name = parse_def_secret_key (ctrl);
|
||||
c->state = 1;
|
||||
break;
|
||||
|
||||
case 1: /* Init list of keys to try. */
|
||||
c->sl = opt.secret_keys_to_try;
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 2: /* Get next item from list. */
|
||||
if (c->sl)
|
||||
{
|
||||
name = c->sl->d;
|
||||
c->sl = c->sl->next;
|
||||
}
|
||||
else
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 3: /* Init list of card keys to try. */
|
||||
err = agent_scd_cardlist (&c->card_list);
|
||||
if (!err)
|
||||
agent_scd_serialno (&c->serialno, NULL);
|
||||
c->sl = c->card_list;
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 4: /* Get next item from card list. */
|
||||
if (c->sl)
|
||||
{
|
||||
err = agent_scd_serialno (&serialno, c->sl->d);
|
||||
if (err)
|
||||
{
|
||||
if (opt.verbose)
|
||||
log_info (_("error getting serial number of card: %s\n"),
|
||||
gpg_strerror (err));
|
||||
c->sl = c->sl->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
xfree (serialno);
|
||||
err = agent_scd_getattr ("KEY-FPR", &c->info);
|
||||
if (!err)
|
||||
{
|
||||
if (c->info.fpr2valid)
|
||||
{
|
||||
c->fpr2[0] = '0';
|
||||
c->fpr2[1] = 'x';
|
||||
bin2hex (c->info.fpr2, sizeof c->info.fpr2,
|
||||
c->fpr2 + 2);
|
||||
name = c->fpr2;
|
||||
}
|
||||
}
|
||||
else if (gpg_err_code (err) == GPG_ERR_INV_NAME)
|
||||
{
|
||||
/* KEY-FPR not supported by the card - get
|
||||
* the key using the keygrip. */
|
||||
char *keyref;
|
||||
strlist_t kplist, sl;
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
err = agent_scd_getattr_one ("$ENCRKEYID", &keyref);
|
||||
if (!err)
|
||||
{
|
||||
err = agent_scd_keypairinfo (ctrl, &kplist);
|
||||
if (!err)
|
||||
{
|
||||
for (sl = kplist; sl; sl = sl->next)
|
||||
if ((s = strchr (sl->d, ' '))
|
||||
&& !strcmp (s+1, keyref))
|
||||
break;
|
||||
if (sl)
|
||||
{
|
||||
c->fpr2[0] = '&';
|
||||
for (i=1, s=sl->d;
|
||||
(*s && *s != ' '
|
||||
&& i < sizeof c->fpr2 - 3);
|
||||
s++, i++)
|
||||
c->fpr2[i] = *s;
|
||||
c->fpr2[i] = 0;
|
||||
name = c->fpr2;
|
||||
}
|
||||
else /* Restore error. */
|
||||
err = gpg_error (GPG_ERR_INV_NAME);
|
||||
free_strlist (kplist);
|
||||
}
|
||||
}
|
||||
xfree (keyref);
|
||||
}
|
||||
if (err)
|
||||
log_error ("error retrieving key from card: %s\n",
|
||||
gpg_strerror (err));
|
||||
|
||||
c->sl = c->sl->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
serialno = c->serialno;
|
||||
if (serialno)
|
||||
{
|
||||
/* Select the original card again. */
|
||||
agent_scd_serialno (&c->serialno, serialno);
|
||||
xfree (serialno);
|
||||
}
|
||||
c->state++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: /* Init search context to enum all secret keys. */
|
||||
err = getkey_bynames (ctrl, &c->ctx, NULL, NULL, 1,
|
||||
&keyblock);
|
||||
if (err)
|
||||
{
|
||||
release_kbnode (keyblock);
|
||||
keyblock = NULL;
|
||||
getkey_end (ctrl, c->ctx);
|
||||
c->ctx = NULL;
|
||||
}
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
case 6: /* Get next item from the context. */
|
||||
if (c->ctx)
|
||||
{
|
||||
err = getkey_next (ctrl, c->ctx, NULL, &keyblock);
|
||||
if (err)
|
||||
{
|
||||
release_kbnode (keyblock);
|
||||
keyblock = NULL;
|
||||
getkey_end (ctrl, c->ctx);
|
||||
c->ctx = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
c->state++;
|
||||
break;
|
||||
|
||||
default: /* No more names to check - stop. */
|
||||
c->eof = 1;
|
||||
free_public_key (sk);
|
||||
return gpg_error (GPG_ERR_EOF);
|
||||
}
|
||||
}
|
||||
while ((!name || !*name) && !keyblock);
|
||||
|
||||
if (keyblock)
|
||||
c->node = c->keyblock = keyblock;
|
||||
else
|
||||
{
|
||||
err = getkey_byname (ctrl, NULL, NULL, name, 1, &c->keyblock);
|
||||
if (err)
|
||||
{
|
||||
/* getkey_byname might return a keyblock even in the
|
||||
error case - I have not checked. Thus better release
|
||||
it. */
|
||||
release_kbnode (c->keyblock);
|
||||
c->keyblock = NULL;
|
||||
}
|
||||
else
|
||||
c->node = c->keyblock;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the next key from the current keyblock. */
|
||||
for (; c->node; c->node = c->node->next)
|
||||
{
|
||||
if (c->node->pkt->pkttype == PKT_PUBLIC_KEY
|
||||
|| c->node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
||||
{
|
||||
SK_LIST r;
|
||||
|
||||
/* Skip this candidate if it's already enumerated. */
|
||||
for (r = c->results; r; r = r->next)
|
||||
if (!cmp_public_keys (r->pk, c->node->pkt->pkt.public_key))
|
||||
break;
|
||||
if (r)
|
||||
continue;
|
||||
|
||||
copy_public_key (sk, c->node->pkt->pkt.public_key);
|
||||
c->node = c->node->next;
|
||||
|
||||
r = xtrycalloc (1, sizeof (*r));
|
||||
if (!r)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
free_public_key (sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
r->pk = sk;
|
||||
r->next = c->results;
|
||||
c->results = r;
|
||||
|
||||
return 0; /* Found. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispose the keyblock and continue. */
|
||||
release_kbnode (c->keyblock);
|
||||
c->keyblock = NULL;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user