Compare commits

...

5 Commits

Author SHA1 Message Date
giomba 932bd27549 libgcns: ArchLinux package 2022-01-08 20:37:35 +01:00
giomba b965e20bfd libgcns: C++ version 2022-01-06 21:12:55 +01:00
giomba 060d8ddb19 libgcns: first commit 2022-01-06 20:27:23 +01:00
giomba 5167b30043 gcns.c: refactoring 2022-01-06 18:04:42 +01:00
giomba 606edadb13 gcns.c: read Italian healthcare smart card 2022-01-06 17:29:06 +01:00
10 changed files with 315 additions and 0 deletions

3
.clang-format Normal file
View File

@ -0,0 +1,3 @@
BasedOnStyle: Google
IndentWidth: 4

1
.gitignore vendored
View File

@ -80,6 +80,7 @@ src/tools/pkcs15-init
src/tools/eidenv
src/tools/opensc-explorer
src/tools/cardos-info
src/tools/gcns
src/tools/sceac-example
src/tools/opensc-notify
src/tools/opensc-notify.plist

1
src/gcns/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

12
src/gcns/CMakeLists.txt Normal file
View File

@ -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)

33
src/gcns/arch/PKGBUILD Normal file
View File

@ -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
}

91
src/gcns/gcns.c Normal file
View File

@ -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;
}

71
src/gcns/gcns.cpp Normal file
View File

@ -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;
}
}
}

13
src/gcns/gcns.h Normal file
View File

@ -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

34
src/gcns/gcns.hpp Normal file
View File

@ -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

56
src/gcns/main.c Normal file
View File

@ -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;
}