1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-03-29 14:48:11 +02:00
parent f129b0e977
commit a494b29af9
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 378 additions and 102 deletions

View File

@ -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); 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. */
if (!gcry_pk_get_keygrip (s_pkey, array)) if (!gcry_pk_get_keygrip (s_pkey, array))
{ {
gcry_sexp_release (s_pkey); gcry_sexp_release (s_pkey);
@ -143,12 +144,14 @@ app_help_pubkey_from_cert (const void *cert, size_t certlen,
{ {
gpg_error_t err; gpg_error_t err;
ksba_cert_t kc; ksba_cert_t kc;
unsigned char *pk; unsigned char *pk, *fixed_pk;
size_t pklen; size_t pklen, fixed_pklen;
*r_pk = NULL; *r_pk = NULL;
*r_pklen = 0; *r_pklen = 0;
pk = NULL; /*(avoid cc warning)*/
err = ksba_cert_new (&kc); err = ksba_cert_new (&kc);
if (err) if (err)
return 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); 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: leave:
if (!err) if (!err)
{ {

View File

@ -84,6 +84,7 @@ card_type_t;
typedef enum typedef enum
{ {
CARD_PRODUCT_UNKNOWN, CARD_PRODUCT_UNKNOWN,
CARD_PRODUCT_RSCS, /* Rohde&Schwarz Cybersecurity */
CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */ CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */
} }
card_product_t; card_product_t;
@ -259,6 +260,11 @@ struct prkdf_object_s
* verified. */ * verified. */
unsigned int pin_verified:1; 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. */ /* The key's usage flags. */
keyusage_flags_t usageflags; keyusage_flags_t usageflags;
@ -283,7 +289,7 @@ struct prkdf_object_s
char keygrip[2*KEYGRIP_LEN+1]; char keygrip[2*KEYGRIP_LEN+1];
/* The Gcrypt algo identifier for the key. It is valid if the /* 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; int keyalgo;
/* The length of the key in bits (e.g. for RSA the length of the /* 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; unsigned char *serialno;
size_t serialnolen; size_t serialnolen;
/* The manufacturerID from the TokenInfo EF. Malloced. */ /* The manufacturerID from the TokenInfo EF. Malloced or NULL. */
char *manufacturer_id; 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. */ /* Information on all certificates. */
cdf_object_t certificate_info; cdf_object_t certificate_info;
/* Information on all trusted certificates. */ /* Information on all trusted certificates. */
@ -491,6 +504,18 @@ cardtype2str (card_type_t cardtype)
return ""; 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 */ /* Release the CDF object A */
static void static void
release_cdflist (cdf_object_t a) release_cdflist (cdf_object_t a)
@ -570,6 +595,8 @@ do_deinit (app_t app)
release_prkdflist (app->app_local->private_key_info); release_prkdflist (app->app_local->private_key_info);
release_aodflist (app->app_local->auth_object_info); release_aodflist (app->app_local->auth_object_info);
xfree (app->app_local->manufacturer_id); 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->serialno);
xfree (app->app_local); xfree (app->app_local);
app->app_local = NULL; app->app_local = NULL;
@ -1570,14 +1597,15 @@ parse_common_key_attr (unsigned char const **buffer, size_t *size,
* } * }
* *
* Sample part for EC objects: * Sample part for EC objects:
* [1] { -- keyAttributes * [1] { -- keyAttributes
* SEQUENCE { * [1] { -- privateECkeyAttributes
* SEQUENCE { * SEQUENCE { -- objectValue
* OCTET STRING 50 72 4B 03 * 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 static gpg_error_t
read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) 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; keyaccess_flags_t accessflags;
unsigned long key_reference = 0; unsigned long key_reference = 0;
int key_reference_valid = 0; int key_reference_valid = 0;
int is_ecc = 0;
where = __LINE__; where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed, 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) switch (tag)
{ {
case 0: break; /* PrivateECKeyAttributes */ case 0: is_ecc = 1; break; /* PrivateECKeyAttributes */
case 1: errstr = "DH key objects are not supported"; break; case 1: errstr = "DH key objects are not supported"; break;
case 2: errstr = "DSA 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; 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 (); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
prkdf->is_ecc = is_ecc;
prkdf->objidlen = objidlen; prkdf->objidlen = objidlen;
prkdf->objid = objid; prkdf->objid = objid;
objid = NULL; objid = NULL;
@ -3276,8 +3307,6 @@ read_ef_tokeninfo (app_t app)
goto leave; goto leave;
} }
if (opt.verbose)
log_info ("p15: TokenInfo:\n");
/* serialNumber. */ /* serialNumber. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed, err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen); &ndef, &objlen, &hdrlen);
@ -3295,12 +3324,6 @@ read_ef_tokeninfo (app_t app)
} }
memcpy (app->app_local->serialno, p, objlen); memcpy (app->app_local->serialno, p, objlen);
app->app_local->serialnolen = 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; p += objlen;
n -= objlen; n -= objlen;
@ -3313,8 +3336,6 @@ read_ef_tokeninfo (app_t app)
goto leave; goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING) 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, app->app_local->manufacturer_id = percent_data_escape (0, NULL,
p, objlen); p, objlen);
p += objlen; p += objlen;
@ -3329,11 +3350,7 @@ read_ef_tokeninfo (app_t app)
} }
if (class == CLASS_CONTEXT && tag == 0) if (class == CLASS_CONTEXT && tag == 0)
{ {
if (opt.verbose) app->app_local->token_label = percent_data_escape (0, NULL, p, objlen);
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;
p += objlen; p += objlen;
n -= objlen; n -= objlen;
@ -3348,38 +3365,18 @@ read_ef_tokeninfo (app_t app)
/* The next is the mandatory tokenflags object. */ /* The next is the mandatory tokenflags object. */
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING) 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 ...:"); err = gpg_error_from_syserror ();
print_tokeninfo_tokenflags (p, objlen); goto leave;
log_printf ("\n");
} }
memcpy (app->app_local->tokenflags, p, objlen);
p += objlen; p += objlen;
n -= 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: leave:
xfree (buffer); xfree (buffer);
@ -3395,6 +3392,7 @@ read_p15_info (app_t app)
{ {
gpg_error_t err; gpg_error_t err;
prkdf_object_t prkdf; prkdf_object_t prkdf;
unsigned int flag;
if (!read_ef_tokeninfo (app)) if (!read_ef_tokeninfo (app))
{ {
@ -3560,10 +3558,45 @@ read_p15_info (app_t app)
xfree (extusage); 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. */ /* Now print the info about the PrKDF. */
if (opt.verbose) if (opt.verbose)
{ {
int i; int i;
unsigned char *atr;
size_t atrlen;
const char *cardstr;
for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) 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) if (prkdf->key_reference_valid)
log_printf (" keyref=0x%02lX", prkdf->key_reference); log_printf (" keyref=0x%02lX", prkdf->key_reference);
log_printf (" type=%s", prkdf->is_ecc? "ecc":"rsa");
if (prkdf->accessflags.any) if (prkdf->accessflags.any)
dump_keyaccess_flags (prkdf->accessflags); dump_keyaccess_flags (prkdf->accessflags);
dump_keyusage_flags (prkdf->usageflags); dump_keyusage_flags (prkdf->usageflags);
@ -3602,6 +3636,55 @@ read_p15_info (app_t app)
log_printf ("\n"); 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; return err;
@ -3702,12 +3785,14 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
xfree (der); xfree (der);
if (!err) if (!err)
err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey, NULL); 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; /* Try to get the CN and the SerialNumber from the certificate;
* we use a very simple approach here which should work in many * we use a very simple approach here which should work in many
* cases. Eventually we should add a rfc-2253 parser into * cases. Eventually we should add a rfc-2253 parser into
* libksba to make it easier to parse such a string. * 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: * First example string:
* "CN=Otto Schily,O=Miniluv,C=DE" * "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]; char usage[5];
size_t usagelen = 0; size_t usagelen = 0;
if ((prkdf->usageflags.sign if (prkdf->gpgusage.any)
|| prkdf->usageflags.sign_recover {
|| prkdf->usageflags.non_repudiation) if (prkdf->gpgusage.sign)
&& (!prkdf->extusage.valid usage[usagelen++] = 's';
|| prkdf->extusage.sign)) if (prkdf->gpgusage.cert)
usage[usagelen++] = 's'; usage[usagelen++] = 'c';
if ((prkdf->usageflags.sign if (prkdf->gpgusage.encr)
|| prkdf->usageflags.sign_recover) usage[usagelen++] = 'e';
&& (!prkdf->extusage.valid || prkdf->extusage.sign)) if (prkdf->gpgusage.auth)
usage[usagelen++] = 'c'; usage[usagelen++] = 'a';
if ((prkdf->usageflags.decrypt }
|| prkdf->usageflags.unwrap) else
&& (!prkdf->extusage.valid || prkdf->extusage.encr)) {
usage[usagelen++] = 'e'; if ((prkdf->usageflags.sign
if ((prkdf->usageflags.sign || prkdf->usageflags.sign_recover
|| prkdf->usageflags.sign_recover) || prkdf->usageflags.non_repudiation)
&& (!prkdf->extusage.valid || prkdf->extusage.auth)) && (!prkdf->extusage.valid
usage[usagelen++] = 'a'; || 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); log_assert (strlen (prkdf->keygrip) == 40);
send_status_info (ctrl, "KEYPAIRINFO", send_status_info (ctrl, "KEYPAIRINFO",
@ -4506,9 +4605,17 @@ make_pin_prompt (app_t app, int remaining, const char *firstline,
prkdf_object_t prkdf) prkdf_object_t prkdf)
{ {
char *serial, *tmpbuf, *result; char *serial, *tmpbuf, *result;
const char *holder;
serial = get_dispserialno (app, prkdf); 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 /* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep * used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */ * 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"), "%s"),
"\x1e", "\x1e",
serial, serial,
prkdf && prkdf->common_name? prkdf->common_name: "", holder,
""); "");
xfree (serial); xfree (serial);
if (!result) 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. */ int mse_done = 0; /* Set to true if the MSE has been done. */
unsigned int hashlen; /* Length of the hash. */ unsigned int hashlen; /* Length of the hash. */
unsigned int datalen; /* Length of the data to sign (prefix+hash). */ unsigned int datalen; /* Length of the data to sign (prefix+hash). */
unsigned char *dataptr; const unsigned char *dataptr;
int exmode, le_value; int exmode, le_value;
(void)ctrl; (void)ctrl;
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
&& indatalen != (32+19))
return gpg_error (GPG_ERR_INV_VALUE);
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err) if (err)
return err; return err;
if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover if (!(prkdf->usageflags.sign
||prkdf->usageflags.non_repudiation)) || 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); log_error ("p15: key %s may not be used for signing\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE); 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; 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 /* Prepare PIN verification. This is split so that we can do
* MSE operation for some task after having selected the key file but * MSE operation for some task after having selected the key file but
* before sending the verify APDU. */ * before sending the verify APDU. */
@ -4894,9 +5054,13 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (err) if (err)
return err; return err;
/* Prepare the input data from INDATA. */
/* Prepare the DER object from INDATA. */ if (prkdf->is_ecc)
if (indatalen == 36) {
/* Already checked. */
datalen = indatalen;
}
else if (indatalen == 36)
{ {
/* No ASN.1 container used. */ /* No ASN.1 container used. */
if (hashalgo != MD_USER_TLS_MD5SHA1) 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; return err;
} }
if (prkdf->is_ecc)
dataptr = data;
if (no_data_padding)
{ {
dataptr += datalen - hashlen; dataptr = indata;
datalen = hashlen; datalen = indatalen;
}
else
{
dataptr = data;
if (no_data_padding)
{
dataptr += datalen - hashlen;
datalen = hashlen;
}
} }
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048) 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); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err) if (err)
return 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); log_error ("p15: key %s may not be used for authentication\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE); 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); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err) if (err)
return 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); log_error ("p15: key %s may not be used for decryption\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE); 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; return err;
} }
/* Verify the PIN. */ /* Verify the PIN. */
err = prepare_verify_pin (app, keyidstr, prkdf, aodf); err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
if (!err) if (!err)
@ -5121,6 +5293,18 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (err) if (err)
return 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. */ /* The next is guess work for CardOS. */
if (app->app_local->card_product == CARD_PRODUCT_DTRUST) 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) 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 /* Note: This works with CardOS but the D-Trust card has the
* problem that the next created signature would be broken. */ * problem that the next created signature would be broken. */
mse[0] = 0x80; /* Algorithm reference. */ i = 0;
mse[1] = 1; if (!prkdf->is_ecc)
mse[2] = 0x0a; /* RSA, no padding. */ {
mse[3] = 0x84; mse[i++] = 0x80; /* Algorithm reference. */
mse[4] = 1; mse[i++] = 1;
mse[5] = prkdf->key_reference; 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, err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
mse, sizeof mse); mse, i);
} }
/* Check for MSE error. */ /* Check for MSE error. */
if (err) 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) if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
padind = 0x81; padind = 0x81;
err = iso7816_decipher (app_get_slot (app), exmode, if (prkdf->is_ecc && IS_CARDOS_5(app))
indata, indatalen, {
le_value, padind, if ((indatalen & 1) && *(const char *)indata == 0x04)
outdata, outdatalen); {
/* 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; return err;
} }

View File

@ -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. */ /* For LE see do_generate_keypair. */
gpg_error_t gpg_error_t
iso7816_internal_authenticate (int slot, int extended_mode, iso7816_internal_authenticate (int slot, int extended_mode,

View File

@ -118,6 +118,9 @@ gpg_error_t iso7816_decipher (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le, int padind, int le, int padind,
unsigned char **result, size_t *resultlen); 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, gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le, int le,