Implement decryption for TCOS 3 cards.

This commit is contained in:
Werner Koch 2009-03-30 12:46:06 +00:00
parent 990585ad7d
commit 98e1a75e20
13 changed files with 277 additions and 121 deletions

5
TODO
View File

@ -23,7 +23,10 @@
** mark all unimplemented commands and options. ** mark all unimplemented commands and options.
** Implement --default-key ** Implement --default-key
** support the anyPolicy semantic ** support the anyPolicy semantic
** Should we prefer nonRepudiation certs over plain signing certs?
Also: Do we need a way to allow the selection of a qualSig cert
over a plain one? The background is that the Telesec cards have 3
certs capable of signing all with the same subject name.
* sm/keydb.c * sm/keydb.c
** Check file permissions ** Check file permissions

View File

@ -1,3 +1,7 @@
2009-03-27 Werner Koch <wk@g10code.com>
* learncard.c (agent_handle_learn): Add new certtype 111.
2009-03-26 Werner Koch <wk@g10code.com> 2009-03-26 Werner Koch <wk@g10code.com>
* agent.h (MAX_DIGEST_LEN): Change to 64. * agent.h (MAX_DIGEST_LEN): Change to 64.

View File

@ -298,10 +298,12 @@ agent_handle_learn (ctrl_t ctrl, void *assuan_context)
char *p; char *p;
int i; int i;
static int certtype_list[] = { static int certtype_list[] = {
111, /* Root CA */
101, /* trusted */ 101, /* trusted */
102, /* useful */ 102, /* useful */
100, /* regular */ 100, /* regular */
/* We don't include 110 here because gpgsm can't handle it. */ /* We don't include 110 here because gpgsm can't handle that
special root CA format. */
-1 /* end of list */ -1 /* end of list */
}; };

View File

@ -1,3 +1,16 @@
2009-03-30 Werner Koch <wk@g10code.com>
* app-nks.c (do_decipher): Make it work for TCOS 3.
* iso7816.c (iso7816_decipher): Add arg EXTENDED_MODE.
* apdu.c (apdu_send): Add arg EXTENDED_MODE and change all callers.
(apdu_send_le): Ditto.
(apdu_send_direct): Ditto, but not yet functional.
(send_le): Fix command chaining. Implement extended length option.
* ccid-driver.c (ccid_transceive): Remove restriction on apdu length.
(struct ccid_driver_s): Add field IFSC.
(ccid_get_atr): Set IFSC.
(ccid_transceive): Use negotiated IFSC and support S(IFS) command.
2009-03-26 Werner Koch <wk@g10code.com> 2009-03-26 Werner Koch <wk@g10code.com>
* command.c (cmd_pksign): Allow more hash algorithms. * command.c (cmd_pksign): Allow more hash algorithms.

View File

