mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
scd:p15: Support signing with CardOS 5 cards.
* scd/app-help.c (app_help_get_keygrip_string_pk): Add optional arg r_pkey and change all callers. (app_help_get_keygrip_string): Ditto. * scd/app-p15.c (struct cdf_object_s): Use bit flags (struct aodf_object_s): Ditto. Add field 'fid'. (struct prkdf_object_s): Ditto. Add fields keygrip, keyalgo, and keynbits. (parse_certid): Allow a keygrip instead of a certid aka keyref. (read_ef_aodf): Store the FID. (keygripstr_from_prkdf): Rename to ... (keygrip_from_prkdf): this. Remove arg r_gripstr and implement cache. Change callers to directly use the values from the object. Also store the algo and length of the key ion the object. (keyref_from_keyinfo): New. Factored out code. (do_sign): Support SHA-256 and >2048 bit RSA keys. (do_with_keygrip): New. (app_select_p15): Register new function. -- This has been tested with a D-Trust card featuring 3072 bit keys. Note that non-repudiation key for a qualified signature does not yet work because we do not yet support rsaPSS padding. Thus a gpgsm --learn shows a couple of Bad Signature errors for this key. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
2bdd4fc7b6
commit
103c1576b7
@ -216,8 +216,10 @@ app_get_slot (app_t app)
|
|||||||
/*-- app-help.c --*/
|
/*-- app-help.c --*/
|
||||||
unsigned int app_help_count_bits (const unsigned char *a, size_t len);
|
unsigned int app_help_count_bits (const unsigned char *a, size_t len);
|
||||||
gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
|
gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
|
||||||
char *hexkeygrip);
|
char *hexkeygrip,
|
||||||
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
|
gcry_sexp_t *r_pkey);
|
||||||
|
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip,
|
||||||
|
gcry_sexp_t *r_pkey);
|
||||||
gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen,
|
gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen,
|
||||||
unsigned char **r_pk, size_t *r_pklen);
|
unsigned char **r_pk, size_t *r_pklen);
|
||||||
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
|
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
|
||||||
|
@ -137,7 +137,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
|
|||||||
ksba_cert_release (cert);
|
ksba_cert_release (cert);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = app_help_get_keygrip_string (cert, hexkeygrip);
|
err = app_help_get_keygrip_string (cert, hexkeygrip, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);
|
log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);
|
||||||
|
@ -53,14 +53,21 @@ app_help_count_bits (const unsigned char *a, size_t len)
|
|||||||
|
|
||||||
/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
|
/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
|
||||||
* as an hex encoded string in the user provided buffer HEXKEYGRIP
|
* as an hex encoded string in the user provided buffer HEXKEYGRIP
|
||||||
* which must be of at least 41 bytes. */
|
* which must be of at least 41 bytes. If R_PKEY is not NULL and the
|
||||||
|
* function succeeded, the S-expression representing the key is
|
||||||
|
* stored there. The caller needs to call gcry_sexp_release on
|
||||||
|
* that. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip)
|
app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip,
|
||||||
|
gcry_sexp_t *r_pkey)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
gcry_sexp_t s_pkey;
|
gcry_sexp_t s_pkey;
|
||||||
unsigned char array[KEYGRIP_LEN];
|
unsigned char array[KEYGRIP_LEN];
|
||||||
|
|
||||||
|
if (r_pkey)
|
||||||
|
*r_pkey = NULL;
|
||||||
|
|
||||||
err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
|
err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
|
||||||
if (err)
|
if (err)
|
||||||
return err; /* Can't parse that S-expression. */
|
return err; /* Can't parse that S-expression. */
|
||||||
@ -69,7 +76,11 @@ app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip)
|
|||||||
gcry_sexp_release (s_pkey);
|
gcry_sexp_release (s_pkey);
|
||||||
return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/
|
return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/
|
||||||
}
|
}
|
||||||
gcry_sexp_release (s_pkey);
|
|
||||||
|
if (r_pkey)
|
||||||
|
*r_pkey = s_pkey;
|
||||||
|
else
|
||||||
|
gcry_sexp_release (s_pkey);
|
||||||
|
|
||||||
bin2hex (array, KEYGRIP_LEN, hexkeygrip);
|
bin2hex (array, KEYGRIP_LEN, hexkeygrip);
|
||||||
|
|
||||||
@ -78,22 +89,28 @@ app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip)
|
|||||||
|
|
||||||
|
|
||||||
/* Return the KEYGRIP for the certificate CERT as an hex encoded
|
/* Return the KEYGRIP for the certificate CERT as an hex encoded
|
||||||
string in the user provided buffer HEXKEYGRIP which must be of at
|
* string in the user provided buffer HEXKEYGRIP which must be of at
|
||||||
least 41 bytes. */
|
* least 41 bytes. If R_PKEY is not NULL and the function succeeded,
|
||||||
|
* the S-expression representing the key is stored there. The caller
|
||||||
|
* needs to call gcry_sexp_release on that. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
|
app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip,
|
||||||
|
gcry_sexp_t *r_pkey)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
ksba_sexp_t p;
|
ksba_sexp_t p;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
|
if (r_pkey)
|
||||||
|
*r_pkey = NULL;
|
||||||
|
|
||||||
p = ksba_cert_get_public_key (cert);
|
p = ksba_cert_get_public_key (cert);
|
||||||
if (!p)
|
if (!p)
|
||||||
return gpg_error (GPG_ERR_BUG);
|
return gpg_error (GPG_ERR_BUG);
|
||||||
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
|
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
|
||||||
if (!n)
|
if (!n)
|
||||||
return gpg_error (GPG_ERR_INV_SEXP);
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip);
|
err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip, r_pkey);
|
||||||
ksba_free (p);
|
ksba_free (p);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
414
scd/app-p15.c
414
scd/app-p15.c
@ -138,6 +138,9 @@ struct cdf_object_s
|
|||||||
/* Link to next item when used in a linked list. */
|
/* Link to next item when used in a linked list. */
|
||||||
struct cdf_object_s *next;
|
struct cdf_object_s *next;
|
||||||
|
|
||||||
|
/* Flags to indicate whether fields are valid. */
|
||||||
|
unsigned int have_off:1;
|
||||||
|
|
||||||
/* Length and allocated buffer with the Id of this object.
|
/* Length and allocated buffer with the Id of this object.
|
||||||
* This field is used for X.509 in PKCS#11 to make it easier to
|
* This field is used for X.509 in PKCS#11 to make it easier to
|
||||||
* match a private key with a certificate. */
|
* match a private key with a certificate. */
|
||||||
@ -149,8 +152,6 @@ struct cdf_object_s
|
|||||||
size_t imagelen;
|
size_t imagelen;
|
||||||
unsigned char *image;
|
unsigned char *image;
|
||||||
|
|
||||||
/* Set to true if a length and offset is available. */
|
|
||||||
int have_off;
|
|
||||||
/* The offset and length of the object. They are only valid if
|
/* The offset and length of the object. They are only valid if
|
||||||
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
|
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
|
||||||
unsigned long off, len;
|
unsigned long off, len;
|
||||||
@ -174,6 +175,25 @@ struct prkdf_object_s
|
|||||||
/* Link to next item when used in a linked list. */
|
/* Link to next item when used in a linked list. */
|
||||||
struct prkdf_object_s *next;
|
struct prkdf_object_s *next;
|
||||||
|
|
||||||
|
/* Flags to indicate whether fields are valid. */
|
||||||
|
unsigned int keygrip_valid:1;
|
||||||
|
unsigned int key_reference_valid:1;
|
||||||
|
unsigned int have_off:1;
|
||||||
|
|
||||||
|
/* The key's usage flags. */
|
||||||
|
keyusage_flags_t usageflags;
|
||||||
|
|
||||||
|
/* The keygrip of the key. This is used as a cache. */
|
||||||
|
char keygrip[2*KEYGRIP_LEN+1];
|
||||||
|
|
||||||
|
/* The Gcrypt algo identifier for the key. It is valid if the
|
||||||
|
* keygrip is also valid. */
|
||||||
|
int keyalgo;
|
||||||
|
|
||||||
|
/* The length of the key in bits (e.g. for RSA the length of the
|
||||||
|
* modulus). It is valid if the keygrip is also valid. */
|
||||||
|
unsigned int keynbits;
|
||||||
|
|
||||||
/* Length and allocated buffer with the Id of this object. */
|
/* Length and allocated buffer with the Id of this object. */
|
||||||
size_t objidlen;
|
size_t objidlen;
|
||||||
unsigned char *objid;
|
unsigned char *objid;
|
||||||
@ -183,17 +203,11 @@ struct prkdf_object_s
|
|||||||
size_t authidlen;
|
size_t authidlen;
|
||||||
unsigned char *authid;
|
unsigned char *authid;
|
||||||
|
|
||||||
/* The key's usage flags. */
|
|
||||||
keyusage_flags_t usageflags;
|
|
||||||
|
|
||||||
/* The keyReference and a flag telling whether it is valid. */
|
/* The keyReference and a flag telling whether it is valid. */
|
||||||
unsigned long key_reference;
|
unsigned long key_reference;
|
||||||
int key_reference_valid;
|
|
||||||
|
|
||||||
/* Set to true if a length and offset is available. */
|
|
||||||
int have_off;
|
|
||||||
/* The offset and length of the object. They are only valid if
|
/* The offset and length of the object. They are only valid if
|
||||||
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
|
* HAVE_OFF is true otherwise they are set to 0. */
|
||||||
unsigned long off, len;
|
unsigned long off, len;
|
||||||
|
|
||||||
/* The length of the path as given in the PrKDF and the path itself.
|
/* The length of the path as given in the PrKDF and the path itself.
|
||||||
@ -214,6 +228,9 @@ struct aodf_object_s
|
|||||||
/* Link to next item when used in a linked list. */
|
/* Link to next item when used in a linked list. */
|
||||||
struct aodf_object_s *next;
|
struct aodf_object_s *next;
|
||||||
|
|
||||||
|
/* Flags to indicate whether fields are valid. */
|
||||||
|
unsigned int have_off:1;
|
||||||
|
|
||||||
/* Length and allocated buffer with the Id of this object. */
|
/* Length and allocated buffer with the Id of this object. */
|
||||||
size_t objidlen;
|
size_t objidlen;
|
||||||
unsigned char *objid;
|
unsigned char *objid;
|
||||||
@ -223,6 +240,9 @@ struct aodf_object_s
|
|||||||
size_t authidlen;
|
size_t authidlen;
|
||||||
unsigned char *authid;
|
unsigned char *authid;
|
||||||
|
|
||||||
|
/* The file ID of this AODF. */
|
||||||
|
unsigned short fid;
|
||||||
|
|
||||||
/* The PIN Flags. */
|
/* The PIN Flags. */
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@ -261,9 +281,6 @@ struct aodf_object_s
|
|||||||
char pad_char;
|
char pad_char;
|
||||||
int pad_char_valid;
|
int pad_char_valid;
|
||||||
|
|
||||||
|
|
||||||
/* Set to true if a length and offset is available. */
|
|
||||||
int have_off;
|
|
||||||
/* The offset and length of the object. They are only valid if
|
/* The offset and length of the object. They are only valid if
|
||||||
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
|
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
|
||||||
unsigned long off, len;
|
unsigned long off, len;
|
||||||
@ -326,6 +343,7 @@ struct app_local_s
|
|||||||
|
|
||||||
|
|
||||||
/*** Local prototypes. ***/
|
/*** Local prototypes. ***/
|
||||||
|
static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf);
|
||||||
static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
|
static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
|
||||||
unsigned char **r_cert, size_t *r_certlen);
|
unsigned char **r_cert, size_t *r_certlen);
|
||||||
|
|
||||||
@ -498,35 +516,55 @@ parse_certid (app_t app, const char *certid,
|
|||||||
*r_objid = NULL;
|
*r_objid = NULL;
|
||||||
*r_objidlen = 0;
|
*r_objidlen = 0;
|
||||||
|
|
||||||
if (app->app_local->home_df)
|
if (certid[0] != 'P' && strlen (certid) == 40) /* This is a keygrip. */
|
||||||
snprintf (tmpbuf, sizeof tmpbuf,
|
|
||||||
"P15-%04X.", (unsigned int)(app->app_local->home_df & 0xffff));
|
|
||||||
else
|
|
||||||
strcpy (tmpbuf, "P15.");
|
|
||||||
if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
|
|
||||||
{
|
{
|
||||||
if (!strncmp (certid, "P15.", 4)
|
prkdf_object_t keyinfo;
|
||||||
|| (!strncmp (certid, "P15-", 4)
|
|
||||||
&& hexdigitp (certid+4)
|
for (keyinfo = app->app_local->private_key_info;
|
||||||
&& hexdigitp (certid+5)
|
keyinfo; keyinfo = keyinfo->next)
|
||||||
&& hexdigitp (certid+6)
|
if (!keygrip_from_prkdf (app, keyinfo)
|
||||||
&& hexdigitp (certid+7)
|
&& !strcmp (certid, keyinfo->keygrip))
|
||||||
&& certid[8] == '.'))
|
break;
|
||||||
return gpg_error (GPG_ERR_NOT_FOUND);
|
if (!keyinfo || !keyinfo->objidlen || !keyinfo->objid)
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
}
|
objidlen = keyinfo->objidlen;
|
||||||
certid += strlen (tmpbuf);
|
objid = xtrymalloc (objidlen);
|
||||||
|
if (!objid)
|
||||||
|
return gpg_error_from_syserror ();
|
||||||
|
memcpy (objid, keyinfo->objid, keyinfo->objidlen);
|
||||||
|
}
|
||||||
|
else /* This is a usual keyref. */
|
||||||
|
{
|
||||||
|
if (app->app_local->home_df)
|
||||||
|
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
|
||||||
|
(unsigned int)(app->app_local->home_df & 0xffff));
|
||||||
|
else
|
||||||
|
strcpy (tmpbuf, "P15.");
|
||||||
|
if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
|
||||||
|
{
|
||||||
|
if (!strncmp (certid, "P15.", 4)
|
||||||
|
|| (!strncmp (certid, "P15-", 4)
|
||||||
|
&& hexdigitp (certid+4)
|
||||||
|
&& hexdigitp (certid+5)
|
||||||
|
&& hexdigitp (certid+6)
|
||||||
|
&& hexdigitp (certid+7)
|
||||||
|
&& certid[8] == '.'))
|
||||||
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
}
|
||||||
|
certid += strlen (tmpbuf);
|
||||||
|
for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
|
||||||
|
;
|
||||||
|
if (*s || !objidlen || (objidlen%2))
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
objidlen /= 2;
|
||||||
|
objid = xtrymalloc (objidlen);
|
||||||
|
if (!objid)
|
||||||
|
return gpg_error_from_syserror ();
|
||||||
|
for (s=certid, i=0; i < objidlen; i++, s+=2)
|
||||||
|
objid[i] = xtoi_2 (s);
|
||||||
|
}
|
||||||
|
|
||||||
for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
|
|
||||||
;
|
|
||||||
if (*s || !objidlen || (objidlen%2))
|
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
|
||||||
objidlen /= 2;
|
|
||||||
objid = xtrymalloc (objidlen);
|
|
||||||
if (!objid)
|
|
||||||
return gpg_error_from_syserror ();
|
|
||||||
for (s=certid, i=0; i < objidlen; i++, s+=2)
|
|
||||||
objid[i] = xtoi_2 (s);
|
|
||||||
*r_objid = objid;
|
*r_objid = objid;
|
||||||
*r_objidlen = objidlen;
|
*r_objidlen = objidlen;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1644,6 +1682,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
|
|||||||
aodf = xtrycalloc (1, sizeof *aodf);
|
aodf = xtrycalloc (1, sizeof *aodf);
|
||||||
if (!aodf)
|
if (!aodf)
|
||||||
goto no_core;
|
goto no_core;
|
||||||
|
aodf->fid = fid;
|
||||||
|
|
||||||
/* Parse the commonObjectAttributes. */
|
/* Parse the commonObjectAttributes. */
|
||||||
where = __LINE__;
|
where = __LINE__;
|
||||||
@ -2552,21 +2591,26 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
|
|||||||
|
|
||||||
|
|
||||||
/* Get the keygrip of the private key object PRKDF. On success the
|
/* Get the keygrip of the private key object PRKDF. On success the
|
||||||
keygrip gets returned in the caller provided 41 byte buffer
|
* keygrip, the algo and the length are stored in the KEYGRIP,
|
||||||
R_GRIPSTR. */
|
* KEYALFO, and KEYNBITS fields of the PRKDF object. */
|
||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
|
keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
cdf_object_t cdf;
|
cdf_object_t cdf;
|
||||||
unsigned char *der;
|
unsigned char *der;
|
||||||
size_t derlen;
|
size_t derlen;
|
||||||
ksba_cert_t cert;
|
ksba_cert_t cert;
|
||||||
|
gcry_sexp_t s_pkey = NULL;
|
||||||
|
|
||||||
|
/* Easy if we got a cached version. */
|
||||||
|
if (prkdf->keygrip_valid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* FIXME: We should check whether a public key directory file and a
|
/* FIXME: We should check whether a public key directory file and a
|
||||||
matching public key for PRKDF is available. This should make
|
matching public key for PRKDF is available. This should make
|
||||||
extraction of the key much easier. My current test card doesn't
|
extraction of the key much easier. My current test card doesn't
|
||||||
have one, so we can only use the fallback solution bu looking for
|
have one, so we can only use the fallback solution by looking for
|
||||||
a matching certificate and extract the key from there. */
|
a matching certificate and extract the key from there. */
|
||||||
|
|
||||||
/* Look for a matching certificate. A certificate matches if the Id
|
/* Look for a matching certificate. A certificate matches if the Id
|
||||||
@ -2586,24 +2630,68 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
|
|||||||
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
|
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
|
||||||
break;
|
break;
|
||||||
if (!cdf)
|
if (!cdf)
|
||||||
return gpg_error (GPG_ERR_NOT_FOUND);
|
{
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
err = readcert_by_cdf (app, cdf, &der, &derlen);
|
err = readcert_by_cdf (app, cdf, &der, &derlen);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto leave;
|
||||||
|
|
||||||
err = ksba_cert_new (&cert);
|
err = ksba_cert_new (&cert);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = ksba_cert_init_from_mem (cert, der, derlen);
|
err = ksba_cert_init_from_mem (cert, der, derlen);
|
||||||
xfree (der);
|
xfree (der);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = app_help_get_keygrip_string (cert, r_gripstr);
|
err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey);
|
||||||
ksba_cert_release (cert);
|
ksba_cert_release (cert);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
prkdf->keyalgo = get_pk_algo_from_key (s_pkey);
|
||||||
|
if (!prkdf->keyalgo)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
prkdf->keynbits = gcry_pk_get_nbits (s_pkey);
|
||||||
|
if (!prkdf->keynbits)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
prkdf->keygrip_valid = 1; /* Yeah, got everything. */
|
||||||
|
|
||||||
|
leave:
|
||||||
|
gcry_sexp_release (s_pkey);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return a malloced keyref string for KEYINFO. Returns NULL on
|
||||||
|
* malloc failure. */
|
||||||
|
static char *
|
||||||
|
keyref_from_keyinfo (app_t app, prkdf_object_t keyinfo)
|
||||||
|
{
|
||||||
|
char *buf, *p;
|
||||||
|
|
||||||
|
buf = xtrymalloc (4 + 5 + keyinfo->objidlen*2 + 1);
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
p = stpcpy (buf, "P15");
|
||||||
|
if (app->app_local->home_df)
|
||||||
|
{
|
||||||
|
snprintf (p, 6, "-%04X",
|
||||||
|
(unsigned int)(app->app_local->home_df & 0xffff));
|
||||||
|
p += 5;
|
||||||
|
}
|
||||||
|
p = stpcpy (p, ".");
|
||||||
|
bin2hex (keyinfo->objid, keyinfo->objidlen, p);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Helper to do_learn_status: Send information about all known
|
/* Helper to do_learn_status: Send information about all known
|
||||||
@ -2616,36 +2704,26 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
|
|||||||
|
|
||||||
for (; keyinfo; keyinfo = keyinfo->next)
|
for (; keyinfo; keyinfo = keyinfo->next)
|
||||||
{
|
{
|
||||||
char gripstr[40+1];
|
char *buf;
|
||||||
char *buf, *p;
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
buf = xtrymalloc (9 + keyinfo->objidlen*2 + 1);
|
buf = keyref_from_keyinfo (app, keyinfo);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return gpg_error_from_syserror ();
|
return gpg_error_from_syserror ();
|
||||||
p = stpcpy (buf, "P15");
|
|
||||||
if (app->app_local->home_df)
|
|
||||||
{
|
|
||||||
snprintf (p, 6, "-%04X",
|
|
||||||
(unsigned int)(app->app_local->home_df & 0xffff));
|
|
||||||
p += 5;
|
|
||||||
}
|
|
||||||
p = stpcpy (p, ".");
|
|
||||||
bin2hex (keyinfo->objid, keyinfo->objidlen, p);
|
|
||||||
|
|
||||||
err = keygripstr_from_prkdf (app, keyinfo, gripstr);
|
err = keygrip_from_prkdf (app, keyinfo);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
log_error ("p15: error getting keygrip from ");
|
log_error ("p15: error getting keygrip from ");
|
||||||
for (j=0; j < keyinfo->pathlen; j++)
|
for (j=0; j < keyinfo->pathlen; j++)
|
||||||
log_printf ("%04hX", keyinfo->path[j]);
|
log_printf ("%s%04hX", j?"/":"", keyinfo->path[j]);
|
||||||
log_printf (": %s\n", gpg_strerror (err));
|
log_printf (": %s\n", gpg_strerror (err));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert (strlen (gripstr) == 40);
|
log_assert (strlen (keyinfo->keygrip) == 40);
|
||||||
send_status_info (ctrl, "KEYPAIRINFO",
|
send_status_info (ctrl, "KEYPAIRINFO",
|
||||||
gripstr, 40,
|
keyinfo->keygrip, 2*KEYGRIP_LEN,
|
||||||
buf, strlen (buf),
|
buf, strlen (buf),
|
||||||
NULL, (size_t)0);
|
NULL, (size_t)0);
|
||||||
}
|
}
|
||||||
@ -3029,6 +3107,9 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
const void *indata, size_t indatalen,
|
const void *indata, size_t indatalen,
|
||||||
unsigned char **outdata, size_t *outdatalen )
|
unsigned char **outdata, size_t *outdatalen )
|
||||||
{
|
{
|
||||||
|
static unsigned char sha256_prefix[19] = /* OID: 2.16.840.1.101.3.4.2.1 */
|
||||||
|
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||||
|
0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
|
||||||
static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
|
static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
|
||||||
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
|
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
|
||||||
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
|
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
|
||||||
@ -3038,19 +3119,26 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
|
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
int i;
|
int i;
|
||||||
unsigned char data[36]; /* Must be large enough for a SHA-1 digest
|
unsigned char data[32+19]; /* Must be large enough for a SHA-256 digest
|
||||||
+ the largest OID prefix above and also
|
* + the largest OID prefix above and also
|
||||||
fit the 36 bytes of md5sha1. */
|
* fit the 36 bytes of md5sha1. */
|
||||||
prkdf_object_t prkdf; /* The private key object. */
|
prkdf_object_t prkdf; /* The private key object. */
|
||||||
aodf_object_t aodf; /* The associated authentication object. */
|
aodf_object_t aodf; /* The associated authentication object. */
|
||||||
int no_data_padding = 0; /* True if the card want the data without padding.*/
|
int no_data_padding = 0; /* True if the card want the data without padding.*/
|
||||||
int mse_done = 0; /* Set to true if the MSE has been done. */
|
int mse_done = 0; /* Set to true if the MSE has been done. */
|
||||||
|
unsigned int hashlen; /* Length of the hash. */
|
||||||
|
unsigned int datalen; /* Length of the data to sign (prefix+hash). */
|
||||||
|
unsigned char *dataptr;
|
||||||
|
int exmode, le_value;
|
||||||
|
|
||||||
(void)ctrl;
|
(void)ctrl;
|
||||||
|
|
||||||
|
log_debug ("p15:sign: keyidstr='%s' indatalen=%zu\n", keyidstr, indatalen);
|
||||||
if (!keyidstr || !*keyidstr)
|
if (!keyidstr || !*keyidstr)
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
return gpg_error (GPG_ERR_INV_VALUE);
|
||||||
if (indatalen != 20 && indatalen != 16 && indatalen != 35 && indatalen != 36)
|
if (indatalen != 20 && indatalen != 16
|
||||||
|
&& indatalen != 35 && indatalen != 36
|
||||||
|
&& indatalen != (32+19))
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
return gpg_error (GPG_ERR_INV_VALUE);
|
||||||
|
|
||||||
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
|
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
|
||||||
@ -3081,12 +3169,19 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
log_error ("p15: authentication object for %s missing\n", keyidstr);
|
log_error ("p15: authentication object for %s missing\n", keyidstr);
|
||||||
return gpg_error (GPG_ERR_INV_CARD);
|
return gpg_error (GPG_ERR_INV_CARD);
|
||||||
}
|
}
|
||||||
if (aodf->authid)
|
|
||||||
|
|
||||||
|
if (opt.verbose)
|
||||||
{
|
{
|
||||||
log_error ("p15: PIN verification is protected by an "
|
log_info ("p15: using AODF %04hX id=", aodf->fid);
|
||||||
"additional authentication token\n");
|
for (i=0; i < aodf->objidlen; i++)
|
||||||
return gpg_error (GPG_ERR_BAD_PIN_METHOD);
|
log_printf ("%02X", aodf->objid[i]);
|
||||||
|
log_printf ("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aodf->authid && opt.verbose)
|
||||||
|
log_info ("p15: PIN is controlled by another authentication token\n");
|
||||||
|
|
||||||
if (aodf->pinflags.integrity_protected
|
if (aodf->pinflags.integrity_protected
|
||||||
|| aodf->pinflags.confidentiality_protected)
|
|| aodf->pinflags.confidentiality_protected)
|
||||||
{
|
{
|
||||||
@ -3284,6 +3379,9 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
else
|
else
|
||||||
pinvaluelen = strlen (pinvalue);
|
pinvaluelen = strlen (pinvalue);
|
||||||
|
|
||||||
|
log_printhex (pinvalue, pinvaluelen,
|
||||||
|
"about to verify with ref %lu pin:",
|
||||||
|
aodf->pin_reference_valid? aodf->pin_reference : 0);
|
||||||
err = iso7816_verify (app_get_slot (app),
|
err = iso7816_verify (app_get_slot (app),
|
||||||
aodf->pin_reference_valid? aodf->pin_reference : 0,
|
aodf->pin_reference_valid? aodf->pin_reference : 0,
|
||||||
pinvalue, pinvaluelen);
|
pinvalue, pinvaluelen);
|
||||||
@ -3304,6 +3402,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
if (hashalgo != MD_USER_TLS_MD5SHA1)
|
if (hashalgo != MD_USER_TLS_MD5SHA1)
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
memcpy (data, indata, indatalen);
|
memcpy (data, indata, indatalen);
|
||||||
|
datalen = hashlen = 36;
|
||||||
}
|
}
|
||||||
else if (indatalen == 35)
|
else if (indatalen == 35)
|
||||||
{
|
{
|
||||||
@ -3318,17 +3417,56 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
else
|
else
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
memcpy (data, indata, indatalen);
|
memcpy (data, indata, indatalen);
|
||||||
|
datalen = 35;
|
||||||
|
hashlen = 20;
|
||||||
|
}
|
||||||
|
else if (indatalen == 32 + 19)
|
||||||
|
{
|
||||||
|
/* Seems to be a prepared SHA256 DER object. */
|
||||||
|
if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
|
||||||
|
;
|
||||||
|
else
|
||||||
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
|
memcpy (data, indata, indatalen);
|
||||||
|
datalen = 51;
|
||||||
|
hashlen = 32;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Need to prepend the prefix. */
|
/* Need to prepend the prefix. */
|
||||||
if (hashalgo == GCRY_MD_SHA1)
|
if (hashalgo == GCRY_MD_SHA256)
|
||||||
memcpy (data, sha1_prefix, 15);
|
{
|
||||||
|
memcpy (data, sha256_prefix, 19);
|
||||||
|
memcpy (data+19, indata, indatalen);
|
||||||
|
datalen = 51;
|
||||||
|
hashlen = 32;
|
||||||
|
}
|
||||||
|
else if (hashalgo == GCRY_MD_SHA1)
|
||||||
|
{
|
||||||
|
memcpy (data, sha1_prefix, 15);
|
||||||
|
memcpy (data+15, indata, indatalen);
|
||||||
|
datalen = 35;
|
||||||
|
hashlen = 20;
|
||||||
|
}
|
||||||
else if (hashalgo == GCRY_MD_RMD160)
|
else if (hashalgo == GCRY_MD_RMD160)
|
||||||
memcpy (data, rmd160_prefix, 15);
|
{
|
||||||
|
memcpy (data, rmd160_prefix, 15);
|
||||||
|
memcpy (data+15, indata, indatalen);
|
||||||
|
datalen = 35;
|
||||||
|
hashlen = 20;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
memcpy (data+15, indata, indatalen);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* We need some more info about the key - get the keygrip to
|
||||||
|
* populate these fields. */
|
||||||
|
err = keygrip_from_prkdf (app, prkdf);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Manage security environment needs to be weaked for certain cards. */
|
/* Manage security environment needs to be weaked for certain cards. */
|
||||||
@ -3364,15 +3502,32 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashalgo == MD_USER_TLS_MD5SHA1)
|
|
||||||
err = iso7816_compute_ds (app_get_slot (app),
|
dataptr = data;
|
||||||
0, data, 36, 0, outdata, outdatalen);
|
if (no_data_padding)
|
||||||
else if (no_data_padding)
|
{
|
||||||
err = iso7816_compute_ds (app_get_slot (app),
|
dataptr += datalen - hashlen;
|
||||||
0, data+15, 20, 0,outdata,outdatalen);
|
datalen = hashlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
|
||||||
|
{
|
||||||
|
exmode = 1;
|
||||||
|
le_value = prkdf->keynbits / 8;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
err = iso7816_compute_ds (app_get_slot (app),
|
{
|
||||||
0, data, 35, 0, outdata, outdatalen);
|
exmode = 0;
|
||||||
|
le_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iso7816_compute_ds (app_get_slot (app),
|
||||||
|
exmode, dataptr, datalen,
|
||||||
|
le_value, outdata, outdatalen);
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
log_printhex (*outdata, *outdatalen, "sign output:");
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3412,6 +3567,100 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Process the various keygrip based info requests. */
|
||||||
|
static gpg_error_t
|
||||||
|
do_with_keygrip (app_t app, ctrl_t ctrl, int action,
|
||||||
|
const char *want_keygripstr, int capability)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
char *serialno = NULL;
|
||||||
|
int as_data = 0;
|
||||||
|
prkdf_object_t keyinfo;
|
||||||
|
|
||||||
|
/* First a quick check for valid parameters. */
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case KEYGRIP_ACTION_LOOKUP:
|
||||||
|
if (!want_keygripstr)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEYGRIP_ACTION_SEND_DATA:
|
||||||
|
as_data = 1;
|
||||||
|
break;
|
||||||
|
case KEYGRIP_ACTION_WRITE_STATUS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = gpg_error (GPG_ERR_INV_ARG);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the s/n string if needed. */
|
||||||
|
if (action != KEYGRIP_ACTION_LOOKUP)
|
||||||
|
{
|
||||||
|
serialno = app_get_serialno (app);
|
||||||
|
if (!serialno)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (keyinfo = app->app_local->private_key_info;
|
||||||
|
keyinfo; keyinfo = keyinfo->next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (keygrip_from_prkdf (app, keyinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (action == KEYGRIP_ACTION_LOOKUP)
|
||||||
|
{
|
||||||
|
if (!strcmp (keyinfo->keygrip, want_keygripstr))
|
||||||
|
{
|
||||||
|
err = 0; /* Found */
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!want_keygripstr || !strcmp (keyinfo->keygrip, want_keygripstr))
|
||||||
|
{
|
||||||
|
char *keyref;
|
||||||
|
|
||||||
|
/* FIXME: Consider ... */
|
||||||
|
(void)capability;
|
||||||
|
|
||||||
|
keyref = keyref_from_keyinfo (app, keyinfo);
|
||||||
|
if (!keyref)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_keyinfo (ctrl, as_data, keyinfo->keygrip, serialno, keyref);
|
||||||
|
if (want_keygripstr)
|
||||||
|
{
|
||||||
|
err = 0; /* Found */
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return an error so that the dispatcher keeps on looping over the
|
||||||
|
* other applications. For clarity we use a different error code
|
||||||
|
* when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR
|
||||||
|
* is not NULL. */
|
||||||
|
if (!want_keygripstr)
|
||||||
|
err = gpg_error (GPG_ERR_TRUE);
|
||||||
|
else
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
|
||||||
|
leave:
|
||||||
|
xfree (serialno);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Assume that EF(DIR) has been selected. Read its content and figure
|
/* Assume that EF(DIR) has been selected. Read its content and figure
|
||||||
out the home EF of pkcs#15. Return that home DF or 0 if not found
|
out the home EF of pkcs#15. Return that home DF or 0 if not found
|
||||||
@ -3604,6 +3853,7 @@ app_select_p15 (app_t app)
|
|||||||
app->fnc.decipher = NULL;
|
app->fnc.decipher = NULL;
|
||||||
app->fnc.change_pin = NULL;
|
app->fnc.change_pin = NULL;
|
||||||
app->fnc.check_pin = NULL;
|
app->fnc.check_pin = NULL;
|
||||||
|
app->fnc.with_keygrip = do_with_keygrip;
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -1356,7 +1356,7 @@ get_keygrip_by_tag (app_t app, unsigned int tag,
|
|||||||
err = ksba_cert_init_from_mem (cert, certbuf, certbuflen);
|
err = ksba_cert_init_from_mem (cert, certbuf, certbuflen);
|
||||||
if (err)
|
if (err)
|
||||||
goto leave;
|
goto leave;
|
||||||
err = app_help_get_keygrip_string (cert, *r_keygripstr);
|
err = app_help_get_keygrip_string (cert, *r_keygripstr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
@ -1564,7 +1564,7 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags,
|
|||||||
char idbuf[50];
|
char idbuf[50];
|
||||||
const char *usage;
|
const char *usage;
|
||||||
|
|
||||||
err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
|
err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
log_error ("app_help_get_keygrip_string_pk failed: %s\n",
|
log_error ("app_help_get_keygrip_string_pk failed: %s\n",
|
||||||
|
@ -1355,7 +1355,7 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
|
|||||||
err = ksba_cert_init_from_mem (cert, der, derlen);
|
err = ksba_cert_init_from_mem (cert, der, derlen);
|
||||||
xfree (der);
|
xfree (der);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = app_help_get_keygrip_string (cert, r_gripstr);
|
err = app_help_get_keygrip_string (cert, r_gripstr, NULL);
|
||||||
ksba_cert_release (cert);
|
ksba_cert_release (cert);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -2251,7 +2251,7 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str,
|
|||||||
log_debug ("slot %d, app %s: calling with_keygrip(%s)\n",
|
log_debug ("slot %d, app %s: calling with_keygrip(%s)\n",
|
||||||
c->slot, xstrapptype (a),
|
c->slot, xstrapptype (a),
|
||||||
action == KEYGRIP_ACTION_SEND_DATA? "send_data":
|
action == KEYGRIP_ACTION_SEND_DATA? "send_data":
|
||||||
action == KEYGRIP_ACTION_WRITE_STATUS? "write_data":
|
action == KEYGRIP_ACTION_WRITE_STATUS? "status":
|
||||||
action == KEYGRIP_ACTION_LOOKUP? "lookup":"?");
|
action == KEYGRIP_ACTION_LOOKUP? "lookup":"?");
|
||||||
if (!a->fnc.with_keygrip (a, ctrl, action, keygrip_str, capability))
|
if (!a->fnc.with_keygrip (a, ctrl, action, keygrip_str, capability))
|
||||||
goto leave_the_loop; /* ACTION_LOOKUP succeeded. */
|
goto leave_the_loop; /* ACTION_LOOKUP succeeded. */
|
||||||
|
@ -648,7 +648,8 @@ do_readkey (card_t card, ctrl_t ctrl, const char *line,
|
|||||||
{
|
{
|
||||||
char keygripstr[KEYGRIP_LEN*2+1];
|
char keygripstr[KEYGRIP_LEN*2+1];
|
||||||
|
|
||||||
rc = app_help_get_keygrip_string_pk (*pk_p, *pklen_p, keygripstr);
|
rc = app_help_get_keygrip_string_pk (*pk_p, *pklen_p,
|
||||||
|
keygripstr, NULL);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
log_error ("app_help_get_keygrip_string failed: %s\n",
|
log_error ("app_help_get_keygrip_string failed: %s\n",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user