Compare commits
5 Commits
0.22.0
...
golem/tess
Author | SHA1 | Date |
---|---|---|
giomba | 932bd27549 | |
giomba | b965e20bfd | |
giomba | 060d8ddb19 | |
giomba | 5167b30043 | |
giomba | 606edadb13 |
|
@ -0,0 +1,3 @@
|
||||||
|
BasedOnStyle: Google
|
||||||
|
IndentWidth: 4
|
||||||
|
|
|
@ -80,6 +80,7 @@ src/tools/pkcs15-init
|
||||||
src/tools/eidenv
|
src/tools/eidenv
|
||||||
src/tools/opensc-explorer
|
src/tools/opensc-explorer
|
||||||
src/tools/cardos-info
|
src/tools/cardos-info
|
||||||
|
src/tools/gcns
|
||||||
src/tools/sceac-example
|
src/tools/sceac-example
|
||||||
src/tools/opensc-notify
|
src/tools/opensc-notify
|
||||||
src/tools/opensc-notify.plist
|
src/tools/opensc-notify.plist
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
|
@ -0,0 +1,12 @@
|
||||||
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
|
project(gcns VERSION 1.0 DESCRIPTION "Italian healthcare smart card parsing utility")
|
||||||
|
|
||||||
|
add_library(gcns SHARED gcns.c gcns.cpp)
|
||||||
|
target_include_directories(gcns PUBLIC ../.. .. .)
|
||||||
|
install(TARGETS gcns LIBRARY)
|
||||||
|
install(FILES gcns.h gcns.hpp DESTINATION include)
|
||||||
|
|
||||||
|
add_executable(main main.c ../tools/util.c)
|
||||||
|
target_link_libraries(main gcns opensc bsd)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Maintainer: Giovan Battista Rolandi <giomba@linux.it>
|
||||||
|
|
||||||
|
pkgname=gcns
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc='Tools for Italian healthcare smart card'
|
||||||
|
arch=('x86_64')
|
||||||
|
url='https://git.golem.linux.it/giomba/opensc'
|
||||||
|
license=('LGPL')
|
||||||
|
depends=('opensc')
|
||||||
|
source=('git+https://git.golem.linux.it/giomba/opensc#branch=golem/tessera-sanitaria')
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd opensc
|
||||||
|
|
||||||
|
./bootstrap
|
||||||
|
./configure
|
||||||
|
make -j$(nproc)
|
||||||
|
cd src/gcns
|
||||||
|
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd opensc/src/gcns/build
|
||||||
|
make DESTDIR=$pkgdir install
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* gcns.c: A reader of Italian healtcare smartcards with libopensc
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 Giovan Battista Rolandi <giomba@linux.it>
|
||||||
|
* based on previous work by
|
||||||
|
* Copyright (C) 2001 Juha Yrjölä <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 "gcns.h"
|
||||||
|
|
||||||
|
#include "libopensc/asn1.h"
|
||||||
|
#include "tools/util.h"
|
||||||
|
|
||||||
|
static int opt_wait = 0;
|
||||||
|
static const char *opt_reader = NULL;
|
||||||
|
static sc_context_t *ctx = NULL;
|
||||||
|
static sc_card_t *card = NULL;
|
||||||
|
sc_context_param_t ctx_param;
|
||||||
|
|
||||||
|
int gcns_init() {
|
||||||
|
int r, err = 0;
|
||||||
|
int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN;
|
||||||
|
|
||||||
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
||||||
|
ctx_param.ver = 0;
|
||||||
|
|
||||||
|
r = sc_context_create(&ctx, &ctx_param);
|
||||||
|
if (r) {
|
||||||
|
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
||||||
|
return GCNS_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
|
||||||
|
|
||||||
|
err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0, 0);
|
||||||
|
if (err) {
|
||||||
|
return GCNS_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sc_lock(card);
|
||||||
|
if (r == SC_SUCCESS)
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle);
|
||||||
|
sc_unlock(card);
|
||||||
|
if (r && r != SC_ERROR_NOT_SUPPORTED) {
|
||||||
|
fprintf(stderr, "unable to change lifecycle: %s\n", sc_strerror(r));
|
||||||
|
return GCNS_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GCNS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gcns_close() {
|
||||||
|
if (card) {
|
||||||
|
sc_disconnect_card(card);
|
||||||
|
}
|
||||||
|
if (ctx) sc_release_context(ctx);
|
||||||
|
return GCNS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gcns_read_personal_data(u8 *buffer, size_t len) {
|
||||||
|
sc_path_t path;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
sc_format_path("3F0011001102", &path);
|
||||||
|
r = sc_select_file(card, &path, NULL);
|
||||||
|
if (r) {
|
||||||
|
fprintf(stderr, "no select file: 3F0011001102\n");
|
||||||
|
return GCNS_READ_PERSONAL_DATA;
|
||||||
|
}
|
||||||
|
r = sc_read_binary(card, 0, buffer, 0x180, 0);
|
||||||
|
if (r < 0) {
|
||||||
|
fprintf(stderr, "no read binary: %d\n", r);
|
||||||
|
return GCNS_READ_PERSONAL_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "gcns.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace gcns;
|
||||||
|
|
||||||
|
PersonalData::PersonalData(const uint8_t* buffer, size_t len) {
|
||||||
|
std::vector<std::string> field;
|
||||||
|
|
||||||
|
// TODO check length at the beginning?
|
||||||
|
for (int i = 12; i < len;) {
|
||||||
|
if (buffer[i] == '\0') break;
|
||||||
|
|
||||||
|
std::string hexstring((const char*)&buffer[i], 2);
|
||||||
|
int len = std::stoi(hexstring, nullptr, 16);
|
||||||
|
i += 2;
|
||||||
|
std::string fieldData((const char*)&buffer[i], len);
|
||||||
|
i += len;
|
||||||
|
|
||||||
|
field.push_back(fieldData);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)field.size(); ++i) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
this->issue_date.year =
|
||||||
|
std::stoi(field[i].substr(4, 4), nullptr);
|
||||||
|
this->issue_date.month =
|
||||||
|
std::stoi(field[i].substr(2, 2), nullptr);
|
||||||
|
this->issue_date.day =
|
||||||
|
std::stoi(field[i].substr(0, 2), nullptr);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this->expiration_date.year =
|
||||||
|
std::stoi(field[i].substr(4, 4), nullptr);
|
||||||
|
this->expiration_date.month =
|
||||||
|
std::stoi(field[i].substr(2, 2), nullptr);
|
||||||
|
this->expiration_date.day =
|
||||||
|
std::stoi(field[i].substr(0, 2), nullptr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this->family_name = field[i];
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this->first_name = field[i];
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this->birth_date.year =
|
||||||
|
std::stoi(field[i].substr(4, 4), nullptr);
|
||||||
|
this->birth_date.month =
|
||||||
|
std::stoi(field[i].substr(2, 2), nullptr);
|
||||||
|
this->birth_date.day =
|
||||||
|
std::stoi(field[i].substr(0, 2), nullptr);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this->gender = field[i] == "F" ? GENDER_FEMALE : GENDER_MALE;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
this->fiscal_code = field[i];
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
this->birth_place = field[i];
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
this->residence_place = field[i];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef GCNS_H
|
||||||
|
#define GCNS_H
|
||||||
|
|
||||||
|
#define GCNS_SUCCESS 0
|
||||||
|
#define GCNS_INIT -1001
|
||||||
|
#define GCNS_READ_PERSONAL_DATA -1002
|
||||||
|
#define GCNS_CLOSE -1003
|
||||||
|
|
||||||
|
int gcns_init();
|
||||||
|
int gcns_read_personal_data(u8 *buffer, size_t len);
|
||||||
|
int gcns_close();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef GCNS_CPP
|
||||||
|
#define GCNS_CPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gcns {
|
||||||
|
|
||||||
|
enum Gender { GENDER_MALE, GENDER_FEMALE };
|
||||||
|
|
||||||
|
struct Date {
|
||||||
|
uint16_t year;
|
||||||
|
uint8_t month;
|
||||||
|
uint8_t day;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PersonalData {
|
||||||
|
private:
|
||||||
|
std::string first_name;
|
||||||
|
std::string family_name;
|
||||||
|
std::string fiscal_code;
|
||||||
|
std::string birth_place;
|
||||||
|
Date birth_date;
|
||||||
|
std::string residence_place;
|
||||||
|
Gender gender;
|
||||||
|
Date issue_date;
|
||||||
|
Date expiration_date;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PersonalData(const uint8_t* personal_data, size_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gcns
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#ifdef ENABLE_READLINE
|
||||||
|
#include <readline/history.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#endif
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
#include <arpa/inet.h> /* for htons() */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "common/compat_strlcpy.h"
|
||||||
|
#include "gcns.h"
|
||||||
|
#include "libopensc/asn1.h"
|
||||||
|
#include "libopensc/cardctl.h"
|
||||||
|
#include "libopensc/cards.h"
|
||||||
|
#include "libopensc/internal.h"
|
||||||
|
#include "libopensc/iso7816.h"
|
||||||
|
#include "libopensc/log.h"
|
||||||
|
#include "libopensc/opensc.h"
|
||||||
|
#include "tools/util.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
printf("OpenSC version: %s\n", sc_get_version());
|
||||||
|
|
||||||
|
r = gcns_init();
|
||||||
|
if (r != GCNS_SUCCESS) {
|
||||||
|
fprintf(stderr, "Init Error\n");
|
||||||
|
return GCNS_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 buffer[2048];
|
||||||
|
r = gcns_read_personal_data(buffer, 2048);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
fprintf(stderr, "Read personal data error\n");
|
||||||
|
return GCNS_READ_PERSONAL_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_hex_dump_asc(stdout, buffer, r, 0);
|
||||||
|
|
||||||
|
r = gcns_close();
|
||||||
|
if (r != GCNS_SUCCESS) {
|
||||||
|
return GCNS_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GCNS_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue