From 69cb239e007f8b3fad167a0121f61bb2e47fb0f1 Mon Sep 17 00:00:00 2001 From: tomkp Date: Sun, 28 Dec 2025 14:46:01 +0000 Subject: [PATCH] Update README with modern examples and requirements - Update examples to use async/await instead of .then()/.catch() - Add PC/SC driver requirements for different platforms - Add TypeScript usage example with type annotations - Document maxRetries parameter for issueCommand() - Add createCommandApdu usage example - Document ResponseApdu methods - Update demo file to use async/await and import from dist/ Fixes #28 --- README.md | 107 ++++++++++++++++++++++++++++++++----------- demo/iso7816-demo.js | 34 ++++++++------ 2 files changed, 100 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 66f9874..3fb4285 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,14 @@ npm install iso7816 ## Requirements - Node.js 18.0.0 or higher +- PC/SC driver installed on your system: + - **macOS**: Built-in (no installation required) + - **Windows**: Built-in (no installation required) + - **Linux**: Install `pcsclite` (`sudo apt-get install libpcsclite-dev` on Debian/Ubuntu) -## Examples +## Usage + +### JavaScript ```javascript import { Devices } from 'smartcard'; @@ -20,35 +26,58 @@ import iso7816 from 'iso7816'; const devices = new Devices(); -devices.on('reader-attached', function (reader) { - console.info(`Reader '${reader.name}' attached`); -}); +devices.on('card-inserted', async ({ reader, card }) => { + console.log(`Card inserted into '${reader.name}'`); -devices.on('reader-detached', function (reader) { - console.info(`Reader '${reader.name}' detached`); -}); + const application = iso7816(card); -devices.on('card-removed', function ({ reader }) { - console.info(`Card removed from '${reader.name}'`); + try { + // Select PSE (Payment System Environment) + const response = await application.selectFile([ + 0x31, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, + ]); + + console.log(`Response: ${response.getStatus().meaning}`); + + if (response.isOk()) { + // Read a record + const record = await application.readRecord(1, 1); + console.log(`Record: ${record.toString()}`); + } + } catch (error) { + console.error('Error:', error.message); + } }); -devices.on('error', function (error) { +devices.on('error', (error) => { console.error(`Error: ${error.message}`); }); -devices.on('card-inserted', function ({ reader, card }) { - console.info(`Card inserted into '${reader.name}', atr: '${card.atr.toString('hex')}'`); +devices.start(); +``` - const application = iso7816(card); +### TypeScript + +```typescript +import { Devices } from 'smartcard'; +import iso7816, { Iso7816, ResponseApdu, createCommandApdu } from 'iso7816'; + +const devices = new Devices(); - // Select PSE (Payment System Environment) - application - .selectFile([0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31]) - .then(function (response) { - console.info(`Select PSE Response: '${response}' '${response.getStatus().meaning}'`); - }).catch(function (error) { - console.error('Error:', error, error.stack); - }); +devices.on('card-inserted', async ({ reader, card }) => { + const application: Iso7816 = iso7816(card); + + try { + const response: ResponseApdu = await application.selectFile([0xa0, 0x00, 0x00]); + + if (response.isOk()) { + console.log('Selection successful'); + } else { + console.log(`Error: ${response.getStatus().meaning}`); + } + } catch (error) { + console.error('Error:', error); + } }); devices.start(); @@ -64,13 +93,13 @@ Create an ISO 7816 application instance for the given card. Returns an `Iso7816` instance with the following methods: -### `application.selectFile(bytes, p1, p2)` +### `application.selectFile(bytes, p1?, p2?)` Select a file on the smartcard. - `bytes` - Array of bytes representing the file identifier (e.g., AID) -- `p1` - Optional P1 parameter (default: 0x04) -- `p2` - Optional P2 parameter (default: 0x00) +- `p1` - Optional P1 parameter (default: `0x04`) +- `p2` - Optional P2 parameter (default: `0x00`) Returns a Promise that resolves to a `ResponseApdu`. @@ -100,14 +129,40 @@ Get additional response bytes. Returns a Promise that resolves to a `ResponseApdu`. -### `application.issueCommand(commandApdu)` +### `application.issueCommand(commandApdu, maxRetries?)` Issue a raw APDU command. -- `commandApdu` - A CommandApdu instance +- `commandApdu` - A `CommandApdu` instance +- `maxRetries` - Maximum retries for wrong length responses (default: `3`) Returns a Promise that resolves to a `ResponseApdu`. +### `createCommandApdu(options)` + +Create a command APDU. + +```typescript +import { createCommandApdu } from 'iso7816'; + +const apdu = createCommandApdu({ + cla: 0x00, + ins: 0xa4, + p1: 0x04, + p2: 0x00, + data: [0xa0, 0x00, 0x00], + le: 0x00, +}); +``` + +### `ResponseApdu` methods + +- `isOk()` - Returns `true` if status is 9000 (success) +- `getStatusCode()` - Returns 4-character hex status code (e.g., "9000") +- `getStatus()` - Returns `{ code: string, meaning: string }` +- `getBuffer()` - Returns the raw response buffer +- `toString()` - Returns hex string of the response + ## Compatible Readers Tested on Mac OSX with the SCM SCR3500 Smart Card Reader. diff --git a/demo/iso7816-demo.js b/demo/iso7816-demo.js index 3f04d0b..8a42698 100644 --- a/demo/iso7816-demo.js +++ b/demo/iso7816-demo.js @@ -1,35 +1,39 @@ import { Devices } from 'smartcard'; -import iso7816 from '../src/iso7816-application.js'; +import iso7816 from '../dist/index.js'; const devices = new Devices(); -devices.on('reader-attached', function (reader) { +devices.on('reader-attached', (reader) => { console.log(`Reader '${reader.name}' attached`); }); -devices.on('reader-detached', function (reader) { +devices.on('reader-detached', (reader) => { console.log(`Reader '${reader.name}' detached`); }); -devices.on('card-removed', function ({ reader }) { +devices.on('card-removed', ({ reader }) => { console.log(`Card removed from '${reader.name}'`); }); -devices.on('error', function (error) { - console.log(`Error: ${error.message}`); +devices.on('error', (error) => { + console.error(`Error: ${error.message}`); }); -devices.on('card-inserted', function ({ reader, card }) { - console.log(`Card inserted into '${reader.name}', atr: '${card.atr.toString('hex')}'`); +devices.on('card-inserted', async ({ reader, card }) => { + console.log(`Card inserted into '${reader.name}', ATR: ${card.atr.toString('hex')}`); const application = iso7816(card); - application - .selectFile([0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31]) - .then(function (response) { - console.info(`Select PSE Response: '${response}' '${response.getStatus().meaning}'`); - }).catch(function (error) { - console.error('Error:', error, error.stack); - }); + + try { + // Select PSE (Payment System Environment) + const response = await application.selectFile([ + 0x31, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, + ]); + + console.log(`Select PSE Response: ${response} (${response.getStatus().meaning})`); + } catch (error) { + console.error('Error:', error.message); + } }); devices.start();