diff --git a/common/percent.c b/common/percent.c index 569c5fd99..224de789b 100644 --- a/common/percent.c +++ b/common/percent.c @@ -87,6 +87,89 @@ percent_plus_escape (const char *string) } +/* Create a newly malloced string from (DATA,DATALEN) with embedded + * nuls quoted as %00. The standard percent unescaping can be used to + * reverse this encoding. With PLUS_ESCAPE set plus-escaping (spaces + * are replaced by a '+') and escaping of characters with values less + * than 0x20 is used. If PREFIX is not NULL it will be prepended to + * the output in standard escape format; that is PLUS_ESCAPING is + * ignored for PREFIX. */ +char * +percent_data_escape (int plus_escape, const char *prefix, + const void *data, size_t datalen) +{ + char *buffer, *p; + const unsigned char *s; + size_t n; + size_t length = 1; + + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + length += 3; + else + length++; + } + } + + for (s=data, n=datalen; n; s++, n--) + { + if (!*s || *s == '%' || (plus_escape && (*s < ' ' || *s == '+'))) + length += 3; + else + length++; + } + + buffer = p = xtrymalloc (length); + if (!buffer) + return NULL; + + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + } + + for (s=data, n=datalen; n; s++, n--) + { + if (!*s) + { + memcpy (p, "%00", 3); + p += 3; + } + else if (*s == '%') + { + memcpy (p, "%25", 3); + p += 3; + } + else if (plus_escape && *s == ' ') + { + *p++ = '+'; + } + else if (plus_escape && (*s < ' ' || *s == '+')) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + *p = 0; + + return buffer; +} + + /* Do the percent and plus/space unescaping from STRING to BUFFER and return the length of the valid buffer. Plus unescaping is only done if WITHPLUS is true. An escaped Nul character will be diff --git a/common/util.h b/common/util.h index dad326aa3..891431b5c 100644 --- a/common/util.h +++ b/common/util.h @@ -224,6 +224,8 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); +char *percent_data_escape (int plus_escape, const char *prefix, + const void *data, size_t datalen); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); diff --git a/scd/app-p15.c b/scd/app-p15.c index 98a9c6202..104f60a9e 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -56,6 +56,17 @@ typedef enum } card_type_t; +/* The OS of card as specified by card_type_t is not always + * sufficient. Thus we also distinguish the actual product build upon + * the given OS. */ +typedef enum + { + CARD_PRODUCT_UNKNOWN, + CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */ + } +card_product_t; + + /* A list card types with ATRs noticed with these cards. */ #define X(a) ((unsigned char const *)(a)) static struct @@ -306,9 +317,12 @@ struct app_local_s hierarchy. Thus we assume this is directly below the MF. */ unsigned short home_df; - /* The type of the card. */ + /* The type of the card's OS. */ card_type_t card_type; + /* The vendor's product. */ + card_product_t card_product; + /* Flag indicating whether we may use direct path selection. */ int direct_path_selection; @@ -2371,8 +2385,10 @@ read_ef_tokeninfo (app_t app) xfree (app->app_local->manufacturer_id); app->app_local->manufacturer_id = NULL; + app->app_local->card_product = CARD_PRODUCT_UNKNOWN; err = select_and_read_binary (app->slot, 0x5032, "TokenInfo", + &buffer, &buflen); if (err) return err; @@ -2467,6 +2483,10 @@ read_ef_tokeninfo (app_t app) { 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; + p += objlen; n -= objlen; /* Get next TLV. */ @@ -3169,12 +3189,33 @@ prepare_verify_pin (app_t app, const char *keyref, return gpg_error (GPG_ERR_INV_CARD); } - /* Select the key file. Note that this may change the security - * environment thus we need to do it before PIN verification. */ - err = select_ef_by_path (app, prkdf->path, prkdf->pathlen); - if (err) - log_error ("p15: error selecting file for key %s: %s\n", - keyref, gpg_strerror (err)); + + if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + { + /* According to our protocol analysis we need to select a + * special AID here. Before that the master file needs to be + * selected. (RID A000000167 is assigned to IBM) */ + static char const dtrust_aid[] = + { 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E }; + + err = iso7816_select_mf (app_get_slot (app)); + if (!err) + err = iso7816_select_application (app_get_slot (app), + dtrust_aid, sizeof dtrust_aid, 0); + if (err) + log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n", + keyref, gpg_strerror (err)); + } + else + { + /* Standard case: Select the key file. Note that this may + * change the security environment thus we need to do it before + * PIN verification. */ + err = select_ef_by_path (app, prkdf->path, prkdf->pathlen); + if (err) + log_error ("p15: error selecting file for key %s: %s\n", + keyref, gpg_strerror (err)); + } return err; } @@ -3706,10 +3747,30 @@ do_decipher (app_t app, const char *keyidstr, /* The next is guess work for CardOS. */ - if (prkdf->key_reference_valid) + if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + { + /* From analyzing an USB trace of a Windows signing application + * we see that the SE is simply reset to 0x14. It seems to be + * sufficient to do this for decryption; signing still works + * with the standard code despite that our trace showed that + * there the SE is restored to 0x09. Note that the special + * D-Trust AID is in any case select by prepare_verify_pin. + * + * Hey, D-Trust please hand over the specs so that you can + * actually sell your cards and we can properly implement it; + * other vendors understand this and do not demand ridiculous + * paper work or complicated procedures to get samples. */ + err = iso7816_manage_security_env (app_get_slot (app), + 0xF3, 0x14, NULL, 0); + + } + else if (prkdf->key_reference_valid) { unsigned char mse[6]; + /* 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. */ @@ -3718,13 +3779,13 @@ do_decipher (app_t app, const char *keyidstr, mse[5] = prkdf->key_reference; err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, mse, sizeof mse); - if (err) - { - log_error ("p15: MSE failed: %s\n", gpg_strerror (err)); - return err; - } } - + /* Check for MSE error. */ + if (err) + { + log_error ("p15: MSE failed: %s\n", gpg_strerror (err)); + return err; + } exmode = le_value = 0; padind = 0; @@ -3734,6 +3795,9 @@ do_decipher (app_t app, const char *keyidstr, le_value = prkdf->keynbits / 8; } + if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + padind = 0x81; + err = iso7816_decipher (app_get_slot (app), exmode, indata, indatalen, le_value, padind, @@ -3886,6 +3950,8 @@ app_select_p15 (app_t app) the common APP structure. */ app->app_local->card_type = card_type; + app->app_local->card_product = CARD_PRODUCT_UNKNOWN; + /* Store whether we may and should use direct path selection. */ app->app_local->direct_path_selection = direct; diff --git a/scd/iso7816.c b/scd/iso7816.c index 6920a683e..a2a4034ef 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -139,6 +139,32 @@ iso7816_select_application (int slot, const char *aid, size_t aidlen, } +/* This is the same as iso7816_select_application but may return data + * at RESULT,RESULTLEN). */ +gpg_error_t +iso7816_select_application_ext (int slot, const char *aid, size_t aidlen, + unsigned int flags, + unsigned char **result, size_t *resultlen) +{ + int sw; + sw = apdu_send (slot, 0, 0x00, CMD_SELECT_FILE, 4, + (flags&1)? 0:0x0c, aidlen, aid, + result, resultlen); + return map_sw (sw); +} + + +/* Simple MF selection as supported by some cards. */ +gpg_error_t +iso7816_select_mf (int slot) +{ + int sw; + + sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 0x000, 0x0c, -1, NULL); + return map_sw (sw); +} + + gpg_error_t iso7816_select_file (int slot, int tag, int is_dir) { diff --git a/scd/iso7816.h b/scd/iso7816.h index b46c7601b..c0d67e89b 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -51,6 +51,12 @@ gpg_error_t iso7816_map_sw (int sw); gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen, unsigned int flags); +gpg_error_t iso7816_select_application_ext (int slot, + const char *aid, size_t aidlen, + unsigned int flags, + unsigned char **result, + size_t *resultlen); +gpg_error_t iso7816_select_mf (int slot); gpg_error_t iso7816_select_file (int slot, int tag, int is_dir); gpg_error_t iso7816_select_path (int slot, const unsigned short *path, size_t pathlen);