opensc/src/tools/egk-tool.c

275 lines
6.8 KiB
C

/*
* Copyright (C) 2017 Frank Morgner <frankmorgner@gmail.com>
*
* This file is part of OpenSC.
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "egk-tool-cmdline.h"
#include "libopensc/log.h"
#include "libopensc/opensc.h"
#include "util.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
#ifdef ENABLE_ZLIB
#include <zlib.h>
int uncompress_gzip(void* uncompressed, size_t *uncompressed_len,
const void* compressed, size_t compressed_len)
{
z_stream stream;
memset(&stream, 0, sizeof stream);
stream.total_in = compressed_len;
stream.avail_in = compressed_len;
stream.total_out = *uncompressed_len;
stream.avail_out = *uncompressed_len;
stream.next_in = (Bytef *) compressed;
stream.next_out = (Bytef *) uncompressed;
/* 15 window bits, and the +32 tells zlib to to detect if using gzip or zlib */
if (Z_OK == inflateInit2(&stream, (15 + 32))
&& Z_STREAM_END == inflate(&stream, Z_FINISH)) {
*uncompressed_len = stream.total_out;
} else {
return SC_ERROR_INVALID_DATA;
}
inflateEnd(&stream);
return SC_SUCCESS;
}
#else
int uncompress_gzip(void* uncompressed, size_t *uncompressed_len,
const void* compressed, size_t compressed_len)
{
return SC_ERROR_NOT_SUPPORTED;
}
#endif
#define PRINT(c) (isprint(c) ? c : '?')
void dump_binary(void *buf, size_t buf_len)
{
#ifdef _WIN32
_setmode(fileno(stdout), _O_BINARY);
#endif
fwrite(buf, 1, buf_len, stdout);
#ifdef _WIN32
_setmode(fileno(stdout), _O_TEXT);
#endif
}
const unsigned char aid_hca[] = {0xD2, 0x76, 0x00, 0x00, 0x01, 0x02};
static const char *app_name = "egk-tool";
int read_file(struct sc_card *card, char *str_path, unsigned char **data, size_t *data_len)
{
struct sc_path path;
struct sc_file *file;
unsigned char *p;
int ok = 0;
int r;
size_t len;
sc_format_path(str_path, &path);
if (SC_SUCCESS != sc_select_file(card, &path, &file)) {
goto err;
}
len = file && file->size > 0 ? file->size : 4096;
p = realloc(*data, len);
if (!p) {
goto err;
}
*data = p;
*data_len = len;
r = sc_read_binary(card, 0, p, len, 0);
if (r < 0)
goto err;
*data_len = r;
ok = 1;
err:
sc_file_free(file);
return ok;
}
void decode_version(unsigned char *bcd, unsigned int *major, unsigned int *minor, unsigned int *fix)
{
*major = 0;
*minor = 0;
*fix = 0;
/* decode BCD to decimal */
if ((bcd[0]>>4) < 10 && ((bcd[0]&0xF) < 10) && ((bcd[1]>>4) < 10)) {
*major = (bcd[0]>>4)*100 + (bcd[0]&0xF)*10 + (bcd[1]>>4);
}
if (((bcd[1]&0xF) < 10) && ((bcd[2]>>4) < 10) && ((bcd[2]&0xF) < 10)) {
*minor = (bcd[1]&0xF)*100 + (bcd[2]>>4)*10 + (bcd[2]&0xF);
}
if ((bcd[3]>>4) < 10 && ((bcd[3]&0xF) < 10)
&& (bcd[4]>>4) < 10 && ((bcd[4]&0xF) < 10)) {
*fix = (bcd[3]>>4)*1000 + (bcd[3]&0xF)*100
+ (bcd[4]>>4)*10 + (bcd[4]&0xF);
}
}
int
main (int argc, char **argv)
{
struct gengetopt_args_info cmdline;
struct sc_path path;
struct sc_context *ctx;
struct sc_card *card = NULL;
unsigned char *data = NULL;
size_t data_len = 0;
int r;
sc_context_param_t ctx_param;
if (cmdline_parser(argc, argv, &cmdline) != 0)
exit(1);
memset(&ctx_param, 0, sizeof(ctx_param));
ctx_param.ver = 0;
ctx_param.app_name = app_name;
r = sc_context_create(&ctx, &ctx_param);
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
exit(1);
}
if (cmdline.verbose_given > 1) {
ctx->debug = cmdline.verbose_given;
sc_ctx_log_to_file(ctx, "stderr");
}
r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0, cmdline.verbose_given);
if (r)
goto err;
sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_hca, sizeof aid_hca, 0, 0);
if (SC_SUCCESS != sc_select_file(card, &path, NULL))
goto err;
if (cmdline.pd_flag
&& read_file(card, "D001", &data, &data_len)
&& data_len >= 2) {
size_t len_pd = (data[0] << 8) | data[1];
if (len_pd + 2 <= data_len) {
unsigned char uncompressed[1024];
size_t uncompressed_len = sizeof uncompressed;
if (uncompress_gzip(uncompressed, &uncompressed_len,
data + 2, len_pd) == SC_SUCCESS) {
dump_binary(uncompressed, uncompressed_len);
} else {
dump_binary(data + 2, len_pd);
}
}
}
if ((cmdline.vd_flag || cmdline.gvd_flag)
&& read_file(card, "D002", &data, &data_len)
&& data_len >= 8) {
size_t off_vd = (data[0] << 8) | data[1];
size_t end_vd = (data[2] << 8) | data[3];
size_t off_gvd = (data[4] << 8) | data[5];
size_t end_gvd = (data[6] << 8) | data[7];
size_t len_vd = end_vd - off_vd + 1;
size_t len_gvd = end_gvd - off_gvd + 1;
if (off_vd <= end_vd && end_vd < data_len
&& off_gvd <= end_gvd && end_gvd < data_len) {
unsigned char uncompressed[1024];
size_t uncompressed_len = sizeof uncompressed;
if (cmdline.vd_flag) {
if (uncompress_gzip(uncompressed, &uncompressed_len,
data + off_vd, len_vd) == SC_SUCCESS) {
dump_binary(uncompressed, uncompressed_len);
} else {
dump_binary(data + off_vd, len_vd);
}
}
if (cmdline.gvd_flag) {
if (uncompress_gzip(uncompressed, &uncompressed_len,
data + off_gvd, len_gvd) == SC_SUCCESS) {
dump_binary(uncompressed, uncompressed_len);
} else {
dump_binary(data + off_gvd, len_gvd);
}
}
}
}
if (cmdline.vsd_status_flag
&& read_file(card, "D00C", &data, &data_len)
&& data_len >= 25) {
char *status;
unsigned int major, minor, fix;
switch (data[0]) {
case '0':
status = "Transactions pending";
break;
case '1':
status = "No transactions pending";
break;
default:
status = "Unknown";
break;
}
decode_version(data+15, &major, &minor, &fix);
printf(
"Status %s\n"
"Timestamp %c%c.%c%c.%c%c%c%c at %c%c:%c%c:%c%c\n"
"Version %u.%u.%u\n",
status,
PRINT(data[7]), PRINT(data[8]),
PRINT(data[5]), PRINT(data[6]),
PRINT(data[1]), PRINT(data[2]), PRINT(data[3]), PRINT(data[4]),
PRINT(data[9]), PRINT(data[10]),
PRINT(data[11]), PRINT(data[12]),
PRINT(data[13]), PRINT(data[14]),
major, minor, fix);
}
err:
sc_disconnect_card(card);
sc_release_context(ctx);
cmdline_parser_free (&cmdline);
return 0;
}