Smartcard: Accessing Secure Elements
This tutorial demonstrates how you can manage smart card features and access secure elements (SE).
Warm-up
Become familiar with the Smartcard API basics by learning about:
-
Using the SE Service
Initialize the SE service and retrieve available readers.
-
Managing the Reader
Retrieve the reader name, check whether the SE is present in the reader, open sessions, and close all sessions.
-
Managing Sessions
Retrieve session information, open basic and logical channels, and close channels and the session.
-
Managing Channels
Retrieve channel information, select the next applet, transmit APDU commands, and close the channel.
Follow-up
Once we have learned the basics of the Smartcard API, we can now move on to more advanced tasks, including:
-
Sending a Transmission
Retrieve a reader, open a session and a logical channel, and transmit an APDU command over the channel.
Using the SE Service
To use the SE service:
-
To use the functions and data types of the Smartcard API (in mobile and wearable applications), include the <smartcard.h> header file in your application:
#include <smartcard.h>
-
Add the http://tizen.org/privilege/secureelement privilege to the manifest file of the application.
-
Initialize the smart card service for use:
int ret; ret = smartcard_initialize(); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard initialize successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard initialize failed");
When the service is no longer needed, deinitialize it:
ret = smartcard_deinitialize(); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard deinitialize successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard deinitialize failed");
-
Retrieve the available readers with the smartcard_get_readers() function:
int pLength; int *phReaders = NULL; ret = smartcard_get_readers(&phReaders, &pLength); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_get_readers successful"); dlog_print(DLOG_INFO, LOG_TAG, "readers length : %d", pLength); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_get_readers failed : %d", ret); } if(phReaders != NULL) free(phReaders);
Managing the Reader
To manage a reader:
-
Retrieve the name of the reader with the smartcard_reader_get_name() function:
int ret; int reader = phReaders[0]; // Get the reader using smartcard_get_readers() char * pReader = NULL; ret = smartcard_reader_get_name(reader, &pReader); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_get_name successful"); dlog_print(DLOG_INFO, LOG_TAG, "reader name : %s", pReader); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_get_name failed : %d", ret); }
-
Before establishing a session, use the smartcard_reader_is_secure_element_present() function to make sure that the SE is present in the reader:
bool is_present = false; ret = smartcard_reader_is_secure_element_present(reader, &is_present); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_is_secure_element_present successful"); dlog_print(DLOG_INFO, LOG_TAG, "reader secure element present : %d", is_present); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_is_secure_element_present failed : %d", ret); }
-
Open a session to connect to the SE in the reader using the smartcard_reader_open_session() function.
When you no longer need the reader, use the smartcard_reader_close_sessions() function to close all sessions opened on the specific reader.
int session; ret = smartcard_reader_open_session(reader, &session); if (ret == SMARTCARD_ERROR_NONE) { ret = smartcard_reader_close_sessions(reader); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_close_sessions successful"); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_reader_close_sessions failed : %d", ret); } } else { dlog_print(DLOG_INFO, LOG_TAG, "open session failed : %d", ret); }
Managing Sessions
You can manage a session using the session instance that you have created when opening the session with a reader. The session instance is the first parameter in all session-related APIs.
To manage sessions:
- Retrieve the reader that provides the session:
int ret; int reader; ret = smartcard_session_get_reader(session, &reader); ret = smartcard_reader_get_name(reader, &pReader); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_get_reader successful"); dlog_print(DLOG_INFO, LOG_TAG, "reader name : %s", pReader); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_get_reader failed"); }
- Retrieve the answer to reset (ATR) of the SE:
int i; int ret; unsigned char *pAtr; int pLength; ret = smartcard_session_get_atr(session, &pAtr, &pLength); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_get_atr successful : %d", pLength); for (i = 0; i < pLength; i++) dlog_print(DLOG_INFO, LOG_TAG, "%x ", (int)pAtr[i]); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_get_atr failed"); }
- Open a basic or logical channel.
A basic channel is defined in the ISO/IEC 7816-4 specification (the one that has number 0). To open a logical channel with the SE, you must select the applet represented by the given Application ID (AID).
int ret; unsigned char aid[] = {0x00, 0x01, 0x02, 0x03}; int channel; // Open basic channel ret = smartcard_session_open_basic_channel(session, aid, 4, 0x00, &channel); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_open_basic_channel successful : %d", channel); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_open_basic_channel failed"); // Open logical channel ret = smartcard_session_open_logical_channel(session, aid, 12, 0x04, &channel); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_open_logical_channel successful : %d", (channel); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_open_logical_channel failed : %d", ret);
- Close all channels opened for a specific session:
int ret; ret = smartcard_session_close_channels(session); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_close_channels successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_close_channels failed");
- Close a session and check that it is truly closed:
int ret; bool is_closed; ret = smartcard_session_close(session); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_close successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_close failed"); ret = smartcard_session_is_closed(session, &is_closed); if (ret == SMARTCARD_ERROR_NONE && is_closed == true) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_is_closed successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_session_is_closed failed");
Managing Channels
You can manage a channel using the channel instance that you have created when opening the channel with a session. The channel instance is the first parameter in all channel-related APIs.
To manage channels:
- Retrieve the session that has opened the specific channel:
int ret; int session_handle; ret = smartcard_session_open_logical_channel(session, aid, 12, 0x00, &channel); if (ret == SMARTCARD_ERROR_NONE) { ret = smartcard_channel_get_session(channel, &session_handle); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_get_session successful: %d", session_handle); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_get_session failed"); }
- Check whether a specific channel is a basic or logical channel:
int ret; bool is_basic; ret = smartcard_channel_is_basic_channel(channel, &is_basic); if (ret == SMARTCARD_ERROR_NONE && is_basic == false) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_is_basic_channel successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_is_basic_channel failed");
- Select the next applet on the specific channel that matches to the partial Application ID (AID):
ret = smartcard_session_open_logical_channel(session, aid, 12, 0x00, &channel); if (ret == SMARTCARD_ERROR_NONE) { bool is_next = true; ret = smartcard_channel_select_next(channel, &is_next); if (ret == SMARTCARD_ERROR_NONE && is_next == false) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_select_next successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_select_next failed"); }
To get a response for the selection command, use the smartcard_channel_get_select_response() function:
int i, ret; unsigned char* pSelectResponse; int pLength; ret = smartcard_channel_get_select_response(channel, &pSelectResponse, &pLength); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_get_select_response successful"); for (i = 0; i < pLength; i++) g_print("%x ", (int)pSelectResponse[i]); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_get_select_response failed"); }
- Check whether a specific channel is closed:
int ret; bool is_close; ret = smartcard_channel_close(channel); ret = smartcard_channel_is_closed(channel, &is_close); if (ret == SMARTCARD_ERROR_NONE && is_close == true) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_is_closed successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_is_closed failed");
- Close the channel opened for a specific SE:
int ret; ret = smartcard_channel_close(channel); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_close successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_close failed : %d", ret);
- Transmit an APDU command (as per ISO/IEC 7816-4) to the SE:
int i; int ret; int resp_len; unsigned char *response = NULL; unsigned char command[] = {0x00, 0x01, 0x02, 0x03}; ret = smartcard_channel_transmit(channel, command, 4, &response, &resp_len); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit successful"); dlog_print(DLOG_INFO, LOG_TAG, "response is "); for (i = 0; i < resp_len; i++) dlog_print(DLOG_INFO, LOG_TAG, "%x ", (int)response[i]); dlog_print(DLOG_INFO, LOG_TAG, "\n"); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit failed"); }
To get a response for the transmission, use the smartcard_channel_transmit_retrieve_response() function:
int i; unsigned char * ptransmitResponse; ret = smartcard_session_open_logical_channel(session, aid, 12, 0x00, &channel); if (ret == SMARTCARD_ERROR_NONE) { ret = smartcard_channel_transmit(channel, command, 11, &response, &resp_len); ret = smartcard_channel_transmit_retrieve_response(channel, &ptransmitResponse, &pLength); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit_get_response successful"); dlog_print(DLOG_INFO, LOG_TAG, "response is "); for (i = 0; i < pLength; i++) dlog_print(DLOG_INFO, LOG_TAG, "%x ", (int)ptransmitResponse[i]); dlog_print(DLOG_INFO, LOG_TAG, "\n"); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit_get_response failed"); } }
Sending a Transmission
This use case covers the entire work flow of sending an APDU transmission from getting a reader to closing the session afterwards.
To send a transmission:
- Define the required variables and initialize the smart card service for use:
int i = 0; int pLength; int *phReaders = NULL; int session; int channel; unsigned char aid[] = {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35}; unsigned char command[] = {0x00, 0x28, 0x00, 0x00}; unsigned char *response = NULL; int resp_len = 50; ret = smartcard_initialize(); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard initialize successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard initialize failed");
- Get the available readers:
ret = smartcard_get_readers(&phReaders, &pLength);
- Open a session:
if (ret == SMARTCARD_ERROR_NONE && pLength != 0) { ret = smartcard_reader_open_session(phReaders[0], &session);
- Open a logical channel:
if (ret == SMARTCARD_ERROR_NONE && session != 0) { ret = smartcard_session_open_logical_channel(session, aid, 12, 0x00, &channel);
- Transmit the command:
if (ret == SMARTCARD_ERROR_NONE) { ret = smartcard_channel_transmit(channel, command, 4, &response, &resp_len); if (ret == SMARTCARD_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit successful"); dlog_print(DLOG_INFO, LOG_TAG, "response is "); for (i = 0; i < resp_len; i++) dlog_print(DLOG_INFO, LOG_TAG, "%x ", (int)response[i]); dlog_print(DLOG_INFO, LOG_TAG, "\n"); } else { dlog_print(DLOG_INFO, LOG_TAG, "smartcard_channel_transmit failed"); } } }
- Close the session:
ret = smartcard_session_close(session); }
- Deinitialize the service:
ret = smartcard_deinitialize(); if (ret == SMARTCARD_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "smartcard deinitialize successful"); else dlog_print(DLOG_INFO, LOG_TAG, "smartcard deinitialize failed");