/* * Copyright (C) 2017 Frank Morgner * * 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 #include #include #ifdef _WIN32 #include #include #endif #ifdef ENABLE_ZLIB #include 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; }