Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 81 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,73 @@ 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';
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();
Expand All @@ -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`.

Expand Down Expand Up @@ -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.
Expand Down
34 changes: 19 additions & 15 deletions demo/iso7816-demo.js
Original file line number Diff line number Diff line change
@@ -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();