npm package: @paddek/smartcard
Description: Changes: * more flexible (hopefully) see: **Class: Devices constructor** * events for card detection, card leaving and state change for better control * depends on [PaddeK's](https://github.com/paddek) fork of [pcsclite](https://github.com/paddek/node-
Author: paddek
Latest version: 1.0.2

# Fork of [tomkp's](https://www.npmjs.com/~tomkp) [smartcard](https://github.com/tomkp/smartcard) library.

Changes:
* more flexible (hopefully) see: **Class: Devices constructor**
* events for card detection, card leaving and state change for better control
* depends on [PaddeK's](https://github.com/paddek) fork of [pcsclite](https://github.com/paddek/node-pcsclite) which fixes node v12 compilation errors and warnings

# smartcard

This is a simple wrapper around [Santiago Gimeno's](https://www.npmjs.org/~sgimeno) great [pcsclite](https://github.com/santigimeno/node-pcsclite) library.

Used by [Card Spy](http://card-spy.surge.sh)

## API

The following objects are defined by the `smartcard` library, each contains its own set of methods and events.

### Class: Devices
A general object that provides access to all smartcard related devices.

#### Events
The `devices` object emits the following events

##### Event: 'device-activated'
Emitted when a card reader is attached.
Returns:
* `Object`
  * `Device`
  * `Array`: List of all devices, returned via `devices.listDevices()`

##### Event: 'device-deactivated'
Emitted when a card reader is detached.
Returns:
* `Object`
  * `Device`
  * `Array`: List of all devices, returned via `devices.listDevices()`

##### Event: 'error'
Emitted when an error occurs
Returns `Object`:
* _error_ `Error`

#### Methods
The following methods are available within the `devices` class.

##### Constructor `Devices(options)`
The constructor for a devices object takes one optional argument.
* _options_ `Object`
  * **_shareMode_** `Number` (optional): The shareMode the autoConnect uses **_(default: SCARD_SHARE_SHARED)_**
  * **_disposition_** `Number` (optional): The disposition the autoDisconnect uses **_(default: SCARD_LEAVE_CARD)_**
  * **_autoConnect_** `Boolean` (optional): Auto connect after card-detected event **_(default: true)_**
  * **_autoDisconnect_** `Boolean` (optional): Auto disconnect after card-left event **_(default: true)_**
  * **_isCardInserted_** `Function` (optional): Function to determine if card is inserted **_(default: preserve smartcard behaviour)_**
  * **_isCardRemoved_** `Function` (optional): Function to determine if card is removed **_(default: preserve smartcard behaviour)_**

##### `devices.listDevices()`
Returns `Object` a list of the different devices attached, each a `device` object

##### `devices.lookup(name)`
* _name_ `String`: The text name of a device


### Class: Device
An object representing a specific card reader (device).

#### Methods
The following methods are available within the `device` class.

##### `device.getCard()`
Returns the `Card` inserted in the attached device.

##### `device.getName()`
Returns the name of the attached device.

##### `device.getStatus()`
Returns the current status of the attached device.

##### `device.connect(options)`
Connects to attached device and sets reference to inserted `Card`.
* _options_ `Object`
  * _share_mode_ `Number` (optional): The share mode to use _(default: SCARD_SHARE_EXCLUSIVE)_
  * _protocol_ `Number` (optional): The prefered protocol _(default: SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)_ 

Returns `Promise`
  * Resolves with _event_ `Object`
  * Rejects with _error_ `Error`

##### `device.disconnect(disposition)`
Disconnect from attached device and remove reference to inserted `Card`.
* _disposition_ `Number` (optional): The share mode to use _(default: SCARD_UNPOWER_CARD)_

Returns `Promise`
  * Resolves with _event_ `Object`
  * Rejects with _error_ `Error`

##### `device.transmit(data, res_len, protocol, cb)`
Sends a command to the connected device
* _data_ `Buffer`: data to be transmitted
* _res_len_ `Number`: Maximum length of the expected response, includes the 2 byte response (sw1 and sw2)
* _protocol_ `Number`: Protocol to be used in the transmission

Returns `Promise`
  * Resolves with _response_ `Buffer`
  * Rejects with _error_ `Error`

#### Events
The `device` object emits the following events

##### Event: 'card-inserted'
Emitted when a smartcard is inserted into a card reader

Returns `Object`:
* _device_ `Device`
* _protocol_ `Number`
* _card_ `Card`

##### Event: 'card-removed'
Emitted when a smartcard is removed from a card reader

Returns `Object`:
* _name_ `String`
* _card_ `Card`

##### Event: 'card-detected'
Emitted when a smartcard is about to be inserted into a card reader

Returns `Object`:
* _state_ `Number`
* _atr_ `String`

##### Event: 'status'
Emitted when the state of a card reader changes

Returns `Object`:
* _state_ `Number`
* _atr_ `String`

##### Event: 'card-left'
Emitted when a smartcard is about to be removed from a card reader

Returns `Object`:
* _state_ `Number`
* _atr_ `String`

### Class: Card
An object representing an attached smart card.

#### Methods
The following methods are available within the `card` class.

##### `card.getAtr()`
Returns `String` containing the atr of the card

##### `card.issueCommand(commandApdu)`
Sends a command to the card
* _commandApdu_: The command to be sent to the card
  * `String`
  * `Buffer`
  * `Array`
  * `CommandApdu`

Returns `Promise`
  * Resolves with _response_ `Buffer`
  * Rejects with _error_ `Error`

If no callback is specified, returns a `Promise`
*
#### Events
The `card` object emits the following events

##### Event: 'command-issued'
Emitted when a command is sent to the smartcard.

Returns `Object`:
* _card_ `Card`
* _command_ `CommandApdu`

##### Event: 'response-received'
Emitted when a response is received from the card.

Returns `Object`:
* _card_ `Card`
* _command_ `CommandApdu`
* _response_ `ResponseApdu`

### Class: CommandApdu
An object representing a command to send to a smart card

#### Methods
The `CommandApdu` class has the following methods.

##### Constructor `CommandApdu(obj)`
Creates a new instance and sets the appropriate items
* _obj_ `Object`
  * _cla_ `Number`: The class of the command, typically 0
  * _ins_ `Number`: The instruction
  * _p1_ `Number`: The value of p1
  * _p2_ `Number`: The value of p2
  * _data_ `Array` (optional): The value of data
  * _le_ `Number` (optional): The value of le

OR
* _obj_ `Object`
    * _bytes_ `Array`: Byte array representing the whole command
##### `CommandApdu.toBuffer()`
Converts the command to a Buffer
* Returns `Buffer`

##### `CommandApdu.toString()`
Converts the command to a hex string
* Returns `String`

##### `CommandApdu.toByteArray()`
Converts the command to a byte array
* Returns `Array`

##### `CommandApdu.setLe(le)`
Updates the le value of the command
* _le_ `Number`: The new le value


### Class: ResponseApdu
Class representing a response from the card

#### Methods
The `ResponseApdu` class has the following methods.

##### `ResponseApdu.meaning()`
Interprets the return code and attempts to provide a text translation.
* Returns `String`

##### `ResponseApdu.getDataOnly()`
Returns the response data without including the status code
* Returns `String`

##### `ResponseApdu.getStatusCode()`
Returns only the status code
* Returns `String`

##### `ResponseApdu.isOk()`
Check if the status code is 9000
* Returns `Boolean`

##### `ResponseApdu.buffer()`
Returns the whole buffer, status code and data
* Returns `Buffer`

##### `ResponseApdu.hasMoreBytesAvailable()`
Reads the status code and looks for a 61 as sw1, meaning more data is available
* Returns `Boolean`

##### `ResponseApdu.numberOfBytesAvailable()`
Reads sw2 staus code to return number of bytes left, when sw1 is 61. A value of 0 means there are more than 256 bytes remaining.
* Returns `Number`

##### `ResponseApdu.isWrongLength()`
Checks status code for 6c as sw1
* Returns `Boolean`

##### `ResponseApdu.correctLength()`
If sw1 is 6c, returns the correct length from sw2. A value of 0 means there are more than 256 bytes remaining.
* Returns `Number`

### Class: Iso7816Application
An object offering general commands to most ISO7816 compliant smart cards.

#### Methods

##### Constructor `Iso7816Application(card)`
Sets up the `Iso7816Application` object
* _card_ `Card`: The card to communicate with using ISO7816 standards

##### `Iso7816Application.issueCommand(commandApdu)`
Sends the provided command to the card. Automatically retrieve the full response, even if it requires multiple GET_RESPONSE commands
* _commandApdu_ `CommandApdu`: Command to send to the card

Returns
* `ResponseApdu` Complete response from card

##### `Iso7816Application.selectFile(bytes, p1, p2)`
Sends the SELECT command to the card, often called selecting an application
* _bytes_ `Buffer`: The resource locater (AID, etc)
* _p1_ `Number`: Value to specify as the p1 value
* _p2_ `Number`: Value to specify as the p2 value

Returns
* `ResponseApdu` Complete response from card

##### `Iso7816Application.getResponse(length)`
Sends a single GET_RESPONSE command to the card
* _length_ `Number`: The length of the response expected, maximum is 0xFF

Returns
* `ResponseApdu` Complete response from card

##### `Iso7816Application.getResponse(sfi,record)`
Sends a READ_RECORD command to the card
* _sfi_ `Number`: The sfi
* _record_ `Number`: The record

Returns
* `ResponseApdu` Complete response from card

##### `Iso7816Application.getData(p1, p2)`
Sends a GET_DATA command to the card
* _p1_ `Number`: Value to specify as the p1 value
* _p2_ `Number`: Value to specify as the p2 value

Returns
* `ResponseApdu` Complete response from card

#### Events
The `Iso7816Application` class emits the following events

##### Event: 'application-selected'
Emitted when a successful reply to a `selectFile()` command is received.

Returns `Object`:
* _application_ `String`

## Examples


### With event emitter

```javascript
'use strict';

const smartcard = require('@paddek/smartcard');
const Devices = smartcard.Devices;
const devices = new Devices();


devices.on('device-activated', (event => {
    console.log(`Device '${event.device}' activated`);
    event.devices.map((device, index) => {
        console.log(`Device #${index + 1}: '${device.name}'`);
    });
}));
```

### Selecting the Payment Systems Environment on an EMV (Chip & Pin) card


```javascript
'use strict';

const smartcard = require('@paddek/smartcard');
const Devices = smartcard.Devices;
const Iso7816Application = smartcard.Iso7816Application;

const devices = new Devices();

devices.on('device-activated', event => {
    const currentDevices = event.devices;
    let device = event.device;
    console.log(`Device '${device}' activated, devices: ${currentDevices}`);
    for (let prop in currentDevices) {
        console.log("Devices: " + currentDevices[prop]);
    }

    device.on('card-inserted', event => {
        let card = event.card;
        console.log(`Card '${card.getAtr()}' inserted into '${event.device}'`);

        card.on('command-issued', event => {
            console.log(`Command '${event.command}' issued to '${event.card}' `);
        });

        card.on('response-received', event => {
            console.log(`Response '${event.response}' received from '${event.card}' in response to '${event.command}'`);
        });

        const application = new Iso7816Application(card);
        application.selectFile([0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31])
            .then(response => {
                console.info(`Select PSE Response: '${response}' '${response.meaning()}'`);
            }).catch(error => {
                console.error('Error:', error, error.stack);
            });

    });
    device.on('card-removed', event => {
        console.log(`Card removed from '${event.name}' `);
    });

});

devices.on('device-deactivated', event => {
    console.log(`Device '${event.device}' deactivated, devices: [${event.devices}]`);
});
```