This project aims at providing an open-source implementation of the ISO7816-3 communication protocol from the reader side. This protocol is ruling the interactions between a smartcard and a card reader when using its contacts to communicate. It is defined by the ISO/IEC 7816-3 specification.
This code is designed to be run on an embedded target and is organized accordingly to several abstraction layers, thus allowing you to easily make your own implementation for a target which might not be supported yet. The software is written in pure C code and is thoroughly tested to asses its compliance with the protocol specification.
At its higher abstraction level, this library provides an API allowing you to send and receive APDUs to/from a smartcard. At the same time, the lower level APIs are allowing you to interact directly with the arcanes of the T=0 and T=1 underlying protocols.
Copyright ANSSI (2017-2021)
This software is licensed under MIT license. See LICENSE file in the root folder of the project.
- Boris SIMUNOVIC (mailto:boris.simunovic@ssi.gouv.fr)
- Guillaume BOUFFARD (mailto:guillaume.bouffard@ssi.gouv.fr)
This project has been developed as a part of an internship project supervised by Guillaume BOUFFARD (ANSSI) and carried out by Boris SIMUNOVIC (Grenoble Institute of Technology - ESISAR)
The currently supported targets are :
- stm32f407
- stm32f411
It is recommended to use the development boards associated to those components (discovery boards). So you won't have to change some settings in the code (especially oscillator frequency provided by the board).
If you are interested into adding a new target, your contributions are welcome, please have a look at the CONTRIBUTING.md file.
First, clone project's repository on your local machine :
$ git clone https://github.com/ANSSI-FR/Open-ISO7816-Stack.git
$ git submodule update --init --recursive
The project is relying on the following submodules :
- Unity, is a unit-test framework for testing the project. (see project's page)
- CMock, is a mocking framework used jointly with the Unity. Useful for testing parts of code relying on hardware peripherals. (see project's page)
- ./lib/target/STM32CubeF4, is a set of hardware abstraction layer libraries provided by STMicroelectronics.
You have to install the ARM tool-chain and cross-compiler on your local machine. Typically can be achieved by doing :
$ apt-get install arm-none-eabi-*
You need as well to install the ST tool-chain for flashing the target (stlink), especially the st-flash executable. You can get it from the git repository, or on debian-like distributions you can do :
$ apt-get install stlink-tools
In the main directory Makefile set the following parameters to their correct values :
CC=arm-none-eabi-gcc
LD=arm-none-eabi-gcc
AR=arm-none-eabi-ar
OBJCOPY=arm-none-eabi-objcopy
STFLASH=st-flash
Then, do the same for the Makefile in the ./lib directory :
CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar
OBJCOPY=arm-none-eabi-objcopy
STFLASH=st-flash
NOTE: if you have several ARM cross-compilers installed on your local machine (typically if you have a concurrent Xilinx toolchain) it is recommended to explicitly indicate the absolute path to the correct compiler to avoid problems.
To select the target for which the firmware has to be compiled you have to follow those steps :
- In the main directory open the Makefile and uncoment the line corresponding to your target :
# Target can be stm32f407, stm32f411.
#TARGET=stm32f411
#TARGET=stm32f407
- In the ./lib directory open the Makefile and uncoment the line corresponding to your target :
# Target can be stm32f407, stm32f411.
#TARGET=stm32f411
#TARGET=stm32f407
Use the following commands from the main directory to compile the project and flash it to the target.
$ make all
$ make upload
In the reader local directory execute the following command:
$ make clean
$ make reader
A file libreader.a containing the static library is then generated.
To compile your project with the reader library, the static library and all the headers in reader/inc must be accessible by your project.
Your project should include reader_lib.h file in this way:
#include "reader_lib.h"
To build your project, all reader/inc headers might have to be passed to the compiler, for example with the gcc -I option in the following way :
$ gcc -Ireader/inc
When linking your own project you have to point out the path to the static library :
$ ld -Lpath/to/staticlib/folder/ -lreader
For the currently supported stm32 targets, the wiring of the smartcard to the reader is always the same. The follwing signals have to be wired to the discovery board :
Smartcard signal | stm32 port | stm32 pin number |
---|---|---|
Clock | PORTA | GPIO PIN 4 |
I/O | PORTA | GPIO PIN 2 |
Power | PORTA | GPIO PIN 6 |
Reset | PORTA | GPIO PIN 5 |
Ground | Ground | Ground |
Feature | Implemented? | Tested? |
---|---|---|
ATR reception and decoding | X | |
PPS Negotiation | ||
T=0 Case 1 | X | X |
T=0 Case 2S | X | X |
T=0 Case 3S | X | X |
T=0 Case 4S | X | X |
T=0 Case 2E | X | |
T=0 Case 3E | X | |
T=0 Case 4E | ||
T=1 Normal operation | X | X |
T=1 Chaining | X | X |
T=1 Error handling | X | X |
T=1 IFS Adjustment | X | X |
T=1 WTX Adjustment | X | X |
T=1 ABORT Request | X | |
T=1 RESYNCH Request | X |
The following data structures are involved in the process of sending an APDU to the card and receiving its response.
READER_HAL_CommSettings settings;
settings is a data structure which contains all the parameters values currently used by READER_HAL hardware abstraction layer used by the library for low level communications (emission and reception of characters). The user should not modify directly the values present in this structure. It's recommended to use the functions exposed in the file reader_hal_comm_settings.h to operate safely on it.
READER_APDU_Command apduCmd;
READER_APDU_Response apduResp;
apduCmd and apduResp are data structures respectively representing an APDU command and APDU response objects as defined in the ISO/IEC7816-3.
READER_T0_ContextHandler context1;
READER_T1_ContextHandler context2;
context1 is a data structure which contains the current communication context (current state of all the variables and protocol parameters) when using the protocol T=0. context2 is a data structure which contains the current communication context when using the protocol T=1.
/* Initialization of the hardware abstraction layer with default values ... */
READER_HAL_InitWithDefaults(&settings);
/* Resetting the card ... */
READER_HAL_DoColdReset();
/* Initialization of the APDU API when using the protocol T=0 ... */
READER_T0_APDU_Init(&context, &settings);
/* Initialization of the APDU API when using the protocol T=1 ... */
READER_T1_APDU_Init(&context, &settings);
/* Executing an APDU when using the protocol T=0 */
READER_T0_APDU_Execute(&context, &apduCmd, &apduResp);
/* Executing an APDU when using the protocol T=1 */
READER_T1_APDU_Execute(&context, &apduCmd, &apduResp);
uint8_t *dataPtr;
uint32_t dataSize;
uint8_t SW1, SW2;
/* Getting the SW from the APDU Response data structure ... */
READER_APDU_ExtractRespSW(&apduResp, &SW1, &SW2);
/* Getting the data contained in the APDU Response data structure ... */
READER_APDU_ExtractRespDataPtr(&apduResp, &dataPtr, &dataSize);
READER_HAL_CommSettings settings;
READER_T0_ContextHandler context;
READER_APDU_Command apduCmd;
READER_APDU_Response apduResp;
uint8_t buffer[] = {0x45, 0x75, 0x74, 0x77, 0x74, 0x75, 0x36, 0x41, 0x70, 0x70};
/* Initialization of the hardware abstraction layer with default values ... */
READER_HAL_InitWithDefaults(&settings);
READER_HAL_DoColdReset();
/* Initialization of the APDU communication context when using the protocol T=0 ... */
READER_T0_APDU_Init(&context, &settings);
/* Forging the APDU command ... */
READER_APDU_Forge(&apduCmd, 0x00, 0xA4, 0x04, 0x00, 10, buffer, 0);
/* Executing an APDU when using the protocol T=0 */
READER_T0_APDU_Execute(&context, &apduCmd, &apduResp);
Pull requests are welcome. See CONTRIBUTING.md
.
If you’ve found a bug, or have an idea/suggestion/request, fill an issue here on GitHub or contact the authors by email.