@ -414,6 +414,7 @@ apdu_strerror (int rc)
case SW_FILE_NOT_FOUND : return "file not found"; case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record not found"; case SW_RECORD_NOT_FOUND:return "record not found";
case SW_REF_NOT_FOUND : return "reference not found"; case SW_REF_NOT_FOUND : return "reference not found";
case SW_BAD_LC : return "bad Lc";
case SW_BAD_P0_P1 : return "bad P0 or P1"; case SW_BAD_P0_P1 : return "bad P0 or P1";
case SW_INS_NOT_SUP : return "instruction not supported"; case SW_INS_NOT_SUP : return "instruction not supported";
case SW_CLA_NOT_SUP : return "class not supported"; case SW_CLA_NOT_SUP : return "class not supported";
@ -2806,14 +2807,18 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
/* Core APDU tranceiver function. Parameters are described at /* Core APDU tranceiver function. Parameters are described at
apdu_send_le with the exception of PININFO which indicates keypad apdu_send_le with the exception of PININFO which indicates keypad
related operations if not NULL. If EXTENDED_MODE is not NULL related operations if not NULL. If EXTENDED_MODE is not 0
command chaining or extended length will be used according to these command chaining or extended length will be used according to these
values: values:
n < 0 := Use command chaining with the data part limited to -n n < 0 := Use command chaining with the data part limited to -n
in each chunk. If -1 is used a default value is used. in each chunk. If -1 is used a default value is used.
n == 0 := No extended mode or command chaining.
n == 1 := Use extended length for input and output without a n == 1 := Use extended length for input and output without a
length limit. length limit.
n > 1 := Use extended length with up to N bytes. n > 1 := Use extended length with up to N bytes.
FIXME: We don't support extended length return values larger
than 256 bytes due to a static buffer.
*/ */
static int static int
send_le (int slot, int class, int ins, int p0, int p1, send_le (int slot, int class, int ins, int p0, int p1,
@ -2825,12 +2830,16 @@ send_le (int slot, int class, int ins, int p0, int p1,
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
the driver. */ the driver. */
size_t resultlen; size_t resultlen;
unsigned char apdu[5+256+1]; unsigned char short_apdu_buffer[5+256+1];
unsigned char *apdu_buffer = NULL;
size_t apdu_buffer_size;
unsigned char *apdu;
size_t apdulen; size_t apdulen;
int sw; int sw;
long rc; /* We need a long here due to PC/SC. */ long rc; /* We need a long here due to PC/SC. */
int did_exact_length_hack = 0; int did_exact_length_hack = 0;
int use_chaining = 0; int use_chaining = 0;
int use_extended_length = 0;
int lc_chunk; int lc_chunk;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
@ -2847,7 +2856,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (!extended_mode) if (!extended_mode)
return SW_WRONG_LENGTH; /* No way to send such an APDU. */ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
else if (extended_mode > 0) else if (extended_mode > 0)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */ use_extended_length = 1;
else if (extended_mode < 0) else if (extended_mode < 0)
{ {
/* Send APDU using chaining mode. */ /* Send APDU using chaining mode. */
@ -2861,51 +2870,99 @@ send_le (int slot, int class, int ins, int p0, int p1,
else else
return SW_HOST_INV_VALUE; return SW_HOST_INV_VALUE;
} }
else if (lc == -1 && extended_mode > 0)
use_extended_length = 1;
if (le != -1 && (le > 256 || le < 0)) if (le != -1 && (le > 256 || le < 0))
return SW_WRONG_LENGTH; return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1)) if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE; return SW_HOST_INV_VALUE;
if (use_extended_length)
{
if (reader_table[slot].is_t0)
return SW_HOST_NOT_SUPPORTED;
/* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */
apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
apdu_buffer = xtrymalloc (apdu_buffer_size);
if (!apdu_buffer)
return SW_HOST_OUT_OF_CORE;
apdu = apdu_buffer;
}
else
{
apdu_buffer_size = sizeof short_apdu_buffer;
apdu = short_apdu_buffer;
}
if ((sw = lock_slot (slot))) if ((sw = lock_slot (slot)))
return sw; return sw;
do do
{ {
apdulen = 0; if (use_extended_length)
apdu[apdulen] = class;
if (use_chaining && lc > 255)
{ {
apdu[apdulen] |= 0x10; use_chaining = 0;
assert (use_chaining < 256); apdulen = 0;
lc_chunk = use_chaining; apdu[apdulen++] = class;
lc -= use_chaining; apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
if (lc >= 0)
{
apdu[apdulen++] = ((lc >> 8) & 0xff);
apdu[apdulen++] = (lc & 0xff);
memcpy (apdu+apdulen, data, lc);
data += lc;
apdulen += lc;
}
if (le != -1)
{
apdu[apdulen++] = ((le >> 8) & 0xff);
apdu[apdulen++] = (le & 0xff);
}
} }
else else
{ {
use_chaining = 0; apdulen = 0;
lc_chunk = lc; apdu[apdulen] = class;
if (use_chaining && lc > 255)
{
apdu[apdulen] |= 0x10;
assert (use_chaining < 256);
lc_chunk = use_chaining;
lc -= use_chaining;
}
else
{
use_chaining = 0;
lc_chunk = lc;
}
apdulen++;
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
if (lc_chunk != -1)
{
apdu[apdulen++] = lc_chunk;
memcpy (apdu+apdulen, data, lc_chunk);
data += lc_chunk;
apdulen += lc_chunk;
/* T=0 does not allow the use of Lc together with Le;
thus disable Le in this case. */
if (reader_table[slot].is_t0)
le = -1;
}
if (le != -1 && !use_chaining)
apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
} }
apdulen++;
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
if (lc_chunk != -1)
{
apdu[apdulen++] = lc_chunk;
memcpy (apdu+apdulen, data, lc_chunk);
data += lc_chunk;
apdulen += lc_chunk;
/* T=0 does not allow the use of Lc together with Le; thus
disable Le in this case. */
if (reader_table[slot].is_t0)
le = -1;
}
if (le != -1)
apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
/* As safeguard don't pass any garbage from the stack to the driver. */
assert (sizeof (apdu) >= apdulen);
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
exact_length_hack: exact_length_hack:
/* As a safeguard don't pass any garbage to the driver. */
assert (apdulen <= apdu_buffer_size);
memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
resultlen = RESULTLEN; resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
if (rc || resultlen < 2) if (rc || resultlen < 2)
@ -2916,7 +2973,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
} }
sw = (result[resultlen-2] << 8) | result[resultlen-1]; sw = (result[resultlen-2] << 8) | result[resultlen-1];
if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw)) if (!use_extended_length
&& !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
{ {
apdu[apdulen-1] = (sw & 0x00ff); apdu[apdulen-1] = (sw & 0x00ff);
did_exact_length_hack = 1; did_exact_length_hack = 1;
@ -2925,6 +2983,13 @@ send_le (int slot, int class, int ins, int p0, int p1,
} }
while (use_chaining && sw == SW_SUCCESS); while (use_chaining && sw == SW_SUCCESS);
if (apdu_buffer)
{
xfree (apdu_buffer);
apdu_buffer = NULL;
apdu_buffer_size = 0;
}
/* Store away the returned data but strip the statusword. */ /* Store away the returned data but strip the statusword. */
resultlen -= 2; resultlen -= 2;
if (DBG_CARD_IO) if (DBG_CARD_IO)
@ -2976,13 +3041,16 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (DBG_CARD_IO) if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n", log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len); slot, len);
apdu_buffer_size = sizeof short_apdu_buffer;
apdu = short_apdu_buffer;
apdulen = 0; apdulen = 0;
apdu[apdulen++] = class; apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0; apdu[apdulen++] = 0;
apdu[apdulen++] = 0; apdu[apdulen++] = 0;
apdu[apdulen++] = len; apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); assert (apdulen <= apdu_buffer_size);
memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
resultlen = RESULTLEN; resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2) if (rc || resultlen < 2)
@ -3052,47 +3120,52 @@ send_le (int slot, int class, int ins, int p0, int p1,
/* Send an APDU to the card in SLOT. The APDU is created from all /* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
for LC won't sent this field and the data field; in this case DATA for LC won't sent this field and the data field; in this case DATA
must also be passed as NULL. The return value is the status word must also be passed as NULL. If EXTENDED_MODE is not 0 command
or -1 for an invalid SLOT or other non card related error. If chaining or extended length will be used; see send_le for details.
RETBUF is not NULL, it will receive an allocated buffer with the The return value is the status word or -1 for an invalid SLOT or
returned data. The length of that data will be put into other non card related error. If RETBUF is not NULL, it will
*RETBUFLEN. The caller is reponsible for releasing the buffer even receive an allocated buffer with the returned data. The length of
in case of errors. */ that data will be put into *RETBUFLEN. The caller is reponsible
for releasing the buffer even in case of errors. */
int int
apdu_send_le(int slot, int class, int ins, int p0, int p1, apdu_send_le(int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, int le, int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen) unsigned char **retbuf, size_t *retbuflen)
{ {
return send_le (slot, class, ins, p0, p1, return send_le (slot, class, ins, p0, p1,
lc, data, le, lc, data, le,
retbuf, retbuflen, retbuf, retbuflen,
NULL, 0); NULL, extended_mode);
} }
/* Send an APDU to the card in SLOT. The APDU is created from all /* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. The return value is the status word or -1 also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
for an invalid SLOT or other non card related error. If RETBUF is or extended length will be used; see send_le for details. The
not NULL, it will receive an allocated buffer with the returned return value is the status word or -1 for an invalid SLOT or other
data. The length of that data will be put into *RETBUFLEN. The non card related error. If RETBUF is not NULL, it will receive an
caller is reponsible for releasing the buffer even in case of allocated buffer with the returned data. The length of that data
errors. */ will be put into *RETBUFLEN. The caller is reponsible for
releasing the buffer even in case of errors. */
int int
apdu_send (int slot, int class, int ins, int p0, int p1, apdu_send (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{ {
return send_le (slot, class, ins, p0, p1, lc, data, 256, return send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen, NULL, 0); retbuf, retbuflen, NULL, extended_mode);
} }
/* Send an APDU to the card in SLOT. The APDU is created from all /* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. The return value is the status word or -1 also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
for an invalid SLOT or other non card related error. No data will be or extended length will be used; see send_le for details. The
returned. */ return value is the status word or -1 for an invalid SLOT or other
non card related error. No data will be returned. */
int int
apdu_send_simple (int slot, int extended_mode, apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1, int class, int ins, int p0, int p1,
@ -3126,11 +3199,13 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
and returns with an APDU including the status word. With and returns with an APDU including the status word. With
HANDLE_MORE set to true this function will handle the MORE DATA HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at status and return all APDUs concatenated with one status word at
the end. The function does not return a regular status word but 0 the end. If EXTENDED_MODE is not 0 command chaining or extended
on success. If the slot is locked, the function returns length will be used; see send_le for details. The function does
immediately with an error. */ not return a regular status word but 0 on success. If the slot is
locked, the function returns immediately with an error. */
int int
apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, apdu_send_direct (int slot, int extended_mode,
const unsigned char *apdudata, size_t apdudatalen,
int handle_more, int handle_more,
unsigned char **retbuf, size_t *retbuflen) unsigned char **retbuf, size_t *retbuflen)
{ {
@ -3147,6 +3222,9 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER; return SW_HOST_NO_DRIVER;
if (extended_mode)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */
if ((sw = trylock_slot (slot))) if ((sw = trylock_slot (slot)))
return sw; return sw;

View File

@ -41,6 +41,7 @@ enum {
SW_NOT_SUPPORTED = 0x6a81, SW_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82, SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83, SW_RECORD_NOT_FOUND = 0x6a83,
SW_BAD_LC = 0x6a87, /* Lc does not match command or p1/p2. */
SW_REF_NOT_FOUND = 0x6a88, SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00, SW_BAD_P0_P1 = 0x6b00,
SW_EXACT_LENGTH = 0x6c00, SW_EXACT_LENGTH = 0x6c00,
@ -117,13 +118,14 @@ int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int lc, const char *data,
int pin_mode, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen); int pinlen_min, int pinlen_max, int pin_padlen);
int apdu_send (int slot, int class, int ins, int p0, int p1, int apdu_send (int slot, int extended_mode,
int lc, const char *data, int class, int ins, int p0, int p1, int lc, const char *data,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);
int apdu_send_le (int slot, int class, int ins, int p0, int p1, int apdu_send_le (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, int le, int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);
int apdu_send_direct (int slot, int apdu_send_direct (int slot, int extended_mode,
const unsigned char *apdudata, size_t apdudatalen, const unsigned char *apdudata, size_t apdudatalen,
int handle_more, int handle_more,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);

View File

@ -121,8 +121,7 @@ app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff)
if ( !(class == CLASS_UNIVERSAL && constructed if ( !(class == CLASS_UNIVERSAL && constructed
&& (tag == TAG_SEQUENCE || tag == TAG_SET))) && (tag == TAG_SEQUENCE || tag == TAG_SET)))
{ {
log_info ("contents of FID 0x%04X does not look like a certificate\n", log_info ("data at FID 0x%04X does not look like a certificate\n", fid);
fid);
return 0; return 0;
} }

View File

@ -22,8 +22,11 @@
- We are now targeting TCOS 3 cards and it may happen that there is - We are now targeting TCOS 3 cards and it may happen that there is
a regression towards TCOS 2 cards. Please report. a regression towards TCOS 2 cards. Please report.
- The TKS3 AUT key is not used by our authentication command but - The TKS3 AUT key is not used. It seems that it is only useful for
accessible via the decrypt command. the internal authentication command and not accessible by other
applications. The key itself is in the encryption class but the
corresponding certificate has only the digitalSignature
capability.
- If required, we automagically switch between the NKS application - If required, we automagically switch between the NKS application
and the SigG application. This avoids to use the DINSIG and the SigG application. This avoids to use the DINSIG
@ -70,9 +73,7 @@ static struct
unsigned char kid; /* Corresponding key references. */ unsigned char kid; /* Corresponding key references. */
} filelist[] = { } filelist[] = {
{ 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */ { 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */
{ 1, 0x4531, 3, 0, 0x0000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */
{ 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
{ 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
{ 0, 0x4331, 0, 100 }, { 0, 0x4331, 0, 100 },
{ 0, 0x4332, 0, 100 }, { 0, 0x4332, 0, 100 },
{ 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
@ -80,11 +81,15 @@ static struct
{ 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
{ 0, 0x43B1, 0, 100 }, { 0, 0x43B1, 0, 100 },
{ 0, 0x43B2, 0, 100 }, { 0, 0x43B2, 0, 100 },
{ 0, 0x4571, 3, 0, 0xc500, 0, 0, 0x82 }, /* EF_PK.NKS.AUT */ /* The authentication key is not used. */
{ 0, 0xC500, 3, 101 }, /* EF_C.NKS.AUT */ /* { 0, 0x4571, 3, 0, 0xC500, 0, 0, 0x82 }, /\* EF_PK.NKS.AUT *\/ */
/* { 0, 0xC500, 3, 101 }, /\* EF_C.NKS.AUT *\/ */
{ 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */ { 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */
{ 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
/* { 1, 0xB000, 3, ... */ { 1, 0x4531, 3, 0, 0xC000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */
{ 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
{ 1, 0xC008, 3, 101 }, /* EF_C.CA.SIG */
{ 1, 0xC00E, 3, 111 }, /* EF_C.RCA.SIG */
{ 0, 0 } { 0, 0 }
}; };
@ -249,7 +254,7 @@ get_chv_status (app_t app, int sigg, int pwid)
command[2] = 0x00; command[2] = 0x00;
command[3] = pwid; command[3] = pwid;
if (apdu_send_direct (app->slot, command, 4, 0, &result, &resultlen)) if (apdu_send_direct (app->slot, 0, command, 4, 0, &result, &resultlen))
rc = -1; /* Error. */ rc = -1; /* Error. */
else if (resultlen < 2) else if (resultlen < 2)
rc = -1; /* Error. */ rc = -1; /* Error. */
@ -808,13 +813,10 @@ do_decipher (app_t app, const char *keyidstr,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ) unsigned char **outdata, size_t *outdatalen )
{ {
static const unsigned char mse_parm[] = {
0x80, 1, 0x10, /* Select algorithm RSA. */
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
int rc, i; int rc, i;
int is_sigg = 0; int is_sigg = 0;
int fid; int fid;
int kid;
if (!keyidstr || !*keyidstr || !indatalen) if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -847,15 +849,40 @@ do_decipher (app_t app, const char *keyidstr,
return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_NOT_FOUND);
if (!filelist[i].isenckey) if (!filelist[i].isenckey)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
kid = filelist[i].kid;
if (app->app_local->nks_version > 2)
{
unsigned char mse[6];
mse[0] = 0x80; /* Algorithm reference. */
mse[1] = 1;
mse[2] = 0x0a; /* RSA no padding. (0x1A is pkcs#1.5 padding.) */
mse[3] = 0x84; /* Private key reference. */
mse[4] = 1;
mse[5] = kid;
rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8,
mse, sizeof mse);
}
else
{
static const unsigned char mse[] =
{
0x80, 1, 0x10, /* Select algorithm RSA. */
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8,
mse, sizeof mse);
}
/* Do the TCOS specific MSE. */
rc = iso7816_manage_security_env (app->slot,
0xC1, 0xB8,
mse_parm, sizeof mse_parm);
if (!rc) if (!rc)
rc = verify_pin (app, 0, NULL, pincb, pincb_arg); rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
/* Note that we need to use extended length APDUs for TCOS 3 cards.
Command chaining does not work. */
if (!rc) if (!rc)
rc = iso7816_decipher (app->slot, indata, indatalen, 0x81, rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0,
indata, indatalen, 0x81,
outdata, outdatalen); outdata, outdatalen);
return rc; return rc;
} }

View File

@ -3066,14 +3066,16 @@ do_decipher (app_t app, const char *keyidstr,
{ {
memset (fixbuf, 0, fixuplen); memset (fixbuf, 0, fixuplen);
memcpy (fixbuf+fixuplen, indata, indatalen); memcpy (fixbuf+fixuplen, indata, indatalen);
rc = iso7816_decipher (app->slot, fixbuf, fixuplen+indatalen, -1, rc = iso7816_decipher (app->slot, 0,
fixbuf, fixuplen+indatalen, -1,
outdata, outdatalen); outdata, outdatalen);
xfree (fixbuf); xfree (fixbuf);
} }
} }
else else
rc = iso7816_decipher (app->slot, indata, indatalen, 0, rc = iso7816_decipher (app->slot, 0,
indata, indatalen, 0,
outdata, outdatalen); outdata, outdatalen);
} }
return rc; return rc;

View File

@ -243,6 +243,7 @@ struct ccid_driver_s
int auto_ifsd; int auto_ifsd;
int max_ifsd; int max_ifsd;
int ifsd; int ifsd;
int ifsc;
int powered_off; int powered_off;
int has_pinpad; int has_pinpad;
int apdu_level; /* Reader supports short APDU level exchange. */ int apdu_level; /* Reader supports short APDU level exchange. */
@ -1840,7 +1841,6 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
*nread = msglen = rc; *nread = msglen = rc;
} }
if (msglen < 10) if (msglen < 10)
{ {
DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
@ -1880,7 +1880,6 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
return CCID_DRIVER_ERR_INV_VALUE; return CCID_DRIVER_ERR_INV_VALUE;
} }
if (debug_level && (!no_debug || debug_level >= 3)) if (debug_level && (!no_debug || debug_level >= 3))
{ {
switch (buffer[0]) switch (buffer[0])
@ -2326,6 +2325,11 @@ ccid_get_atr (ccid_driver_t handle,
if (rc) if (rc)
DEBUGOUT ("SetParameters failed (ignored)\n"); DEBUGOUT ("SetParameters failed (ignored)\n");
if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
handle->ifsc = msg[15];
else
handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */
handle->t1_ns = 0; handle->t1_ns = 0;
handle->t1_nr = 0; handle->t1_nr = 0;
@ -2582,22 +2586,15 @@ ccid_transceive (ccid_driver_t handle,
assert (apdulen); assert (apdulen);
/* Construct an I-Block. */ /* Construct an I-Block. */
/* Fixme: I am not sure whether limiting the length to 259
as per CCID spec is required. The code blow chops the
APDU anyway into 128 byte blocks. Needs to be addressed
when supporting extended length APDUs. */
if (apdulen > 259)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
tpdu = msg+10; tpdu = msg+10;
/* NAD: DAD=1, SAD=0 */ /* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
if (apdulen > 128 /* fixme: replace by ifsc */) if (apdulen > handle->ifsc )
{ {
apdulen = 128; apdulen = handle->ifsc;
apdu_buf += 128; apdu_buf += handle->ifsc;
apdu_buflen -= 128; apdu_buflen -= handle->ifsc;
tpdu[1] |= (1 << 5); /* Set more bit. */ tpdu[1] |= (1 << 5); /* Set more bit. */
} }
tpdu[2] = apdulen; tpdu[2] = apdulen;
@ -2752,8 +2749,31 @@ ccid_transceive (ccid_driver_t handle,
DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n", DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f)); (tpdu[1] & 0x1f));
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
{ /* Wait time extension request. */ {
/* Information field size request. */
unsigned char ifsc = tpdu[3];
if (ifsc < 16 || ifsc > 254)
return CCID_DRIVER_ERR_CARD_IO_ERROR;
msg = send_buffer;
tpdu = msg+10;
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
tpdu[2] = 1;
tpdu[3] = ifsc;
tpdulen = 4;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
DEBUGOUT_1 ("T=1 requesting an ifsc=%d\n", ifsc);
}
else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
{
/* Wait time extension request. */
unsigned char bwi = tpdu[3]; unsigned char bwi = tpdu[3];
msg = send_buffer; msg = send_buffer;
tpdu = msg+10; tpdu = msg+10;

View File

@ -596,7 +596,8 @@ cmd_serialno (assuan_context_t ctx, char *line)
100 := Regular X.509 cert 100 := Regular X.509 cert
101 := Trusted X.509 cert 101 := Trusted X.509 cert
102 := Useful X.509 cert 102 := Useful X.509 cert
110 := Root CA cert (e.g. DINSIG) 110 := Root CA cert in a special format (e.g. DINSIG)
111 := Root CA cert as standard X509 cert.
For certain cards, more information will be returned: For certain cards, more information will be returned:
@ -963,7 +964,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
xfree (keyidstr); xfree (keyidstr);
if (rc) if (rc)
{ {
log_error ("card_sign failed: %s\n", gpg_strerror (rc)); log_error ("app_sign failed: %s\n", gpg_strerror (rc));
} }
else else
{ {
@ -1013,7 +1014,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
xfree (keyidstr); xfree (keyidstr);
if (rc) if (rc)
{ {
log_error ("app_auth_sign failed: %s\n", gpg_strerror (rc)); log_error ("app_auth failed: %s\n", gpg_strerror (rc));
} }
else else
{ {
@ -1057,7 +1058,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
xfree (keyidstr); xfree (keyidstr);
if (rc) if (rc)
{ {
log_error ("card_create_signature failed: %s\n", gpg_strerror (rc)); log_error ("app_decipher failed: %s\n", gpg_strerror (rc));
} }
else else
{ {
@ -1821,7 +1822,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
unsigned char *result = NULL; unsigned char *result = NULL;
size_t resultlen; size_t resultlen;
rc = apdu_send_direct (ctrl->reader_slot, apdu, apdulen, handle_more, rc = apdu_send_direct (ctrl->reader_slot, 0, apdu, apdulen, handle_more,
&result, &resultlen); &result, &resultlen);
if (rc) if (rc)
log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc)); log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc));

View File

@ -213,7 +213,7 @@ iso7816_list_directory (int slot, int list_dirs,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL, sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -244,7 +244,7 @@ iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send_direct (slot, apdudata, apdudatalen, handle_more, sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
result, resultlen); result, resultlen);
if (!sw) if (!sw)
{ {
@ -430,7 +430,7 @@ iso7816_get_data (int slot, int tag,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_GET_DATA, sw = apdu_send (slot, 0, 0x00, CMD_GET_DATA,
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
@ -462,7 +462,7 @@ iso7816_put_data (int slot, int extended_mode, int tag,
return map_sw (sw); return map_sw (sw);
} }
/* Same as iso7816_put_data but uses an odd instrcution byte. */ /* Same as iso7816_put_data but uses an odd instruction byte. */
gpg_error_t gpg_error_t
iso7816_put_data_odd (int slot, int extended_mode, int tag, iso7816_put_data_odd (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen) const unsigned char *data, size_t datalen)
@ -509,7 +509,8 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data, sw = apdu_send (slot, 0,
0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -530,7 +531,8 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
and the plaintext is available in a newly allocated buffer stored and the plaintext is available in a newly allocated buffer stored
at RESULT with its length stored at RESULTLEN. */ at RESULT with its length stored at RESULTLEN. */
gpg_error_t gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen, iso7816_decipher (int slot, int extended_mode,
const unsigned char *data, size_t datalen,
int padind, unsigned char **result, size_t *resultlen) int padind, unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
@ -547,17 +549,19 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
buf = xtrymalloc (datalen + 1); buf = xtrymalloc (datalen + 1);
if (!buf) if (!buf)
return gpg_error (gpg_err_code_from_errno (errno)); return gpg_error (gpg_err_code_from_errno (errno));
*buf = padind; /* Padding indicator. */ *buf = padind; /* Padding indicator. */
memcpy (buf+1, data, datalen); memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, sw = apdu_send (slot, extended_mode,
0x00, CMD_PSO, 0x80, 0x86,
datalen+1, (char*)buf, datalen+1, (char*)buf,
result, resultlen); result, resultlen);
xfree (buf); xfree (buf);
} }
else else
{ {
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, sw = apdu_send (slot, extended_mode,
0x00, CMD_PSO, 0x80, 0x86,
datalen, (const char *)data, datalen, (const char *)data,
result, resultlen); result, resultlen);
} }
@ -586,7 +590,7 @@ iso7816_internal_authenticate (int slot,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0, sw = apdu_send (slot, 0, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
datalen, (const char*)data, result, resultlen); datalen, (const char*)data, result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -613,7 +617,8 @@ do_generate_keypair (int slot, int readonly,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0, sw = apdu_send (slot, 0,
0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
datalen, (const char*)data, result, resultlen); datalen, (const char*)data, result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -661,8 +666,8 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
{ {
result = NULL; result = NULL;
n = length > 254? 254 : length; n = length > 254? 254 : length;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, sw = apdu_send_le (slot, 0,
n, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, n,
&result, &resultlen); &result, &resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -711,13 +716,13 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
n = read_all? 0 : nmax; n = read_all? 0 : nmax;
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen); n, &buffer, &bufferlen);
if ( SW_EXACT_LENGTH_P(sw) ) if ( SW_EXACT_LENGTH_P(sw) )
{ {
n = (sw & 0x00ff); n = (sw & 0x00ff);
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen); n, &buffer, &bufferlen);
} }
@ -804,7 +809,7 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD, sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD,
recno, recno,
short_ef? short_ef : 0x04, short_ef? short_ef : 0x04,
-1, NULL, -1, NULL,

View File

@ -96,7 +96,7 @@ gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
gpg_error_t iso7816_compute_ds (int slot, gpg_error_t iso7816_compute_ds (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_decipher (int slot, 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 padind, int padind,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);