demo · v143 · capabilities (fugu)

PIV card reader

A fully interactive simulation of reading a PIV (Personal Identity Verification) / CAC smart card using the Web Smart Card API. Insert a virtual card, select the PIV applet, choose a data object, send the GET DATA APDU, and verify a PIN — with hex dumps and parsed field labels throughout.

Origin trial / behind a flag: navigator.smartCard ships behind chrome://flags/#web-smart-card-api in Chrome 143. The full simulation below runs without a physical card or the flag enabled. When the API is present, the "Real API path" panel at the bottom shows the actual call chain.

1 — Insert card

PIV / CACUS Federal ID
SN: 9876-A
No card present

2 — Select PIV applet

PIV AID
A0 00 00 03 08 00 00 10 00 01 00
NIST SP 800-73-4 registered AID
APDU sent
00 A4 04 00 0B A0 00 00 03 08 00 00 10 00 01 00 00
SELECT (CLA=00 INS=A4 P1=04 P2=00)
Insert a card first.

3 — Choose data object

5F C1 05

Certificate for PIV Authentication — X.509 cert used for card authentication.

5F C1 07

Card Capability Container — FIPS 201 capabilities and GUID.

5F C1 08

Cardholder Facial Image — JPEG2000 biometric reference.

5F C1 02

CHUID — Cardholder Unique Identifier with FASC-N and expiry date.

Select PIV applet first.

4 — PIN verification

PIV cards use a 6–8 digit PIN. Three consecutive failures (SW 69 83) permanently blocks the PIN reference. Retry counter decrements on each wrong attempt (SW 63 Cx).

Select PIV applet first.
VERIFY APDU detail
CLA=00 INS=20 P1=00 P2=80
Lc=08 (PIN padded to 8 bytes with 0xFF)
Data: PIN bytes + FF padding
Le: (absent — VERIFY has no response data)
Correct PIN → SW 90 00
Wrong PIN, n retries left → SW 63 Cn
Blocked → SW 69 83

Real API path (Web Smart Card API)

1
const ctx = await navigator.smartCard.requestContext();
2
const readers = await ctx.listReaders();
const reader = readers[0]; // e.g. "Yubico YubiKey 5"
3
const connection = await reader.connect('shared');
4
await connection.startTransaction(async () => {
// SELECT PIV applet
const pivAid = new Uint8Array([0xA0,0x00,0x00,0x03,0x08,0x00,0x00,0x10,0x00,0x01,0x00]);
const sel = new Uint8Array([0x00,0xA4,0x04,0x00,pivAid.length,...pivAid,0x00]);
const selResp = await connection.transmit(sel);
// GET DATA for CHUID (tag 5F C1 02)
const getData = new Uint8Array([0x00,0xCB,0x3F,0xFF,0x05,0x5C,0x03,0x5F,0xC1,0x02]);
const resp = await connection.transmit(getData);
// Parse TLV, extract FASC-N, expiry, signature
return resp;
});
5
await connection.disconnect();
Click to attempt navigator.smartCard.requestContext()

see also