ATKey.Card NFC References for Developers
ATKey.Card NFC is a Smart Card that supports FIDO2, fingerprint authentication, Personal Identity Verification (PIV), and NFC Data Exchange Format (NDEF) through applets. Developers can use application protocol data unit (APDU) commands to interact with the applets and create custom applications or integrate ATKey.Card NFC with their existing stack.
Chapters
ATKey.Card NFC PIV APDU Commands References
Get Bio Metadata
With ATKey.Card NFC users can secure their PIV logins with fingerprint authentication. In order to do it their ATKey.Card NFC needs to have biometrics configured through the FIDO2 applet. to verify if biometric enrollment has been completed the command Get Bio Metadata can be used.
Command APDU Info
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | F7 | 00 | 96 | (absent) | (absent) | (absent) |
Response APDU Info
Total length: 10 + 2
Data length: 10
| Data | SW1 | SW2 |
|---|---|---|
| bio metadata as set of TLV | 90 | 00 |
The data consists of a set of Type-Length-Value (TLVs). The possible valid tags (T of TLV) are listed in the table below. The length (L of TLV) is one. The values (V of TLV) are dependent on the tags, described in the table below.
| Tag | Name | Meaning | Data |
|---|---|---|---|
| 07 | IsConfigured | state of biometric verification configuration (i.e. fingerprints are enrolled) | 1B data - 01 (configured) - 00 (not configured) |
| 06 | RetriesRemaining | indicates how many biometric match attempts are left | 2B data - 1st byte indicates the max bio retry count it’s 08 now. - 2nd byte is remaining retries count, the value range is 00-08 and 00 means biometric usage in PIV is locked. |
| 08 | Bio-for-pin-protection is enabled or not | indicates if Bio-for-pin-protection is enabled or not | 1B data - 01 (enabled) - 00 (disabled) |
Enable/Disable Bio-for-pin-protection
To activate, or disactivate, biometric protection for PIV logins this command can be used.
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | 20 | 00 | 96 | 09 | 8B-PIN + 1B-Val | (absent) |
The 1B-Val has different intentions as below.
01 - Enable Bio-for-pin-protection
00 - Disable Bio-for-pin-protection.
NB: the Bio-for-pin-protection feature will be disabled automatically in the case of this two events:
PIV-Reset
Bio is removed, i.e. FIDO2 applet removes it or processes FIDO2-Reset.
Response APDU Info
| Outcome | Data | SW1 | SW2 |
|---|---|---|---|
| Success | (no data) | 90 | 00 |
| Failure, no enrolled biometric | (no data) | 69 | 86 |
| Failure, invalid PIN | (no data) | 63 | CX |
If the PIN entered is incorrect, then the error displayed is 63 CX where X is the number of attempts remaining. When no attempts remain, the error returned is 0x6983.
.
Verify Biometric
This command requires the user to verify their fingerprint on the card to proceed.
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | 20 | 00 | 96 | (absent) | (absent) | (absent) |
Response APDU Info
| Outcome | Data | SW1 | SW2 |
|---|---|---|---|
| Success - Total length 16 + 2 - Data length: 16 |
16B temporary PIN | 90 | 00 |
| Failed biometric match | (no data) | 63 | CX |
| Waiting biometric timeout (around 1400ms) |
(no data) | 69 | 85 |
| No biometric enrolled | (no data) | 69 | 86 |
If the biometric match fails, the error is 63 CX where X is the number of attempts remaining. When no attempts remain, biometric verification is blocked, and the error returned is 0x6983.
Verify Temporary PIN
This command should be used after a successful response from the Verify Bio command, which would have established a temporary PIN.
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | 20 | 00 | 96 | 10 | temporary PIN | (absent) |
Response APDU Info
| Outcome | Data | SW1 | SW2 |
|---|---|---|---|
| Success | (no data) | 90 | 00 |
| Invalid temporary PIN | (no data) | 63 | CX |
If the temporary PIN entered is incorrect, then the error is 63 CX where X is the number of retries remaining, the retries value is shared with PIV-PIN. When no retries remain, the error returned is 0x6983.
Reset Biometrics Attempts
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | 2C | 00 | 96 | 10 | 8B Current PUK + 8B any data |
(absent) |
The second voice for Data (8B any data) does not have any use in this command but maintain consistent command format with Reset PIN Retries.
Response APDU Info
| Outcome | Data | SW1 | SW2 |
|---|---|---|---|
| Success | (no data) | 90 | 00 |
| Invalid PUK | (no data) | 63 | CX |
If the PUK entered is incorrect, then the error is 63 CX where X is the number of retries remaining. When no retries remain, the error returned is 0x6983.
Establishing Fingerprint Verification Policy
The fingerprint verification policy can be chosen for any slot and can be set as:
Never: fingerprint verification is never needed when using this slot
Always: fingerprint verification is needed for every use
Once: fingerprint verification is needed only once per power cycle session
Set management key (0xFF)
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | FF | FF | touch policy: - FF: Never - FE: Always - FD: Once |
1B | new management key data | (absent) |
Generate asymmetric key pair (0x47)
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | 47 | 00 | slot number | data len | [AC <remaining bytes> 80 01 <alg> …] [AA 01 <pin policy> … ] [AB 01 <touch policy> - 01: Never - 02: Always - 03: Once] |
(absent) |
Import asymmetric key pair (0xFE)
| CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|
| 00 | FE | alg | slot number | data len | set of TLV containing key elements [AA 01 <pin policy> … ] [AB 01 <touch policy> - 01: Never - 02: Always - 03: Once ] |
(absent) |
Response APDU Info
Below shows the response for Command APDU Info (0xF7) with P2 = slot number.
Note that the values meaning of PIN and Touch policies are different.
| Tag | Name | Meaning | Data | Slots |
|---|---|---|---|---|
| 02 | Policy | PIN and Touch policies | PIN: - 00: Default = Never - 01: Never - 02: Once - 03: Always Touch: - 00: Default = Never - 01: Never - 02: Always - 03: Once |
9a, 9b, 9c, 9d, 9e, f9, 82~ |
ATKey.Card NFC NDEF APDU Commands References
ATKey.Card NFC NDEF APDU Commands References
The NDEF (NFC Data Exchange Format) feature on the ATKey.Card uses a Javacard applet to emulate NDEF Message over NFC complying NFC Forum Type 4 Tag Operation Specification but only the exact minimum of APDU commands that the NDEF specification prescribes. And the NDEF applet has its own application ID and is processed separately.
NDEF AID: 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01
Available ATKey.Card NFC SKUs
Select File and Read Data: 630B02, 630B03, 630B04, 630B12, 630B13
Select File, Read Data, and Write Data: 630B04, 630B13
Supported APDU Commands
There are 2 defined files in this NDEF applet, but this document only focuses on NDEF data part.
0xE103 - NDEF capabilities
0xE104 - NDEF data
| Commands | CLA | INS | P1 | P2 | Lc | Data | Le |
|---|---|---|---|---|---|---|---|
| Select NDEF data File | 0x00 | 0xA4 | 0x00 | 0x0C | 0x02 | 0xE1 0x04 | (absent) |
| Read Data | 0x00 | 0xB0 | 0x00 | 0x00 | (absent) | (absent) | 0x00 |
| Write Data | 0x00 | 0xD6 | 0x00 | 0x00 | 6B NDEF Record Header + nB NDEF Record Payload |
NDEF Record Payload | (absent) |
The NDEF record header is defined as below:
| Field | Size | Description |
|---|---|---|
| Tag | 1 | Always 0 |
| Length | 1 | Length of the NDEF record |
| NDEF Header | 1 | Always 0xD1: Message Begin+End, Short Record, Type Name Format = NFC Forum well known |
| Length of record type | 1 | Always 1 |
| Payload Length | 1 | |
| Type | 1 | NFC Forum global type "U” = 0x55 |
| Payload | (varies) | The actual NDEF message. Of length "payload length" bytes. |
NB. The first byte of NDEF record payload is to indicate the following data protocol. In this document, it uses 0x04 meaning “https://” for example value.
Example
Below example demo how to use OpenSC to execute by below commands flow
Select applet with NDEF AID
Select NDEF data file to use
Write NDEF Data https://authentrend.com
Read NDEF Data back
opensc-tool.exe -c default -r 0 -s 00:A4:04:00:07:D2:76:00:00:85:01:01 -s 00:A4:00:0C:02:E1:04 -s 00:D6:00:00:16:00:14:D1:01:10:55:04:61:75:74:68:65:6E:74:72:65:6E:64:2E:63:6F:6D -s 00:B0:00:00:00
Sending: 00 A4 04 00 07 D2 76 00 00 85 01 01
Received (SW1=0x90, SW2=0x00)
Sending: 00 A4 00 0C 02 E1 04
Received (SW1=0x90, SW2=0x00)
Sending: 00 D6 00 00 16 00 14 D1 01 10 55 04 61 75 74 68 65 6E 74 72 65 6E 64 2E 63 6F 6D
Received (SW1=0x90, SW2=0x00)
Sending: 00 B0 00 00 00
Received (SW1=0x90, SW2=0x00):
00 14 D1 01 10 55 04 61 75 74 68 65 6E 74 72 65 .....U.authentre
6E 64 2E 63 6F 6D 00 00 00 00 00 00 00 00 00 00 nd.com..........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
How to send command to AT-USB-HID interface? Web Sample Code
The below sample code can be run on https://seleniumbase.io/w3schools/.
<!DOCTYPE html>
<script>
const CMD_INIT = 0x86;
const CMD_MSG = 0x83;
var usb_device;
var channel_id = [0xFF, 0xFF, 0xFF, 0xFF];
async function request_hid() {
const filters = [
{
vendorId: 0x31bb,
productId: 0x0635,
usagePage: 0x4154,
},
];
const devices = await navigator.hid.requestDevice({ filters });
usb_device = devices[0];
await usb_device.open();
usb_device.addEventListener("inputreport", (event) => {
const { data, device, reportId } = event;
const cid = data.getUint32(0);
const cmd = data.getUint8(4);
if (cmd == CMD_INIT) {
const cid = data.buffer.slice(14, 4);
channel_id = [data.getUint8(15), data.getUint8(16), data.getUint8(17), data.getUint8(18)];
console.log(`CID: ${channel_id}`);
}
});
//const cmd_get_cid = [0xFF, 0xFF, 0xFF, 0xFF, CMD_INIT, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0];
let nonce = [0, 0, 0, 0, 0, 0, 0, 0];
let packet = new ArrayBuffer(64);
let cmd_get_cid = new Uint8Array(packet);
cmd_get_cid.set(new Uint8Array(channel_id), 0);
cmd_get_cid.set(new Uint8Array([CMD_INIT, 0, nonce.length]), 4);
cmd_get_cid.set(new Uint8Array(nonce), 4 + 3);
await usb_device.sendReport(0x00, cmd_get_cid);
}
async function get_keycode() {
usb_device.addEventListener("inputreport", (event) => {
const { data, device, reportId } = event;
const cid = data.getUint32(0);
const cmd = data.getUint8(4);
if (cmd == CMD_MSG) {
//const keycode = [data.getUint8(15), data.getUint8(16), data.getUint8(17), data.getUint8(18)];
const keycode = data.getUint32(15);
console.log(`Keycode: ${keycode}`);
alert(`Keycode: ${keycode.toString(16)}`);
}
});
//const cmd_get_keycode = [??, ??, ??, ??, CMD_MSG, 0, 19, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x55, 0xAA, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x7C, 0x00, 0x00];
const payload = [0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x55, 0xAA, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x7C, 0x00, 0x00];
let packet = new ArrayBuffer(64);
let cmd_get_keycode = new Uint8Array(packet);
cmd_get_keycode.set(new Uint8Array(channel_id), 0);
cmd_get_keycode.set(new Uint8Array([CMD_MSG, 0, payload.length]), 4);
cmd_get_keycode.set(new Uint8Array(payload), 4 + 3);
await usb_device.sendReport(0x00, cmd_get_keycode);
}
</script>
<html>
<body>
<p>Step 1: </p>
<button onclick="request_hid()">List Devices</button>
<br>
<br>
<p>Step 2: </p>
<button onclick="get_keycode()">Get Keycode</button>
</body>
</html>
