1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-03 22:56:33 +02:00

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

View file

@ -414,6 +414,7 @@ apdu_strerror (int rc)
case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record 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_INS_NOT_SUP : return "instruction 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
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
values:
n < 0 := Use command chaining with the data part limited to -n
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
length limit.
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
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
the driver. */
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;
int sw;
long rc; /* We need a long here due to PC/SC. */
int did_exact_length_hack = 0;
int use_chaining = 0;
int use_extended_length = 0;
int lc_chunk;
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)
return SW_WRONG_LENGTH; /* No way to send such an APDU. */
else if (extended_mode > 0)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */
use_extended_length = 1;
else if (extended_mode < 0)
{
/* Send APDU using chaining mode. */
@ -2861,51 +2870,99 @@ send_le (int slot, int class, int ins, int p0, int p1,
else
return SW_HOST_INV_VALUE;
}
else if (lc == -1 && extended_mode > 0)
use_extended_length = 1;
if (le != -1 && (le > 256 || le < 0))
return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1))
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)))
return sw;
do
{
apdulen = 0;
apdu[apdulen] = class;
if (use_chaining && lc > 255)
if (use_extended_length)
{
apdu[apdulen] |= 0x10;
assert (use_chaining < 256);
lc_chunk = use_chaining;
lc -= use_chaining;
use_chaining = 0;
apdulen = 0;
apdu[apdulen++] = class;
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
{
use_chaining = 0;
lc_chunk = lc;
apdulen = 0;
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:
/* 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;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
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;
}
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);
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);
if (apdu_buffer)
{
xfree (apdu_buffer);
apdu_buffer = NULL;
apdu_buffer_size = 0;
}
/* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
@ -2976,13 +3041,16 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
apdu_buffer_size = sizeof short_apdu_buffer;
apdu = short_apdu_buffer;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
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;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
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
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
must also be passed as NULL. The return value is the status word
or -1 for an invalid SLOT or other non card related error. If
RETBUF is not NULL, it will receive an allocated buffer with the
returned data. The length of that data will be put into
*RETBUFLEN. The caller is reponsible for releasing the buffer even
in case of errors. */
must also be passed as NULL. If EXTENDED_MODE is not 0 command
chaining or extended length will be used; see send_le for details.
The return value is the status word or -1 for an invalid SLOT or
other non card related error. If RETBUF is not NULL, it will
receive an allocated buffer with the returned data. The length of
that data will be put into *RETBUFLEN. The caller is reponsible
for releasing the buffer even in case of errors. */
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,
unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1,
lc, data, le,
retbuf, retbuflen,
NULL, 0);
NULL, extended_mode);
}
/* 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
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
for an invalid SLOT or other non card related error. If RETBUF is
not NULL, it will receive an allocated buffer with the returned
data. The length of that data will be put into *RETBUFLEN. The
caller is reponsible for releasing the buffer even in case of
errors. */
also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
or extended length will be used; see send_le for details. The
return value is the status word or -1 for an invalid SLOT or other
non card related error. If RETBUF is not NULL, it will receive an
allocated buffer with the returned data. The length of that data
will be put into *RETBUFLEN. The caller is reponsible for
releasing the buffer even in case of errors. */
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)
{
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
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
also be passed as NULL. The return value is the status word or -1
for an invalid SLOT or other non card related error. No data will be
returned. */
also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
or extended length will be used; see send_le for details. The
return value is the status word or -1 for an invalid SLOT or other
non card related error. No data will be returned. */
int
apdu_send_simple (int slot, int extended_mode,
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
HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at
the end. The function does not return a regular status word but 0
on success. If the slot is locked, the function returns
immediately with an error. */
the end. If EXTENDED_MODE is not 0 command chaining or extended
length will be used; see send_le for details. The function does
not return a regular status word but 0 on success. If the slot is
locked, the function returns immediately with an error. */
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,
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 )
return SW_HOST_NO_DRIVER;
if (extended_mode)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */
if ((sw = trylock_slot (slot)))
return sw;