#define __CARD_DNIE_C__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(ENABLE_OPENSSL) && defined(ENABLE_SM)
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include "opensc.h"
#include "cardctl.h"
#include "internal.h"
#include "compression.h"
#include "cwa14890.h"
#include "cwa-dnie.h"
#ifdef _WIN32
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#endif
#ifdef __APPLE__
#include <Carbon/Carbon.h>
#endif
#define MAX_RESP_BUFFER_SIZE 2048
#define USER_CONSENT_TITLE "Confirm"
extern int dnie_read_file(
sc_card_t * card,
const sc_path_t * path,
sc_file_t ** file,
u8 ** buffer, size_t * length);
#define DNIE_CHIP_NAME "DNIe: Spanish eID card"
#define DNIE_CHIP_SHORTNAME "dnie"
#define DNIE_MF_NAME "Master.File"
#define USER_CONSENT_CMD "/usr/bin/pinentry"
static const struct sc_card_error dnie_errors[] = {
{0x6688, SC_ERROR_SM, "Cryptographic checksum invalid"},
{0x6987, SC_ERROR_SM, "Expected SM Data Object missing"},
{0x6988, SC_ERROR_SM, "SM Data Object incorrect"},
{0, 0, NULL}
};
static struct sc_atr_table dnie_atrs[] = {
{
"3B:7F:00:00:00:00:6A:44:4E:49:65:00:00:00:00:00:00:03:90:00",
"FF:FF:00:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:FF:FF:FF",
DNIE_CHIP_SHORTNAME,
SC_CARD_TYPE_DNIE_USER,
0,
NULL},
{
"3B:7F:00:00:00:00:6A:44:4E:49:65:00:00:00:00:00:00:0F:65:81",
"FF:FF:00:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:FF:FF:FF",
DNIE_CHIP_SHORTNAME,
SC_CARD_TYPE_DNIE_TERMINATED,
0,
NULL},
{NULL, NULL, NULL, 0, 0, NULL}
};
const char *user_consent_title="Signature Requested";
#ifdef linux
const char *user_consent_message="Está a punto de realizar una firma electrónica con su clave de FIRMA del DNI electrónico. ¿Desea permitir esta operación?";
#else
const char *user_consent_message="Esta a punto de realizar una firma digital\ncon su clave de FIRMA del DNI electronico.\nDesea permitir esta operacion?";
#endif
#ifdef ENABLE_DNIE_UI
char *user_consent_msgs[] = { "SETTITLE", "SETDESC", "CONFIRM", "BYE" };
#if !defined(__APPLE__) && !defined(_WIN32)
static char *nointr_fgets(char *s, int size, FILE *stream)
{
while (fgets(s, size, stream) == NULL) {
if (feof(stream) || errno != EINTR)
return NULL;
}
return s;
}
#endif
int dnie_ask_user_consent(struct sc_card * card, const char *title, const char *message)
{
#ifdef __APPLE__
CFOptionFlags result;
CFStringRef header_ref;
CFStringRef message_ref;
#endif
#if !defined(__APPLE__) && !defined(_WIN32)
pid_t pid;
FILE *fin=NULL;
FILE *fout=NULL;
struct stat st_file;
int srv_send[2];
int srv_recv[2];
char outbuf[1024];
char buf[1024];
int n = 0;
#endif
int res = SC_ERROR_INTERNAL;
char *msg = NULL;
if ((card == NULL) || (card->ctx == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if ((title==NULL) || (message==NULL))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
if (GET_DNIE_UI_CTX(card).user_consent_enabled == 0
|| card->ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS) {
sc_log(card->ctx,
"User Consent or popups are disabled in configuration file");
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
#ifdef _WIN32
res = MessageBox (
NULL,
TEXT(message),
TEXT(title),
MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2 | MB_APPLMODAL
);
if ( res == IDOK )
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED);
#elif __APPLE__
header_ref = CFStringCreateWithCString( NULL, title, strlen(title) );
message_ref = CFStringCreateWithCString( NULL,message, strlen(message) );
CFUserNotificationDisplayAlert(
0,
kCFUserNotificationNoteAlertLevel,
NULL,
NULL,
NULL,
header_ref,
message_ref,
CFSTR("Cancel"),
CFSTR("OK"),
NULL,
&result
);
CFRelease( header_ref );
CFRelease( message_ref );
if( result == kCFUserNotificationAlternateResponse )
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED);
#else
if (pipe(srv_send) < 0) {
msg = "pipe(srv_send)";
goto do_error;
}
if (pipe(srv_recv) < 0) {
msg = "pipe(srv_recv)";
goto do_error;
}
pid = fork();
switch (pid) {
case -1:
msg = "fork()";
goto do_error;
case 0:
dup2(srv_send[0], STDIN_FILENO);
dup2(srv_recv[1], STDOUT_FILENO);
close(srv_send[0]);
close(srv_send[1]);
close(srv_recv[0]);
close(srv_recv[1]);
res = stat(GET_DNIE_UI_CTX(card).user_consent_app, &st_file);
if (res != 0) {
sc_log(card->ctx, "Invalid pinentry application: %s\n",
GET_DNIE_UI_CTX(card).user_consent_app);
} else {
execlp(GET_DNIE_UI_CTX(card).user_consent_app, GET_DNIE_UI_CTX(card).user_consent_app, (char *)NULL);
sc_log(card->ctx, "execlp() error");
}
abort();
default:
close(srv_send[0]);
close(srv_recv[1]);
fin = fdopen(srv_recv[0], "r");
if (fin == NULL) {
msg = "fdopen(in)";
goto do_error;
}
fout = fdopen(srv_send[1], "w");
if (fout == NULL) {
msg = "fdopen(out)";
goto do_error;
}
if (nointr_fgets(buf, sizeof(buf), fin) == NULL) {
res = SC_ERROR_INTERNAL;
msg = "nointr_fgets() Unexpected IOError/EOF";
goto do_error;
}
for (n = 0; n<4; n++) {
char *pt;
if (n==0) snprintf(outbuf, sizeof outbuf,"%s %s\n",user_consent_msgs[0],title);
else if (n==1) snprintf(outbuf, sizeof outbuf,"%s %s\n",user_consent_msgs[1],message);
else snprintf(outbuf, sizeof outbuf,"%s\n",user_consent_msgs[n]);
fputs(outbuf, fout);
fflush(fout);
pt=nointr_fgets(buf, sizeof(buf), fin);
if (pt==NULL) {
res = SC_ERROR_INTERNAL;
msg = "nointr_fgets() Unexpected IOError/EOF";
goto do_error;
}
if (strstr(buf, "OK") == NULL) {
res = SC_ERROR_NOT_ALLOWED;
msg = "fail/cancel";
goto do_error;
}
}
}
res = SC_SUCCESS;
msg = NULL;
do_error:
if (fout != NULL) fclose(fout);
if (fin != NULL) fclose(fin);
#endif
if (msg != NULL)
sc_log(card->ctx, "%s", msg);
LOG_FUNC_RETURN(card->ctx, res);
}
#endif
static struct sc_card_operations dnie_ops;
static struct sc_card_operations *iso_ops = NULL;
static sc_card_driver_t dnie_driver = {
DNIE_CHIP_NAME,
DNIE_CHIP_SHORTNAME,
&dnie_ops,
dnie_atrs,
0,
NULL
};
#ifdef ENABLE_DNIE_UI
static int dnie_get_environment(
sc_card_t * card,
ui_context_t * ui_context)
{
int i;
scconf_block **blocks, *blk;
sc_context_t *ctx;
ui_context->user_consent_app = USER_CONSENT_CMD;
ui_context->user_consent_enabled = 1;
ctx = card->ctx;
for (i = 0; ctx->conf_blocks[i]; i++) {
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
"card_driver", "dnie");
if (!blocks)
continue;
blk = blocks[0];
free(blocks);
if (blk == NULL)
continue;
ui_context->user_consent_app =
(char *)scconf_get_str(blk, "user_consent_app",
USER_CONSENT_CMD);
ui_context->user_consent_enabled =
scconf_get_bool(blk, "user_consent_enabled", 1);
}
return SC_SUCCESS;
}
#endif
static int dnie_generate_key(sc_card_t * card, void *data)
{
int result = SC_ERROR_NOT_SUPPORTED;
if ((card == NULL) || (data == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
LOG_FUNC_RETURN(card->ctx, result);
}
static char *findPattern(u8 *pat, u8 *buf, size_t len)
{
char *res = NULL;
u8 *from = buf;
int size = 0;
for ( from = buf; from < buf+len-6; from++) {
if (memcmp(from,pat,6) == 0 ) goto data_found;
}
return NULL;
data_found:
size = 0x000000ff & (int) *(from+6);
if ( size == 0 ) return NULL;
res = calloc( size+1, sizeof(char) );
if ( res == NULL) return NULL;
memcpy(res,from+7,size);
return res;
}
static int dnie_get_info(sc_card_t * card, char *data[])
{
sc_file_t *file = NULL;
sc_path_t path;
u8 *buffer = NULL;
size_t bufferlen = 0;
char *msg = NULL;
u8 SerialNumber [] = { 0x06, 0x03, 0x55, 0x04, 0x05, 0x13 };
u8 Name [] = { 0x06, 0x03, 0x55, 0x04, 0x04, 0x0C };
u8 GivenName [] = { 0x06, 0x03, 0x55, 0x04, 0x2A, 0x0C };
int res = SC_ERROR_NOT_SUPPORTED;
if ((card == NULL) || (data == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
sc_format_path("3F0050156004", &path);
res = dnie_read_file(card, &path, &file, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot read EF(CDF)";
goto get_info_end;
}
data[0]= findPattern(SerialNumber,buffer,bufferlen);
data[1]= findPattern(Name,buffer,bufferlen);
data[2]= findPattern(GivenName,buffer,bufferlen);
if ( ! data[0] || !data[1] || !data[2] ) {
res = SC_ERROR_INVALID_DATA;
msg = "Cannot retrieve info from EF(CDF)";
goto get_info_end;
}
sc_format_path("3F000006", &path);
sc_file_free(file);
file = NULL;
if (buffer) {
free(buffer);
buffer=NULL;
bufferlen=0;
}
res = dnie_read_file(card, &path, &file, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
data[3]=NULL;
goto get_info_ph3;
}
data[3]=calloc(bufferlen+1,sizeof(char));
if ( !data[3] ) {
msg = "Cannot allocate memory for IDESP data";
res = SC_ERROR_OUT_OF_MEMORY;
goto get_info_end;
}
memcpy(data[3],buffer,bufferlen);
get_info_ph3:
sc_format_path("3F002F03", &path);
sc_file_free(file);
file = NULL;
if (buffer) {
free(buffer);
buffer=NULL;
bufferlen=0;
}
res = dnie_read_file(card, &path, &file, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot read DNIe Version EF";
data[4]=NULL;
res = SC_SUCCESS;
goto get_info_end;
}
data[4]=calloc(bufferlen+1,sizeof(char));
if ( !data[4] ) {
msg = "Cannot allocate memory for DNIe Version data";
res = SC_ERROR_OUT_OF_MEMORY;
goto get_info_end;
}
memcpy(data[4],buffer,bufferlen);
res = SC_SUCCESS;
msg = NULL;
get_info_end:
sc_file_free(file);
file = NULL;
if (buffer) {
free(buffer);
buffer=NULL;
bufferlen=0;
}
if (msg)
sc_log(card->ctx, "%s", msg);
LOG_FUNC_RETURN(card->ctx, res);
}
static int dnie_get_serialnr(sc_card_t * card, sc_serial_number_t * serial)
{
int result;
sc_apdu_t apdu;
u8 rbuf[MAX_RESP_BUFFER_SIZE];
if ((card == NULL) || (card->ctx == NULL) || (serial == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if (card->type != SC_CARD_TYPE_DNIE_USER)
return SC_ERROR_NOT_SUPPORTED;
if (card->serialnr.len) {
memcpy(serial, &card->serialnr, sizeof(*serial));
sc_log_hex(card->ctx, "Serial Number (cached)", serial->value, serial->len);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
dnie_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xb8, 0x00, 0x00, 0x07, 0,
rbuf, sizeof(rbuf), NULL, 0);
apdu.cla = 0x90;
result = sc_transmit_apdu(card, &apdu);
if (result != SC_SUCCESS) {
LOG_TEST_RET(card->ctx, result, "APDU transmit failed");
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
return SC_ERROR_INTERNAL;
memcpy(card->serialnr.value, apdu.resp, 7 * sizeof(u8));
card->serialnr.len = 7 * sizeof(u8);
memcpy(serial, &card->serialnr, sizeof(*serial));
sc_log_hex(card->ctx, "Serial Number (apdu)", serial->value, serial->len);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static void dnie_clear_cache(dnie_private_data_t * data)
{
if (data == NULL) return;
if (data->cache != NULL)
free(data->cache);
data->cache = NULL;
data->cachelen = 0;
}
static void init_flags(struct sc_card *card)
{
unsigned long algoflags;
card->name = DNIE_CHIP_SHORTNAME;
card->cla = 0x00;
card->caps |= SC_CARD_CAP_RNG;
card->max_send_size = (255 - 12);
card->max_recv_size = 255;
algoflags = SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_PAD_PKCS1;
_sc_card_add_rsa_alg(card, 1024, algoflags, 0);
_sc_card_add_rsa_alg(card, 1920, algoflags, 0);
_sc_card_add_rsa_alg(card, 2048, algoflags, 0);
}
int dnie_match_card(struct sc_card *card)
{
int result = 0;
int matched = -1;
LOG_FUNC_CALLED(card->ctx);
matched = _sc_match_atr(card, dnie_atrs, &card->type);
result = (matched >= 0) ? 1 : 0;
LOG_FUNC_RETURN(card->ctx, result);
}
static int dnie_sm_free_wrapped_apdu(struct sc_card *card,
struct sc_apdu *plain, struct sc_apdu **sm_apdu)
{
struct sc_context *ctx = card->ctx;
cwa_provider_t *provider = NULL;
int rv = SC_SUCCESS;
LOG_FUNC_CALLED(ctx);
provider = GET_DNIE_PRIV_DATA(card)->cwa_provider;
if (!sm_apdu)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
if (!(*sm_apdu))
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
if ((*sm_apdu) != plain) {
rv = cwa_decode_response(card, provider, *sm_apdu);
if (plain && rv == SC_SUCCESS) {
if (plain->resp) {
if ((*sm_apdu)->resplen <= plain->resplen) {
memcpy(plain->resp, (*sm_apdu)->resp, (*sm_apdu)->resplen);
plain->resplen = (*sm_apdu)->resplen;
} else {
sc_log(card->ctx, "Invalid initial length,"
" needed %"SC_FORMAT_LEN_SIZE_T"u bytes"
" but has %"SC_FORMAT_LEN_SIZE_T"u",
(*sm_apdu)->resplen, plain->resplen);
rv = SC_ERROR_BUFFER_TOO_SMALL;
}
}
plain->sw1 = (*sm_apdu)->sw1;
plain->sw2 = (*sm_apdu)->sw2;
}
free((unsigned char *) (*sm_apdu)->data);
free((*sm_apdu)->resp);
free(*sm_apdu);
}
*sm_apdu = NULL;
LOG_FUNC_RETURN(ctx, rv);
}
static int dnie_sm_get_wrapped_apdu(struct sc_card *card,
struct sc_apdu *plain, struct sc_apdu **sm_apdu)
{
struct sc_context *ctx = card->ctx;
struct sc_apdu *apdu = NULL;
cwa_provider_t *provider = NULL;
int rv = SC_SUCCESS;
LOG_FUNC_CALLED(ctx);
if (!plain || !sm_apdu)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
provider = GET_DNIE_PRIV_DATA(card)->cwa_provider;
if (((plain->cla & 0x0C) == 0) && (plain->ins != 0xC0)) {
*sm_apdu = NULL;
apdu = calloc(1, sizeof(struct sc_apdu));
if (!apdu)
return SC_ERROR_OUT_OF_MEMORY;
memcpy(apdu, plain, sizeof(sc_apdu_t));
rv = cwa_encode_apdu(card, provider, plain, apdu);
if (rv != SC_SUCCESS) {
dnie_sm_free_wrapped_apdu(card, NULL, &apdu);
goto err;
}
*sm_apdu = apdu;
} else
*sm_apdu = plain;
apdu = NULL;
err:
free(apdu);
LOG_FUNC_RETURN(ctx, rv);
}
static int dnie_init(struct sc_card *card)
{
int res = SC_SUCCESS;
sc_context_t *ctx = card->ctx;
cwa_provider_t *provider = NULL;
LOG_FUNC_CALLED(ctx);
if (card->type == SC_CARD_TYPE_DNIE_TERMINATED)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "DNIe card is terminated.");
provider = dnie_get_cwa_provider(card);
if (!provider)
LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Error initializing cwa-dnie provider");
memset(&(card->sm_ctx), 0, sizeof(sm_context_t));
card->sm_ctx.ops.get_sm_apdu = dnie_sm_get_wrapped_apdu;
card->sm_ctx.ops.free_sm_apdu = dnie_sm_free_wrapped_apdu;
card->sm_ctx.sm_mode = SM_MODE_NONE;
res=cwa_create_secure_channel(card,provider,CWA_SM_OFF);
LOG_TEST_RET(card->ctx, res, "Failure creating CWA secure channel.");
card->drv_data = calloc(1, sizeof(dnie_private_data_t));
if (card->drv_data == NULL)
LOG_TEST_RET(card->ctx, SC_ERROR_OUT_OF_MEMORY, "Could not allocate DNIe private data.");
#ifdef ENABLE_DNIE_UI
res = dnie_get_environment(card, &(GET_DNIE_UI_CTX(card)));
if (res != SC_SUCCESS) {
free(card->drv_data);
LOG_TEST_RET(card->ctx, res, "Failure reading DNIe environment.");
}
#endif
init_flags(card);
GET_DNIE_PRIV_DATA(card)->cwa_provider = provider;
LOG_FUNC_RETURN(card->ctx, res);
}
static int dnie_finish(struct sc_card *card)
{
int result = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
dnie_clear_cache(GET_DNIE_PRIV_DATA(card));
result = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_OFF);
free(GET_DNIE_PRIV_DATA(card)->cwa_provider);
free(card->drv_data);
LOG_FUNC_RETURN(card->ctx, result);
}
static unsigned long le2ulong(u8 * pt)
{
unsigned long res = 0L;
if (pt==NULL) return res;
res = (0xff & *(pt + 0)) +
((0xff & *(pt + 1)) << 8) +
((0xff & *(pt + 2)) << 16) + ((0xff & *(pt + 3)) << 24);
return res;
}
static u8 *dnie_uncompress(sc_card_t * card, u8 * from, size_t *len)
{
u8 *upt = from;
#ifdef ENABLE_ZLIB
int res = SC_SUCCESS;
size_t uncompressed = 0L;
size_t compressed = 0L;
if (!card || !card->ctx || !from || !len)
return NULL;
LOG_FUNC_CALLED(card->ctx);
if (*len < 8)
goto compress_exit;
uncompressed = le2ulong(from);
compressed = le2ulong(from + 4);
if (compressed != (*len) - 8)
goto compress_exit;
if (uncompressed < compressed)
goto compress_exit;
sc_log(card->ctx, "Data seems to be compressed. calling uncompress");
upt = calloc(uncompressed, sizeof(u8));
if (!upt) {
sc_log(card->ctx, "alloc() for uncompressed buffer failed");
return NULL;
}
res = sc_decompress(upt,
(size_t *) & uncompressed,
from + 8, (size_t) compressed, COMPRESSION_ZLIB);
if (res != SC_SUCCESS) {
sc_log(card->ctx, "Uncompress() failed or data not compressed");
goto compress_exit;
}
*len = uncompressed;
sc_log_hex(card->ctx, "Compressed data", from + 8, compressed);
sc_log_hex(card->ctx, "Uncompressed data", upt, uncompressed);
compress_exit:
#endif
sc_log(card->ctx, "uncompress: returning with%s de-compression ",
(upt == from) ? "out" : "");
return upt;
}
static int dnie_fill_cache(sc_card_t * card)
{
u8 tmp[MAX_RESP_BUFFER_SIZE];
sc_apdu_t apdu;
size_t count = 0;
size_t len = 0;
u8 *buffer = NULL;
u8 *pt = NULL, *p;
sc_context_t *ctx = NULL;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
dnie_clear_cache(GET_DNIE_PRIV_DATA(card));
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x00, 0x00);
count = card->max_recv_size;
for (len = 0; len < 0x7fff;) {
int r = SC_SUCCESS;
apdu.p1 = 0xff & (len >> 8);
apdu.p2 = 0xff & len;
apdu.le = count;
apdu.resplen = MAX_RESP_BUFFER_SIZE;
apdu.resp = tmp;
r = sc_transmit_apdu(card, &apdu);
if (r != SC_SUCCESS) {
free(buffer);
if (apdu.resp != tmp)
free(apdu.resp);
sc_log(ctx, "read_binary() APDU transmit failed");
LOG_FUNC_RETURN(ctx, r);
}
if (apdu.resplen == 0) {
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r == SC_ERROR_WRONG_LENGTH) {
count = 0xff & apdu.sw2;
if (count != 0) {
if (apdu.resp != tmp)
free(apdu.resp);
continue;
}
goto read_done;
}
if (r == SC_ERROR_INCORRECT_PARAMETERS)
goto read_done;
free(buffer);
if (apdu.resp != tmp)
free(apdu.resp);
LOG_FUNC_RETURN(ctx, r);
}
count = apdu.resplen;
p = realloc(buffer, len + count);
if (!p) {
free(buffer);
free((void *)apdu.data);
if (apdu.resp != tmp)
free(apdu.resp);
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
}
buffer = p;
memcpy(buffer + len, apdu.resp, count);
if (apdu.resp != tmp) {
free(apdu.resp);
apdu.resp = tmp;
}
len += count;
if (count != card->max_recv_size)
goto read_done;
}
read_done:
pt = dnie_uncompress(card, buffer, &len);
free((void *)apdu.data);
if (apdu.resp != tmp)
free(apdu.resp);
if (pt == NULL) {
sc_log(ctx, "Uncompress process failed");
free(buffer);
LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL);
}
if (pt != buffer)
free(buffer);
GET_DNIE_PRIV_DATA(card)->cache = pt;
GET_DNIE_PRIV_DATA(card)->cachelen = len;
sc_log(ctx,
"fill_cache() done. length '%"SC_FORMAT_LEN_SIZE_T"u' bytes",
len);
LOG_FUNC_RETURN(ctx,len);
}
static int dnie_read_binary(struct sc_card *card,
unsigned int idx,
u8 * buf, size_t count, unsigned long flags)
{
int res = 0;
sc_context_t *ctx = NULL;
if (!card || !card->ctx || !buf || (count <= 0))
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (idx == 0 || GET_DNIE_PRIV_DATA(card)->cache == NULL) {
res = dnie_fill_cache(card);
if (res < 0) {
sc_log(ctx, "Cannot fill cache. using iso_read_binary()");
return iso_ops->read_binary(card, idx, buf, count, flags);
}
}
if (idx >= GET_DNIE_PRIV_DATA(card)->cachelen)
return 0;
res = MIN(count, GET_DNIE_PRIV_DATA(card)->cachelen - idx);
memcpy(buf, GET_DNIE_PRIV_DATA(card)->cache + idx, res);
sc_log(ctx, "dnie_read_binary() '%d' bytes", res);
LOG_FUNC_RETURN(ctx, res);
}
static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pathlen,
u8 p1, sc_file_t **file_out)
{
int res = 0;
sc_apdu_t apdu;
u8 rbuf[MAX_RESP_BUFFER_SIZE];
sc_file_t *file = NULL;
sc_context_t *ctx = NULL;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0,
sc_get_max_recv_size(card), pathlen,
rbuf, sizeof(rbuf), path, pathlen);
if (p1 == 3)
apdu.cse= SC_APDU_CASE_1;
if (file_out == NULL)
apdu.cse = SC_APDU_CASE_4_SHORT;
res = sc_transmit_apdu(card, &apdu);
if ((res != SC_SUCCESS) || (file_out == NULL))
LOG_TEST_RET(ctx, res, "SelectFile() APDU transmit failed");
if (file_out == NULL) {
if (apdu.sw1 == 0x61)
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, 0);
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,
sc_check_sw(card, apdu.sw1, apdu.sw2));
}
res = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (res != SC_SUCCESS) {
LOG_TEST_RET(ctx, res, "SelectFile() check_sw failed");
}
if ((apdu.resplen < 2) || (apdu.resp[0] == 0x00)) {
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
}
file = sc_file_new();
if (file == NULL) {
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
}
res = card->ops->process_fci(card, file, apdu.resp + 2, apdu.resp[1]);
sc_file_free(*file_out);
*file_out = file;
LOG_FUNC_RETURN(ctx, res);
}
static int dnie_select_file(struct sc_card *card,
const struct sc_path *in_path,
struct sc_file **file_out)
{
int res = SC_SUCCESS;
sc_context_t *ctx = NULL;
unsigned char tmp_path[sizeof(DNIE_MF_NAME)];
int reminder = 0;
if (!card || !card->ctx || !in_path)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
switch (in_path->type) {
case SC_PATH_TYPE_FILE_ID:
if (in_path->len != 2)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
sc_log_hex(ctx, "select_file(ID)", in_path->value, in_path->len);
res = dnie_compose_and_send_apdu(card, in_path->value, in_path->len, 0, file_out);
break;
case SC_PATH_TYPE_DF_NAME:
sc_log_hex(ctx, "select_file(NAME)", in_path->value, in_path->len);
res = dnie_compose_and_send_apdu(card, in_path->value, in_path->len, 4, file_out);
break;
case SC_PATH_TYPE_PATH:
if ((in_path->len == 0) || ((in_path->len & 1) != 0))
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
sc_log_hex(ctx, "select_file(PATH): requested", in_path->value, in_path->len);
res = sc_lock(card);
LOG_TEST_RET(ctx, res, "sc_lock() failed");
if (memcmp(in_path->value, "\x3F\x00", 2) == 0) {
strcpy((char *)tmp_path, DNIE_MF_NAME);
sc_log_hex(ctx, "select_file(NAME): requested", tmp_path, sizeof(DNIE_MF_NAME) - 1);
res = dnie_compose_and_send_apdu(card, tmp_path, sizeof(DNIE_MF_NAME) - 1, 4, file_out);
if (res != SC_SUCCESS) {
sc_unlock(card);
LOG_TEST_RET(ctx, res, "select_file(NAME) failed");
}
tmp_path[2] = 0;
reminder = in_path->len - 2;
} else {
tmp_path[2] = 0;
reminder = in_path->len;
}
while (reminder > 0) {
tmp_path[0] = in_path->value[in_path->len - reminder];
tmp_path[1] = in_path->value[1 + in_path->len - reminder];
sc_log(ctx, "select_file(PATH): requested:%s ", sc_dump_hex(tmp_path, 2));
res = dnie_compose_and_send_apdu(card, tmp_path, 2, 0, file_out);
if (res != SC_SUCCESS) {
sc_unlock(card);
LOG_TEST_RET(ctx, res, "select_file(PATH) failed");
}
reminder -= 2;
}
sc_unlock(card);
break;
case SC_PATH_TYPE_FROM_CURRENT:
LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT);
break;
case SC_PATH_TYPE_PARENT:
sc_log(ctx, "select_file(PARENT)");
if (in_path->len != 0)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
res = dnie_compose_and_send_apdu(card, NULL, 0, 3, file_out);
break;
default:
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
break;
}
dnie_clear_cache(GET_DNIE_PRIV_DATA(card));
LOG_FUNC_RETURN(ctx, res);
}
static int dnie_get_challenge(struct sc_card *card, u8 * rnd, size_t len)
{
u8 rbuf[8];
size_t out_len;
int r;
LOG_FUNC_CALLED(card->ctx);
r = iso_ops->get_challenge(card, rbuf, sizeof rbuf);
LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed");
if (len < (size_t) r) {
out_len = len;
} else {
out_len = (size_t) r;
}
memcpy(rnd, rbuf, out_len);
LOG_FUNC_RETURN(card->ctx, (int) out_len);
}
static int dnie_logout(struct sc_card *card)
{
int result = SC_SUCCESS;
sc_file_t *file = NULL;
if ((card == NULL) || (card->ctx == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if (card->sm_ctx.sm_mode != SM_MODE_NONE) {
result = cwa_create_secure_channel(card,
GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_OFF);
LOG_TEST_RET(card->ctx, result, "Cannot close the secure channel");
result = dnie_compose_and_send_apdu(card, (const u8 *) DNIE_MF_NAME,
sizeof(DNIE_MF_NAME) - 1, 4, &file);
if (result == SC_ERROR_SM)
result = SC_SUCCESS;
}
if (file != NULL)
sc_file_free(file);
LOG_FUNC_RETURN(card->ctx, result);
}
static int dnie_set_security_env(struct sc_card *card,
const struct sc_security_env *env, int se_num)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 rbuf[MAX_RESP_BUFFER_SIZE];
u8 *p = sbuf;
int result = SC_SUCCESS;
if ((card == NULL) || (card->ctx == NULL) || (env == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if (se_num!=0) {
sc_log(card->ctx,"DNIe cannot handle several security envs");
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
}
if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
sc_log(card->ctx, "checking algorithms");
switch (env->algorithm) {
case SC_ALGORITHM_RSA:
result = SC_SUCCESS;
break;
case SC_ALGORITHM_DSA:
case SC_ALGORITHM_EC:
case SC_ALGORITHM_GOSTR3410:
default:
result = SC_ERROR_NOT_SUPPORTED;
break;
}
LOG_TEST_RET(card->ctx, result, "Unsupported algorithm");
if ((env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) == 0) {
result = SC_ERROR_NOT_SUPPORTED;
}
LOG_TEST_RET(card->ctx, result,
"Only RSA with SHA1 is supported");
*p++ = 0x80;
*p++ = 0x01;
*p++ = env->algorithm_ref & 0xff;
}
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
sc_log(card->ctx, "checking key references");
if (env->key_ref_len != 1) {
sc_log(card->ctx, "Null or invalid key ID reference");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
sc_log(card->ctx, "Using key reference '%s'",
sc_dump_hex(env->key_ref, env->key_ref_len));
*p++ = 0x84;
*p++ = 0x02;
*p++ = 0x01;
memcpy(p, env->key_ref, env->key_ref_len);
p += env->key_ref_len;
GET_DNIE_PRIV_DATA(card)->rsa_key_ref = 0xff & env->key_ref[0];
}
dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x22, 0x00, 0x00, 255, p - sbuf,
rbuf, MAX_RESP_BUFFER_SIZE, sbuf, p - sbuf);
switch (env->operation) {
case SC_SEC_OPERATION_DECIPHER:
apdu.p1 = 0xC1;
apdu.p2 = 0xB8;
break;
case SC_SEC_OPERATION_SIGN:
apdu.p1 = 0x41;
apdu.p2 = 0xB6;
break;
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
result = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, result, "Set Security Environment failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_FUNC_RETURN(card->ctx, result);
}
static int dnie_decipher(struct sc_card *card,
const u8 * crgram, size_t crgram_len,
u8 * out, size_t outlen)
{
struct sc_apdu apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[MAX_RESP_BUFFER_SIZE];
size_t len;
int result = SC_SUCCESS;
if ((card == NULL) || (card->ctx == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if ((crgram == NULL) || (out == NULL) || (crgram_len > 255)) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT,
0x2A,
0x80,
0x86,
256, crgram_len + 1, rbuf, sizeof(rbuf), sbuf, crgram_len + 1
);
sbuf[0] = 0;
memcpy(sbuf + 1, crgram, crgram_len);
result = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, result, "APDU transmit failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, result, "decipher returned error");
len = apdu.resplen > outlen ? outlen : apdu.resplen;
memcpy(out, apdu.resp, len);
LOG_FUNC_RETURN(card->ctx, result);
}
static int dnie_compute_signature(struct sc_card *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
int result = SC_SUCCESS;
int result_resplen = 0;
struct sc_apdu apdu;
u8 rbuf[MAX_RESP_BUFFER_SIZE];
if ((card == NULL) || (card->ctx == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if ((data == NULL) || (out == NULL))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
if (datalen > SC_MAX_APDU_BUFFER_SIZE)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
#ifdef ENABLE_DNIE_UI
if (GET_DNIE_PRIV_DATA(card)->rsa_key_ref == 0x02) {
result = dnie_ask_user_consent(card,user_consent_title,user_consent_message);
LOG_TEST_RET(card->ctx, result, "User consent denied");
}
#endif
sc_log_hex(card->ctx,
"Compute signature\n============================================================",
data, datalen);
dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A, 256, datalen,
rbuf, sizeof(rbuf), data, datalen);
result = sc_transmit_apdu(card, &apdu);
if (result != SC_SUCCESS) {
LOG_TEST_RET(card->ctx, result, "compute_signature() failed");
}
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (result != SC_SUCCESS) {
LOG_TEST_RET(card->ctx, result, "compute_signature() response error");
}
result_resplen = apdu.resplen;
if ((int)outlen<result_resplen)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
memcpy(out, apdu.resp, result_resplen);
LOG_FUNC_RETURN(card->ctx, result_resplen);
}
static int dnie_list_files(sc_card_t * card, u8 * buf, size_t buflen)
{
int res = SC_SUCCESS;
int id1 = 0;
int id2 = 0;
size_t count = 0;
u8 data[2];
sc_apdu_t apdu;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if (!buf || (buflen < 2))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00, 0, 2,
NULL, 0, data, 2);
for (id1 = 0; id1 < 256; id1++) {
for (id2 = 0; id2 < 256; id2++) {
if (count >= (buflen - 2)) {
sc_log(card->ctx,
"list_files: end of buffer. Listing stopped");
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
if ((id1 == 0x3F) && (id2 == 0xFF))
continue;
if ((id1 == 0x2F) && (id2 == 0x00))
continue;
if ((id1 == 0x2F) && (id2 == 0x01))
continue;
data[0] = (u8) (0xff & id1);
data[1] = (u8) (0xff & id2);
res = sc_transmit_apdu(card, &apdu);
if (res != SC_SUCCESS) {
sc_log(card->ctx, "List file '%02X%02X' failed",
id1, id2);
if (res != SC_ERROR_FILE_NOT_FOUND)
LOG_FUNC_RETURN(card->ctx, res);
continue;
}
sc_log(card->ctx, "Found File ID '%02X%02X'", id1, id2);
*(buf + count++) = data[0];
*(buf + count++) = data[1];
}
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int dnie_check_sw(struct sc_card *card,
unsigned int sw1, unsigned int sw2)
{
int res = SC_SUCCESS;
int n = 0;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
for (n = 0; dnie_errors[n].SWs != 0; n++) {
if (dnie_errors[n].SWs == ((sw1 << 8) | sw2)) {
sc_log(card->ctx, "%s", dnie_errors[n].errorstr);
return dnie_errors[n].errorno;
}
}
res = iso_ops->check_sw(card, sw1, sw2);
LOG_FUNC_RETURN(card->ctx, res);
}
static int dnie_card_ctl(struct sc_card *card,
unsigned long request, void *data)
{
int result = SC_SUCCESS;
if ((card == NULL) || (card->ctx == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
if (data == NULL) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
switch (request) {
case SC_CARDCTL_LIFECYCLE_GET:
switch (card->type) {
case SC_CARD_TYPE_DNIE_ADMIN:
result = SC_CARDCTRL_LIFECYCLE_ADMIN;
break;
case SC_CARD_TYPE_DNIE_USER:
result = SC_CARDCTRL_LIFECYCLE_USER;
break;
case SC_CARD_TYPE_DNIE_BLANK:
case SC_CARD_TYPE_DNIE_TERMINATED:
result = SC_CARDCTRL_LIFECYCLE_OTHER;
break;
}
*(int *)data = result;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
case SC_CARDCTL_GET_SERIALNR:
result = dnie_get_serialnr(card, (sc_serial_number_t *) data);
LOG_FUNC_RETURN(card->ctx, result);
case SC_CARDCTL_DNIE_GENERATE_KEY:
result = dnie_generate_key(card, data);
LOG_FUNC_RETURN(card->ctx, result);
case SC_CARDCTL_DNIE_GET_INFO:
result = dnie_get_info(card, data);
LOG_FUNC_RETURN(card->ctx, result);
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
}
static int dnie_read_header(struct sc_card *card)
{
sc_apdu_t apdu;
int r;
u8 buf[MAX_RESP_BUFFER_SIZE];
unsigned long uncompressed = 0L;
unsigned long compressed = 0L;
sc_context_t *ctx = NULL;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
dnie_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x00, 0x00, 8, 0,
buf, MAX_RESP_BUFFER_SIZE, NULL, 0);
r = sc_transmit_apdu(card, &apdu);
if (r != SC_SUCCESS) {
sc_log(ctx, "read_header() APDU transmit failed");
LOG_FUNC_RETURN(ctx, r);
}
if (apdu.resplen != 8)
goto header_notcompressed;
uncompressed = le2ulong(apdu.resp);
compressed = le2ulong(apdu.resp + 4);
if (uncompressed < compressed)
goto header_notcompressed;
if (uncompressed > 32767)
goto header_notcompressed;
sc_log(ctx, "read_header: uncompressed file size is %lu", uncompressed);
return (int)(0x7FFF & uncompressed);
header_notcompressed:
sc_log(ctx, "response doesn't match compressed file header");
return 0;
}
static int df_acl[] = {
SC_AC_OP_CREATE, SC_AC_OP_DELETE,
SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE,
-1
};
static int ef_acl[] = {
SC_AC_OP_READ, SC_AC_OP_UPDATE,
SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE,
-1
};
static int dnie_process_fci(struct sc_card *card,
struct sc_file *file, const u8 * buf, size_t buflen)
{
int res = SC_SUCCESS;
int *op = df_acl;
int n = 0;
sc_context_t *ctx = NULL;
if ((card == NULL) || (card->ctx == NULL) || (file == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
res = iso_ops->process_fci(card, file, buf, buflen);
LOG_TEST_RET(ctx, res, "iso7816_process_fci() failed");
if (file->prop_attr_len == 0) {
res = SC_SUCCESS;
goto dnie_process_fci_end;
}
if (file->prop_attr_len < 10) {
res = SC_ERROR_WRONG_LENGTH;
goto dnie_process_fci_end;
}
switch (file->prop_attr[0]) {
case 0x01:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
break;
case 0x15:
file->type = SC_FILE_TYPE_WORKING_EF;
if ( ( file->prop_attr[2] == 0x00 ) && (file->prop_attr[3] == 0x00 ) ) {
sc_log(ctx,"Processing pin EF");
break;
}
if (file->prop_attr_len < 13) {
sc_log(ctx, "FCI response len for Keys EF should be 13 bytes");
res = SC_ERROR_WRONG_LENGTH;
goto dnie_process_fci_end;
}
break;
case 0x24:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
res = dnie_read_header(card);
if (res == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)
goto dnie_process_fci_end;
if (res <= 0) {
sc_log(ctx,
"Cannot evaluate uncompressed size. use fci length");
} else {
sc_log(ctx, "Storing uncompressed size '%d' into fci",
res);
file->prop_attr[3] = (u8) ((res >> 8) & 0xff);
file->prop_attr[4] = (u8) (res & 0xff);
}
break;
case 0x38:
file->type = SC_FILE_TYPE_DF;
break;
default:
res = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto dnie_process_fci_end;
}
file->id = ( ( 0xff & (int)file->prop_attr[1] ) << 8 ) |
( 0xff & (int)file->prop_attr[2] ) ;
file->size = ( ( 0xff & (int)file->prop_attr[3] ) << 8 ) |
( 0xff & (int)file->prop_attr[4] ) ;
op = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
for (n = 0; n < 5; n++) {
int key_ref = 0;
if (*(op + n) == -1)
continue;
key_ref = file->prop_attr[5 + n] & 0x0F;
switch (0xF0 & file->prop_attr[5 + n]) {
case 0x00:
sc_file_add_acl_entry(file, *(op + n), SC_AC_NONE,
SC_AC_KEY_REF_NONE);
break;
case 0x10:
case 0x30:
sc_file_add_acl_entry(file, *(op + n), SC_AC_CHV,
key_ref);
break;
case 0x40:
sc_file_add_acl_entry(file, *(op + n), SC_AC_TERM,
key_ref);
break;
case 0xF0:
sc_file_add_acl_entry(file, *(op + n), SC_AC_NEVER,
SC_AC_KEY_REF_NONE);
break;
default:
sc_file_add_acl_entry(file, *(op + n), SC_AC_UNKNOWN,
SC_AC_KEY_REF_NONE);
break;
}
}
if (file->prop_attr[0] == 0x15) {
sc_log(card->ctx,
"Processing flags for Cryptographic key files");
}
res = SC_SUCCESS;
dnie_process_fci_end:
LOG_FUNC_RETURN(card->ctx, res);
}
static int dnie_pin_change(struct sc_card *card, struct sc_pin_cmd_data * data)
{
int res=SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON);
LOG_TEST_RET(card->ctx, res, "Establish SM failed");
LOG_FUNC_RETURN(card->ctx,SC_ERROR_NOT_SUPPORTED);
}
static int dnie_pin_verify(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
int res=SC_SUCCESS;
sc_apdu_t apdu;
u8 pinbuffer[SC_MAX_APDU_BUFFER_SIZE];
int pinlen = 0;
int padding = 0;
LOG_FUNC_CALLED(card->ctx);
if (card->atr.value[15] >= DNIE_30_VERSION) {
sc_log(card->ctx, "DNIe 3.0 detected doing PIN initialization");
dnie_change_cwa_provider_to_pin(card);
}
res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON);
LOG_TEST_RET(card->ctx, res, "Establish SM failed");
data->apdu = &apdu;
if (data->flags & SC_PIN_CMD_NEED_PADDING)
padding = 1;
data->pin1.offset = 0;
res = sc_build_pin(pinbuffer, sizeof(pinbuffer), &data->pin1, padding);
if (res < 0)
LOG_FUNC_RETURN(card->ctx, res);
pinlen = res;
dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x00, 0, pinlen,
NULL, 0, pinbuffer, pinlen);
res = sc_transmit_apdu(card, &apdu);
if (res != SC_SUCCESS) {
LOG_TEST_RET(card->ctx, res, "VERIFY APDU Transmit fail");
}
if (tries_left != NULL) {
if ((apdu.sw1 == 0x63) && ((apdu.sw2 & 0xF0) == 0xC0)) {
*tries_left = apdu.sw2 & 0x0F;
LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT);
}
}
res = dnie_check_sw(card, apdu.sw1, apdu.sw2);
if (card->atr.value[15] >= DNIE_30_VERSION) {
sc_log(card->ctx, "DNIe 3.0 detected => re-establish secure channel");
dnie_change_cwa_provider_to_secure(card);
res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON);
}
LOG_FUNC_RETURN(card->ctx, res);
}
static int dnie_pin_cmd(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
int res = SC_SUCCESS;
int lc = SC_CARDCTRL_LIFECYCLE_USER;
if ((card == NULL) || (card->ctx == NULL) || (data == NULL))
return SC_ERROR_INVALID_ARGUMENTS;
LOG_FUNC_CALLED(card->ctx);
data->flags &= ~SC_PIN_CMD_NEED_PADDING;
data->flags &= ~SC_PIN_CMD_USE_PINPAD;
res = dnie_card_ctl(card, SC_CARDCTL_LIFECYCLE_GET, &lc);
LOG_TEST_RET(card->ctx, res, "Cannot get card LC status");
if (lc != SC_CARDCTRL_LIFECYCLE_USER) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
}
switch (data->pin_type) {
case SC_AC_CHV:
break;
case SC_AC_TERM:
case SC_AC_PRO:
case SC_AC_AUT:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
res = dnie_pin_verify(card,data,tries_left);
break;
case SC_PIN_CMD_CHANGE:
res = dnie_pin_change(card,data);
break;
case SC_PIN_CMD_UNBLOCK:
case SC_PIN_CMD_GET_INFO:
res= SC_ERROR_NOT_SUPPORTED;
break;
default:
res= SC_ERROR_INVALID_ARGUMENTS;
break;
}
LOG_FUNC_RETURN(card->ctx, res);
}
static sc_card_driver_t *get_dnie_driver(void)
{
sc_card_driver_t *iso_drv = sc_get_iso7816_driver();
if (iso_ops == NULL)
iso_ops = iso_drv->ops;
dnie_ops = *iso_drv->ops;
dnie_ops.match_card = dnie_match_card;
dnie_ops.init = dnie_init;
dnie_ops.finish = dnie_finish;
dnie_ops.read_binary = dnie_read_binary;
dnie_ops.write_binary = NULL;
dnie_ops.update_binary = NULL;
dnie_ops.erase_binary = NULL;
dnie_ops.read_record = NULL;
dnie_ops.write_record = NULL;
dnie_ops.append_record = NULL;
dnie_ops.update_record = NULL;
dnie_ops.select_file = dnie_select_file;
dnie_ops.get_challenge = dnie_get_challenge;
dnie_ops.verify = NULL;
dnie_ops.logout = dnie_logout;
dnie_ops.set_security_env = dnie_set_security_env;
dnie_ops.decipher = dnie_decipher;
dnie_ops.compute_signature = dnie_compute_signature;
dnie_ops.change_reference_data = NULL;
dnie_ops.reset_retry_counter = NULL;
dnie_ops.create_file = NULL;
dnie_ops.delete_file = NULL;
dnie_ops.list_files = dnie_list_files;
dnie_ops.check_sw = dnie_check_sw;
dnie_ops.card_ctl = dnie_card_ctl;
dnie_ops.process_fci = dnie_process_fci;
dnie_ops.pin_cmd = dnie_pin_cmd;
dnie_ops.get_data = NULL;
dnie_ops.put_data = NULL;
dnie_ops.delete_record = NULL;
return &dnie_driver;
}
sc_card_driver_t *sc_get_dnie_driver(void)
{
return get_dnie_driver();
}
#undef __CARD_DNIE_C__
#endif