smartcard 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -20
- package/lib/devices.js +34 -2
- package/lib/errors.js +5 -0
- package/lib/index.js +1 -2
- package/package.json +1 -1
- package/src/reader_monitor.cpp +8 -0
package/README.md
CHANGED
|
@@ -1,42 +1,108 @@
|
|
|
1
1
|
# smartcard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```
|
|
4
|
+
╭────────────────────────────────────────╮
|
|
5
|
+
│ │
|
|
6
|
+
│ ╭───────────╮ │
|
|
7
|
+
│ │ ▄▄▄ ▄▄▄▄▄ │ │
|
|
8
|
+
│ │ ███ ▄▄▄▄▄ │ │
|
|
9
|
+
│ │ ▀▀▀ ▀▀▀▀▀ │ │
|
|
10
|
+
│ ╰───────────╯ │
|
|
11
|
+
│ │
|
|
12
|
+
│ ░░░░░░░░░░░░ │
|
|
13
|
+
│ ░░░░░░░░░░░░ │
|
|
14
|
+
╰────────────────────────────────────────╯
|
|
15
|
+
```
|
|
4
16
|
|
|
5
|
-
|
|
17
|
+
Stable PC/SC smart card bindings for Node.js.
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
Works with Node.js 12+ without recompilation. Built on N-API for long-term stability.
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
- **Async/Promise-based**: Non-blocking card operations
|
|
11
|
-
- **Event-driven API**: High-level `Devices` class with EventEmitter
|
|
12
|
-
- **TypeScript support**: Full type definitions included
|
|
13
|
-
- **Cross-platform**: Windows, macOS, and Linux
|
|
21
|
+
## Getting Started
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
### 1. Install the package
|
|
16
24
|
|
|
17
25
|
```bash
|
|
18
26
|
npm install smartcard
|
|
19
27
|
```
|
|
20
28
|
|
|
21
|
-
###
|
|
22
|
-
|
|
23
|
-
**macOS**: No additional setup required (uses built-in PCSC.framework)
|
|
29
|
+
### 2. Platform setup
|
|
24
30
|
|
|
25
|
-
**Windows**:
|
|
31
|
+
**macOS/Windows**: Ready to go - no additional setup needed.
|
|
26
32
|
|
|
27
33
|
**Linux**:
|
|
28
34
|
```bash
|
|
29
|
-
#
|
|
30
|
-
sudo apt-get install libpcsclite-dev pcscd
|
|
35
|
+
# Install PC/SC libraries
|
|
36
|
+
sudo apt-get install libpcsclite-dev pcscd # Debian/Ubuntu
|
|
37
|
+
sudo dnf install pcsc-lite-devel pcsc-lite # Fedora/RHEL
|
|
31
38
|
|
|
32
|
-
#
|
|
33
|
-
sudo dnf install pcsc-lite-devel pcsc-lite
|
|
34
|
-
|
|
35
|
-
# Start the PC/SC daemon
|
|
39
|
+
# Start the daemon
|
|
36
40
|
sudo systemctl start pcscd
|
|
37
41
|
```
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
### 3. Connect a reader and run your first script
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
const { Devices } = require('smartcard');
|
|
47
|
+
|
|
48
|
+
const devices = new Devices();
|
|
49
|
+
|
|
50
|
+
devices.on('card-inserted', async ({ reader, card }) => {
|
|
51
|
+
console.log(`Card detected in ${reader.name}`);
|
|
52
|
+
console.log(`ATR: ${card.atr.toString('hex')}`);
|
|
53
|
+
|
|
54
|
+
// Get card UID (works with most contactless cards)
|
|
55
|
+
const response = await card.transmit([0xFF, 0xCA, 0x00, 0x00, 0x00]);
|
|
56
|
+
console.log(`UID: ${response.slice(0, -2).toString('hex')}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
devices.on('error', (err) => console.error(err.message));
|
|
60
|
+
|
|
61
|
+
devices.start();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run it:
|
|
65
|
+
```bash
|
|
66
|
+
node app.js
|
|
67
|
+
# Tap a card on your reader...
|
|
68
|
+
# Card detected in ACS ACR122U
|
|
69
|
+
# ATR: 3b8f8001804f0ca0000003060300030000000068
|
|
70
|
+
# UID: 04a23b7a
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Recommended Hardware
|
|
74
|
+
|
|
75
|
+
### Readers
|
|
76
|
+
|
|
77
|
+
| Reader | Type | Notes |
|
|
78
|
+
|--------|------|-------|
|
|
79
|
+
| **ACR122U** | USB contactless | Affordable, widely available. Great for getting started. |
|
|
80
|
+
| **ACR1252U** | USB dual-interface | Supports both contactless and contact cards. |
|
|
81
|
+
| **SCM SCR35xx** | USB contact | Tested with SCR35xx v2.0. Good for contact smart cards. |
|
|
82
|
+
| **HID Omnikey 5427** | USB contactless | Enterprise-grade, faster reads. |
|
|
83
|
+
| **Identiv uTrust 3700F** | USB contactless | Compact, reliable. |
|
|
84
|
+
|
|
85
|
+
Any PC/SC compatible reader should work. The library uses standard PC/SC APIs.
|
|
86
|
+
|
|
87
|
+
### Cards
|
|
88
|
+
|
|
89
|
+
| Card Type | Interface | Notes |
|
|
90
|
+
|-----------|-----------|-------|
|
|
91
|
+
| MIFARE Classic 1K/4K | Contactless | Most common NFC cards |
|
|
92
|
+
| MIFARE Ultralight / NTAG | Contactless | Stickers, wristbands, keyfobs |
|
|
93
|
+
| MIFARE DESFire | Contactless | Higher security applications |
|
|
94
|
+
| ISO 14443-4 | Contactless | Generic contactless smart cards |
|
|
95
|
+
| ISO 7816 | Contact | Standard contact smart cards (SIM, bank cards, ID cards) |
|
|
96
|
+
|
|
97
|
+
## Features
|
|
98
|
+
|
|
99
|
+
- **ABI Stable**: Works across Node.js versions without recompilation
|
|
100
|
+
- **Async/Promise-based**: Non-blocking card operations
|
|
101
|
+
- **Event-driven API**: High-level `Devices` class with EventEmitter
|
|
102
|
+
- **TypeScript support**: Full type definitions included
|
|
103
|
+
- **Cross-platform**: Windows, macOS, and Linux
|
|
104
|
+
|
|
105
|
+
## More Examples
|
|
40
106
|
|
|
41
107
|
### High-Level API (Event-Driven)
|
|
42
108
|
|
package/lib/devices.js
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
const EventEmitter = require('events');
|
|
4
5
|
const addon = require('../build/Release/smartcard_napi.node');
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./index').Reader} Reader
|
|
9
|
+
* @typedef {import('./index').Card} Card
|
|
10
|
+
* @typedef {import('./index').MonitorEvent} MonitorEvent
|
|
11
|
+
* @typedef {import('./index').Context} ContextType
|
|
12
|
+
* @typedef {import('./index').ReaderMonitor} ReaderMonitorType
|
|
13
|
+
*/
|
|
14
|
+
|
|
6
15
|
const { Context, ReaderMonitor } = addon;
|
|
7
16
|
const SCARD_STATE_PRESENT = addon.SCARD_STATE_PRESENT;
|
|
8
17
|
const SCARD_SHARE_SHARED = addon.SCARD_SHARE_SHARED;
|
|
9
18
|
const SCARD_PROTOCOL_T0 = addon.SCARD_PROTOCOL_T0;
|
|
10
19
|
const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
|
|
11
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} ReaderState
|
|
23
|
+
* @property {boolean} hasCard
|
|
24
|
+
* @property {Card|null} card
|
|
25
|
+
*/
|
|
26
|
+
|
|
12
27
|
/**
|
|
13
28
|
* High-level event-driven API for PC/SC devices
|
|
14
29
|
*
|
|
@@ -21,14 +36,20 @@ const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
|
|
|
21
36
|
* - 'card-inserted': Emitted when a card is inserted
|
|
22
37
|
* - 'card-removed': Emitted when a card is removed
|
|
23
38
|
* - 'error': Emitted on errors
|
|
39
|
+
*
|
|
40
|
+
* @extends EventEmitter
|
|
24
41
|
*/
|
|
25
42
|
class Devices extends EventEmitter {
|
|
26
43
|
constructor() {
|
|
27
44
|
super();
|
|
45
|
+
/** @type {ReaderMonitorType|null} */
|
|
28
46
|
this._monitor = null;
|
|
47
|
+
/** @type {ContextType|null} */
|
|
29
48
|
this._context = null;
|
|
49
|
+
/** @type {boolean} */
|
|
30
50
|
this._running = false;
|
|
31
|
-
|
|
51
|
+
/** @type {Map<string, ReaderState>} */
|
|
52
|
+
this._readers = new Map();
|
|
32
53
|
}
|
|
33
54
|
|
|
34
55
|
/**
|
|
@@ -95,7 +116,7 @@ class Devices extends EventEmitter {
|
|
|
95
116
|
|
|
96
117
|
/**
|
|
97
118
|
* List currently known readers
|
|
98
|
-
* @returns {
|
|
119
|
+
* @returns {Reader[]} Array of readers
|
|
99
120
|
*/
|
|
100
121
|
listReaders() {
|
|
101
122
|
if (!this._context || !this._context.isValid) {
|
|
@@ -110,6 +131,8 @@ class Devices extends EventEmitter {
|
|
|
110
131
|
|
|
111
132
|
/**
|
|
112
133
|
* Handle events from native monitor
|
|
134
|
+
* @param {MonitorEvent} event
|
|
135
|
+
* @returns {Promise<void>}
|
|
113
136
|
*/
|
|
114
137
|
async _handleEvent(event) {
|
|
115
138
|
if (!this._running) {
|
|
@@ -144,6 +167,9 @@ class Devices extends EventEmitter {
|
|
|
144
167
|
|
|
145
168
|
/**
|
|
146
169
|
* Handle reader attached
|
|
170
|
+
* @param {string} readerName
|
|
171
|
+
* @param {number} state
|
|
172
|
+
* @param {Buffer|null} atr
|
|
147
173
|
*/
|
|
148
174
|
_handleReaderAttached(readerName, state, atr) {
|
|
149
175
|
// Initialize reader state
|
|
@@ -169,6 +195,7 @@ class Devices extends EventEmitter {
|
|
|
169
195
|
|
|
170
196
|
/**
|
|
171
197
|
* Handle reader detached
|
|
198
|
+
* @param {string} readerName
|
|
172
199
|
*/
|
|
173
200
|
_handleReaderDetached(readerName) {
|
|
174
201
|
const state = this._readers.get(readerName);
|
|
@@ -186,6 +213,10 @@ class Devices extends EventEmitter {
|
|
|
186
213
|
|
|
187
214
|
/**
|
|
188
215
|
* Handle card inserted
|
|
216
|
+
* @param {string} readerName
|
|
217
|
+
* @param {number} eventState
|
|
218
|
+
* @param {Buffer|null} atr
|
|
219
|
+
* @returns {Promise<void>}
|
|
189
220
|
*/
|
|
190
221
|
async _handleCardInserted(readerName, eventState, atr) {
|
|
191
222
|
let state = this._readers.get(readerName);
|
|
@@ -222,6 +253,7 @@ class Devices extends EventEmitter {
|
|
|
222
253
|
|
|
223
254
|
/**
|
|
224
255
|
* Handle card removed
|
|
256
|
+
* @param {string} readerName
|
|
225
257
|
*/
|
|
226
258
|
_handleCardRemoved(readerName) {
|
|
227
259
|
const state = this._readers.get(readerName);
|
package/lib/errors.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Base error class for PC/SC errors
|
|
5
6
|
*/
|
|
6
7
|
class PCSCError extends Error {
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} message
|
|
10
|
+
* @param {number} code
|
|
11
|
+
*/
|
|
7
12
|
constructor(message, code) {
|
|
8
13
|
super(message);
|
|
9
14
|
this.name = 'PCSCError';
|
package/lib/index.js
CHANGED
package/package.json
CHANGED
package/src/reader_monitor.cpp
CHANGED
|
@@ -136,6 +136,14 @@ void ReaderMonitor::MonitorLoop() {
|
|
|
136
136
|
// Get initial reader list
|
|
137
137
|
UpdateReaderList();
|
|
138
138
|
|
|
139
|
+
// Emit reader-attached events for all pre-existing readers (Issue #30)
|
|
140
|
+
{
|
|
141
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
142
|
+
for (const auto& reader : readers_) {
|
|
143
|
+
EmitEvent("reader-attached", reader.name, reader.lastState, reader.atr);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
139
147
|
// Build initial states array with PnP notification
|
|
140
148
|
std::vector<SCARD_READERSTATE> states;
|
|
141
149
|
std::vector<std::string> readerNames;
|