From 405feca2bdeeb620dc406667a702035a123ae848 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 3 Jan 2019 15:18:15 +0100 Subject: [PATCH] scd: Add two variants to the set of ISO7816 functions. * scd/iso7816.c (iso7816_select_application_ext): New. (iso7816_get_data_odd): New. Signed-off-by: Werner Koch --- scd/iso7816.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ scd/iso7816.h | 7 +++++ 2 files changed, 86 insertions(+) diff --git a/scd/iso7816.c b/scd/iso7816.c index 01faca5b4..43c0bcd8e 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -138,6 +138,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) { @@ -396,6 +411,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 diff --git a/scd/iso7816.h b/scd/iso7816.h index 4c71bbd50..332fc0e81 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -51,6 +51,11 @@ 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_file (int slot, int tag, int is_dir); gpg_error_t iso7816_select_path (int slot, const unsigned short *path, size_t pathlen); @@ -78,6 +83,8 @@ gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno, size_t datalen); gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag, unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_get_data_odd (int slot, int extended_mode, unsigned int tag, + unsigned char **result, size_t *resultlen); gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag, const void *data, size_t datalen); gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag,