diff options
Diffstat (limited to 'scd')
-rw-r--r-- | scd/app-common.h | 4 | ||||
-rw-r--r-- | scd/app-p15.c | 155 | ||||
-rw-r--r-- | scd/app-piv.c | 56 | ||||
-rw-r--r-- | scd/app.c | 53 |
4 files changed, 244 insertions, 24 deletions
diff --git a/scd/app-common.h b/scd/app-common.h index 988cddf3f..f4035f766 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -56,8 +56,8 @@ typedef enum CARDTYPE_GENERIC = 0, CARDTYPE_GNUK, CARDTYPE_YUBIKEY, - CARDTYPE_ZEITCONTROL - + CARDTYPE_ZEITCONTROL, + CARDTYPE_SCE7 /* G+D SmartCafe Expert 7.0 */ } cardtype_t; /* List of supported card applications. The source code for each diff --git a/scd/app-p15.c b/scd/app-p15.c index 8edd737a6..2bb90beaa 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -91,7 +91,8 @@ typedef enum CARD_PRODUCT_DTRUST3, /* D-Trust GmbH (bundesdruckerei.de) */ CARD_PRODUCT_DTRUST4, CARD_PRODUCT_GENUA, /* GeNUA mbH */ - CARD_PRODUCT_NEXUS /* Technology Nexus */ + CARD_PRODUCT_NEXUS, /* Technology Nexus */ + CARD_PRODUCT_CVISION /* Cryptovision GmbH */ } card_product_t; @@ -143,6 +144,8 @@ static struct #define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \ || (a)->app_local->card_type == CARD_TYPE_CARDOS_53 \ || (a)->app_local->card_type == CARD_TYPE_CARDOS_54) +#define IS_STARCOS_3(a) ((a)->app_local->card_type == CARD_TYPE_STARCOS_32) + /* The default PKCS-15 home DF */ #define DEFAULT_HOME_DF 0x5015 @@ -532,8 +535,6 @@ struct app_local_s /*** Local prototypes. ***/ static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen); -static gpg_error_t select_df_by_path (app_t app, const unsigned short *path, - size_t pathlen); 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); @@ -571,6 +572,7 @@ cardproduct2str (card_product_t cardproduct) case CARD_PRODUCT_DTRUST4: return "D-Trust 4.1/4.4"; case CARD_PRODUCT_GENUA: return "GeNUA"; case CARD_PRODUCT_NEXUS: return "Nexus"; + case CARD_PRODUCT_CVISION: return "Cryptovison"; } return ""; } @@ -803,7 +805,7 @@ select_by_path (app_t app, const unsigned short *path, size_t pathlen, log_debug ("%s: path=", __func__); for (j=0; j < pathlen; j++) log_printf ("%s%04hX", j? "/":"", path[j]); - log_printf ("%s\n",expect_df?" (DF requested)":""); + log_printf ("%s", expect_df?" (DF requested)":""); log_printf ("%s\n",app->app_local->direct_path_selection?" (direct)":""); } @@ -867,11 +869,13 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) } +#if 0 /* Currently not used. */ static gpg_error_t select_df_by_path (app_t app, const unsigned short *path, size_t pathlen) { return select_by_path (app, path, pathlen, 1); } +#endif /* Parse a cert Id string (or a key Id string) and return the binary @@ -3611,6 +3615,7 @@ read_p15_info (app_t app) gpg_error_t err; prkdf_object_t prkdf; unsigned int flag; + const char *manu; err = read_ef_tokeninfo (app); if (err) @@ -3631,18 +3636,22 @@ read_p15_info (app_t app) release_lists (app); /* Set a product type from the manufacturer_id. */ - if (IS_CARDOS_5 (app) && app->app_local->manufacturer_id) + if (!(manu = app->app_local->manufacturer_id) || !*manu) + ; /* No manufacturer_id. */ + else if (app->app_local->card_product) + ; /* Already set. */ + else if (IS_CARDOS_5 (app)) { - const char *manu = app->app_local->manufacturer_id; - - if (app->app_local->card_product) - ; /* Already set. */ - else if (!ascii_strcasecmp (manu, "GeNUA mbH")) + if (!ascii_strcasecmp (manu, "GeNUA mbH")) app->app_local->card_product = CARD_PRODUCT_GENUA; else if (!ascii_strcasecmp (manu, "Technology Nexus")) app->app_local->card_product = CARD_PRODUCT_NEXUS; } - + else if (app->app_local->card_type == CARD_TYPE_STARCOS_32) + { + if (strstr (manu, "cryptovision")) + app->app_local->card_product = CARD_PRODUCT_CVISION; + } /* Read the ODF so that we know the location of all directory files. */ @@ -5053,11 +5062,18 @@ prepare_verify_pin (app_t app, const char *keyref, log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n", keyref, gpg_strerror (err)); } - else if (prkdf && app->app_local->card_type == CARD_TYPE_STARCOS_32) + else if (app->app_local->card_product == CARD_PRODUCT_CVISION) { - err = select_df_by_path (app, prkdf->path, prkdf->pathlen); + /* According to our protocol analysis we need to select the + * PKCS#15 AID here. The traces actually show that this is done + * two times but that looks more like a bug. Before that the + * master file needs to be selected. */ + err = iso7816_select_mf (app_get_slot (app)); + if (!err) + err = iso7816_select_application (app_get_slot (app), + pkcs15_aid, sizeof pkcs15_aid, 0); if (err) - log_error ("p15: error selecting file for key %s: %s\n", + log_error ("p15: error selecting PKCS#15 AID for key %s: %s\n", keyref, gpg_strerror (err)); } else if (prkdf) @@ -5497,6 +5513,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, unsigned char oidbuf[64]; size_t oidbuflen; size_t n; + int i; unsigned char *indata_buffer = NULL; /* Malloced helper. */ (void)ctrl; @@ -5594,7 +5611,6 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, { unsigned int framelen; unsigned char *frame; - int i; framelen = (prkdf->keynbits+7) / 8; if (!framelen) @@ -5669,6 +5685,23 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, memcpy (frame, indata, indatalen); framelen = indatalen; } + else if (hashalgo && IS_STARCOS_3 (app) + && app->app_local->card_product != CARD_PRODUCT_CVISION) + { + /* For Starcos we need the plain hash w/o the prefix. */ + /* Note: This has never been tested because the cvision + * sample cards seem not to work this way. */ + if (indatalen != oidbuflen + digestlen + || memcmp (indata, oidbuf, oidbuflen)) + { + log_error ("p15: non-matching input data for Starcos:" + " hash=%d len=%zu\n", hashalgo, indatalen); + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + framelen = indatalen - oidbuflen; + memcpy (frame, (const char*)indata + oidbuflen, framelen); + } else { n = 0; @@ -5758,7 +5791,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, else { /* The D-TRUST Card 4.x doesn't support setting a security - * environment, at least as specified in the specs. Insted a + * environment, at least as specified in the specs. Instead a * predefined security environment has to be loaded depending on the * cipher and message digest used. The spec states SE-ID 0x25 for * SHA256, 0x26 for SHA384 and 0x27 for SHA512, when using PKCS#1 @@ -5772,6 +5805,61 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, 0xf3, 0x25, NULL, 0); } } + else if (app->app_local->card_product == CARD_PRODUCT_CVISION) + { + /* I can't make the Starcos 3.2 work the correct way, so let's + * make them work in the same way the cryptovision product seems + * to do it: Use the plain RSA decryption instead. */ + unsigned char mse[9]; + + i = 0; + mse[i++] = 0x84; /* Key reference. */ + mse[i++] = 1; + mse[i++] = prkdf->key_reference; + mse[i++] = 0x89; /* Algorithm reference (BCD encoded). */ + mse[i++] = 2; + mse[i++] = 0x11; /* RSA no padding (1 1 3 0). */ + mse[i++] = 0x30; + log_assert (i <= DIM(mse)); + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, + mse, i); + } + else if (prkdf->key_reference_valid && IS_STARCOS_3 (app)) + { + unsigned char mse[9]; + + i = 0; + if (prkdf->is_ecc) + { + log_info ("Note: ECC is not yet implemented for Starcos 3 cards\n"); + err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + else + { + err = 0; + mse[i++] = 0x84; /* Key reference. */ + mse[i++] = 1; + mse[i++] = prkdf->key_reference; + mse[i++] = 0x89; /* Algorithm reference (BCD encoded). */ + mse[i++] = 3; + mse[i++] = 0x13; /* RSA PKCS#1 (standard) (1 3 2 3). */ + mse[i++] = 0x23; + switch (hashalgo) + { + case GCRY_MD_SHA1: mse[i++] = 0x10; break; + case GCRY_MD_RMD160: mse[i++] = 0x20; break; + case GCRY_MD_SHA256: mse[i++] = 0x30; break; + case GCRY_MD_SHA384: mse[i++] = 0x40; break; + case GCRY_MD_SHA512: mse[i++] = 0x50; break; + case GCRY_MD_SHA224: mse[i++] = 0x60; break; + default: err = gpg_error (GPG_ERR_DIGEST_ALGO); break; + } + log_assert (i <= DIM(mse)); + if (!err) + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, + mse, i); + } + } else if (prkdf->key_reference_valid) { unsigned char mse[3]; @@ -5801,9 +5889,14 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, le_value = 0; } - err = iso7816_compute_ds (app_get_slot (app), + if (app->app_local->card_product == CARD_PRODUCT_CVISION) + err = iso7816_decipher (app_get_slot (app), exmode, indata, indatalen, - le_value, outdata, outdatalen); + le_value, 0, outdata, outdatalen); + else + err = iso7816_compute_ds (app_get_slot (app), + exmode, indata, indatalen, + le_value, outdata, outdatalen); leave: xfree (indata_buffer); @@ -5862,6 +5955,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, prkdf_object_t prkdf; /* The private key object. */ aodf_object_t aodf; /* The associated authentication object. */ int exmode, le_value, padind; + int i; (void)ctrl; (void)r_info; @@ -5960,10 +6054,33 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, 0xF3, 0x31, NULL, 0); } } + else if (prkdf->key_reference_valid && IS_STARCOS_3 (app)) + { + unsigned char mse[9]; + + i = 0; + if (prkdf->is_ecc) + { + log_info ("Note: ECC is not yet implemented for Starcos 3 cards\n"); + err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + else + { + mse[i++] = 0x84; /* Key reference. */ + mse[i++] = 1; + mse[i++] = prkdf->key_reference; + mse[i++] = 0x89; /* Algorithm reference (BCD encoded). */ + mse[i++] = 2; + mse[i++] = 0x11; /* RSA no padding (1 1 3 0). */ + mse[i++] = 0x30; + log_assert (i <= DIM(mse)); + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, + mse, i); + } + } else if (prkdf->key_reference_valid) { 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. */ diff --git a/scd/app-piv.c b/scd/app-piv.c index c8ef7b43a..dc92bd2e2 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -1,5 +1,5 @@ /* app-piv.c - The OpenPGP card application. - * Copyright (C) 2019, 2020 g10 Code GmbH + * Copyright (C) 2019, 2020, 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -3642,6 +3642,7 @@ app_select_piv (app_t app) size_t aptlen; const unsigned char *s; size_t n; + void *relptr1 = NULL; /* Note that we select using the AID without the 2 octet version * number. This allows for better reporting of future specs. We @@ -3667,7 +3668,21 @@ app_select_piv (app_t app) s = find_tlv (apt, aptlen, 0x4F, &n); /* Some cards (new Yubikey) return only the PIX, while others - * (old Yubikey, PivApplet) return the RID+PIX. */ + * (old Yubikey, PivApplet) return the RID+PIX. + * Sample APTs: + * Yubikey 5.4.3: 6111 4f06 000010000100 7907 4f05 a000000308 + * SCE7.0-G-F-P : 610f 4f06 001000010000 7905 a000000308 + */ + if (app->card->cardtype == CARDTYPE_SCE7 + && s && apt && aptlen == 17 + && !memcmp (apt, ("\x61\x0f\x4f\x06\x00\x10\x00\x01" + "\x00\x00\x79\x05\xa0\x00\x00\x03\x08"), aptlen)) + { + if (opt.verbose) + log_info ("piv: assuming G&D SCE7.0-G-F-P\n"); + app->appversion = 0x0100; /* Let's assume this. */ + goto apt_checked; + } if (!s || !((n == 6 && !memcmp (s, piv_aid+5, 4)) || (n == 11 && !memcmp (s, piv_aid, 9)))) { @@ -3702,6 +3717,7 @@ app_select_piv (app_t app) goto leave; } + apt_checked: app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { @@ -3712,6 +3728,41 @@ app_select_piv (app_t app) if (app->card->cardtype == CARDTYPE_YUBIKEY) app->app_local->flags.yubikey = 1; + /* If we don't have a s/n construct it from the CHUID. */ + if (!APP_CARD(app)->serialno) + { + unsigned char *chuid; + size_t chuidlen; + + relptr1 = get_one_do (app, 0x5FC102, &chuid, &chuidlen, NULL); + if (!relptr1) + log_error ("piv: CHUID not found\n"); + else + { + s = find_tlv (chuid, chuidlen, 0x34, &n); + if (!s || n != 16) + { + log_error ("piv: Card UUID %s in CHUID\n", + s? "invalid":"missing"); + if (opt.debug && s) + log_printhex (s, n, "got"); + } + else + { + APP_CARD(app)->serialno = xtrymalloc (n); + if (!APP_CARD(app)->serialno) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (APP_CARD(app)->serialno, s, n); + APP_CARD(app)->serialnolen = n; + err = app_munge_serialno (APP_CARD(app)); + if (err) + goto leave; + } + } + } /* FIXME: Parse the optional and conditional DOs in the APT. */ @@ -3739,6 +3790,7 @@ app_select_piv (app_t app) leave: + xfree (relptr1); xfree (apt); if (err) do_deinit (app); @@ -112,6 +112,7 @@ strcardtype (cardtype_t t) case CARDTYPE_GNUK: return "gnuk"; case CARDTYPE_YUBIKEY: return "yubikey"; case CARDTYPE_ZEITCONTROL: return "zeitcontrol"; + case CARDTYPE_SCE7: return "smartcafe"; } return "?"; } @@ -549,6 +550,51 @@ card_reset (card_t card) return err; } + +/* Return the card type from (ATR,ATRLEN) or CARDTYPE_GENERIC in case + * of error or if the ATR was not found. If ATR is NULL, SLOT is used + * to retrieve the ATR from the reader. */ +static cardtype_t +atr_to_cardtype (int slot, const unsigned char *atr, size_t atrlen) +{ +#define X(a) ((unsigned char const *)(a)) + static struct + { + size_t atrlen; + unsigned char const *atr; + cardtype_t type; + } atrlist[] = { + { 19, X("\x3b\xf9\x96\x00\x00\x80\x31\xfe" + "\x45\x53\x43\x45\x37\x20\x0f\x00\x20\x46\x4e"), + CARDTYPE_SCE7 }, + { 0 } + }; +#undef X + unsigned char *atrbuf = NULL; + cardtype_t cardtype = 0; + int i; + + if (atr) + { + atrbuf = apdu_get_atr (slot, &atrlen); + if (!atrbuf) + return 0; + atr = atrbuf; + } + + for (i=0; atrlist[i].atrlen; i++) + if (atrlist[i].atrlen == atrlen + && !memcmp (atrlist[i].atr, atr, atrlen)) + { + cardtype = atrlist[i].type; + break; + } + xfree (atrbuf); + return cardtype; +} + + + static gpg_error_t app_new_register (int slot, ctrl_t ctrl, const char *name, int periodical_check_needed) @@ -666,13 +712,16 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, } xfree (buf); } + else + card->cardtype = atr_to_cardtype (slot, NULL, 0); } - else + else /* Got 3F00 */ { unsigned char *atr; size_t atrlen; /* This is heuristics to identify different implementations. */ + /* FIXME: The first two checks are pretty OpenPGP card specific. */ atr = apdu_get_atr (slot, &atrlen); if (atr) { @@ -680,6 +729,8 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, card->cardtype = CARDTYPE_GNUK; else if (atrlen == 21 && atr[7] == 0x75) card->cardtype = CARDTYPE_ZEITCONTROL; + else + card->cardtype = atr_to_cardtype (slot, atr, atrlen); xfree (atr); } } |