aboutsummaryrefslogtreecommitdiffstats
path: root/scd/iso7816.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/iso7816.c')
-rw-r--r--scd/iso7816.c195
1 files changed, 177 insertions, 18 deletions
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 29208c254..d9f3336c7 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -50,6 +50,7 @@
#define CMD_PUT_DATA 0xDA
#define CMD_MSE 0x22
#define CMD_PSO 0x2A
+#define CMD_GENERAL_AUTHENTICATE 0x87
#define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84
@@ -66,6 +67,7 @@ map_sw (int sw)
case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
case SW_TERM_STATE: ec = GPG_ERR_OBJ_TERM_STATE; break;
case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
+ case SW_ACK_TIMEOUT: ec = GPG_ERR_TIMEOUT; break;
case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
@@ -138,6 +140,21 @@ 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);
+}
+
+
gpg_error_t
iso7816_select_file (int slot, int tag, int is_dir)
{
@@ -205,29 +222,66 @@ iso7816_list_directory (int slot, int list_dirs,
}
+/* Wrapper around apdu_send. RESULT can be NULL if no result is
+ * expected. In addition to an gpg-error return code the actual
+ * status word is stored at R_SW unless that is NULL. */
+gpg_error_t
+iso7816_send_apdu (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const void *data,
+ unsigned int *r_sw,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (result)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ }
+
+ sw = apdu_send (slot, extended_mode, class, ins, p0, p1, lc, data,
+ result, resultlen);
+ if (sw != SW_SUCCESS && result)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ if (r_sw)
+ *r_sw = sw;
+ return map_sw (sw);
+}
+
+
/* This function sends an already formatted APDU to the card. With
HANDLE_MORE set to true a MORE DATA status will be handled
internally. The return value is a gpg error code (i.e. a mapped
status word). This is basically the same as apdu_send_direct but
it maps the status word and does not return it in the result
- buffer. */
+ buffer. However, it R_SW is not NULL the status word is stored
+ R_SW for closer inspection. */
gpg_error_t
iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
- int handle_more,
+ int handle_more, unsigned int *r_sw,
unsigned char **result, size_t *resultlen)
{
- int sw;
+ int sw, sw2;
- if (!result || !resultlen)
- return gpg_error (GPG_ERR_INV_VALUE);
- *result = NULL;
- *resultlen = 0;
+ if (result)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ }
sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
- result, resultlen);
+ &sw2, result, resultlen);
if (!sw)
{
- if (*resultlen < 2)
+ if (!result)
+ sw = sw2;
+ else if (*resultlen < 2)
sw = SW_HOST_GENERAL_ERROR;
else
{
@@ -236,13 +290,15 @@ iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
(*resultlen)--;
}
}
- if (sw != SW_SUCCESS)
+ if (sw != SW_SUCCESS && result)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
}
+ if (r_sw)
+ *r_sw = sw;
return map_sw (sw);
}
@@ -324,6 +380,7 @@ iso7816_change_reference_data (int slot, int chvno,
sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+ wipememory (buf, oldchvlen+newchvlen);
xfree (buf);
return map_sw (sw);
@@ -396,6 +453,70 @@ iso7816_get_data (int slot, int extended_mode, int tag,
}
+/* Perform a GET DATA command requesting TAG and storing the result in
+ * a newly allocated buffer at the address passed by RESULT. Return
+ * the length of this data at the address of RESULTLEN. This variant
+ * is needed for long (3 octet) tags. */
+gpg_error_t
+iso7816_get_data_odd (int slot, int extended_mode, unsigned int tag,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ int le;
+ int datalen;
+ unsigned char data[5];
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (extended_mode > 0 && extended_mode < 256)
+ le = 65534; /* Not 65535 in case it is used as some special flag. */
+ else if (extended_mode > 0)
+ le = extended_mode;
+ else
+ le = 256;
+
+ data[0] = 0x5c;
+ if (tag <= 0xff)
+ {
+ data[1] = 1;
+ data[2] = tag;
+ datalen = 3;
+ }
+ else if (tag <= 0xffff)
+ {
+ data[1] = 2;
+ data[2] = (tag >> 8);
+ data[3] = tag;
+ datalen = 4;
+ }
+ else
+ {
+ data[1] = 3;
+ data[2] = (tag >> 16);
+ data[3] = (tag >> 8);
+ data[4] = tag;
+ datalen = 5;
+ }
+
+ sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA + 1,
+ 0x3f, 0xff, datalen, data, le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
/* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. EXTENDED_MODE controls whether extended length
headers or command chaining is used instead of single length
@@ -427,7 +548,7 @@ iso7816_put_data_odd (int slot, int extended_mode, int tag,
/* Manage Security Environment. This is a weird operation and there
is no easy abstraction for it. Furthermore, some card seem to have
- a different interpreation of 7816-8 and thus we resort to let the
+ a different interpretation of 7816-8 and thus we resort to let the
caller decide what to do. */
gpg_error_t
iso7816_manage_security_env (int slot, int p1, int p2,
@@ -445,7 +566,7 @@ iso7816_manage_security_env (int slot, int p1, int p2,
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
- success 0 is returned and the data is availavle in a newly
+ success 0 is returned and the data is available in a newly
allocated buffer stored at RESULT with its length stored at
RESULTLEN. For LE see do_generate_keypair. */
gpg_error_t
@@ -542,7 +663,7 @@ iso7816_decipher (int slot, int extended_mode,
}
-/* For LE see do_generate_keypair. */
+/* For LE see do_generate_keypair. */
gpg_error_t
iso7816_internal_authenticate (int slot, int extended_mode,
const unsigned char *data, size_t datalen,
@@ -579,12 +700,50 @@ iso7816_internal_authenticate (int slot, int extended_mode,
}
+/* For LE see do_generate_keypair. */
+gpg_error_t
+iso7816_general_authenticate (int slot, int extended_mode,
+ int algoref, int keyref,
+ const unsigned char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_GENERAL_AUTHENTICATE, algoref, keyref,
+ datalen, (const char*)data,
+ le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
/* LE is the expected return length. This is usually 0 except if
extended length mode is used and more than 256 byte will be
returned. In that case a value of -1 uses a large default
(e.g. 4096 bytes), a value larger 256 used that value. */
static gpg_error_t
-do_generate_keypair (int slot, int extended_mode, int read_only,
+do_generate_keypair (int slot, int extended_mode, int p1, int p2,
const char *data, size_t datalen, int le,
unsigned char **result, size_t *resultlen)
{
@@ -596,7 +755,7 @@ do_generate_keypair (int slot, int extended_mode, int read_only,
*resultlen = 0;
sw = apdu_send_le (slot, extended_mode,
- 0x00, CMD_GENERATE_KEYPAIR, read_only? 0x81:0x80, 0,
+ 0x00, CMD_GENERATE_KEYPAIR, p1, p2,
datalen, data,
le >= 0 && le < 256? 256:le,
result, resultlen);
@@ -614,12 +773,12 @@ do_generate_keypair (int slot, int extended_mode, int read_only,
gpg_error_t
-iso7816_generate_keypair (int slot, int extended_mode,
+iso7816_generate_keypair (int slot, int extended_mode, int p1, int p2,
const char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen)
{
- return do_generate_keypair (slot, extended_mode, 0,
+ return do_generate_keypair (slot, extended_mode, p1, p2,
data, datalen, le, result, resultlen);
}
@@ -630,7 +789,7 @@ iso7816_read_public_key (int slot, int extended_mode,
int le,
unsigned char **result, size_t *resultlen)
{
- return do_generate_keypair (slot, extended_mode, 1,
+ return do_generate_keypair (slot, extended_mode, 0x81, 0,
data, datalen, le, result, resultlen);
}