diff --git a/common/util.h b/common/util.h index 4d763f078..dad326aa3 100644 --- a/common/util.h +++ b/common/util.h @@ -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" diff --git a/scd/app-common.h b/scd/app-common.h index 6bf432e65..89467dca7 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -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); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index bea285687..b0c47af9f 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -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); diff --git a/scd/app-help.c b/scd/app-help.c index 842a73d5a..4b8a02461 100644 --- a/scd/app-help.c +++ b/scd/app-help.c @@ -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.*/ } - gcry_sexp_release (s_pkey); - bin2hex (array, 20, hexkeygrip); + if (r_pkey) + *r_pkey = s_pkey; + else + gcry_sexp_release (s_pkey); + + 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 diff --git a/scd/app-p15.c b/scd/app-p15.c index 68b024fa3..52487d50f 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -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,35 +516,55 @@ parse_certid (app_t app, const char *certid, *r_objid = NULL; *r_objidlen = 0; - 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 (certid[0] != 'P' && strlen (certid) == 40) /* This is a keygrip. */ { - 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); + 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)); + 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_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) - memcpy (data, sha1_prefix, 15); + 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, 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; } diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 8094b2463..352b16c7d 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -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;