1
0
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.
common/scd:p15: Support signing with CardOS 5 cards.
* common/util.h (KEYGRIP_LEN): New.
--

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>

Back ported from master:
- Removed do_with_keygrip
- Added KEYGRIP_LEN
- app_help_get_keygrip_string_pk actually added.
- Move keygrip_from_prkdf in do_sign before the verification.
  It used to work in master only because there it is implictly
  called prior to signing by do_with_keygrip

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-03-31 19:55:15 +02:00
parent 368f006a28
commit e730444e7b
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
6 changed files with 289 additions and 100 deletions

View File

@ -66,6 +66,11 @@
/* Hash function used with libksba. */
#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
/* The length of the keygrip. This is a SHA-1 hash of the key
* parameters as generated by gcry_pk_get_keygrip. */
#define KEYGRIP_LEN 20
/* Get all the stuff from jnlib. */
#include "../common/logging.h"
#include "../common/argparse.h"

View File

@ -134,7 +134,11 @@ app_get_slot (app_t app)
/*-- app-help.c --*/
unsigned int app_help_count_bits (const unsigned char *a, size_t len);
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
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);
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);

View File

@ -137,7 +137,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
ksba_cert_release (cert);
return err;
}
err = app_help_get_keygrip_string (cert, hexkeygrip);
err = app_help_get_keygrip_string (cert, hexkeygrip, NULL);
if (err)
{
log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);

View File

@ -52,26 +52,24 @@ app_help_count_bits (const unsigned char *a, size_t len)
}
/* Return the KEYGRIP for the certificate CERT as an hex encoded
string in the user provided buffer HEXKEYGRIP which must be of at
least 41 bytes. */
/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
* as an hex encoded string in the user provided buffer HEXKEYGRIP
* 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
app_help_get_keygrip_string (ksba_cert_t cert, 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;
gcry_sexp_t s_pkey;
ksba_sexp_t p;
size_t n;
unsigned char array[20];
unsigned char array[KEYGRIP_LEN];
p = ksba_cert_get_public_key (cert);
if (!p)
return gpg_error (GPG_ERR_BUG);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n);
xfree (p);
if (r_pkey)
*r_pkey = NULL;
err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
if (err)
return err; /* Can't parse that S-expression. */
if (!gcry_pk_get_keygrip (s_pkey, array))
@ -79,14 +77,45 @@ app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
gcry_sexp_release (s_pkey);
return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/
}
if (r_pkey)
*r_pkey = s_pkey;
else
gcry_sexp_release (s_pkey);
bin2hex (array, 20, hexkeygrip);
bin2hex (array, KEYGRIP_LEN, hexkeygrip);
return 0;
}
/* Return the KEYGRIP for the certificate CERT as an hex encoded
* string in the user provided buffer HEXKEYGRIP 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
app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip,
gcry_sexp_t *r_pkey)
{
gpg_error_t err;
ksba_sexp_t p;
size_t n;
if (r_pkey)
*r_pkey = NULL;
p = ksba_cert_get_public_key (cert);
if (!p)
return gpg_error (GPG_ERR_BUG);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip, r_pkey);
ksba_free (p);
return err;
}
/* Given the SLOT and the File ID FID, return the length of the
certificate contained in that file. Returns 0 if the file does not

View File

@ -139,6 +139,9 @@ struct cdf_object_s
/* Link to next item when used in a linked list. */
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.
* This field is used for X.509 in PKCS#11 to make it easier to
* match a private key with a certificate. */
@ -150,8 +153,6 @@ struct cdf_object_s
size_t imagelen;
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
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
unsigned long off, len;
@ -175,6 +176,25 @@ struct prkdf_object_s
/* Link to next item when used in a linked list. */
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. */
size_t objidlen;
unsigned char *objid;
@ -184,17 +204,11 @@ struct prkdf_object_s
size_t authidlen;
unsigned char *authid;
/* The key's usage flags. */
keyusage_flags_t usageflags;
/* The keyReference and a flag telling whether it is valid. */
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
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;
/* The length of the path as given in the PrKDF and the path itself.
@ -215,6 +229,9 @@ struct aodf_object_s
/* Link to next item when used in a linked list. */
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. */
size_t objidlen;
unsigned char *objid;
@ -224,6 +241,9 @@ struct aodf_object_s
size_t authidlen;
unsigned char *authid;
/* The file ID of this AODF. */
unsigned short fid;
/* The PIN Flags. */
struct
{
@ -262,9 +282,6 @@ struct aodf_object_s
char pad_char;
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
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
unsigned long off, len;
@ -327,6 +344,7 @@ struct app_local_s
/*** 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,
unsigned char **r_cert, size_t *r_certlen);
@ -498,9 +516,28 @@ parse_certid (app_t app, const char *certid,
*r_objid = NULL;
*r_objidlen = 0;
if (certid[0] != 'P' && strlen (certid) == 40) /* This is a keygrip. */
{
prkdf_object_t keyinfo;
for (keyinfo = app->app_local->private_key_info;
keyinfo; keyinfo = keyinfo->next)
if (!keygrip_from_prkdf (app, keyinfo)
&& !strcmp (certid, keyinfo->keygrip))
break;
if (!keyinfo || !keyinfo->objidlen || !keyinfo->objid)
return gpg_error (GPG_ERR_NOT_FOUND);
objidlen = keyinfo->objidlen;
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));
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
(unsigned int)(app->app_local->home_df & 0xffff));
else
strcpy (tmpbuf, "P15.");
if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
@ -516,7 +553,6 @@ parse_certid (app_t app, const char *certid,
return gpg_error (GPG_ERR_INV_ID);
}
certid += strlen (tmpbuf);
for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
;
if (*s || !objidlen || (objidlen%2))
@ -527,6 +563,8 @@ parse_certid (app_t app, const char *certid,
return gpg_error_from_syserror ();
for (s=certid, i=0; i < objidlen; i++, s+=2)
objid[i] = xtoi_2 (s);
}
*r_objid = objid;
*r_objidlen = objidlen;
return 0;
@ -1640,6 +1678,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
aodf = xtrycalloc (1, sizeof *aodf);
if (!aodf)
goto no_core;
aodf->fid = fid;
/* Parse the commonObjectAttributes. */
where = __LINE__;
@ -2548,21 +2587,26 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
/* Get the keygrip of the private key object PRKDF. On success the
keygrip gets returned in the caller provided 41 byte buffer
R_GRIPSTR. */
* keygrip, the algo and the length are stored in the KEYGRIP,
* KEYALFO, and KEYNBITS fields of the PRKDF object. */
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;
cdf_object_t cdf;
unsigned char *der;
size_t derlen;
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
matching public key for PRKDF is available. This should make
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. */
/* Look for a matching certificate. A certificate matches if the Id
@ -2582,24 +2626,68 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
break;
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);
if (err)
return err;
goto leave;
err = ksba_cert_new (&cert);
if (!err)
err = ksba_cert_init_from_mem (cert, der, derlen);
xfree (der);
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);
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 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
@ -2612,36 +2700,26 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
for (; keyinfo; keyinfo = keyinfo->next)
{
char gripstr[40+1];
char *buf, *p;
char *buf;
int j;
buf = xtrymalloc (9 + keyinfo->objidlen*2 + 1);
buf = keyref_from_keyinfo (app, keyinfo);
if (!buf)
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)
{
log_error ("p15: error getting keygrip from ");
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));
}
else
{
assert (strlen (gripstr) == 40);
log_assert (strlen (keyinfo->keygrip) == 40);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
keyinfo->keygrip, 2*KEYGRIP_LEN,
buf, strlen (buf),
NULL, (size_t)0);
}
@ -3022,6 +3100,9 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
const void *indata, size_t indatalen,
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 */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
@ -3031,17 +3112,25 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t err;
int i;
unsigned char data[36]; /* Must be large enough for a SHA-1 digest
+ the largest OID prefix above and also
fit the 36 bytes of md5sha1. */
unsigned char data[32+19]; /* Must be large enough for a SHA-256 digest
* + the largest OID prefix above and also
* fit the 36 bytes of md5sha1. */
prkdf_object_t prkdf; /* The private key object. */
aodf_object_t aodf; /* The associated authentication object. */
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. */
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;
log_debug ("p15:sign: keyidstr='%s' indatalen=%zu\n", keyidstr, indatalen);
if (!keyidstr || !*keyidstr)
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);
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
@ -3072,12 +3161,27 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
log_error ("p15: authentication object for %s missing\n", keyidstr);
return gpg_error (GPG_ERR_INV_CARD);
}
if (aodf->authid)
/* 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: PIN verification is protected by an "
"additional authentication token\n");
return gpg_error (GPG_ERR_BAD_PIN_METHOD);
log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
return err;
}
if (opt.verbose)
{
log_info ("p15: using AODF %04hX id=", aodf->fid);
for (i=0; i < aodf->objidlen; i++)
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
|| aodf->pinflags.confidentiality_protected)
{
@ -3275,7 +3379,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else
pinvaluelen = strlen (pinvalue);
err = iso7816_verify (app->slot,
err = iso7816_verify (app_get_slot (app),
aodf->pin_reference_valid? aodf->pin_reference : 0,
pinvalue, pinvaluelen);
xfree (pinvalue);
@ -3295,6 +3399,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
if (hashalgo != MD_USER_TLS_MD5SHA1)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data, indata, indatalen);
datalen = hashlen = 36;
}
else if (indatalen == 35)
{
@ -3309,19 +3414,49 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
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
{
/* Need to prepend the prefix. */
if (hashalgo == GCRY_MD_SHA1)
if (hashalgo == GCRY_MD_SHA256)
{
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)
{
memcpy (data, rmd160_prefix, 15);
memcpy (data+15, indata, indatalen);
datalen = 35;
hashlen = 20;
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data+15, indata, indatalen);
}
/* Manage security environment needs to be weaked for certain cards. */
if (mse_done)
err = 0;
@ -3355,12 +3490,28 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
return err;
}
if (hashalgo == MD_USER_TLS_MD5SHA1)
err = iso7816_compute_ds (app->slot, 0, data, 36, 0, outdata, outdatalen);
else if (no_data_padding)
err = iso7816_compute_ds (app->slot, 0, data+15, 20, 0,outdata,outdatalen);
dataptr = data;
if (no_data_padding)
{
dataptr += datalen - hashlen;
datalen = hashlen;
}
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
{
exmode = 1;
le_value = prkdf->keynbits / 8;
}
else
err = iso7816_compute_ds (app->slot, 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);
return err;
}

View File

@ -1354,7 +1354,7 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
err = ksba_cert_init_from_mem (cert, der, derlen);
xfree (der);
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);
return err;