2002-02-20 09:56:47 +00:00
|
|
|
|
/*
|
|
|
|
|
* dir.c: Stuff for handling EF(DIR)
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2001 Juha Yrj<EFBFBD>l<EFBFBD> <juha.yrjola@iki.fi>
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "sc-internal.h"
|
|
|
|
|
#include "sc-log.h"
|
|
|
|
|
#include "sc-asn1.h"
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
struct app_entry {
|
|
|
|
|
const char *aid;
|
|
|
|
|
size_t aid_len;
|
|
|
|
|
const char *desc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct app_entry apps[] = {
|
|
|
|
|
{ "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct app_entry * find_app_entry(const u8 * aid, size_t aid_len)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(apps)/sizeof(apps[0]); i++) {
|
|
|
|
|
if (apps[i].aid_len == aid_len &&
|
|
|
|
|
memcmp(apps[i].aid, aid, aid_len) == 0)
|
|
|
|
|
return &apps[i];
|
|
|
|
|
}
|
|
|
|
|
return 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, SC_ASN1_OPTIONAL, NULL },
|
|
|
|
|
{ "ddo", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL },
|
|
|
|
|
{ NULL }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct sc_asn1_entry c_asn1_dir[] = {
|
|
|
|
|
{ "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL },
|
|
|
|
|
{ NULL }
|
|
|
|
|
};
|
|
|
|
|
|
2002-02-26 11:23:25 +00:00
|
|
|
|
static int parse_dir_record(struct sc_card *card, u8 ** buf, size_t *buflen,
|
|
|
|
|
int rec_nr)
|
2002-02-20 09:56:47 +00:00
|
|
|
|
{
|
|
|
|
|
struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
|
|
|
|
|
struct sc_app_info *app = NULL;
|
|
|
|
|
const struct app_entry *ae;
|
|
|
|
|
int r;
|
|
|
|
|
u8 aid[128], label[128], path[128];
|
|
|
|
|
u8 ddo[128];
|
|
|
|
|
int aid_len = sizeof(aid), label_len = sizeof(label),
|
|
|
|
|
path_len = sizeof(path), ddo_len = sizeof(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, 0);
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 0, aid, &aid_len, 0);
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0);
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0);
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 3, ddo, &ddo_len, 0);
|
|
|
|
|
|
|
|
|
|
r = sc_asn1_decode(card->ctx, asn1_dir, *buf, *buflen, (const u8 **) buf, buflen);
|
|
|
|
|
if (r == SC_ERROR_ASN1_END_OF_CONTENTS)
|
|
|
|
|
return r;
|
|
|
|
|
if (r) {
|
|
|
|
|
error(card->ctx, "EF(DIR) parsing failed: %s\n",
|
|
|
|
|
sc_strerror(r));
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
if (aid_len > SC_MAX_AID_SIZE) {
|
|
|
|
|
error(card->ctx, "AID is too long.\n");
|
|
|
|
|
return SC_ERROR_INVALID_ASN1_OBJECT;
|
|
|
|
|
}
|
|
|
|
|
app = malloc(sizeof(struct sc_app_info));
|
|
|
|
|
if (app == NULL)
|
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
|
|
memcpy(app->aid, aid, aid_len);
|
|
|
|
|
app->aid_len = aid_len;
|
|
|
|
|
if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT)
|
|
|
|
|
app->label = strdup((char *) label);
|
|
|
|
|
else
|
|
|
|
|
app->label = NULL;
|
|
|
|
|
if (asn1_dirrecord[2].flags & SC_ASN1_PRESENT) {
|
|
|
|
|
if (path_len > SC_MAX_PATH_SIZE) {
|
|
|
|
|
error(card->ctx, "Application path is too long.\n");
|
|
|
|
|
return SC_ERROR_INVALID_ASN1_OBJECT;
|
|
|
|
|
}
|
|
|
|
|
memcpy(app->path.value, path, path_len);
|
|
|
|
|
app->path.len = path_len;
|
|
|
|
|
app->path.type = SC_PATH_TYPE_PATH;
|
|
|
|
|
} else
|
|
|
|
|
app->path.len = 0;
|
|
|
|
|
if (asn1_dirrecord[3].flags & SC_ASN1_PRESENT) {
|
|
|
|
|
app->ddo = malloc(ddo_len);
|
|
|
|
|
if (app->ddo == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
memcpy(app->ddo, ddo, ddo_len);
|
|
|
|
|
app->ddo_len = ddo_len;
|
|
|
|
|
} else {
|
|
|
|
|
app->ddo = NULL;
|
|
|
|
|
app->ddo_len = 0;
|
|
|
|
|
}
|
|
|
|
|
ae = find_app_entry(aid, aid_len);
|
|
|
|
|
if (ae != NULL)
|
|
|
|
|
app->desc = ae->desc;
|
|
|
|
|
else
|
|
|
|
|
app->desc = NULL;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
app->rec_nr = rec_nr;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
card->app[card->app_count] = app;
|
|
|
|
|
card->app_count++;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sc_enum_apps(struct sc_card *card)
|
|
|
|
|
{
|
|
|
|
|
struct sc_path path;
|
|
|
|
|
int ef_structure;
|
|
|
|
|
size_t file_size;
|
2002-03-12 13:00:57 +00:00
|
|
|
|
int r, log_errors;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
|
if (card->app_count < 0)
|
|
|
|
|
card->app_count = 0;
|
|
|
|
|
sc_format_path("3F002F00", &path);
|
2002-02-26 11:23:25 +00:00
|
|
|
|
if (card->ef_dir != NULL) {
|
|
|
|
|
sc_file_free(card->ef_dir);
|
|
|
|
|
card->ef_dir = NULL;
|
|
|
|
|
}
|
2002-03-12 13:00:57 +00:00
|
|
|
|
log_errors = card->ctx->log_errors;
|
|
|
|
|
card->ctx->log_errors = 0;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
r = sc_select_file(card, &path, &card->ef_dir);
|
2002-03-12 13:00:57 +00:00
|
|
|
|
card->ctx->log_errors = log_errors;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
if (r)
|
|
|
|
|
return r;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
if (card->ef_dir->type != SC_FILE_TYPE_WORKING_EF) {
|
2002-02-20 09:56:47 +00:00
|
|
|
|
error(card->ctx, "EF(DIR) is not a working EF.\n");
|
2002-02-26 11:23:25 +00:00
|
|
|
|
sc_file_free(card->ef_dir);
|
|
|
|
|
card->ef_dir = NULL;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
return SC_ERROR_INVALID_CARD;
|
|
|
|
|
}
|
2002-02-26 11:23:25 +00:00
|
|
|
|
ef_structure = card->ef_dir->ef_structure;
|
|
|
|
|
file_size = card->ef_dir->size;
|
|
|
|
|
if (file_size == 0)
|
|
|
|
|
return 0;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
if (ef_structure == SC_FILE_EF_TRANSPARENT) {
|
|
|
|
|
u8 buf[1024], *p = buf;
|
|
|
|
|
size_t bufsize;
|
|
|
|
|
|
|
|
|
|
if (file_size > sizeof(buf))
|
|
|
|
|
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_INTERNAL);
|
|
|
|
|
r = sc_read_binary(card, 0, buf, file_size, 0);
|
|
|
|
|
SC_TEST_RET(card->ctx, r, "read_binary() failed");
|
|
|
|
|
bufsize = file_size;
|
|
|
|
|
while (bufsize > 0) {
|
|
|
|
|
if (card->app_count == SC_MAX_CARD_APPS) {
|
|
|
|
|
error(card->ctx, "Too many applications on card");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2002-02-26 11:23:25 +00:00
|
|
|
|
r = parse_dir_record(card, &p, &bufsize, -1);
|
2002-02-20 09:56:47 +00:00
|
|
|
|
if (r)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else { /* record structure */
|
|
|
|
|
u8 buf[256], *p;
|
|
|
|
|
int rec_nr;
|
|
|
|
|
size_t rec_size;
|
|
|
|
|
|
|
|
|
|
for (rec_nr = 1; ; rec_nr++) {
|
|
|
|
|
r = sc_read_record(card, rec_nr, buf, sizeof(buf), 0);
|
|
|
|
|
if (r == SC_ERROR_RECORD_NOT_FOUND)
|
|
|
|
|
break;
|
|
|
|
|
SC_TEST_RET(card->ctx, r, "read_record() failed");
|
|
|
|
|
if (card->app_count == SC_MAX_CARD_APPS) {
|
|
|
|
|
error(card->ctx, "Too many applications on card");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rec_size = r;
|
|
|
|
|
p = buf;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
parse_dir_record(card, &p, &rec_size, rec_nr);
|
2002-02-20 09:56:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return card->app_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct sc_app_info * sc_find_app_by_aid(struct sc_card *card,
|
|
|
|
|
const u8 *aid, size_t aid_len)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
|
assert(card->app_count > 0);
|
|
|
|
|
for (i = 0; i < card->app_count; i++) {
|
|
|
|
|
if (card->app[i]->aid_len == aid_len &&
|
|
|
|
|
memcmp(card->app[i]->aid, aid, aid_len) == 0)
|
|
|
|
|
return card->app[i];
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int encode_dir_record(struct sc_context *ctx, const struct sc_app_info *app,
|
|
|
|
|
u8 **buf, size_t *buflen)
|
|
|
|
|
{
|
|
|
|
|
struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
|
|
|
|
|
int r;
|
|
|
|
|
size_t label_len;
|
|
|
|
|
|
|
|
|
|
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 *) app->aid, (void *) &app->aid_len, 1);
|
|
|
|
|
if (app->label != NULL) {
|
|
|
|
|
label_len = strlen(app->label);
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 1, app->label, &label_len, 1);
|
|
|
|
|
}
|
|
|
|
|
if (app->path.len)
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 2, (void *) app->path.value,
|
|
|
|
|
(void *) &app->path.len, 1);
|
|
|
|
|
if (app->ddo != NULL)
|
|
|
|
|
sc_format_asn1_entry(asn1_dirrecord + 3, (void *) app->ddo,
|
|
|
|
|
(void *) &app->ddo_len, 1);
|
|
|
|
|
r = sc_asn1_encode(ctx, asn1_dir, buf, buflen);
|
|
|
|
|
if (r) {
|
|
|
|
|
error(ctx, "sc_asn1_encode() failed: %s\n",
|
|
|
|
|
sc_strerror(r));
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-26 11:23:25 +00:00
|
|
|
|
static int update_transparent(struct sc_card *card, struct sc_file *file)
|
|
|
|
|
{
|
|
|
|
|
u8 *rec, *buf = NULL;
|
|
|
|
|
size_t rec_size, buf_size = 0;
|
|
|
|
|
int i, r;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < card->app_count; i++) {
|
|
|
|
|
r = encode_dir_record(card->ctx, card->app[i], &rec, &rec_size);
|
|
|
|
|
if (r) {
|
|
|
|
|
free(buf);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
buf = realloc(buf, buf_size + rec_size);
|
|
|
|
|
if (buf == NULL) {
|
|
|
|
|
free(rec);
|
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
memcpy(buf + buf_size, rec, rec_size);
|
|
|
|
|
buf_size += rec_size;
|
|
|
|
|
}
|
|
|
|
|
if (file->size > buf_size) {
|
|
|
|
|
buf = realloc(buf, file->size);
|
|
|
|
|
memset(buf + buf_size, 0, file->size - buf_size);
|
|
|
|
|
buf_size = file->size;
|
|
|
|
|
}
|
|
|
|
|
r = sc_update_binary(card, 0, buf, buf_size, 0);
|
|
|
|
|
SC_TEST_RET(card->ctx, r, "Unable to update EF(DIR)");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int update_single_record(struct sc_card *card, struct sc_file *file,
|
|
|
|
|
struct sc_app_info *app)
|
2002-02-20 09:56:47 +00:00
|
|
|
|
{
|
|
|
|
|
u8 *rec;
|
|
|
|
|
size_t rec_size;
|
2002-02-26 11:23:25 +00:00
|
|
|
|
int r;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
|
2002-02-26 11:23:25 +00:00
|
|
|
|
r = encode_dir_record(card->ctx, app, &rec, &rec_size);
|
|
|
|
|
if (r)
|
|
|
|
|
return r;
|
|
|
|
|
r = sc_update_record(card, app->rec_nr, rec, rec_size, 0);
|
|
|
|
|
free(rec);
|
|
|
|
|
SC_TEST_RET(card->ctx, r, "Unable to update EF(DIR) record");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int update_records(struct sc_card *card, struct sc_file *file)
|
|
|
|
|
{
|
|
|
|
|
int i, r;
|
2002-02-20 09:56:47 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < card->app_count; i++) {
|
2002-02-26 11:23:25 +00:00
|
|
|
|
r = update_single_record(card, file, card->app[i]);
|
2002-02-20 09:56:47 +00:00
|
|
|
|
if (r)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2002-02-26 11:23:25 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sc_update_dir(struct sc_card *card, struct sc_app_info *app)
|
|
|
|
|
{
|
|
|
|
|
struct sc_path path;
|
|
|
|
|
struct sc_file *file;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
sc_format_path("3F002F00", &path);
|
|
|
|
|
|
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
|
SC_TEST_RET(card->ctx, r, "unable to select EF(DIR)");
|
|
|
|
|
if (file->ef_structure == SC_FILE_EF_TRANSPARENT)
|
|
|
|
|
r = update_transparent(card, file);
|
|
|
|
|
else if (app == NULL)
|
|
|
|
|
r = update_records(card, file);
|
|
|
|
|
else
|
|
|
|
|
r = update_single_record(card, file, app);
|
|
|
|
|
sc_file_free(file);
|
2002-02-20 09:56:47 +00:00
|
|
|
|
return r;
|
|
|
|
|
}
|