- pretty much finished the DER encoder
- added delete and create file support for 'flex cards - PKCS #15 DF's are now stored more flexibly; this makes adding new types of DF's (such as PuKDF's) easier - added 'get' and 'put' commands to opensc-explorer git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@160 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
921bbce7f5
commit
452b9d4193
|
@ -116,6 +116,24 @@ void sc_copy_asn1_entry(const struct sc_asn1_entry *src,
|
||||||
dest->name = NULL;
|
dest->name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t sc_count_bit_string_size(const void * buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
const u8 *p = (const u8 *) buf + bufsize - 1;
|
||||||
|
u8 c;
|
||||||
|
size_t skip = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
while (p >= (const u8 *) buf && *p == 0) {
|
||||||
|
skip += 8;
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
if (p < (const u8 *) buf)
|
||||||
|
return 0;
|
||||||
|
c = *p;
|
||||||
|
for (i = 0; (c >> (7-i)) == 0; i++);
|
||||||
|
return bufsize * 8 - (skip + i);
|
||||||
|
}
|
||||||
|
|
||||||
static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen)
|
static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -403,10 +421,41 @@ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen,
|
||||||
return decode_bit_string(inbuf, inlen, outbuf, outlen, 0);
|
return decode_bit_string(inbuf, inlen, outbuf, outlen, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int encode_bit_string(const u8 * inbuf, size_t inlen, u8 **outbuf,
|
static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf,
|
||||||
size_t *outlen, int invert)
|
size_t *outlen, int invert)
|
||||||
{
|
{
|
||||||
return SC_ERROR_NOT_SUPPORTED;
|
const u8 *in = inbuf;
|
||||||
|
u8 *out;
|
||||||
|
size_t bytes;
|
||||||
|
int skipped = 0;
|
||||||
|
|
||||||
|
bytes = (bits_left + 7)/8 + 1;
|
||||||
|
*outbuf = out = malloc(bytes);
|
||||||
|
if (out == NULL)
|
||||||
|
return SC_ERROR_OUT_OF_MEMORY;
|
||||||
|
*outlen = bytes;
|
||||||
|
out += 1;
|
||||||
|
while (bits_left) {
|
||||||
|
int i, bits_to_go = 8;
|
||||||
|
|
||||||
|
*out = 0;
|
||||||
|
if (bits_left < 8) {
|
||||||
|
bits_to_go = bits_left;
|
||||||
|
skipped = 8 - bits_left;
|
||||||
|
}
|
||||||
|
if (invert) {
|
||||||
|
for (i = 0; i < bits_to_go; i++)
|
||||||
|
*out |= ((*in >> i) & 1) << (7 - i);
|
||||||
|
} else {
|
||||||
|
*out = *in;
|
||||||
|
if (bits_left < 8)
|
||||||
|
return SC_ERROR_NOT_SUPPORTED; /* FIXME */
|
||||||
|
}
|
||||||
|
bits_left -= bits_to_go;
|
||||||
|
}
|
||||||
|
out = *outbuf;
|
||||||
|
out[0] = skipped;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out)
|
int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out)
|
||||||
|
@ -425,18 +474,25 @@ int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out)
|
||||||
|
|
||||||
static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize)
|
static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize)
|
||||||
{
|
{
|
||||||
int i = sizeof(in) * 8;
|
int i = sizeof(in) * 8, skip = 1;
|
||||||
u8 *p;
|
u8 *p, b;
|
||||||
|
|
||||||
*obj = p = malloc(sizeof(in));
|
*obj = p = malloc(sizeof(in));
|
||||||
if (*obj == NULL)
|
if (*obj == NULL)
|
||||||
return SC_ERROR_OUT_OF_MEMORY;
|
return SC_ERROR_OUT_OF_MEMORY;
|
||||||
*objsize = sizeof(in);
|
|
||||||
do {
|
do {
|
||||||
i -= 8;
|
i -= 8;
|
||||||
*p++ = (in >> i) & 0xFF;
|
b = in >> i;
|
||||||
|
if (b == 0 && skip)
|
||||||
|
continue;
|
||||||
|
skip = 0;
|
||||||
|
*p++ = b;
|
||||||
} while (i > 0);
|
} while (i > 0);
|
||||||
|
*objsize = p - *obj;
|
||||||
|
if (*objsize == 0) {
|
||||||
|
*objsize = 1;
|
||||||
|
(*obj)[0] = 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,15 +735,17 @@ static int asn1_encode_p15_object(struct sc_context *ctx, const struct sc_pkcs15
|
||||||
int r;
|
int r;
|
||||||
const struct sc_pkcs15_common_obj_attr *com_attr = obj->com_attr;
|
const struct sc_pkcs15_common_obj_attr *com_attr = obj->com_attr;
|
||||||
struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5];
|
struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5];
|
||||||
size_t flags_len = sizeof(com_attr->flags);
|
size_t flags_len;
|
||||||
size_t label_len = strlen(com_attr->label);
|
size_t label_len = strlen(com_attr->label);
|
||||||
|
|
||||||
sc_copy_asn1_entry(c_asn1_com_obj_attr, asn1_c_attr);
|
sc_copy_asn1_entry(c_asn1_com_obj_attr, asn1_c_attr);
|
||||||
sc_copy_asn1_entry(c_asn1_p15_obj, asn1_p15_obj);
|
sc_copy_asn1_entry(c_asn1_p15_obj, asn1_p15_obj);
|
||||||
if (label_len != 0)
|
if (label_len != 0)
|
||||||
sc_format_asn1_entry(asn1_c_attr + 0, (void *) com_attr->label, &label_len, 1);
|
sc_format_asn1_entry(asn1_c_attr + 0, (void *) com_attr->label, &label_len, 1);
|
||||||
if (com_attr->flags)
|
if (com_attr->flags) {
|
||||||
|
flags_len = sc_count_bit_string_size(&com_attr->flags, sizeof(com_attr->flags));
|
||||||
sc_format_asn1_entry(asn1_c_attr + 1, (void *) &com_attr->flags, &flags_len, 1);
|
sc_format_asn1_entry(asn1_c_attr + 1, (void *) &com_attr->flags, &flags_len, 1);
|
||||||
|
}
|
||||||
if (com_attr->auth_id.len)
|
if (com_attr->auth_id.len)
|
||||||
sc_format_asn1_entry(asn1_c_attr + 2, (void *) &com_attr->auth_id, NULL, 1);
|
sc_format_asn1_entry(asn1_c_attr + 2, (void *) &com_attr->auth_id, NULL, 1);
|
||||||
if (com_attr->user_consent)
|
if (com_attr->user_consent)
|
||||||
|
@ -964,7 +1022,7 @@ static int asn1_encode_entry(struct sc_context *ctx, const struct sc_asn1_entry
|
||||||
case SC_ASN1_BIT_STRING_NI:
|
case SC_ASN1_BIT_STRING_NI:
|
||||||
case SC_ASN1_BIT_STRING:
|
case SC_ASN1_BIT_STRING:
|
||||||
assert(len != NULL);
|
assert(len != NULL);
|
||||||
if (entry->type == SC_ASN1_BIT_STRING_NI)
|
if (entry->type == SC_ASN1_BIT_STRING)
|
||||||
r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1);
|
r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1);
|
||||||
else
|
else
|
||||||
r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0);
|
r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0);
|
||||||
|
@ -1028,7 +1086,6 @@ static int asn1_encode_entry(struct sc_context *ctx, const struct sc_asn1_entry
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int asn1_encode(struct sc_context *ctx, const struct sc_asn1_entry *asn1,
|
static int asn1_encode(struct sc_context *ctx, const struct sc_asn1_entry *asn1,
|
||||||
u8 **ptr, size_t *size, int depth)
|
u8 **ptr, size_t *size, int depth)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include "sc-log.h"
|
#include "sc-log.h"
|
||||||
|
|
||||||
static const char *flex_atrs[] = {
|
static const char *flex_atrs[] = {
|
||||||
"3B:95:94:40:FF:63:01:01:02:01", /* CryptoFlex 16k */
|
"3B:95:94:40:FF:63:01:01:02:01", /* Cryptoflex 16k */
|
||||||
"3B:19:14:55:90:01:02:02:00:05:04:B0",
|
"3B:19:14:55:90:01:02:02:00:05:04:B0",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,7 @@ static const char *flex_atrs[] = {
|
||||||
static struct sc_card_operations flex_ops;
|
static struct sc_card_operations flex_ops;
|
||||||
static const struct sc_card_driver flex_drv = {
|
static const struct sc_card_driver flex_drv = {
|
||||||
NULL,
|
NULL,
|
||||||
"Schlumberger Multiflex/CryptoFlex",
|
"Schlumberger Multiflex/Cryptoflex",
|
||||||
"slb",
|
"slb",
|
||||||
&flex_ops
|
&flex_ops
|
||||||
};
|
};
|
||||||
|
@ -240,7 +240,7 @@ static int flex_select_file(struct sc_card *card, const struct sc_path *path,
|
||||||
return SC_ERROR_UNKNOWN_REPLY;
|
return SC_ERROR_UNKNOWN_REPLY;
|
||||||
|
|
||||||
if (apdu.resp[0] == 0x6F) {
|
if (apdu.resp[0] == 0x6F) {
|
||||||
error(card->ctx, "unsupported: Multiflex returned FCI\n");
|
error(card->ctx, "unsupported: card returned FCI\n");
|
||||||
return SC_ERROR_UNKNOWN_REPLY; /* FIXME */
|
return SC_ERROR_UNKNOWN_REPLY; /* FIXME */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +284,146 @@ static int flex_list_files(struct sc_card *card, u8 *buf, size_t buflen)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int flex_delete_file(struct sc_card *card, const struct sc_path *path)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 sbuf[2];
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) {
|
||||||
|
error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n");
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||||
|
}
|
||||||
|
sbuf[0] = path->value[0];
|
||||||
|
sbuf[1] = path->value[1];
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
|
||||||
|
apdu.cla = 0xF0; /* Override CLA byte */
|
||||||
|
apdu.lc = 2;
|
||||||
|
apdu.datalen = 2;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acl_to_ac(unsigned int acl)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int acl_table[16] = {
|
||||||
|
/* 0 */ SC_AC_NONE, SC_AC_CHV1, SC_AC_CHV2, SC_AC_PRO,
|
||||||
|
/* 4 */ SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_CHV1 | SC_AC_PRO,
|
||||||
|
/* 7 */ SC_AC_CHV2 | SC_AC_PRO, SC_AC_CHV1 | SC_AC_AUT,
|
||||||
|
/* 9 */ SC_AC_CHV2 | SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_UNKNOWN,
|
||||||
|
/* c */ SC_AC_UNKNOWN, SC_AC_UNKNOWN, SC_AC_UNKNOWN,
|
||||||
|
/* f */ SC_AC_NEVER };
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(acl_table)/sizeof(acl_table[0]); i++)
|
||||||
|
if (acl == acl_table[i])
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_file_structure(struct sc_card *card, const struct sc_file *file,
|
||||||
|
u8 *buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
u8 *p = buf;
|
||||||
|
int r, r2;
|
||||||
|
|
||||||
|
p[0] = 0xFF;
|
||||||
|
p[1] = 0xFF;
|
||||||
|
p[2] = file->size >> 8;
|
||||||
|
p[3] = file->size & 0xFF;
|
||||||
|
p[4] = file->id >> 8;
|
||||||
|
p[5] = file->id & 0xFF;
|
||||||
|
if (file->type == SC_FILE_TYPE_DF)
|
||||||
|
p[6] = 0x38;
|
||||||
|
else
|
||||||
|
switch (file->ef_structure) {
|
||||||
|
case SC_FILE_EF_TRANSPARENT:
|
||||||
|
p[6] = 0x01;
|
||||||
|
break;
|
||||||
|
case SC_FILE_EF_LINEAR_FIXED:
|
||||||
|
p[6] = 0x02;
|
||||||
|
break;
|
||||||
|
case SC_FILE_EF_LINEAR_VARIABLE:
|
||||||
|
p[6] = 0x04;
|
||||||
|
break;
|
||||||
|
case SC_FILE_EF_CYCLIC:
|
||||||
|
p[6] = 0x06;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p[7] = 0xFF; /* allow Decrease and Increase */
|
||||||
|
if (file->type == SC_FILE_TYPE_DF) {
|
||||||
|
r = acl_to_ac(file->acl[SC_AC_OP_LIST_FILES]);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Invalid ACL value");
|
||||||
|
p[8] = (r & 0x0F) << 8;
|
||||||
|
r = acl_to_ac(file->acl[SC_AC_OP_DELETE]);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Invalid ACL value");
|
||||||
|
r2 = acl_to_ac(file->acl[SC_AC_OP_CREATE]);
|
||||||
|
SC_TEST_RET(card->ctx, r2, "Invalid ACL value");
|
||||||
|
p[9] = ((r & 0x0F) << 8) | (r2 & 0x0F);
|
||||||
|
p[10] = 0;
|
||||||
|
} else {
|
||||||
|
r = acl_to_ac(file->acl[SC_AC_OP_READ]);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Invalid ACL value");
|
||||||
|
r2 = acl_to_ac(file->acl[SC_AC_OP_UPDATE]);
|
||||||
|
SC_TEST_RET(card->ctx, r2, "Invalid ACL value");
|
||||||
|
p[8] = ((r & 0x0F) << 8) | (r2 & 0x0F);
|
||||||
|
p[9] = 0; /* FIXME */
|
||||||
|
r = acl_to_ac(file->acl[SC_AC_OP_INVALIDATE]);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Invalid ACL value");
|
||||||
|
r2 = acl_to_ac(file->acl[SC_AC_OP_REHABILITATE]);
|
||||||
|
SC_TEST_RET(card->ctx, r2, "Invalid ACL value");
|
||||||
|
p[10] = ((r & 0x0F) << 8) | (r2 & 0x0F);
|
||||||
|
}
|
||||||
|
p[11] = (file->status & SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01;
|
||||||
|
if (file->type != SC_FILE_TYPE_DF &&
|
||||||
|
(file->ef_structure == SC_FILE_EF_LINEAR_FIXED ||
|
||||||
|
file->ef_structure == SC_FILE_EF_CYCLIC))
|
||||||
|
p[12] = 0x04;
|
||||||
|
else
|
||||||
|
p[12] = 0x03;
|
||||||
|
p[13] = p[14] = p[15] = 0; /* FIXME */
|
||||||
|
if (p[12] == 0x04) {
|
||||||
|
p[16] = file->record_length;
|
||||||
|
*buflen = 17;
|
||||||
|
} else
|
||||||
|
*buflen = 16;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flex_create_file(struct sc_card *card, const struct sc_file *file)
|
||||||
|
{
|
||||||
|
u8 sbuf[18];
|
||||||
|
size_t sendlen;
|
||||||
|
int r, rec_nr;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
r = encode_file_structure(card, file, sbuf, &sendlen);
|
||||||
|
if (r) {
|
||||||
|
error(card->ctx, "File structure encoding failed.\n");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT)
|
||||||
|
rec_nr = file->record_count;
|
||||||
|
else
|
||||||
|
rec_nr = 0;
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr);
|
||||||
|
apdu.cla = 0xF0;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = sendlen;
|
||||||
|
apdu.lc = sendlen;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct sc_card_driver * sc_get_driver(void)
|
static const struct sc_card_driver * sc_get_driver(void)
|
||||||
{
|
{
|
||||||
const struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
const struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||||
|
@ -294,6 +434,8 @@ static const struct sc_card_driver * sc_get_driver(void)
|
||||||
flex_ops.finish = flex_finish;
|
flex_ops.finish = flex_finish;
|
||||||
flex_ops.select_file = flex_select_file;
|
flex_ops.select_file = flex_select_file;
|
||||||
flex_ops.list_files = flex_list_files;
|
flex_ops.list_files = flex_list_files;
|
||||||
|
flex_ops.delete_file = flex_delete_file;
|
||||||
|
flex_ops.create_file = flex_create_file;
|
||||||
|
|
||||||
return &flex_drv;
|
return &flex_drv;
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,6 +620,45 @@ int sc_write_binary(struct sc_card *card, unsigned int idx,
|
||||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sc_update_binary(struct sc_card *card, unsigned int idx,
|
||||||
|
const u8 *buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(card != NULL && card->ops != NULL && buf != NULL);
|
||||||
|
if (card->ctx->debug >= 2)
|
||||||
|
debug(card->ctx, "sc_update_binary: %d bytes at index %d\n", count, idx);
|
||||||
|
if (card->ops->update_binary == NULL)
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
|
||||||
|
if (count > SC_APDU_CHOP_SIZE && !(card->caps & SC_CARD_CAP_APDU_EXT)) {
|
||||||
|
int bytes_written = 0;
|
||||||
|
const u8 *p = buf;
|
||||||
|
|
||||||
|
r = sc_lock(card);
|
||||||
|
SC_TEST_RET(card->ctx, r, "sc_lock() failed");
|
||||||
|
while (count > 0) {
|
||||||
|
int n = count > SC_APDU_CHOP_SIZE ? SC_APDU_CHOP_SIZE : count;
|
||||||
|
r = sc_update_binary(card, idx, p, n, flags);
|
||||||
|
if (r < 0) {
|
||||||
|
sc_unlock(card);
|
||||||
|
SC_TEST_RET(card->ctx, r, "sc_read_binary() failed");
|
||||||
|
}
|
||||||
|
p += r;
|
||||||
|
idx += r;
|
||||||
|
bytes_written += r;
|
||||||
|
count -= r;
|
||||||
|
if (r == 0) {
|
||||||
|
sc_unlock(card);
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, bytes_written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sc_unlock(card);
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, bytes_written);
|
||||||
|
}
|
||||||
|
r = card->ops->update_binary(card, idx, buf, count, flags);
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||||
|
}
|
||||||
|
|
||||||
int sc_select_file(struct sc_card *card,
|
int sc_select_file(struct sc_card *card,
|
||||||
const struct sc_path *in_path,
|
const struct sc_path *in_path,
|
||||||
struct sc_file *file)
|
struct sc_file *file)
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
/* Internal use only */
|
/* Internal use only */
|
||||||
int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2);
|
int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2);
|
||||||
|
size_t sc_count_bit_string_size(const void * buf, size_t bufsize);
|
||||||
|
|
||||||
/* Default timeout value for SCardGetStatusChange */
|
/* Default timeout value for SCardGetStatusChange */
|
||||||
#ifndef SC_CUSTOM_STATUS_TIMEOUT
|
#ifndef SC_CUSTOM_STATUS_TIMEOUT
|
||||||
|
|
|
@ -98,6 +98,30 @@ static int iso7816_write_binary(struct sc_card *card,
|
||||||
SC_FUNC_RETURN(card->ctx, 3, count);
|
SC_FUNC_RETURN(card->ctx, 3, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int iso7816_update_binary(struct sc_card *card,
|
||||||
|
unsigned int idx, const u8 *buf,
|
||||||
|
size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (count > SC_APDU_CHOP_SIZE) {
|
||||||
|
error(card->ctx, "Too large buffer supplied\n");
|
||||||
|
return SC_ERROR_CMD_TOO_LONG;
|
||||||
|
}
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6,
|
||||||
|
(idx >> 8) & 0x7F, idx & 0xFF);
|
||||||
|
apdu.lc = count;
|
||||||
|
apdu.datalen = count;
|
||||||
|
apdu.data = buf;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
SC_TEST_RET(card->ctx, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2),
|
||||||
|
"Card returned error");
|
||||||
|
SC_FUNC_RETURN(card->ctx, 3, count);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int byte_to_acl(u8 byte)
|
static unsigned int byte_to_acl(u8 byte)
|
||||||
{
|
{
|
||||||
switch (byte >> 4) {
|
switch (byte >> 4) {
|
||||||
|
@ -538,6 +562,7 @@ const struct sc_card_driver * sc_get_iso7816_driver(void)
|
||||||
iso_ops.read_binary = iso7816_read_binary;
|
iso_ops.read_binary = iso7816_read_binary;
|
||||||
iso_ops.read_record = iso7816_read_record;
|
iso_ops.read_record = iso7816_read_record;
|
||||||
iso_ops.write_binary = iso7816_write_binary;
|
iso_ops.write_binary = iso7816_write_binary;
|
||||||
|
iso_ops.update_binary = iso7816_update_binary;
|
||||||
iso_ops.select_file = iso7816_select_file;
|
iso_ops.select_file = iso7816_select_file;
|
||||||
iso_ops.get_challenge = iso7816_get_challenge;
|
iso_ops.get_challenge = iso7816_get_challenge;
|
||||||
iso_ops.create_file = iso7816_create_file;
|
iso_ops.create_file = iso7816_create_file;
|
||||||
|
|
|
@ -34,10 +34,7 @@ extern "C" {
|
||||||
#define SC_PKCS15_MAX_PRKEYS 2
|
#define SC_PKCS15_MAX_PRKEYS 2
|
||||||
#define SC_PKCS15_MAX_LABEL_SIZE 32
|
#define SC_PKCS15_MAX_LABEL_SIZE 32
|
||||||
#define SC_PKCS15_MAX_ID_SIZE 16
|
#define SC_PKCS15_MAX_ID_SIZE 16
|
||||||
#define SC_PKCS15_MAX_CDFS 4 /* Certificate Directory
|
#define SC_PKCS15_MAX_DFS 4
|
||||||
* Files */
|
|
||||||
#define SC_PKCS15_MAX_AODFS 4 /* Authentication Object
|
|
||||||
* Directory Files */
|
|
||||||
#define SC_PKCS15_MAX_CERTS 4 /* Total certificates */
|
#define SC_PKCS15_MAX_CERTS 4 /* Total certificates */
|
||||||
|
|
||||||
struct sc_pkcs15_id {
|
struct sc_pkcs15_id {
|
||||||
|
@ -45,6 +42,8 @@ struct sc_pkcs15_id {
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SC_PKCS15_CO_FLAG_OBJECT_SEEN 0x80000000 /* for PKCS #11 module */
|
||||||
|
|
||||||
struct sc_pkcs15_common_obj_attr {
|
struct sc_pkcs15_common_obj_attr {
|
||||||
char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */
|
char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */
|
||||||
int flags;
|
int flags;
|
||||||
|
@ -137,14 +136,33 @@ struct sc_pkcs15_prkey_info {
|
||||||
int modulus_length;
|
int modulus_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SC_PKCS15_PRKDF 0
|
||||||
|
#define SC_PKCS15_PUKDF 1
|
||||||
|
#define SC_PKCS15_PUKDF_TRUSTED 2
|
||||||
|
#define SC_PKCS15_SKDF 3
|
||||||
|
#define SC_PKCS15_CDF 4
|
||||||
|
#define SC_PKCS15_CDF_TRUSTED 5
|
||||||
|
#define SC_PKCS15_CDF_USEFUL 6
|
||||||
|
#define SC_PKCS15_DODF 7
|
||||||
|
#define SC_PKCS15_AODF 8
|
||||||
|
#define SC_PKCS15_DF_TYPE_COUNT 9
|
||||||
|
|
||||||
|
|
||||||
|
struct sc_pkcs15_df {
|
||||||
|
struct sc_file *file[SC_PKCS15_MAX_DFS];
|
||||||
|
int count, record_length;
|
||||||
|
};
|
||||||
|
|
||||||
struct sc_pkcs15_card {
|
struct sc_pkcs15_card {
|
||||||
struct sc_card *card;
|
struct sc_card *card;
|
||||||
char *label;
|
char *label;
|
||||||
/* fields from TokenInfo: */
|
/* fields from TokenInfo: */
|
||||||
int version;
|
int version;
|
||||||
char *serial_number, *manufacturer_id;
|
char *serial_number, *manufacturer_id;
|
||||||
int flags;
|
unsigned long flags;
|
||||||
struct sc_pkcs15_algorithm_info alg_info[1];
|
struct sc_pkcs15_algorithm_info alg_info[1];
|
||||||
|
/* FIXME: this could be done better with some C pre-processor
|
||||||
|
* magic */
|
||||||
struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS];
|
struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS];
|
||||||
int cert_count;
|
int cert_count;
|
||||||
struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS];
|
struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS];
|
||||||
|
@ -152,15 +170,10 @@ struct sc_pkcs15_card {
|
||||||
struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS];
|
struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS];
|
||||||
int pin_count;
|
int pin_count;
|
||||||
|
|
||||||
|
/* FIXME: Move file_dir somewhere else, perhaps to sc_card */
|
||||||
struct sc_file file_dir, file_app;
|
struct sc_file file_dir, file_app;
|
||||||
/* in app DF */
|
|
||||||
struct sc_file file_tokeninfo, file_odf;
|
struct sc_file file_tokeninfo, file_odf;
|
||||||
struct sc_file file_prkdf;
|
struct sc_pkcs15_df df[SC_PKCS15_DF_TYPE_COUNT];
|
||||||
struct sc_file file_cdf[SC_PKCS15_MAX_CDFS];
|
|
||||||
int cdf_count;
|
|
||||||
struct sc_file file_aodf[SC_PKCS15_MAX_AODFS];
|
|
||||||
int aodf_count;
|
|
||||||
struct sc_file file_dodf;
|
|
||||||
|
|
||||||
int use_cache;
|
int use_cache;
|
||||||
};
|
};
|
||||||
|
|
|
@ -182,6 +182,9 @@ struct sc_file {
|
||||||
int status; /* Status flags */
|
int status; /* Status flags */
|
||||||
unsigned int acl[SC_MAX_AC_OPS]; /* Access Control List */
|
unsigned int acl[SC_MAX_AC_OPS]; /* Access Control List */
|
||||||
|
|
||||||
|
int record_length; /* In case of fixed-length or cyclic EF */
|
||||||
|
int record_count; /* Valid, if not transparent EF or DF */
|
||||||
|
|
||||||
u8 sec_attr[SC_MAX_SEC_ATTR_SIZE];
|
u8 sec_attr[SC_MAX_SEC_ATTR_SIZE];
|
||||||
size_t sec_attr_len;
|
size_t sec_attr_len;
|
||||||
u8 prop_attr[SC_MAX_PROP_ATTR_SIZE];
|
u8 prop_attr[SC_MAX_PROP_ATTR_SIZE];
|
||||||
|
@ -458,6 +461,8 @@ int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf,
|
||||||
size_t count, unsigned long flags);
|
size_t count, unsigned long flags);
|
||||||
int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf,
|
int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf,
|
||||||
size_t count, unsigned long flags);
|
size_t count, unsigned long flags);
|
||||||
|
int sc_update_binary(struct sc_card *card, unsigned int idx, const u8 * buf,
|
||||||
|
size_t count, unsigned long flags);
|
||||||
int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf,
|
int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf,
|
||||||
size_t count, unsigned long flags);
|
size_t count, unsigned long flags);
|
||||||
int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len);
|
int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len);
|
||||||
|
|
|
@ -389,7 +389,7 @@ int sc_pkcs15_create_cdf(struct sc_pkcs15_card *p15card,
|
||||||
bufsize += tmpsize;
|
bufsize += tmpsize;
|
||||||
}
|
}
|
||||||
sc_hex_dump(p15card->card->ctx, buf, bufsize, str, sizeof(str));
|
sc_hex_dump(p15card->card->ctx, buf, bufsize, str, sizeof(str));
|
||||||
printf("\n%s\n", str);
|
printf("CDF:\n%s\n", str);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,15 +445,26 @@ static int get_certs_from_file(struct sc_pkcs15_card *card,
|
||||||
|
|
||||||
int sc_pkcs15_enum_certificates(struct sc_pkcs15_card *card)
|
int sc_pkcs15_enum_certificates(struct sc_pkcs15_card *card)
|
||||||
{
|
{
|
||||||
int r = 0, i;
|
int r = 0, i, j, type;
|
||||||
|
const int df_types[] = {
|
||||||
|
SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_CDF_USEFUL
|
||||||
|
};
|
||||||
|
const int nr_types = sizeof(df_types)/sizeof(df_types[0]);
|
||||||
|
|
||||||
assert(card != NULL);
|
assert(card != NULL);
|
||||||
|
|
||||||
if (card->cert_count)
|
if (card->cert_count)
|
||||||
return card->cert_count; /* already enumerated */
|
return card->cert_count; /* already enumerated */
|
||||||
r = sc_lock(card->card);
|
r = sc_lock(card->card);
|
||||||
SC_TEST_RET(card->card->ctx, r, "sc_lock() failed");
|
SC_TEST_RET(card->card->ctx, r, "sc_lock() failed");
|
||||||
for (i = 0; i < card->cdf_count; i++) {
|
for (j = 0; j < nr_types; j++) {
|
||||||
r = get_certs_from_file(card, &card->file_cdf[i]);
|
type = df_types[j];
|
||||||
|
|
||||||
|
for (i = 0; i < card->df[type].count; i++) {
|
||||||
|
r = get_certs_from_file(card, card->df[type].file[i]);
|
||||||
|
if (r != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,8 +133,12 @@ static int get_pins_from_file(struct sc_pkcs15_card *card,
|
||||||
|
|
||||||
int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card)
|
int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card)
|
||||||
{
|
{
|
||||||
int r, i;
|
int r, i, j;
|
||||||
struct sc_context *ctx = p15card->card->ctx;
|
struct sc_context *ctx = p15card->card->ctx;
|
||||||
|
const int df_types[] = {
|
||||||
|
SC_PKCS15_AODF
|
||||||
|
};
|
||||||
|
const int nr_types = sizeof(df_types)/sizeof(df_types[0]);
|
||||||
|
|
||||||
assert(p15card != NULL);
|
assert(p15card != NULL);
|
||||||
SC_FUNC_CALLED(ctx, 1);
|
SC_FUNC_CALLED(ctx, 1);
|
||||||
|
@ -149,10 +153,11 @@ int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card)
|
||||||
p15card->pin_count = 0;
|
p15card->pin_count = 0;
|
||||||
r = sc_lock(p15card->card);
|
r = sc_lock(p15card->card);
|
||||||
SC_TEST_RET(p15card->card->ctx, r, "sc_lock() failed");
|
SC_TEST_RET(p15card->card->ctx, r, "sc_lock() failed");
|
||||||
for (i = 0; i < p15card->aodf_count; i++) {
|
for (j = 0; r == 0 && j < nr_types; j++) {
|
||||||
r = get_pins_from_file(p15card, &p15card->file_aodf[i]);
|
int type = df_types[j];
|
||||||
if (r != 0)
|
|
||||||
break;
|
for (i = 0; r == 0 && i < p15card->df[type].count; i++)
|
||||||
|
r = get_pins_from_file(p15card, p15card->df[type].file[i]);
|
||||||
}
|
}
|
||||||
sc_unlock(p15card->card);
|
sc_unlock(p15card->card);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
|
|
|
@ -130,17 +130,24 @@ static int get_prkeys_from_file(struct sc_pkcs15_card *card,
|
||||||
|
|
||||||
int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card)
|
int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card)
|
||||||
{
|
{
|
||||||
int r, i;
|
int r, i, j;
|
||||||
assert(card != NULL);
|
struct sc_context *ctx = card->card->ctx;
|
||||||
|
const int df_types[] = {
|
||||||
|
SC_PKCS15_PRKDF
|
||||||
|
};
|
||||||
|
const int nr_types = sizeof(df_types)/sizeof(df_types[0]);
|
||||||
|
|
||||||
|
assert(card != NULL);
|
||||||
|
SC_FUNC_CALLED(ctx, 1);
|
||||||
if (card->prkey_count)
|
if (card->prkey_count)
|
||||||
return card->prkey_count; /* already enumerated */
|
return card->prkey_count; /* already enumerated */
|
||||||
r = sc_lock(card->card);
|
r = sc_lock(card->card);
|
||||||
SC_TEST_RET(card->card->ctx, r, "sc_lock() failed");
|
SC_TEST_RET(card->card->ctx, r, "sc_lock() failed");
|
||||||
for (i = 0; i < 1; i++) {
|
for (j = 0; r == 0 && j < nr_types; j++) {
|
||||||
r = get_prkeys_from_file(card, &card->file_prkdf);
|
int type = df_types[j];
|
||||||
if (r != 0)
|
|
||||||
break;
|
for (i = 0; r == 0 && i < card->df[type].count; i++)
|
||||||
|
r = get_prkeys_from_file(card, card->df[type].file[i]);
|
||||||
}
|
}
|
||||||
sc_unlock(card->card);
|
sc_unlock(card->card);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
|
|
|
@ -55,6 +55,23 @@ void sc_pkcs15_print_card(const struct sc_pkcs15_card *card)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct sc_asn1_entry c_asn1_toki[] = {
|
||||||
|
{ "version", SC_ASN1_INTEGER, ASN1_INTEGER, 0, NULL },
|
||||||
|
{ "serialNumber", SC_ASN1_OCTET_STRING, ASN1_OCTET_STRING, 0, NULL },
|
||||||
|
{ "manufacturerID", SC_ASN1_UTF8STRING, ASN1_UTF8STRING, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "tokenflags", SC_ASN1_BIT_STRING, ASN1_BIT_STRING, 0, NULL },
|
||||||
|
{ "seInfo", SC_ASN1_SEQUENCE, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sc_asn1_entry c_asn1_tokeninfo[] = {
|
||||||
|
{ "TokenInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | ASN1_SEQUENCE, 0, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen)
|
void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen)
|
||||||
{
|
{
|
||||||
int i, r;
|
int i, r;
|
||||||
|
@ -62,25 +79,20 @@ void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen)
|
||||||
int serial_len = sizeof(serial);
|
int serial_len = sizeof(serial);
|
||||||
u8 mnfid[128];
|
u8 mnfid[128];
|
||||||
int mnfid_len = sizeof(mnfid);
|
int mnfid_len = sizeof(mnfid);
|
||||||
|
u8 label[128];
|
||||||
|
int label_len = sizeof(label);
|
||||||
int flags_len = sizeof(card->flags);
|
int flags_len = sizeof(card->flags);
|
||||||
|
struct sc_asn1_entry asn1_toki[9], asn1_tokeninfo[2];
|
||||||
|
|
||||||
struct sc_asn1_entry asn1_tokeninfo[] = {
|
sc_copy_asn1_entry(c_asn1_toki, asn1_toki);
|
||||||
{ "version", SC_ASN1_INTEGER, ASN1_INTEGER, 0, &card->version },
|
sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo);
|
||||||
{ "serialNumber", SC_ASN1_OCTET_STRING, ASN1_OCTET_STRING, 0, serial, &serial_len },
|
sc_format_asn1_entry(asn1_toki + 0, &card->version, NULL, 0);
|
||||||
{ "manufacturerID", SC_ASN1_UTF8STRING, ASN1_UTF8STRING, SC_ASN1_OPTIONAL, mnfid, &mnfid_len },
|
sc_format_asn1_entry(asn1_toki + 1, serial, &serial_len, 0);
|
||||||
{ "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL },
|
sc_format_asn1_entry(asn1_toki + 2, mnfid, &mnfid_len, 0);
|
||||||
{ "tokenflags", SC_ASN1_BIT_STRING, ASN1_BIT_STRING, 0, &card->flags, &flags_len },
|
sc_format_asn1_entry(asn1_toki + 3, label, &label_len, 0);
|
||||||
{ "seInfo", SC_ASN1_SEQUENCE, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL },
|
sc_format_asn1_entry(asn1_toki + 4, &card->flags, &flags_len, 0);
|
||||||
{ "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL },
|
sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 0);
|
||||||
{ "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL },
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
buf = sc_asn1_verify_tag(card->card->ctx, buf, buflen, SC_ASN1_CONS | ASN1_SEQUENCE, &buflen);
|
|
||||||
if (buf == NULL) {
|
|
||||||
error(card->card->ctx, "invalid EF(TokenInfo)\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
r = sc_asn1_decode(card->card->ctx, asn1_tokeninfo, buf, buflen, NULL, NULL);
|
r = sc_asn1_decode(card->card->ctx, asn1_tokeninfo, buf, buflen, NULL, NULL);
|
||||||
if (r) {
|
if (r) {
|
||||||
error(card->card->ctx, "ASN.1 parsing failed: %s\n", sc_strerror(r));
|
error(card->card->ctx, "ASN.1 parsing failed: %s\n", sc_strerror(r));
|
||||||
|
@ -101,6 +113,12 @@ void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen)
|
||||||
else
|
else
|
||||||
card->manufacturer_id = strdup("(unknown)");
|
card->manufacturer_id = strdup("(unknown)");
|
||||||
}
|
}
|
||||||
|
if (card->label == NULL) {
|
||||||
|
if (asn1_tokeninfo[2].flags & SC_ASN1_PRESENT)
|
||||||
|
card->manufacturer_id = strdup((char *) mnfid);
|
||||||
|
else
|
||||||
|
card->manufacturer_id = strdup("(unknown)");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
if (card->serial_number == NULL)
|
if (card->serial_number == NULL)
|
||||||
|
@ -110,35 +128,115 @@ err:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int encode_tokeninfo(struct sc_pkcs15_card *card, u8 ** buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
u8 serial[128];
|
||||||
|
int serial_len = 0;
|
||||||
|
int mnfid_len;
|
||||||
|
int label_len;
|
||||||
|
int flags_len;
|
||||||
|
int version = card->version;
|
||||||
|
|
||||||
|
struct sc_asn1_entry asn1_toki[9], asn1_tokeninfo[2];
|
||||||
|
|
||||||
|
sc_copy_asn1_entry(c_asn1_toki, asn1_toki);
|
||||||
|
sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo);
|
||||||
|
version--;
|
||||||
|
sc_format_asn1_entry(asn1_toki + 0, &version, NULL, 1);
|
||||||
|
if (card->serial_number != NULL) {
|
||||||
|
if (strlen(card->serial_number)/2 > sizeof(serial))
|
||||||
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
||||||
|
for (i = 0; card->serial_number[i] != 0; i += 2) {
|
||||||
|
int c;
|
||||||
|
if (sscanf(&card->serial_number[i], "%02X", &c) != 1)
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
serial[i/2] = c & 0xFF;
|
||||||
|
serial_len++;
|
||||||
|
}
|
||||||
|
sc_format_asn1_entry(asn1_toki + 1, serial, &serial_len, 1);
|
||||||
|
}
|
||||||
|
if (card->manufacturer_id != NULL) {
|
||||||
|
mnfid_len = strlen(card->manufacturer_id);
|
||||||
|
sc_format_asn1_entry(asn1_toki + 2, card->manufacturer_id, &mnfid_len, 1);
|
||||||
|
}
|
||||||
|
if (card->label != NULL) {
|
||||||
|
label_len = strlen(card->label);
|
||||||
|
sc_format_asn1_entry(asn1_toki + 3, card->label, &label_len, 1);
|
||||||
|
}
|
||||||
|
if (card->flags) {
|
||||||
|
flags_len = sc_count_bit_string_size(&card->flags, sizeof(card->flags));
|
||||||
|
sc_format_asn1_entry(asn1_toki + 4, &card->flags, &flags_len, 1);
|
||||||
|
}
|
||||||
|
sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 1);
|
||||||
|
|
||||||
|
r = sc_asn1_encode(card->card->ctx, asn1_tokeninfo, buf, buflen);
|
||||||
|
if (r) {
|
||||||
|
error(card->card->ctx, "sc_asn1_encode() failed: %s\n", sc_strerror(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sc_pkcs15_create_tokeninfo(struct sc_pkcs15_card *card)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 *buf;
|
||||||
|
size_t buflen;
|
||||||
|
u8 line[10240];
|
||||||
|
|
||||||
|
r = encode_tokeninfo(card, &buf, &buflen);
|
||||||
|
if (r) {
|
||||||
|
error(card->card->ctx, "Error encoding EF(TokenInfo): %s\n", sc_strerror(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
sc_hex_dump(card->card->ctx, buf, buflen, line, sizeof(line));
|
||||||
|
printf("%s\n", line);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sc_asn1_entry c_asn1_ddo[] = {
|
||||||
|
{ "oid", SC_ASN1_OBJECT, ASN1_OBJECT, 0, NULL },
|
||||||
|
{ "odfPath", SC_ASN1_PATH, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "tokenInfoPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "unusedPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
static const struct sc_asn1_entry c_asn1_dirrecord[] = {
|
||||||
|
{ "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, NULL },
|
||||||
|
{ "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, 0, NULL },
|
||||||
|
{ "ddo", SC_ASN1_STRUCT, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
/* FIXME: this should be decoded elsewhere */
|
||||||
|
static const struct sc_asn1_entry c_asn1_dir[] = {
|
||||||
|
{ "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 *aidref = (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15";
|
||||||
|
static const int aidref_len = 12;
|
||||||
|
|
||||||
static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card)
|
static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card)
|
||||||
{
|
{
|
||||||
const u8 *aidref = (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15";
|
struct sc_asn1_entry asn1_ddo[5], asn1_dirrecord[5], asn1_dir[2];
|
||||||
const int aidref_len = 12;
|
|
||||||
int r;
|
int r;
|
||||||
u8 aid[128], label[128], path[128];
|
u8 aid[128], label[128], path[128];
|
||||||
int aid_len = sizeof(aid), label_len = sizeof(label),
|
int aid_len = sizeof(aid), label_len = sizeof(label),
|
||||||
path_len = sizeof(path);
|
path_len = sizeof(path);
|
||||||
|
|
||||||
struct sc_asn1_entry asn1_ddo[] = {
|
sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo);
|
||||||
{ "oid", SC_ASN1_OBJECT, ASN1_OBJECT, 0, NULL },
|
sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
|
||||||
{ "odfPath", SC_ASN1_PATH, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, &card->file_odf.path },
|
sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
|
||||||
{ "tokenInfoPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, &card->file_tokeninfo.path },
|
sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 0);
|
||||||
{ "unusedPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL },
|
sc_format_asn1_entry(asn1_dirrecord + 0, aid, &aid_len, 0);
|
||||||
{ NULL }
|
sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0);
|
||||||
};
|
sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0);
|
||||||
struct sc_asn1_entry asn1_dir[] = {
|
sc_format_asn1_entry(asn1_dirrecord + 3, asn1_ddo, NULL, 0);
|
||||||
{ "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, aid, &aid_len },
|
sc_format_asn1_entry(asn1_ddo + 1, &card->file_odf.path, NULL, 0);
|
||||||
{ "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, label, &label_len },
|
sc_format_asn1_entry(asn1_ddo + 2, &card->file_tokeninfo.path, NULL, 0);
|
||||||
{ "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, 0, path, &path_len },
|
|
||||||
{ "ddo", SC_ASN1_STRUCT, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_ddo },
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
buf = sc_asn1_verify_tag(card->card->ctx, buf, buflen, SC_ASN1_APP | 1 | SC_ASN1_CONS, &buflen);
|
|
||||||
if (buf == NULL) {
|
|
||||||
error(card->card->ctx, "No [APPLICATION 1] tag in EF(DIR)\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
r = sc_asn1_decode(card->card->ctx, asn1_dir, buf, buflen, NULL, NULL);
|
r = sc_asn1_decode(card->card->ctx, asn1_dir, buf, buflen, NULL, NULL);
|
||||||
if (r) {
|
if (r) {
|
||||||
error(card->card->ctx, "EF(DIR) parsing failed: %s\n",
|
error(card->card->ctx, "EF(DIR) parsing failed: %s\n",
|
||||||
|
@ -149,7 +247,7 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card)
|
||||||
error(card->card->ctx, "AID in EF(DIR) is invalid\n");
|
error(card->card->ctx, "AID in EF(DIR) is invalid\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (asn1_dir[1].flags & SC_ASN1_PRESENT)
|
if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT)
|
||||||
card->label = strdup((char *) label);
|
card->label = strdup((char *) label);
|
||||||
else
|
else
|
||||||
card->label = strdup("(unknown)");
|
card->label = strdup("(unknown)");
|
||||||
|
@ -162,61 +260,193 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int encode_dir(struct sc_pkcs15_card *card, u8 **buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
struct sc_asn1_entry asn1_ddo[5], asn1_dirrecord[5], asn1_dir[2];
|
||||||
|
struct sc_context *ctx = card->card->ctx;
|
||||||
|
int r;
|
||||||
|
size_t label_len;
|
||||||
|
|
||||||
|
sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo);
|
||||||
|
sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
|
||||||
|
sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
|
||||||
|
sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 1);
|
||||||
|
sc_format_asn1_entry(asn1_dirrecord + 0, (void *) aidref, (void *) &aidref_len, 1);
|
||||||
|
if (card->label != NULL) {
|
||||||
|
label_len = strlen(card->label);
|
||||||
|
sc_format_asn1_entry(asn1_dirrecord + 1, card->label, &label_len, 1);
|
||||||
|
}
|
||||||
|
if (card->file_app.path.len == 0) {
|
||||||
|
error(ctx, "Application path not set.\n");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
sc_format_asn1_entry(asn1_dirrecord + 2, card->file_app.path.value,
|
||||||
|
&card->file_app.path.len, 1);
|
||||||
|
#if 0
|
||||||
|
/* FIXME: encode DDO */
|
||||||
|
sc_format_asn1_entry(asn1_dirrecord + 3, asn1_ddo, NULL, 0);
|
||||||
|
sc_format_asn1_entry(asn1_ddo + 1, &card->file_odf.path, NULL, 0);
|
||||||
|
sc_format_asn1_entry(asn1_ddo + 2, &card->file_tokeninfo.path, NULL, 0);
|
||||||
|
#endif
|
||||||
|
r = sc_asn1_encode(ctx, asn1_dir, buf, buflen);
|
||||||
|
if (r) {
|
||||||
|
error(card->card->ctx, "sc_asn1_encode() failed: %s\n",
|
||||||
|
sc_strerror(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME: This should be done using sc_update_binary(),
|
||||||
|
* and be generally wiser */
|
||||||
|
int sc_pkcs15_create_dir(struct sc_pkcs15_card *p15card)
|
||||||
|
{
|
||||||
|
struct sc_card *card = p15card->card;
|
||||||
|
struct sc_path path;
|
||||||
|
u8 *buf;
|
||||||
|
size_t bufsize;
|
||||||
|
int r;
|
||||||
|
u8 line[10240];
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
sc_format_path("3F00", &path);
|
||||||
|
r = sc_select_file(card, &path, NULL);
|
||||||
|
SC_TEST_RET(card->ctx, r, "sc_select_file(MF) failed");
|
||||||
|
r = encode_dir(p15card, &buf, &bufsize);
|
||||||
|
SC_TEST_RET(card->ctx, r, "EF(DIR) encoding failed");
|
||||||
|
sc_hex_dump(p15card->card->ctx, buf, bufsize, line, sizeof(line));
|
||||||
|
free(buf);
|
||||||
|
printf("%s", line);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sc_asn1_entry c_asn1_odf[] = {
|
||||||
|
{ "privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ "certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ "trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ "dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ "authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int odf_indexes[] = {
|
||||||
|
SC_PKCS15_PRKDF,
|
||||||
|
SC_PKCS15_CDF,
|
||||||
|
SC_PKCS15_CDF_TRUSTED,
|
||||||
|
SC_PKCS15_DODF,
|
||||||
|
SC_PKCS15_AODF,
|
||||||
|
};
|
||||||
|
|
||||||
static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card)
|
static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card)
|
||||||
{
|
{
|
||||||
const u8 *p = buf;
|
const u8 *p = buf;
|
||||||
size_t left = buflen;
|
size_t left = buflen;
|
||||||
int r;
|
int r, i;
|
||||||
struct sc_path path;
|
struct sc_path path;
|
||||||
struct sc_asn1_entry asn1_obj_or_path[] = {
|
struct sc_asn1_entry asn1_obj_or_path[] = {
|
||||||
{ "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path },
|
{ "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
struct sc_asn1_entry asn1_odf[] = {
|
struct sc_asn1_entry asn1_odf[6];
|
||||||
{ "privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, asn1_obj_or_path },
|
|
||||||
{ "certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, asn1_obj_or_path },
|
|
||||||
{ "trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, asn1_obj_or_path },
|
|
||||||
{ "dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, asn1_obj_or_path },
|
|
||||||
{ "authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, asn1_obj_or_path },
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
sc_copy_asn1_entry(c_asn1_odf, asn1_odf);
|
||||||
|
for (i = 0; asn1_odf[i].name != NULL; i++)
|
||||||
|
sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0);
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
|
struct sc_pkcs15_df *df = NULL;
|
||||||
|
struct sc_file *file;
|
||||||
|
|
||||||
r = sc_asn1_decode_choice(card->card->ctx, asn1_odf, p, left, &p, &left);
|
r = sc_asn1_decode_choice(card->card->ctx, asn1_odf, p, left, &p, &left);
|
||||||
if (r == SC_ERROR_ASN1_END_OF_CONTENTS)
|
if (r == SC_ERROR_ASN1_END_OF_CONTENTS)
|
||||||
break;
|
break;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
switch (r) {
|
df = &card->df[odf_indexes[r]];
|
||||||
case 0:
|
if (df->count == SC_PKCS15_MAX_DFS) {
|
||||||
if (card->file_prkdf.path.len)
|
error(card->card->ctx, "too many DF's on card\n");
|
||||||
error(card->card->ctx, "warning: card has too many PrKDF's\n");
|
continue;
|
||||||
card->file_prkdf.path = path;
|
}
|
||||||
break;
|
file = malloc(sizeof(struct sc_file));
|
||||||
case 1:
|
if (file == NULL)
|
||||||
case 2:
|
return SC_ERROR_OUT_OF_MEMORY;
|
||||||
if (card->cdf_count == SC_PKCS15_MAX_CDFS) {
|
memset(file, 0, sizeof(struct sc_file));
|
||||||
error(card->card->ctx, "too many CDFs on card\n");
|
file->path = path;
|
||||||
|
df->file[df->count] = file;
|
||||||
|
df->count++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_odf(struct sc_pkcs15_card *card, u8 **buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
struct sc_path path;
|
||||||
|
struct sc_asn1_entry asn1_obj_or_path[] = {
|
||||||
|
{ "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path },
|
||||||
|
};
|
||||||
|
struct sc_asn1_entry *asn1_paths = NULL;
|
||||||
|
struct sc_asn1_entry *asn1_odf = NULL;
|
||||||
|
int df_count = 0, i, r, c = 0;
|
||||||
|
const int nr_indexes = sizeof(odf_indexes)/sizeof(odf_indexes[0]);
|
||||||
|
|
||||||
|
for (i = 0; i < SC_PKCS15_DF_TYPE_COUNT; i++)
|
||||||
|
df_count += card->df[i].count;
|
||||||
|
asn1_odf = malloc(sizeof(struct sc_asn1_entry) * (df_count + 1));
|
||||||
|
if (asn1_odf == NULL) {
|
||||||
|
r = SC_ERROR_OUT_OF_MEMORY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
asn1_paths = malloc(sizeof(struct sc_asn1_entry) * (df_count * 2));
|
||||||
|
if (asn1_paths == NULL) {
|
||||||
|
r = SC_ERROR_OUT_OF_MEMORY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
for (i = 0; i < SC_PKCS15_DF_TYPE_COUNT; i++) {
|
||||||
|
struct sc_pkcs15_df *df = &card->df[i];
|
||||||
|
int j, type = -1;
|
||||||
|
|
||||||
|
if (!df->count)
|
||||||
|
continue;
|
||||||
|
for (j = 0; j < nr_indexes; j++)
|
||||||
|
if (odf_indexes[j] == i) {
|
||||||
|
type = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
card->file_cdf[card->cdf_count].path = path;
|
if (type == -1) {
|
||||||
card->cdf_count++;
|
error(card->card->ctx, "Unsupported DF type.\n");
|
||||||
break;
|
continue;
|
||||||
case 3:
|
|
||||||
if (card->file_dodf.path.len)
|
|
||||||
error(card->card->ctx, "warning: card has too many DODF's\n");
|
|
||||||
card->file_dodf.path = path;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if (card->aodf_count == SC_PKCS15_MAX_AODFS) {
|
|
||||||
error(card->card->ctx, "too many AODFs on card\n");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
card->file_aodf[card->aodf_count].path = path;
|
for (j = 0; j < df->count; j++) {
|
||||||
card->aodf_count++;
|
asn1_odf[c] = c_asn1_odf[type];
|
||||||
break;
|
sc_format_asn1_entry(asn1_odf + c, asn1_paths + 2*c, NULL, 1);
|
||||||
|
sc_copy_asn1_entry(asn1_obj_or_path, asn1_paths + 2*c);
|
||||||
|
sc_format_asn1_entry(asn1_paths + 2*c, &df->file[j]->path, NULL, 1);
|
||||||
|
c++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
asn1_odf[df_count].name = NULL;
|
||||||
|
r = sc_asn1_encode(card->card->ctx, asn1_odf, buf, buflen);
|
||||||
|
err:
|
||||||
|
if (asn1_paths != NULL)
|
||||||
|
free(asn1_paths);
|
||||||
|
if (asn1_odf != NULL)
|
||||||
|
free(asn1_odf);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sc_pkcs15_create_odf(struct sc_pkcs15_card *p15card)
|
||||||
|
{
|
||||||
|
u8 *buf;
|
||||||
|
size_t buflen;
|
||||||
|
u8 line[10240];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = encode_odf(p15card, &buf, &buflen);
|
||||||
|
SC_TEST_RET(p15card->card->ctx, r, "ODF encoding failed");
|
||||||
|
sc_hex_dump(p15card->card->ctx, buf, buflen, line, sizeof(line));
|
||||||
|
printf("ODF:\n%s", line);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +465,7 @@ int sc_pkcs15_bind(struct sc_card *card,
|
||||||
struct sc_path tmppath;
|
struct sc_path tmppath;
|
||||||
const struct sc_pkcs15_defaults *defaults = NULL;
|
const struct sc_pkcs15_defaults *defaults = NULL;
|
||||||
struct sc_context *ctx;
|
struct sc_context *ctx;
|
||||||
|
struct sc_file file;
|
||||||
|
|
||||||
assert(sc_card_valid(card) && p15card_out != NULL);
|
assert(sc_card_valid(card) && p15card_out != NULL);
|
||||||
ctx = card->ctx;
|
ctx = card->ctx;
|
||||||
|
@ -251,13 +482,13 @@ int sc_pkcs15_bind(struct sc_card *card,
|
||||||
error(ctx, "sc_lock() failed: %s\n", sc_strerror(err));
|
error(ctx, "sc_lock() failed: %s\n", sc_strerror(err));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
err = sc_select_file(card, &tmppath, &p15card->file_dir);
|
err = sc_select_file(card, &tmppath, &file);
|
||||||
if (err) {
|
if (err) {
|
||||||
error(ctx, "Error selecting EF(DIR): %s\n", sc_strerror(err));
|
error(ctx, "Error selecting EF(DIR): %s\n", sc_strerror(err));
|
||||||
err = SC_ERROR_PKCS15_APP_NOT_FOUND;
|
err = SC_ERROR_PKCS15_APP_NOT_FOUND;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
err = sc_read_binary(card, 0, buf, p15card->file_dir.size, 0);
|
err = sc_read_binary(card, 0, buf, file.size, 0);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
error(ctx, "Error reading EF(DIR): %s\n", sc_strerror(err));
|
error(ctx, "Error reading EF(DIR): %s\n", sc_strerror(err));
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -283,10 +514,10 @@ int sc_pkcs15_bind(struct sc_card *card,
|
||||||
} else
|
} else
|
||||||
tmppath = p15card->file_odf.path;
|
tmppath = p15card->file_odf.path;
|
||||||
|
|
||||||
err = sc_select_file(card, &tmppath, &p15card->file_odf);
|
err = sc_select_file(card, &tmppath, &file);
|
||||||
if (err) /* FIXME: finish writing error stuff */
|
if (err) /* FIXME: finish writing error stuff */
|
||||||
goto error;
|
goto error;
|
||||||
err = sc_read_binary(card, 0, buf, p15card->file_odf.size, 0);
|
err = sc_read_binary(card, 0, buf, file.size, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
if (err < 2) {
|
if (err < 2) {
|
||||||
|
@ -308,10 +539,10 @@ int sc_pkcs15_bind(struct sc_card *card,
|
||||||
defaults->defaults_func(p15card, defaults->arg);
|
defaults->defaults_func(p15card, defaults->arg);
|
||||||
tmppath = p15card->file_tokeninfo.path;
|
tmppath = p15card->file_tokeninfo.path;
|
||||||
}
|
}
|
||||||
err = sc_select_file(card, &tmppath, &p15card->file_tokeninfo);
|
err = sc_select_file(card, &tmppath, &file);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
err = sc_read_binary(card, 0, buf, p15card->file_tokeninfo.size, 0);
|
err = sc_read_binary(card, 0, buf, file.size, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
if (err <= 2) {
|
if (err <= 2) {
|
||||||
|
@ -346,8 +577,14 @@ int sc_pkcs15_detect(struct sc_card *card)
|
||||||
|
|
||||||
int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card)
|
int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card)
|
||||||
{
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
assert(p15card != NULL);
|
assert(p15card != NULL);
|
||||||
SC_FUNC_CALLED(p15card->card->ctx, 1);
|
SC_FUNC_CALLED(p15card->card->ctx, 1);
|
||||||
|
for (j = 0; j < SC_PKCS15_DF_TYPE_COUNT; j++)
|
||||||
|
for (i = 0; i < p15card->df[j].count; i++)
|
||||||
|
if (p15card->df[j].file[i])
|
||||||
|
free(p15card->df[j].file[i]);
|
||||||
free(p15card->label);
|
free(p15card->label);
|
||||||
free(p15card->serial_number);
|
free(p15card->serial_number);
|
||||||
free(p15card->manufacturer_id);
|
free(p15card->manufacturer_id);
|
||||||
|
|
|
@ -34,10 +34,7 @@ extern "C" {
|
||||||
#define SC_PKCS15_MAX_PRKEYS 2
|
#define SC_PKCS15_MAX_PRKEYS 2
|
||||||
#define SC_PKCS15_MAX_LABEL_SIZE 32
|
#define SC_PKCS15_MAX_LABEL_SIZE 32
|
||||||
#define SC_PKCS15_MAX_ID_SIZE 16
|
#define SC_PKCS15_MAX_ID_SIZE 16
|
||||||
#define SC_PKCS15_MAX_CDFS 4 /* Certificate Directory
|
#define SC_PKCS15_MAX_DFS 4
|
||||||
* Files */
|
|
||||||
#define SC_PKCS15_MAX_AODFS 4 /* Authentication Object
|
|
||||||
* Directory Files */
|
|
||||||
#define SC_PKCS15_MAX_CERTS 4 /* Total certificates */
|
#define SC_PKCS15_MAX_CERTS 4 /* Total certificates */
|
||||||
|
|
||||||
struct sc_pkcs15_id {
|
struct sc_pkcs15_id {
|
||||||
|
@ -45,6 +42,8 @@ struct sc_pkcs15_id {
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SC_PKCS15_CO_FLAG_OBJECT_SEEN 0x80000000 /* for PKCS #11 module */
|
||||||
|
|
||||||
struct sc_pkcs15_common_obj_attr {
|
struct sc_pkcs15_common_obj_attr {
|
||||||
char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */
|
char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */
|
||||||
int flags;
|
int flags;
|
||||||
|
@ -137,14 +136,33 @@ struct sc_pkcs15_prkey_info {
|
||||||
int modulus_length;
|
int modulus_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SC_PKCS15_PRKDF 0
|
||||||
|
#define SC_PKCS15_PUKDF 1
|
||||||
|
#define SC_PKCS15_PUKDF_TRUSTED 2
|
||||||
|
#define SC_PKCS15_SKDF 3
|
||||||
|
#define SC_PKCS15_CDF 4
|
||||||
|
#define SC_PKCS15_CDF_TRUSTED 5
|
||||||
|
#define SC_PKCS15_CDF_USEFUL 6
|
||||||
|
#define SC_PKCS15_DODF 7
|
||||||
|
#define SC_PKCS15_AODF 8
|
||||||
|
#define SC_PKCS15_DF_TYPE_COUNT 9
|
||||||
|
|
||||||
|
|
||||||
|
struct sc_pkcs15_df {
|
||||||
|
struct sc_file *file[SC_PKCS15_MAX_DFS];
|
||||||
|
int count, record_length;
|
||||||
|
};
|
||||||
|
|
||||||
struct sc_pkcs15_card {
|
struct sc_pkcs15_card {
|
||||||
struct sc_card *card;
|
struct sc_card *card;
|
||||||
char *label;
|
char *label;
|
||||||
/* fields from TokenInfo: */
|
/* fields from TokenInfo: */
|
||||||
int version;
|
int version;
|
||||||
char *serial_number, *manufacturer_id;
|
char *serial_number, *manufacturer_id;
|
||||||
int flags;
|
unsigned long flags;
|
||||||
struct sc_pkcs15_algorithm_info alg_info[1];
|
struct sc_pkcs15_algorithm_info alg_info[1];
|
||||||
|
/* FIXME: this could be done better with some C pre-processor
|
||||||
|
* magic */
|
||||||
struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS];
|
struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS];
|
||||||
int cert_count;
|
int cert_count;
|
||||||
struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS];
|
struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS];
|
||||||
|
@ -152,15 +170,10 @@ struct sc_pkcs15_card {
|
||||||
struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS];
|
struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS];
|
||||||
int pin_count;
|
int pin_count;
|
||||||
|
|
||||||
|
/* FIXME: Move file_dir somewhere else, perhaps to sc_card */
|
||||||
struct sc_file file_dir, file_app;
|
struct sc_file file_dir, file_app;
|
||||||
/* in app DF */
|
|
||||||
struct sc_file file_tokeninfo, file_odf;
|
struct sc_file file_tokeninfo, file_odf;
|
||||||
struct sc_file file_prkdf;
|
struct sc_pkcs15_df df[SC_PKCS15_DF_TYPE_COUNT];
|
||||||
struct sc_file file_cdf[SC_PKCS15_MAX_CDFS];
|
|
||||||
int cdf_count;
|
|
||||||
struct sc_file file_aodf[SC_PKCS15_MAX_AODFS];
|
|
||||||
int aodf_count;
|
|
||||||
struct sc_file file_dodf;
|
|
||||||
|
|
||||||
int use_cache;
|
int use_cache;
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
/* Internal use only */
|
/* Internal use only */
|
||||||
int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2);
|
int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2);
|
||||||
|
size_t sc_count_bit_string_size(const void * buf, size_t bufsize);
|
||||||
|
|
||||||
/* Default timeout value for SCardGetStatusChange */
|
/* Default timeout value for SCardGetStatusChange */
|
||||||
#ifndef SC_CUSTOM_STATUS_TIMEOUT
|
#ifndef SC_CUSTOM_STATUS_TIMEOUT
|
||||||
|
|
|
@ -42,10 +42,7 @@ const char *option_help[] = { NULL };
|
||||||
|
|
||||||
const char *cmds[] = {
|
const char *cmds[] = {
|
||||||
"ls", "cd", "debug", "cat", "info", "create", "delete",
|
"ls", "cd", "debug", "cat", "info", "create", "delete",
|
||||||
"verify"
|
"verify", "put", "get"
|
||||||
};
|
|
||||||
const char *cmdusage[] = {
|
|
||||||
"", "", "", "", "", "", "", "<key type> <key ref>"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nr_cmds = sizeof(cmds)/sizeof(cmds[0]);
|
const int nr_cmds = sizeof(cmds)/sizeof(cmds[0]);
|
||||||
|
@ -87,6 +84,22 @@ void check_ret(int r, int op, const char *err, const struct sc_file *file)
|
||||||
fprintf(stderr, "ACL for operation: %s\n", acl_to_str(file->acl[op]));
|
fprintf(stderr, "ACL for operation: %s\n", acl_to_str(file->acl[op]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int arg_to_path(const char *arg, struct sc_path *path)
|
||||||
|
{
|
||||||
|
char buf[6];
|
||||||
|
|
||||||
|
if (strlen(arg) != 4) {
|
||||||
|
printf("Wrong ID length.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strcpy(buf, "I");
|
||||||
|
strcat(buf, arg);
|
||||||
|
sc_format_path(buf, path);
|
||||||
|
if (path->len != 2)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void print_file(const struct sc_file *file)
|
void print_file(const struct sc_file *file)
|
||||||
{
|
{
|
||||||
const char *st;
|
const char *st;
|
||||||
|
@ -164,7 +177,6 @@ int do_cd(const char *arg)
|
||||||
{
|
{
|
||||||
struct sc_path path;
|
struct sc_path path;
|
||||||
struct sc_file file;
|
struct sc_file file;
|
||||||
char buf[6];
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (strcmp(arg, "..") == 0) {
|
if (strcmp(arg, "..") == 0) {
|
||||||
|
@ -182,15 +194,7 @@ int do_cd(const char *arg)
|
||||||
current_path = path;
|
current_path = path;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (arg_to_path(arg, &path) != 0) {
|
||||||
if (strlen(arg) != 4) {
|
|
||||||
printf("Usage: cd <file_id>\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
strcpy(buf, "I");
|
|
||||||
strcat(buf, arg);
|
|
||||||
sc_format_path(buf, &path);
|
|
||||||
if (path.len != 2) {
|
|
||||||
printf("Usage: cd <file_id>\n");
|
printf("Usage: cd <file_id>\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -215,7 +219,6 @@ int do_cd(const char *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int do_cat(const char *arg)
|
int do_cat(const char *arg)
|
||||||
{
|
{
|
||||||
u8 buf[256];
|
u8 buf[256];
|
||||||
|
@ -231,14 +234,7 @@ int do_cat(const char *arg)
|
||||||
file = current_file;
|
file = current_file;
|
||||||
not_current = 0;
|
not_current = 0;
|
||||||
} else {
|
} else {
|
||||||
if (strlen(arg) != 4) {
|
if (arg_to_path(arg, &path) != 0) {
|
||||||
printf("Usage: cat [file_id]\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
strcpy((char *) buf, "I");
|
|
||||||
strcat((char *) buf, arg);
|
|
||||||
sc_format_path((char *) buf, &path);
|
|
||||||
if (path.len != 2) {
|
|
||||||
printf("Usage: cat [file_id]\n");
|
printf("Usage: cat [file_id]\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -291,17 +287,9 @@ int do_info(const char *arg)
|
||||||
file = current_file;
|
file = current_file;
|
||||||
not_current = 0;
|
not_current = 0;
|
||||||
} else {
|
} else {
|
||||||
char buf[6];
|
|
||||||
struct sc_path tmppath;
|
struct sc_path tmppath;
|
||||||
|
|
||||||
if (strlen(arg) != 4) {
|
if (arg_to_path(arg, &tmppath) != 0) {
|
||||||
printf("Usage: info [file_id]\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
strcpy(buf, "I");
|
|
||||||
strcat(buf, arg);
|
|
||||||
sc_format_path(buf, &tmppath);
|
|
||||||
if (tmppath.len != 2) {
|
|
||||||
printf("Usage: info [file_id]\n");
|
printf("Usage: info [file_id]\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -359,7 +347,23 @@ int do_info(const char *arg)
|
||||||
"Linear fixed, SIMPLE-TLV", "Linear variable",
|
"Linear fixed, SIMPLE-TLV", "Linear variable",
|
||||||
"Cyclic", "Cyclic, SIMPLE-TLV",
|
"Cyclic", "Cyclic, SIMPLE-TLV",
|
||||||
};
|
};
|
||||||
|
const char *ops[] = {
|
||||||
|
"READ", "UPDATE", "WRITE", "ERASE", "REHABILITATE",
|
||||||
|
"INVALIDATE"
|
||||||
|
};
|
||||||
printf("%-15s%s\n", "EF structure:", structs[file.ef_structure]);
|
printf("%-15s%s\n", "EF structure:", structs[file.ef_structure]);
|
||||||
|
for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
sprintf(buf, "ACL for %s:", ops[i]);
|
||||||
|
printf("%-25s%s\n", buf, acl_to_str(file.acl[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file.prop_attr_len) {
|
||||||
|
printf("%-25s", "Proprietary attributes:");
|
||||||
|
for (i = 0; i < file.prop_attr_len; i++)
|
||||||
|
printf("%02X ", file.prop_attr[i]);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (not_current) {
|
if (not_current) {
|
||||||
|
@ -374,18 +378,12 @@ int do_info(const char *arg)
|
||||||
|
|
||||||
int do_create(const char *arg, const char *arg2)
|
int do_create(const char *arg, const char *arg2)
|
||||||
{
|
{
|
||||||
char buf[6];
|
|
||||||
struct sc_path path;
|
struct sc_path path;
|
||||||
struct sc_file file;
|
struct sc_file file;
|
||||||
size_t size;
|
size_t size;
|
||||||
int i, r;
|
int i, r;
|
||||||
|
|
||||||
if (strlen(arg) != 4)
|
if (arg_to_path(arg, &path) != 0)
|
||||||
goto usage;
|
|
||||||
strcpy(buf, "I");
|
|
||||||
strcat(buf, arg);
|
|
||||||
sc_format_path(buf, &path);
|
|
||||||
if (path.len != 2)
|
|
||||||
goto usage;
|
goto usage;
|
||||||
if (sscanf(arg2, "%d", &size) != 1)
|
if (sscanf(arg2, "%d", &size) != 1)
|
||||||
goto usage;
|
goto usage;
|
||||||
|
@ -396,12 +394,20 @@ int do_create(const char *arg, const char *arg2)
|
||||||
for (i = 0; i < SC_MAX_AC_OPS; i++)
|
for (i = 0; i < SC_MAX_AC_OPS; i++)
|
||||||
file.acl[i] = SC_AC_NONE;
|
file.acl[i] = SC_AC_NONE;
|
||||||
file.size = size;
|
file.size = size;
|
||||||
|
file.status = SC_FILE_STATUS_ACTIVATED;
|
||||||
|
|
||||||
r = sc_create_file(card, &file);
|
r = sc_create_file(card, &file);
|
||||||
if (r) {
|
if (r) {
|
||||||
check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", ¤t_file);
|
check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", ¤t_file);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/* Make sure we're back in the parent directory, because on some cards
|
||||||
|
* CREATE FILE also selects the newly created file. */
|
||||||
|
r = sc_select_file(card, ¤t_path, NULL);
|
||||||
|
if (r) {
|
||||||
|
printf("unable to select parent file: %s\n", sc_strerror(r));
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
usage:
|
usage:
|
||||||
printf("Usage: create <file_id> <file_size>\n");
|
printf("Usage: create <file_id> <file_size>\n");
|
||||||
|
@ -410,16 +416,10 @@ usage:
|
||||||
|
|
||||||
int do_delete(const char *arg)
|
int do_delete(const char *arg)
|
||||||
{
|
{
|
||||||
char buf[6];
|
|
||||||
struct sc_path path;
|
struct sc_path path;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (strlen(arg) != 4)
|
if (arg_to_path(arg, &path) != 0)
|
||||||
goto usage;
|
|
||||||
strcpy(buf, "I");
|
|
||||||
strcat(buf, arg);
|
|
||||||
sc_format_path(buf, &path);
|
|
||||||
if (path.len != 2)
|
|
||||||
goto usage;
|
goto usage;
|
||||||
r = sc_delete_file(card, &path);
|
r = sc_delete_file(card, &path);
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -490,6 +490,139 @@ usage:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_get(const char *arg, const char *arg2)
|
||||||
|
{
|
||||||
|
u8 buf[256];
|
||||||
|
int r, error = 0;
|
||||||
|
size_t count = 0;
|
||||||
|
unsigned int idx = 0;
|
||||||
|
struct sc_path path;
|
||||||
|
struct sc_file file;
|
||||||
|
const char *filename;
|
||||||
|
FILE *outf = NULL;
|
||||||
|
|
||||||
|
if (arg_to_path(arg, &path) != 0)
|
||||||
|
goto usage;
|
||||||
|
if (strlen(arg2))
|
||||||
|
filename = arg2;
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%02X%02X", path.value[0], path.value[1]);
|
||||||
|
filename = buf;
|
||||||
|
}
|
||||||
|
outf = fopen(filename, "w");
|
||||||
|
if (outf == NULL) {
|
||||||
|
perror(filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = sc_select_file(card, &path, &file);
|
||||||
|
if (r) {
|
||||||
|
check_ret(r, SC_AC_OP_SELECT, "unable to select file", ¤t_file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
count = file.size;
|
||||||
|
while (count) {
|
||||||
|
int c = count > sizeof(buf) ? sizeof(buf) : count;
|
||||||
|
|
||||||
|
r = sc_read_binary(card, idx, buf, c, 0);
|
||||||
|
if (r < 0) {
|
||||||
|
check_ret(r, SC_AC_OP_READ, "read failed", &file);
|
||||||
|
error = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (r != c) {
|
||||||
|
printf("expecting %d, got only %d bytes.\n", c, r);
|
||||||
|
error = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
fwrite(buf, c, 1, outf);
|
||||||
|
idx += c;
|
||||||
|
count -= c;
|
||||||
|
}
|
||||||
|
printf("Total of %d bytes read.\n", idx);
|
||||||
|
err:
|
||||||
|
r = sc_select_file(card, ¤t_path, NULL);
|
||||||
|
if (r) {
|
||||||
|
printf("unable to select parent file: %s\n", sc_strerror(r));
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
if (outf)
|
||||||
|
fclose(outf);
|
||||||
|
return -error;
|
||||||
|
usage:
|
||||||
|
printf("Usage: get <file id> [output file]\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_put(const char *arg, const char *arg2)
|
||||||
|
{
|
||||||
|
u8 buf[256];
|
||||||
|
int r, error = 0;
|
||||||
|
size_t count = 0;
|
||||||
|
unsigned int idx = 0;
|
||||||
|
struct sc_path path;
|
||||||
|
struct sc_file file;
|
||||||
|
const char *filename;
|
||||||
|
FILE *outf = NULL;
|
||||||
|
|
||||||
|
if (arg_to_path(arg, &path) != 0)
|
||||||
|
goto usage;
|
||||||
|
if (strlen(arg2))
|
||||||
|
filename = arg2;
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%02X%02X", path.value[0], path.value[1]);
|
||||||
|
filename = buf;
|
||||||
|
}
|
||||||
|
outf = fopen(filename, "r");
|
||||||
|
if (outf == NULL) {
|
||||||
|
perror(filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = sc_select_file(card, &path, &file);
|
||||||
|
if (r) {
|
||||||
|
check_ret(r, SC_AC_OP_SELECT, "unable to select file", ¤t_file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
count = file.size;
|
||||||
|
while (count) {
|
||||||
|
int c = count > sizeof(buf) ? sizeof(buf) : count;
|
||||||
|
|
||||||
|
r = fread(buf, 1, c, outf);
|
||||||
|
if (r < 0) {
|
||||||
|
perror("fread");
|
||||||
|
error = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (r != c)
|
||||||
|
count = c = r;
|
||||||
|
r = sc_update_binary(card, idx, buf, c, 0);
|
||||||
|
if (r < 0) {
|
||||||
|
check_ret(r, SC_AC_OP_READ, "update failed", &file);
|
||||||
|
error = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (r != c) {
|
||||||
|
printf("expecting %d, wrote only %d bytes.\n", c, r);
|
||||||
|
error = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
idx += c;
|
||||||
|
count -= c;
|
||||||
|
}
|
||||||
|
printf("Total of %d bytes written.\n", idx);
|
||||||
|
err:
|
||||||
|
r = sc_select_file(card, ¤t_path, NULL);
|
||||||
|
if (r) {
|
||||||
|
printf("unable to select parent file: %s\n", sc_strerror(r));
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
if (outf)
|
||||||
|
fclose(outf);
|
||||||
|
return -error;
|
||||||
|
usage:
|
||||||
|
printf("Usage: put <file id> [output file]\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int handle_cmd(int cmd, const char *arg, const char *arg2)
|
int handle_cmd(int cmd, const char *arg, const char *arg2)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -519,6 +652,10 @@ int handle_cmd(int cmd, const char *arg, const char *arg2)
|
||||||
return do_delete(arg);
|
return do_delete(arg);
|
||||||
case 7:
|
case 7:
|
||||||
return do_verify(arg, arg2);
|
return do_verify(arg, arg2);
|
||||||
|
case 8:
|
||||||
|
return do_put(arg, arg2);
|
||||||
|
case 9:
|
||||||
|
return do_get(arg, arg2);
|
||||||
default:
|
default:
|
||||||
printf("Don't know how to handle command.\n");
|
printf("Don't know how to handle command.\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ const char * acl_to_str(unsigned int acl)
|
||||||
static char line[80];
|
static char line[80];
|
||||||
|
|
||||||
if (acl == SC_AC_UNKNOWN)
|
if (acl == SC_AC_UNKNOWN)
|
||||||
return "UNKN";
|
return "N/A";
|
||||||
if (acl == SC_AC_NEVER)
|
if (acl == SC_AC_NEVER)
|
||||||
return "NEVR";
|
return "NEVR";
|
||||||
if (acl == SC_AC_NONE)
|
if (acl == SC_AC_NONE)
|
||||||
|
|
Loading…
Reference in New Issue