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.
-
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:
-
Initialize the smart card service for use:
int ret; ret = smartcard_initialize(); if (ret == SMARTCARD_ERROR_NONE) TC_PRT("smartcard initialize success"); else TC_PRT("smartcard initialize failed");
The service is no longer needed, deinitialize it:
int ret; ret = smartcard_deinitialize(); if (ret == SMARTCARD_ERROR_NONE) TC_PRT("smartcard deinitialize success"); else TC_PRT("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) { TC_PRT("smartcard_get_readers is success"); TC_PRT("reader handle : %d", phReaders[0]); TC_PRT("readers length : %d", pLength); } else { TC_PRT("smartcard_get_readers failed : %d", ret); }
Managing the Reader
To manage a reader:
-
Retrieve the name of the reader with the smartcard_reader_get_name() function:
int ret; char * pReader = NULL; ret = smartcard_reader_get_name(reader, &pReader); if (ret == SMARTCARD_ERROR_NONE) { TC_PRT("smartcard_reader_get_name success"); TC_PRT("reader name : %s", pReader); } else { TC_PRT("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:
int ret; bool is_present = false; ret = smartcard_reader_is_secure_element_present(reader, &is_present); if (ret == SMARTCARD_ERROR_NONE) { TC_PRT("smartcard_reader_is_secure_element_present is success"); TC_PRT("reader secure element present : %d", is_present); } else { TC_PRT("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 session, use the smartcard_reader_close_sessions() function to close all sessions opened on the specific reader.
int ret; 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) { TC_PRT("smartcard_reader_close_sessions success"); } else { TC_PRT("smartcard_reader_close_sessions failed : %d", ret); } } else { TC_PRT("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) { TC_PRT("smartcard_session_get_reader success"); TC_PRT("reader name : %s", pReader); } else { TC_PRT("smartcard_session_get_reader failed"); }
- Retrieve the answer to reset (ATR) of the SE:
int ret; unsigned char *pAtr; int pLength; ret = smartcard_session_get_atr(session, &pAtr, &pLength); if (ret == SMARTCARD_ERROR_NONE) { TC_PRT("smartcard_session_get_atr success : %d", pLength); for (i = 0; i < pLength; i++) TC_PRT("%x ", (int)pAtr[i]); } else { TC_PRT("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) TC_PRT("smartcard_session_open_basic_channel is success : %d", channel); else TC_PRT("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) TC_PRT("smartcard_session_open_basic_channel is success : %d", (channel); else TC_PRT("smartcard_session_open_basic_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) TC_PRT("smartcard_session_close_channels is success"); else TC_PRT("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) TC_PRT("smartcard_session_close is success"); else TC_PRT("smartcard_session_close failed"); ret = smartcard_session_is_closed(session, &is_closed); if (ret == SMARTCARD_ERROR_NONE && is_closed == true) TC_PRT("smartcard_session_is_closed is success"); else TC_PRT("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) TC_PRT("smartcard_channel_get_session is success: %d", session_handle); else TC_PRT("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) TC_PRT("smartcard_channel_is_basic_channel is success"); else TC_PRT("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) TC_PRT("smartcard_channel_select_next is success"); else TC_PRT("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) { TC_PRT("smartcard_channel_get_select_response is success"); for (i = 0; i < pLength; i++) g_print("%x ", (int)pSelectResponse[i]); } else { TC_PRT("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) TC_PRT("smartcard_channel_is_closed is success"); else TC_PRT("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) TC_PRT("smartcard_channel_close is success"); else TC_PRT("smartcard_channel_close failed : %d", ret);
- Transmit an APDU command (as per ISO/IEC 7816-4) to the SE:
ret = smartcard_channel_transmit(channel, command, 4, &response, &resp_len); if (ret == SMARTCARD_ERROR_NONE) { TC_PRT("smartcard_channel_transmit is success"); TC_PRT("response is "); for (i = 0; i < resp_len; i++) TC_PRT("%x ", (int)response[i]); TC_PRT("\n"); } else { TC_PRT("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) { TC_PRT("smartcard_channel_transmit_get_response is success"); TC_PRT("response is "); for (i = 0; i < pLength; i++) TC_PRT("%x ", (int)ptransmitResponse[i]); TC_PRT("\n"); } else { TC_PRT("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) TC_PRT("smartcard initialize success"); else TC_PRT("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) { TC_PRT("smartcard_channel_transmit is success"); TC_PRT("response is "); for (i = 0; i < resp_len; i++) TC_PRT("%x ", (int)response[i]); TC_PRT("\n"); } else { TC_PRT("smartcard_channel_transmit failed"); } } }
- Close the session:
ret = smartcard_session_close(session); }
- Deinitialize the service:
ret = smartcard_deinitialize(); if (ret == SMARTCARD_ERROR_NONE) TC_PRT("smartcard deinitialize success"); else TC_PRT("smartcard deinitialize failed");