1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-03-28 22:49:59 +01:00

scd:p15: Fix decrypt followed by sign problem for D-Trust cards.

* scd/iso7816.c (iso7816_select_mf): New.
* scd/app-p15.c (card_product_t): New.
(struct app_local_s): Add field 'card_product'.
(read_ef_tokeninfo): Detect D-Trust card.
(prepare_verify_pin): Switch to D-Trust AID.
(do_decipher): Restore a SE for D-TRust cards.  Change the padding
indicator to 0x81.

* common/percent.c (percent_data_escape): new.  Taken from master.
--

Using what I learned from a USB trace running the Governikus Signer
Software on Windows this fixes the left over problem with the new
D-Trust card support.

Signed-off-by: Werner Koch <wk@gnupg.org>

Backported from master.  This required to add the percent_data_escape
function we introduced in master on 2018-07-02:

    commit 58baf40af641f8cbf597e508a292e85ae94688f1
    common: New function percent_data_escape.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-04-07 16:00:11 +02:00
parent 4148976841
commit 471b06e91b
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 197 additions and 14 deletions

@ -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

@ -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);

@ -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;

@ -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)
{

@ -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);