aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2021-03-29 12:48:11 +0000
committerWerner Koch <[email protected]>2021-03-29 12:49:08 +0000
commita494b29af9cc9c4c8c8323bae20e845d5a390448 (patch)
treee17f010a84c7b912ecb2aea8aa0380e437ffb857
parentgpg: Allow ECDH with a smartcard returning just the x-ccordinate. (diff)
downloadgnupg-a494b29af9cc9c4c8c8323bae20e845d5a390448.tar.gz
gnupg-a494b29af9cc9c4c8c8323bae20e845d5a390448.zip
scd:p15: Support ECDSA and ECDH for CardOS.
* scd/iso7816.c (iso7816_pso_csv): New. * scd/app-help.c (app_help_pubkey_from_cert): Uncompress a point if needed. * scd/app-p15.c (CARD_PRODUCT_RSCS): New. (struct prkdf_object_s): Add fields is_ecc, token_label, and tokenflags. (do_deinit): Free new fields. (cardproduct2str): New. (read_ef_prkdf): Set new is_ecc flag. (read_ef_tokeninfo): Store some data and move Tokeninfo diags to ... (read_p15_info): here. set the product info here after all data has been gathered. (send_keypairinfo): Chnage the way the gpgusage flags are used. (make_pin_prompt): If the token has a label and the current cert has no CN, show the label as holder info. (do_sign): Support ECDSA. Take care of the gpgusage flags. (do_decipher): Support ECDH. Take care of the gpgusage flags. -- This has been tested with Trusted Object Manager generated cards by Rohde & Schwarz Cybersecurity. Signed-off-by: Werner Koch <[email protected]>
-rw-r--r--scd/app-help.c17
-rw-r--r--scd/app-p15.c413
-rw-r--r--scd/iso7816.c47
-rw-r--r--scd/iso7816.h3
4 files changed, 378 insertions, 102 deletions
diff --git a/scd/app-help.c b/scd/app-help.c
index b599add71..7774b5465 100644
--- a/scd/app-help.c
+++ b/scd/app-help.c
@@ -76,6 +76,7 @@ app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip,
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))
{
gcry_sexp_release (s_pkey);
@@ -143,12 +144,14 @@ app_help_pubkey_from_cert (const void *cert, size_t certlen,
{
gpg_error_t err;
ksba_cert_t kc;
- unsigned char *pk;
- size_t pklen;
+ unsigned char *pk, *fixed_pk;
+ size_t pklen, fixed_pklen;
*r_pk = NULL;
*r_pklen = 0;
+ pk = NULL; /*(avoid cc warning)*/
+
err = ksba_cert_new (&kc);
if (err)
return err;
@@ -165,6 +168,16 @@ app_help_pubkey_from_cert (const void *cert, size_t certlen,
}
pklen = gcry_sexp_canon_len (pk, 0, NULL, &err);
+ err = uncompress_ecc_q_in_canon_sexp (pk, pklen, &fixed_pk, &fixed_pklen);
+ if (err)
+ goto leave;
+ if (fixed_pk)
+ {
+ ksba_free (pk); pk = NULL;
+ pk = fixed_pk;
+ pklen = fixed_pklen;
+ }
+
leave:
if (!err)
{
diff --git a/scd/app-p15.c b/scd/app-p15.c
index 597159860..642e4dab6 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -84,6 +84,7 @@ card_type_t;
typedef enum
{
CARD_PRODUCT_UNKNOWN,
+ CARD_PRODUCT_RSCS, /* Rohde&Schwarz Cybersecurity */
CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */
}
card_product_t;
@@ -259,6 +260,11 @@ struct prkdf_object_s
* verified. */
unsigned int pin_verified:1;
+ /* PKCS#15 info whether this is an EC key. Default is RSA. Note
+ * that there is also a KEYALGO field which is derived from the
+ * publick key via Libgcrypt. */
+ unsigned int is_ecc:1;
+
/* The key's usage flags. */
keyusage_flags_t usageflags;
@@ -283,7 +289,7 @@ struct prkdf_object_s
char keygrip[2*KEYGRIP_LEN+1];
/* The Gcrypt algo identifier for the key. It is valid if the
- * keygrip is also valid. */
+ * keygrip is also valid. See also is_ecc above. */
int keyalgo;
/* The length of the key in bits (e.g. for RSA the length of the
@@ -443,9 +449,16 @@ struct app_local_s
unsigned char *serialno;
size_t serialnolen;
- /* The manufacturerID from the TokenInfo EF. Malloced. */
+ /* The manufacturerID from the TokenInfo EF. Malloced or NULL. */
char *manufacturer_id;
+ /* The label from the TokenInfo EF. Malloced or NULL. */
+ char *token_label;
+
+ /* The tokenflags from the TokenInfo EF. Malloced or NULL. */
+ unsigned char *tokenflags;
+ unsigned int tokenflagslen;
+
/* Information on all certificates. */
cdf_object_t certificate_info;
/* Information on all trusted certificates. */
@@ -491,6 +504,18 @@ cardtype2str (card_type_t cardtype)
return "";
}
+static const char *
+cardproduct2str (card_product_t cardproduct)
+{
+ switch (cardproduct)
+ {
+ case CARD_PRODUCT_UNKNOWN: return "";
+ case CARD_PRODUCT_RSCS: return "RSCS";
+ case CARD_PRODUCT_DTRUST: return "D-Trust";
+ }
+ return "";
+}
+
/* Release the CDF object A */
static void
release_cdflist (cdf_object_t a)
@@ -570,6 +595,8 @@ do_deinit (app_t app)
release_prkdflist (app->app_local->private_key_info);
release_aodflist (app->app_local->auth_object_info);
xfree (app->app_local->manufacturer_id);
+ xfree (app->app_local->token_label);
+ xfree (app->app_local->tokenflags);
xfree (app->app_local->serialno);
xfree (app->app_local);
app->app_local = NULL;
@@ -1570,14 +1597,15 @@ parse_common_key_attr (unsigned char const **buffer, size_t *size,
* }
*
* Sample part for EC objects:
- * [1] { -- keyAttributes
- * SEQUENCE {
- * SEQUENCE {
- * OCTET STRING 50 72 4B 03
+ * [1] { -- keyAttributes
+ * [1] { -- privateECkeyAttributes
+ * SEQUENCE { -- objectValue
+ * SEQUENCE { --path
+ * OCTET STRING 50 72 4B 03
+ * }
+ * INTEGER 33 -- Not in PKCS#15v1.1, need to buy 7816-15?
* }
- * INTEGER 33 -- Not in PKCS#15v1.1, need to buy 6718-15?
* }
- * }
*/
static gpg_error_t
read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
@@ -1621,6 +1649,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
keyaccess_flags_t accessflags;
unsigned long key_reference = 0;
int key_reference_valid = 0;
+ int is_ecc = 0;
where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
@@ -1635,7 +1664,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
{
switch (tag)
{
- case 0: break; /* PrivateECKeyAttributes */
+ case 0: is_ecc = 1; break; /* PrivateECKeyAttributes */
case 1: errstr = "DH key objects are not supported"; break;
case 2: errstr = "DSA key objects are not supported"; break;
case 3: errstr = "KEA key objects are not supported"; break;
@@ -1763,6 +1792,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
err = gpg_error_from_syserror ();
goto leave;
}
+ prkdf->is_ecc = is_ecc;
+
prkdf->objidlen = objidlen;
prkdf->objid = objid;
objid = NULL;
@@ -3276,8 +3307,6 @@ read_ef_tokeninfo (app_t app)
goto leave;
}
- if (opt.verbose)
- log_info ("p15: TokenInfo:\n");
/* serialNumber. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
@@ -3295,12 +3324,6 @@ read_ef_tokeninfo (app_t app)
}
memcpy (app->app_local->serialno, p, objlen);
app->app_local->serialnolen = objlen;
- if (opt.verbose)
- {
- /* (We use a separate log_info to avoid the "DBG:" prefix.) */
- log_info ("p15: serialNumber .: ");
- log_printhex (p, objlen, "");
- }
p += objlen;
n -= objlen;
@@ -3313,8 +3336,6 @@ read_ef_tokeninfo (app_t app)
goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING)
{
- if (opt.verbose)
- log_info ("p15: manufacturerID: %.*s\n", (int)objlen, p);
app->app_local->manufacturer_id = percent_data_escape (0, NULL,
p, objlen);
p += objlen;
@@ -3329,11 +3350,7 @@ read_ef_tokeninfo (app_t app)
}
if (class == CLASS_CONTEXT && tag == 0)
{
- if (opt.verbose)
- log_info ("p15: label ........: %.*s\n", (int)objlen, p);
- if (objlen > 15 && !memcmp (p, "D-TRUST Card V3", 15)
- && app->app_local->card_type == CARD_TYPE_CARDOS_50)
- app->app_local->card_product = CARD_PRODUCT_DTRUST;
+ app->app_local->token_label = percent_data_escape (0, NULL, p, objlen);
p += objlen;
n -= objlen;
@@ -3348,38 +3365,18 @@ read_ef_tokeninfo (app_t app)
/* The next is the mandatory tokenflags object. */
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
- if (opt.verbose)
+ app->app_local->tokenflagslen = objlen;
+ app->app_local->tokenflags = xtrymalloc (objlen);
+ if (!app->app_local->tokenflags)
{
- log_info ("p15: tokenflags ...:");
- print_tokeninfo_tokenflags (p, objlen);
- log_printf ("\n");
+ err = gpg_error_from_syserror ();
+ goto leave;
}
+ memcpy (app->app_local->tokenflags, p, objlen);
p += objlen;
n -= objlen;
}
- if (opt.verbose)
- {
- unsigned char *atr;
- size_t atrlen;
- const char *cardstr;
-
- log_info ("p15: atr ..........: ");
- atr = apdu_get_atr (app_get_slot (app), &atrlen);
- if (!atr)
- log_printf ("[error]\n");
- else
- {
- log_printhex (atr, atrlen, "");
- xfree (atr);
- }
- cardstr = cardtype2str (app->app_local->card_type);
- log_info ("p15: cardtype .....: %d.%d%s%s%s\n",
- app->app_local->card_type,
- app->app_local->card_product,
- *cardstr? " (":"", cardstr, *cardstr? ")":"");
- }
-
leave:
xfree (buffer);
@@ -3395,6 +3392,7 @@ read_p15_info (app_t app)
{
gpg_error_t err;
prkdf_object_t prkdf;
+ unsigned int flag;
if (!read_ef_tokeninfo (app))
{
@@ -3560,10 +3558,45 @@ read_p15_info (app_t app)
xfree (extusage);
}
+ /* See whether we can figure out something about the card. */
+ if (!app->app_local->card_product
+ && app->app_local->manufacturer_id
+ && !strcmp (app->app_local->manufacturer_id, "www.atos.net/cardos")
+ && IS_CARDOS_5 (app))
+ {
+ /* This is a modern CARDOS card. */
+ flag = 0;
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ if (prkdf->label && !strcmp (prkdf->label, "IdentityKey")
+ && prkdf->key_reference_valid && prkdf->key_reference == 1
+ && !prkdf->authid)
+ flag |= 1;
+ else if (prkdf->label && !strcmp (prkdf->label, "TransportKey")
+ && prkdf->key_reference_valid && prkdf->key_reference==2
+ && prkdf->authid)
+ flag |= 2;
+ }
+ if (flag == 3)
+ app->app_local->card_product = CARD_PRODUCT_RSCS;
+
+ }
+ if (!app->app_local->card_product
+ && app->app_local->token_label
+ && !strcmp (app->app_local->token_label, "D-TRUST Card V3")
+ && app->app_local->card_type == CARD_TYPE_CARDOS_50)
+ {
+ app->app_local->card_product = CARD_PRODUCT_DTRUST;
+ }
+
+
/* Now print the info about the PrKDF. */
if (opt.verbose)
{
int i;
+ unsigned char *atr;
+ size_t atrlen;
+ const char *cardstr;
for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
{
@@ -3585,6 +3618,7 @@ read_p15_info (app_t app)
}
if (prkdf->key_reference_valid)
log_printf (" keyref=0x%02lX", prkdf->key_reference);
+ log_printf (" type=%s", prkdf->is_ecc? "ecc":"rsa");
if (prkdf->accessflags.any)
dump_keyaccess_flags (prkdf->accessflags);
dump_keyusage_flags (prkdf->usageflags);
@@ -3602,6 +3636,55 @@ read_p15_info (app_t app)
log_printf ("\n");
}
+
+ log_info ("p15: TokenInfo:\n");
+ if (app->app_local->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (app->app_local->serialno, app->app_local->serialnolen,
+ "");
+ }
+ else if (APP_CARD(app)->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (APP_CARD(app)->serialno, APP_CARD(app)->serialnolen,
+ "");
+ }
+
+ if (app->app_local->manufacturer_id)
+ log_info ("p15: manufacturerID: %s\n",
+ app->app_local->manufacturer_id);
+ if (app->app_local->card_product)
+ {
+ cardstr = cardproduct2str (app->app_local->card_product);
+ log_info ("p15: product ......: %d%s%s%s\n",
+ app->app_local->card_product,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
+ }
+ if (app->app_local->token_label)
+ log_info ("p15: label ........: %s\n", app->app_local->token_label);
+ if (app->app_local->tokenflags)
+ {
+ log_info ("p15: tokenflags ...:");
+ print_tokeninfo_tokenflags (app->app_local->tokenflags,
+ app->app_local->tokenflagslen);
+ log_printf ("\n");
+ }
+
+ log_info ("p15: atr ..........: ");
+ atr = apdu_get_atr (app_get_slot (app), &atrlen);
+ if (!atr)
+ log_printf ("[error]\n");
+ else
+ {
+ log_printhex (atr, atrlen, "");
+ xfree (atr);
+ }
+
+ cardstr = cardtype2str (app->app_local->card_type);
+ log_info ("p15: cardtype .....: %d%s%s%s\n",
+ app->app_local->card_type,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
}
return err;
@@ -3702,12 +3785,14 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
xfree (der);
if (!err)
err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey, NULL);
- if (!err)
+ if (!err && !prkdf->gpgusage.any)
{
/* Try to get the CN and the SerialNumber from the certificate;
* we use a very simple approach here which should work in many
* cases. Eventually we should add a rfc-2253 parser into
* libksba to make it easier to parse such a string.
+ * We don't do this if this is marked as gpg key and thus
+ * has only a dummy certificate.
*
* First example string:
* "CN=Otto Schily,O=Miniluv,C=DE"
@@ -3834,24 +3919,38 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
char usage[5];
size_t usagelen = 0;
- if ((prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover
- || prkdf->usageflags.non_repudiation)
- && (!prkdf->extusage.valid
- || prkdf->extusage.sign))
- usage[usagelen++] = 's';
- if ((prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover)
- && (!prkdf->extusage.valid || prkdf->extusage.sign))
- usage[usagelen++] = 'c';
- if ((prkdf->usageflags.decrypt
- || prkdf->usageflags.unwrap)
- && (!prkdf->extusage.valid || prkdf->extusage.encr))
- usage[usagelen++] = 'e';
- if ((prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover)
- && (!prkdf->extusage.valid || prkdf->extusage.auth))
- usage[usagelen++] = 'a';
+ if (prkdf->gpgusage.any)
+ {
+ if (prkdf->gpgusage.sign)
+ usage[usagelen++] = 's';
+ if (prkdf->gpgusage.cert)
+ usage[usagelen++] = 'c';
+ if (prkdf->gpgusage.encr)
+ usage[usagelen++] = 'e';
+ if (prkdf->gpgusage.auth)
+ usage[usagelen++] = 'a';
+ }
+ else
+ {
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation)
+ && (!prkdf->extusage.valid
+ || prkdf->extusage.sign))
+ usage[usagelen++] = 's';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.sign))
+ usage[usagelen++] = 'c';
+ if ((prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap)
+ && (!prkdf->extusage.valid || prkdf->extusage.encr))
+ usage[usagelen++] = 'e';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.auth))
+ usage[usagelen++] = 'a';
+ }
log_assert (strlen (prkdf->keygrip) == 40);
send_status_info (ctrl, "KEYPAIRINFO",
@@ -4506,9 +4605,17 @@ make_pin_prompt (app_t app, int remaining, const char *firstline,
prkdf_object_t prkdf)
{
char *serial, *tmpbuf, *result;
+ const char *holder;
serial = get_dispserialno (app, prkdf);
+ if (prkdf && prkdf->common_name)
+ holder = prkdf->common_name;
+ else if (app->app_local->token_label)
+ holder = app->app_local->token_label;
+ else
+ holder = "";
+
/* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */
@@ -4518,7 +4625,7 @@ make_pin_prompt (app_t app, int remaining, const char *firstline,
"%s"),
"\x1e",
serial,
- prkdf && prkdf->common_name? prkdf->common_name: "",
+ holder,
"");
xfree (serial);
if (!result)
@@ -4804,23 +4911,23 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
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;
+ const unsigned char *dataptr;
int exmode, le_value;
(void)ctrl;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
- 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);
if (err)
return err;
- if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
- ||prkdf->usageflags.non_repudiation))
+ if (!(prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation
+ || prkdf->gpgusage.cert
+ || prkdf->gpgusage.sign
+ || prkdf->gpgusage.auth ))
{
log_error ("p15: key %s may not be used for signing\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
@@ -4851,6 +4958,59 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return err;
}
+ /* We handle ECC separately from RSA so that we do not need to touch
+ * working code. In particular we prepare the input data before the
+ * verify and a possible MSE. */
+ if (prkdf->is_ecc)
+ {
+ unsigned int digestlen;
+ unsigned char oidbuf[64];
+ size_t oidbuflen;
+
+ digestlen = gcry_md_get_algo_dlen (hashalgo);
+ if (digestlen != 32 && digestlen != 48 && digestlen != 64)
+ {
+ log_error ("p15: ECC signing not possible: dlen=%u\n", digestlen);
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
+
+ if (indatalen == digestlen)
+ ; /* Already prepared. */
+ else if (indatalen > digestlen)
+ {
+ /* Assume a PKCS#1 prefix and remove it. */
+ oidbuflen = sizeof oidbuf;
+ err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen);
+ if (err)
+ {
+ log_debug ("p15: no OID for hash algo %d\n", hashalgo);
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ if (indatalen != oidbuflen + digestlen
+ || memcmp (indata, oidbuf, oidbuflen))
+ {
+ log_error ("p15: input data too long for ECC: len=%zu\n",
+ indatalen);
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ indata = (const char*)indata + oidbuflen;
+ indatalen -= oidbuflen;
+ }
+ else
+ {
+ log_error ("p15: input data too short for ECC: len=%zu\n",
+ indatalen);
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ }
+ else /* Check for RSA. FIXME: We should rework the RSA part. */
+ {
+ if (indatalen != 20 && indatalen != 16
+ && indatalen != 35 && indatalen != 36
+ && indatalen != (32+19))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
/* Prepare PIN verification. This is split so that we can do
* MSE operation for some task after having selected the key file but
* before sending the verify APDU. */
@@ -4894,9 +5054,13 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (err)
return err;
-
- /* Prepare the DER object from INDATA. */
- if (indatalen == 36)
+ /* Prepare the input data from INDATA. */
+ if (prkdf->is_ecc)
+ {
+ /* Already checked. */
+ datalen = indatalen;
+ }
+ else if (indatalen == 36)
{
/* No ASN.1 container used. */
if (hashalgo != MD_USER_TLS_MD5SHA1)
@@ -4993,12 +5157,19 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return err;
}
-
- dataptr = data;
- if (no_data_padding)
+ if (prkdf->is_ecc)
{
- dataptr += datalen - hashlen;
- datalen = hashlen;
+ dataptr = indata;
+ datalen = indatalen;
+ }
+ else
+ {
+ dataptr = data;
+ if (no_data_padding)
+ {
+ dataptr += datalen - hashlen;
+ datalen = hashlen;
+ }
}
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
@@ -5043,7 +5214,7 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
- if (!prkdf->usageflags.sign)
+ if (!(prkdf->usageflags.sign || prkdf->gpgusage.auth))
{
log_error ("p15: key %s may not be used for authentication\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
@@ -5083,7 +5254,9 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
- if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
+ if (!(prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap
+ || prkdf->gpgusage.encr ))
{
log_error ("p15: key %s may not be used for decryption\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
@@ -5113,7 +5286,6 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
return err;
}
-
/* Verify the PIN. */
err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
if (!err)
@@ -5121,6 +5293,18 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+
+ err = iso7816_manage_security_env (app_get_slot (app), 0xF3, 0x01,
+ NULL, 0);
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+ }
+
/* The next is guess work for CardOS. */
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
@@ -5142,19 +5326,31 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
}
else if (prkdf->key_reference_valid)
{
- unsigned char mse[6];
+ unsigned char mse[9];
+ int i;
/* Note: This works with CardOS but the D-Trust card has the
* problem that the next created signature would be broken. */
- mse[0] = 0x80; /* Algorithm reference. */
- mse[1] = 1;
- mse[2] = 0x0a; /* RSA, no padding. */
- mse[3] = 0x84;
- mse[4] = 1;
- mse[5] = prkdf->key_reference;
+ i = 0;
+ if (!prkdf->is_ecc)
+ {
+ mse[i++] = 0x80; /* Algorithm reference. */
+ mse[i++] = 1;
+ mse[i++] = 0x0a; /* RSA, no padding. */
+ }
+ mse[i++] = 0x84; /* Key reference. */
+ mse[i++] = 1;
+ mse[i++] = prkdf->key_reference;
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ mse[i++] = 0x95; /* ???. */
+ mse[i++] = 1;
+ mse[i++] = 0x40;
+ }
+ log_assert (i <= DIM(mse));
err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
- mse, sizeof mse);
+ mse, i);
}
/* Check for MSE error. */
if (err)
@@ -5174,10 +5370,27 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
padind = 0x81;
- err = iso7816_decipher (app_get_slot (app), exmode,
- indata, indatalen,
- le_value, padind,
- outdata, outdatalen);
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ if ((indatalen & 1) && *(const char *)indata == 0x04)
+ {
+ /* Strip indicator byte. */
+ indatalen--;
+ indata = (const char *)indata + 1;
+ }
+ err = iso7816_pso_csv (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value,
+ outdata, outdatalen);
+ }
+ else
+ {
+ err = iso7816_decipher (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value, padind,
+ outdata, outdatalen);
+ }
+
return err;
}
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 8896486b8..e8ee58db1 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -758,6 +758,53 @@ iso7816_decipher (int slot, int extended_mode,
}
+/* Perform the security operation COMPUTE SHARED SECRET. On success 0
+ is returned and the shared secret is available in a newly allocated
+ buffer stored at RESULT with its length stored at RESULTLEN. For
+ LE see do_generate_keypair. */
+gpg_error_t
+iso7816_pso_csv (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buf;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ /* Data needds to be TLV format. */
+ buf = xtrymalloc (datalen + 2);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ buf[0] = 0x9c;
+ buf[1] = datalen;
+ memcpy (buf+2, data, datalen);
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_PSO, 0x80, 0xa6,
+ datalen+2, (const char *)buf, le,
+ result, resultlen);
+ xfree (buf);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
/* For LE see do_generate_keypair. */
gpg_error_t
iso7816_internal_authenticate (int slot, int extended_mode,
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 3464798c0..67b9f47ab 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -118,6 +118,9 @@ gpg_error_t iso7816_decipher (int slot, int extended_mode,
const unsigned char *data, size_t datalen,
int le, int padind,
unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_pso_csv (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode,
const unsigned char *data, size_t datalen,
int le,