opensc/src/libopensc/card-jcop.c

933 lines
28 KiB
C
Raw Normal View History

/*
* card-jcop.c
*
* Copyright (C) 2003 Chaskiel Grundman <cg2v@andrew.cmu.edu>
*
* 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
*/
2015-04-22 21:55:33 +00:00
#if HAVE_CONFIG_H
#include "config.h"
2015-04-22 21:55:33 +00:00
#endif
#include <string.h>
#include <stdlib.h>
#include "internal.h"
#include "cardctl.h"
static struct sc_atr_table jcop_atrs[] = {
{ "3B:E6:00:FF:81:31:FE:45:4A:43:4F:50:33:31:06", NULL, NULL, SC_CARD_TYPE_JCOP_GENERIC, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
static struct sc_card_operations jcop_ops;
static struct sc_card_driver jcop_drv = {
"JCOP cards with BlueZ PKCS#15 applet",
"jcop",
&jcop_ops,
NULL, 0, NULL
};
#define SELECT_MF 0
#define SELECT_EFDIR 1
#define SELECT_APPDF 2
#define SELECT_EF 3
#define SELECT_UNKNOWN 4
#define SELECTING_TARGET 0xf
#define SELECTING_ABS 0x80
#define SELECTING_VIA_APPDF 0x100
struct jcop_private_data
{
sc_file_t *virtmf;
sc_file_t *virtdir;
sc_path_t aid;
int selected;
int invalid_senv;
int nfiles;
u8 *filelist;
};
#define DRVDATA(card) ((struct jcop_private_data *) ((card)->drv_data))
static int jcop_finish(sc_card_t *card)
{
struct jcop_private_data *drvdata=DRVDATA(card);
if (drvdata) {
sc_file_free(drvdata->virtmf);
sc_file_free(drvdata->virtdir);
free(drvdata);
card->drv_data=NULL;
}
return 0;
}
static int jcop_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, jcop_atrs, &card->type);
if (i < 0)
return 0;
return 1;
}
static unsigned char ef_dir_contents[128] = {
0x61, 0x21,
0x4f, 0xc, 0xA0, 0x0, 0x0, 0x0, 0x63, 'P', 'K', 'C', 'S', '-', '1', '5',
0x50, 0xb, 'O', 'p', 'e', 'n', 'S', 'C', ' ', 'C', 'a', 'r', 'd',
0x51, 0x04, 0x3f, 0x00, 0x50, 0x15
};
static int jcop_init(sc_card_t *card)
{
struct jcop_private_data *drvdata;
sc_file_t *f;
int flags;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
drvdata=malloc(sizeof(struct jcop_private_data));
if (!drvdata)
return SC_ERROR_OUT_OF_MEMORY;
memset(drvdata, 0, sizeof(struct jcop_private_data));
sc_format_path("A000:0000:6350:4B43:532D:3135", &drvdata->aid);
drvdata->aid.type = SC_PATH_TYPE_DF_NAME;
drvdata->selected=SELECT_MF;
drvdata->invalid_senv=1;
drvdata->nfiles=-1;
drvdata->filelist=NULL;
f=sc_file_new();
if (!f){
free(drvdata);
return SC_ERROR_OUT_OF_MEMORY;
}
sc_format_path("3f00", &f->path);
f->type=SC_FILE_TYPE_DF;
f->shareable=0;
f->ef_structure=SC_FILE_EF_UNKNOWN;
f->size=0;
f->id=0x3f00;
f->status=SC_FILE_STATUS_ACTIVATED;
sc_file_add_acl_entry(f, SC_AC_OP_SELECT, SC_AC_NONE, 0);
sc_file_add_acl_entry(f, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0);
sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_DELETE, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_CREATE, SC_AC_NEVER, 0);
drvdata->virtmf=f;
f=sc_file_new();
if (!f){
sc_file_free(drvdata->virtmf);
free(drvdata);
return SC_ERROR_OUT_OF_MEMORY;
}
sc_format_path("3f002f00", &f->path);
f->type=SC_FILE_TYPE_WORKING_EF;
f->shareable=0;
f->ef_structure=SC_FILE_EF_TRANSPARENT;
f->size=128;
f->id=0x2f00;
f->status=SC_FILE_STATUS_ACTIVATED;
sc_file_add_acl_entry(f, SC_AC_OP_READ, SC_AC_NONE, 0);
sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_ERASE, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_UPDATE, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_WRITE, SC_AC_NEVER, 0);
sc_file_add_acl_entry(f, SC_AC_OP_CRYPTO, SC_AC_NEVER, 0);
drvdata->virtdir=f;
card->drv_data = drvdata;
card->cla = 0x00;
/* card supports host-side padding, but not raw rsa */
flags = SC_ALGORITHM_RSA_PAD_PKCS1;
flags |= SC_ALGORITHM_RSA_HASH_NONE;
flags |= SC_ALGORITHM_RSA_HASH_SHA1;
flags |= SC_ALGORITHM_RSA_HASH_MD5;
/* only supports keygen with 3 and F-4 exponents */
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
_sc_card_add_rsa_alg(card, 512, flags, 0);
_sc_card_add_rsa_alg(card, 768, flags, 0);
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
/* State that we have an RNG */
card->caps |= SC_CARD_CAP_RNG;
return 0;
}
static int jcop_get_default_key(sc_card_t *card,
struct sc_cardctl_default_key *data)
{
const char *key;
if (data->method != SC_AC_PRO || data->key_ref > 2)
return SC_ERROR_NO_DEFAULT_KEY;
key = "40:41:42:43:44:45:46:47:48:49:4A:4B:4C:4D:4E:4F";
return sc_hex_to_bin(key, data->key_data, &data->len);
}
/* since the card is actually a javacard, we're expected to use ISO
7816-4 direct application selection instead of reading the DIR
ourselves and selecting the AppDF by path. Since opensc doesn' do
that, I fake an MF containing the AppDF and a fixed DIR pointing at
the fake AppDF. This has the added advantage of allowing
opensc-explorer to be used with this driver */
static int jcop_select_file(sc_card_t *card, const sc_path_t *path,
sc_file_t **file)
{
struct jcop_private_data *drvdata=DRVDATA(card);
int r,selecting;
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
sc_path_t shortpath;
sc_file_t *tfile, **fileptr;
if (!drvdata)
return SC_ERROR_FILE_NOT_FOUND;
/* Something about the card does not like Case 4 APDU's to be sent as
Case 3. you must send a length and accept a response. */
if (file) {
fileptr=file;
} else {
fileptr=&tfile;
}
/* Selecting the MF. return a copy of the constructed MF */
if (path->len == 2 && memcmp(path->value, "\x3F\x00", 2) == 0) {
drvdata->selected=SELECT_MF;
if (file) {
sc_file_dup(file, drvdata->virtmf);
if (*file == NULL)
return SC_ERROR_OUT_OF_MEMORY;
}
return 0;
}
/* Selecting the EF(DIR). return a copy of the constructed EF(DIR) */
if ((path->len == 4 &&
memcmp(path->value, "\x3F\x00\x2F\x00", 4) == 0) ||
(drvdata->selected == SELECT_MF && path->len == 2 &&
memcmp(path->value, "\x2F\x00", 2) == 0)) {
drvdata->selected=SELECT_EFDIR;
if (file) {
sc_file_dup(file, drvdata->virtdir);
if (*file == NULL)
return SC_ERROR_OUT_OF_MEMORY;
}
return 0;
}
/* selecting the PKCS15 AppDF or a file in it. Select the applet, then
pass through any remaining path components to the applet's select
command
*/
selecting=SELECT_UNKNOWN;
if (path->len >= 4 &&
memcmp(path->value, "\x3F\x00\x50\x15", 4) == 0) {
if (path->len == 4)
selecting = SELECTING_ABS | SELECT_APPDF;
else
selecting = SELECTING_ABS | SELECT_EF;
}
if (drvdata->selected==SELECT_MF &&
memcmp(path->value, "\x50\x15", 2) == 0) {
if (path->len == 2)
selecting = SELECTING_VIA_APPDF | SELECT_APPDF;
else
selecting = SELECTING_VIA_APPDF | SELECT_EF;
}
if (selecting & (SELECTING_ABS|SELECTING_VIA_APPDF))
{
if (file == NULL &&
(selecting & SELECTING_TARGET) == SELECT_APPDF &&
drvdata->selected == SELECT_APPDF) {
return 0;
}
if ((r = iso_ops->select_file(card, &drvdata->aid, fileptr)) < 0)
return r;
2015-10-14 21:08:39 +00:00
if (fileptr && (selecting & SELECTING_TARGET) == SELECT_APPDF) {
(*fileptr)->type = SC_FILE_TYPE_DF;
drvdata->selected=SELECT_APPDF;
goto select_ok;
}
sc_file_free(*fileptr);
*fileptr=NULL;
memset(&shortpath, 0, sizeof(sc_path_t));
if (selecting & SELECTING_ABS) {
memcpy(&shortpath.value, &path->value[4], path->len-4);
shortpath.len=path->len-4;
} else {
memcpy(&shortpath.value, &path->value[2], path->len-2);
shortpath.len=path->len-2;
}
shortpath.type = shortpath.len == 2 ? SC_PATH_TYPE_FILE_ID :
path->type;
shortpath.index=path->index;
shortpath.count=path->count;
path=&shortpath;
} else {
/* There seems to be better debugging output if I call sc_check_sw
* with appropriate input than if I just return the appropriate
* SC_ERROR_*, so that's what I do for all errors returned by code
* related to the MF/DIR emulation
*/
if (drvdata->selected == SELECT_MF ||
drvdata->selected == SELECT_EFDIR)
return sc_check_sw(card, 0x6A, 0x82);
}
r = iso_ops->select_file(card, path, fileptr);
if (r)
return r;
drvdata->selected=SELECT_EF;
select_ok:
if (!file) {
sc_file_free(*fileptr);
}
return 0;
}
static int jcop_read_binary(sc_card_t *card, unsigned int idx,
u8 * buf, size_t count, unsigned long flags) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
int r;
if (drvdata->selected == SELECT_MF) {
return sc_check_sw(card, 0x69, 0x86);
}
if (drvdata->selected == SELECT_EFDIR) {
if (idx > 127) {
return sc_check_sw(card, 0x6A, 0x86);
}
if (idx + count > 128) {
count=128-idx;
}
2015-10-14 21:08:39 +00:00
r = iso_ops->select_file(card, &drvdata->aid, NULL);
if (r < 0) { /* no pkcs15 app, so return empty DIR. */
memset(buf, 0, count);
} else {
memcpy(buf, (u8 *)(ef_dir_contents + idx), count);
}
return count;
}
return iso_ops->read_binary(card, idx, buf, count, flags);
}
static int jcop_list_files(sc_card_t *card, u8 *buf, size_t buflen) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
int r;
if (drvdata->selected == SELECT_MF) {
if (buflen < 2)
return 0;
memcpy(buf, "\x2f\x00", 2);
if (buflen < 4)
return 2;
/* AppDF only exists if applet is selectable */
2015-10-14 20:37:33 +00:00
r = iso_ops->select_file(card, &drvdata->aid, NULL);
if (r < 0) {
return 2;
} else {
memcpy(buf+2, "\x50\x15", 2);
return 4;
}
}
if (drvdata->nfiles == -1)
return SC_ERROR_NOT_ALLOWED;
if (drvdata->nfiles == 0)
return 0;
if (buflen > 2 * (size_t)drvdata->nfiles)
buflen=2*drvdata->nfiles;
memcpy(buf, drvdata->filelist, buflen);
return buflen;
}
static int sa_to_acl(sc_file_t *file, unsigned int operation,
int nibble) {
switch (nibble & 0x7) {
case 0:
sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE);
break;
case 1:
sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE);
break;
case 2:
sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1);
break;
case 3:
sc_file_add_acl_entry(file, operation, SC_AC_CHV, 2);
break;
case 4:
sc_file_add_acl_entry(file, operation, SC_AC_CHV, 3);
break;
case 5:
sc_file_add_acl_entry(file, operation, SC_AC_AUT, SC_AC_KEY_REF_NONE);
break;
case 6:
sc_file_add_acl_entry(file, operation, SC_AC_PRO, SC_AC_KEY_REF_NONE);
break;
default:
sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
}
return 0;
}
static int jcop_process_fci(sc_card_t *card, sc_file_t *file,
const u8 *buf, size_t buflen) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
u8 *sa;
int r;
/* the FCI for EF's includes a bogus length for the overall structure! */
if (buflen == 19)
buflen=24;
r=iso_ops->process_fci(card, file, buf, buflen);
if (r < 0)
return r;
if (file->type != SC_FILE_TYPE_DF) {
if (drvdata->nfiles) {
drvdata->nfiles=-1;
free(drvdata->filelist);
drvdata->filelist=NULL;
}
if(file->sec_attr_len >=3) {
/* The security attribute bytes are divided into nibbles and are
as follows:
READ | MODIFY || SIGN | ENCIPHER || DECIPHER | DELETE
*/
sa=file->sec_attr;
sa_to_acl(file, SC_AC_OP_READ, sa[0] >> 4);
sa_to_acl(file, SC_AC_OP_UPDATE, sa[0] & 0xf);
/* Files may be locked by anyone who can MODIFY. */
/* opensc seems to think LOCK ACs are only on DFs */
/* sa_to_acl(file, SC_AC_OP_LOCK, sa[0] & 0xf); */
/* there are separate SIGN, ENCIPHER, and DECIPHER ACs.
I use SIGN for SC_AC_OP_CRYPTO unless it is NEVER, in
which case I use DECIPHER */
if ((sa[1] & 0xf0) == 0x10)
sa_to_acl(file, SC_AC_OP_CRYPTO, sa[1] >> 4);
else
sa_to_acl(file, SC_AC_OP_CRYPTO, sa[2] >> 4);
sa_to_acl(file, SC_AC_OP_ERASE, sa[2] & 0xf);
}
} else {
/* No AC information is reported for the AppDF */
sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, 0);
sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, 3);
sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NONE, 0);
sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0);
if (drvdata->nfiles) {
drvdata->nfiles=0;
free(drvdata->filelist);
drvdata->filelist=NULL;
}
/* the format of the poprietary attributes is:
4 bytes unique id
1 byte # files in DF
2 bytes 1st File ID
2 bytes 2nd File ID
...
*/
if (file->prop_attr_len > 4) {
int nfiles;
u8 *filelist;
nfiles=file->prop_attr[4];
if (nfiles) {
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
filelist=malloc(2*nfiles);
if (!filelist)
return SC_ERROR_OUT_OF_MEMORY;
memcpy(filelist, &file->prop_attr[5], 2*nfiles);
drvdata->nfiles=nfiles;
drvdata->filelist=filelist;
}
}
}
return r;
}
static int acl_to_ac_nibble(const sc_acl_entry_t *e)
{
if (e == NULL)
return -1;
if (e->next != NULL) /* FIXME */
return -1;
switch (e->method) {
case SC_AC_NONE:
return 0x00;
case SC_AC_NEVER:
return 0x01;
case SC_AC_CHV:
switch (e->key_ref) {
case 1:
return 0x02;
case 2:
return 0x03;
case 3:
return 0x04;
}
return -1;
case SC_AC_AUT:
return 0x05;
case SC_AC_PRO:
return 0x06;
}
return -1;
}
static int jcop_create_file(sc_card_t *card, sc_file_t *file) {
struct jcop_private_data *drvdata=DRVDATA(card);
unsigned char sec_attr_data[3];
int ops[6];
int i, r;
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR )
return sc_check_sw(card, 0x69, 0x82);
/* Can't create DFs */
if (file->type != SC_FILE_TYPE_WORKING_EF)
return sc_check_sw(card, 0x6A, 0x80);
ops[0] = SC_AC_OP_READ; /* read */
ops[1] = SC_AC_OP_UPDATE; /* modify */
ops[2] = SC_AC_OP_CRYPTO; /* sign */
ops[3] = -1; /* encipher */
ops[4] = SC_AC_OP_CRYPTO; /* decipher */
ops[5] = SC_AC_OP_ERASE; /* delete */
memset(sec_attr_data, 0, 3);
for (i = 0; i < 6; i++) {
const sc_acl_entry_t *entry;
if (ops[i] == -1) {
sec_attr_data[i/2] |= 1 << ((i % 2) ? 0 : 4);
continue;
}
entry = sc_file_get_acl_entry(file, ops[i]);
r = acl_to_ac_nibble(entry);
sec_attr_data[i/2] |= r << ((i % 2) ? 0 : 4);
}
sc_file_set_sec_attr(file, sec_attr_data, 3);
r=iso_ops->create_file(card, file);
if (r > 0)
drvdata->selected=SELECT_EF;
return r;
}
/* We need to trap these functions so that proper errors can be returned
when one of the virtual files is selected */
static int jcop_write_binary(sc_card_t *card,
unsigned int idx, const u8 *buf,
size_t count, unsigned long flags) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
if (drvdata->selected == SELECT_MF)
return sc_check_sw(card, 0x6A, 0x86);
if (drvdata->selected == SELECT_EFDIR)
return sc_check_sw(card, 0x69, 0x82);
return iso_ops->write_binary(card, idx, buf, count, flags);
}
static int jcop_update_binary(sc_card_t *card,
unsigned int idx, const u8 *buf,
size_t count, unsigned long flags) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
if (drvdata->selected == SELECT_MF)
return sc_check_sw(card, 0x69, 0x86);
if (drvdata->selected == SELECT_EFDIR)
return sc_check_sw(card, 0x69, 0x82);
return iso_ops->update_binary(card, idx, buf, count, flags);
}
static int jcop_delete_file(sc_card_t *card, const sc_path_t *path) {
struct jcop_private_data *drvdata=DRVDATA(card);
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
const struct sc_card_operations *iso_ops = iso_drv->ops;
if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR )
return sc_check_sw(card, 0x69, 0x82);
return iso_ops->delete_file(card, path);
}
/* BlueZ doesn't support stored security environments. you have
to construct one with SET every time */
static int jcop_set_security_env(sc_card_t *card,
const sc_security_env_t *env,
int se_num)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 *p;
int r;
struct jcop_private_data *drvdata=DRVDATA(card);
assert(card != NULL && env != NULL);
if (se_num)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
if (drvdata->selected == SELECT_MF ||
drvdata->selected == SELECT_EFDIR) {
drvdata->invalid_senv=1;
return 0;
}
if (env->flags & SC_SEC_ENV_ALG_PRESENT) {
sc_security_env_t tmp;
tmp = *env;
tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT;
tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT;
if (tmp.algorithm != SC_ALGORITHM_RSA) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)){
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card requires RSA padding\n");
return SC_ERROR_NOT_SUPPORTED;
}
tmp.algorithm_ref = 0x02;
/* potential FIXME: return an error, if an unsupported
* pad or hash was requested, although this shouldn't happen.
*/
if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
tmp.algorithm_ref |= 0x10;
if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5)
tmp.algorithm_ref |= 0x20;
memcpy((sc_security_env_t *) env, &tmp, sizeof(struct sc_security_env));
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0);
switch (env->operation) {
case SC_SEC_OPERATION_DECIPHER:
apdu.p2 = 0xB8;
break;
case SC_SEC_OPERATION_SIGN:
apdu.p2 = 0xB6;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
apdu.le = 0;
2017-10-27 17:11:15 +00:00
if (!(env->flags & SC_SEC_ENV_ALG_REF_PRESENT))
return SC_ERROR_INVALID_ARGUMENTS;
2013-05-25 02:22:28 +00:00
if (!(env->flags & SC_SEC_ENV_FILE_REF_PRESENT))
return SC_ERROR_INVALID_ARGUMENTS;
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
if (env->key_ref_len > 1 || env->key_ref[0] != 0)
return SC_ERROR_INVALID_ARGUMENTS;
}
p = sbuf;
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
*p++ = env->algorithm_ref & 0xFF;
*p++ = 0x81;
*p++ = env->file_ref.len;
memcpy(p, env->file_ref.value, env->file_ref.len);
p += env->file_ref.len;
r = p - sbuf;
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
apdu.resplen = 0;
r = sc_transmit_apdu(card, &apdu);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: APDU transmit failed", sc_strerror(r));
return r;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: Card returned error", sc_strerror(r));
return r;
}
drvdata->invalid_senv=0;
return 0;
}
static int jcop_compute_signature(sc_card_t *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen) {
int r;
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
struct jcop_private_data *drvdata=DRVDATA(card);
assert(card != NULL && data != NULL && out != NULL);
if (datalen > 256)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
if (drvdata->invalid_senv)
return sc_check_sw(card, 0x69, 0x88);
/* INS: 0x2A PERFORM SECURITY OPERATION
* P1: 0x9E Resp: Digital Signature
* P2: 0x9A Cmd: Input for Digital Signature */
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E,
0x9A);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf); /* FIXME */
apdu.le = 256;
if (datalen == 256) {
apdu.p2 = data[0];
memcpy(sbuf, data+1, datalen-1);
apdu.lc = datalen - 1;
apdu.datalen = datalen - 1;
} else {
memcpy(sbuf, data, datalen);
apdu.lc = datalen;
apdu.datalen = datalen;
}
apdu.data = sbuf;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
memcpy(out, apdu.resp, len);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
static int jcop_decipher(sc_card_t *card,
const u8 * crgram, size_t crgram_len,
u8 * out, size_t outlen) {
int r;
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
struct jcop_private_data *drvdata=DRVDATA(card);
assert(card != NULL && crgram != NULL && out != NULL);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
if (crgram_len > 256)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
if (drvdata->invalid_senv)
return sc_check_sw(card, 0x69, 0x88);
/* INS: 0x2A PERFORM SECURITY OPERATION
* P1: 0x80 Resp: Plain value
* P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf); /* FIXME */
apdu.le = crgram_len;
if (crgram_len == 256) {
apdu.p2 = crgram[0];
memcpy(sbuf, crgram+1, crgram_len-1);
apdu.lc = crgram_len - 1;
apdu.datalen = crgram_len -1;
} else {
sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */
memcpy(sbuf + 1, crgram, crgram_len);
apdu.lc = crgram_len + 1;
apdu.datalen = crgram_len + 1;
}
apdu.data = sbuf;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
memcpy(out, apdu.resp, len);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
static int jcop_generate_key(sc_card_t *card, struct sc_cardctl_jcop_genkey *a) {
int r;
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 *p;
int is_f4;
struct jcop_private_data *drvdata=DRVDATA(card);
if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR )
return sc_check_sw(card, 0x6A, 0x82);
is_f4=0;
if (a->exponent == 0x10001) {
is_f4=1;
} else if (a->exponent != 3) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: Invalid exponent", sc_strerror(SC_ERROR_NOT_SUPPORTED));
return SC_ERROR_NOT_SUPPORTED;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0xB6);
p = sbuf;
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
*p++ = is_f4 ? 0x6E : 0x6D;
*p++ = 0x81;
*p++ = a->pub_file_ref.len;
memcpy(p, a->pub_file_ref.value, a->pub_file_ref.len);
p += a->pub_file_ref.len;
*p++ = 0x81;
*p++ = a->pri_file_ref.len;
memcpy(p, a->pri_file_ref.value, a->pri_file_ref.len);
p += a->pri_file_ref.len;
r = p - sbuf;
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
apdu.resplen = 0;
r = sc_transmit_apdu(card, &apdu);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: APDU transmit failed", sc_strerror(r));
return r;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: Card returned error", sc_strerror(r));
return r;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0, 0);
apdu.le = 256;
apdu.resp=rbuf;
apdu.resplen = sizeof(rbuf);
r = sc_transmit_apdu(card, &apdu);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: APDU transmit failed", sc_strerror(r));
return r;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%s: Card returned error", sc_strerror(r));
return r;
}
if (rbuf[0] != 0x4) {
return SC_ERROR_INVALID_DATA;
}
if (a->pubkey_len < rbuf[1])
return SC_ERROR_BUFFER_TOO_SMALL;
a->pubkey_len=rbuf[1] * 4;
memcpy(a->pubkey, &rbuf[2], a->pubkey_len);
return 0;
}
static int jcop_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
switch (cmd) {
case SC_CARDCTL_GET_DEFAULT_KEY:
return jcop_get_default_key(card,
(struct sc_cardctl_default_key *) ptr);
case SC_CARDCTL_JCOP_GENERATE_KEY:
return jcop_generate_key(card,
(struct sc_cardctl_jcop_genkey *) ptr);
}
return SC_ERROR_NOT_SUPPORTED;
}
static struct sc_card_driver * sc_get_driver(void)
{
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
jcop_ops = *iso_drv->ops;
jcop_ops.match_card = jcop_match_card;
jcop_ops.init = jcop_init;
jcop_ops.finish = jcop_finish;
/* no record oriented file services */
jcop_ops.read_record = NULL;
jcop_ops.write_record = NULL;
jcop_ops.append_record = NULL;
jcop_ops.update_record = NULL;
jcop_ops.read_binary = jcop_read_binary;
jcop_ops.write_binary = jcop_write_binary;
jcop_ops.update_binary = jcop_update_binary;
jcop_ops.select_file = jcop_select_file;
jcop_ops.create_file = jcop_create_file;
jcop_ops.delete_file = jcop_delete_file;
jcop_ops.list_files = jcop_list_files;
jcop_ops.set_security_env = jcop_set_security_env;
jcop_ops.compute_signature = jcop_compute_signature;
jcop_ops.decipher = jcop_decipher;
jcop_ops.process_fci = jcop_process_fci;
jcop_ops.card_ctl = jcop_card_ctl;
return &jcop_drv;
}
struct sc_card_driver * sc_get_jcop_driver(void)
{
return sc_get_driver();
}