smartcard 1.0.46 → 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/package.json CHANGED
@@ -1,41 +1,58 @@
1
1
  {
2
- "name": "smartcard",
3
- "version": "1.0.46",
4
- "author": "tomkp <tom@tomkp.com>",
5
- "keywords": [
6
- "pcsc",
7
- "smartcard",
8
- "smart-card",
9
- "iso7816",
10
- "iso-7816",
11
- "emv",
12
- "es6"
13
- ],
14
- "license": "MIT",
15
- "repository": {
16
- "type": "git",
17
- "url": "https://github.com/tomkp/smartcard.git"
18
- },
19
- "bugs": {
20
- "url": "https://github.com/tomkp/smartcard/issues"
21
- },
22
- "homepage": "https://github.com/tomkp/smartcard",
23
- "main": "lib/index.js",
24
- "scripts": {
25
- "compile": "babel -d lib/ src/",
26
- "compile:watch": "babel -w -d lib/ src/",
27
- "release:patch": "npm run compile && npm version patch && git push && yarn publish",
28
- "prettier": "prettier --write \"{src,demo}/**/*.{js,ts}\""
29
- },
30
- "dependencies": {
31
- "@pokusew/pcsclite": "^0.6.0",
32
- "hexify": "^1.0.4",
33
- "pino": "^6.11.1"
34
- },
35
- "devDependencies": {
36
- "@babel/cli": "^7.12.16",
37
- "@babel/core": "^7.12.16",
38
- "@babel/preset-env": "^7.12.16",
39
- "prettier": "^2.2.1"
40
- }
2
+ "name": "smartcard",
3
+ "version": "2.0.1",
4
+ "description": "PC/SC bindings for Node.js using N-API - ABI stable across Node.js versions",
5
+ "author": "Tom KP",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/tomkp/smartcard.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/tomkp/smartcard/issues"
13
+ },
14
+ "homepage": "https://github.com/tomkp/smartcard#readme",
15
+ "main": "lib/index.js",
16
+ "types": "lib/index.d.ts",
17
+ "gypfile": true,
18
+ "scripts": {
19
+ "install": "node-gyp rebuild",
20
+ "build": "node-gyp rebuild",
21
+ "rebuild": "node-gyp rebuild",
22
+ "test": "node test/test.js"
23
+ },
24
+ "files": [
25
+ "lib/",
26
+ "src/",
27
+ "binding.gyp",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "dependencies": {
32
+ "node-addon-api": "^7.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "node-gyp": "^10.0.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=12.0.0"
39
+ },
40
+ "os": [
41
+ "darwin",
42
+ "linux",
43
+ "win32"
44
+ ],
45
+ "keywords": [
46
+ "pcsc",
47
+ "pcsclite",
48
+ "smartcard",
49
+ "smart-card",
50
+ "nfc",
51
+ "rfid",
52
+ "contactless",
53
+ "napi",
54
+ "node-addon-api",
55
+ "acr122",
56
+ "card-reader"
57
+ ]
41
58
  }
package/src/addon.cpp ADDED
@@ -0,0 +1,54 @@
1
+ #include <napi.h>
2
+ #include "pcsc_context.h"
3
+ #include "pcsc_reader.h"
4
+ #include "pcsc_card.h"
5
+ #include "reader_monitor.h"
6
+ #include "platform/pcsc.h"
7
+
8
+ // Export PC/SC constants
9
+ void ExportConstants(Napi::Env env, Napi::Object exports) {
10
+ // Share modes
11
+ exports.Set("SCARD_SHARE_EXCLUSIVE", Napi::Number::New(env, SCARD_SHARE_EXCLUSIVE));
12
+ exports.Set("SCARD_SHARE_SHARED", Napi::Number::New(env, SCARD_SHARE_SHARED));
13
+ exports.Set("SCARD_SHARE_DIRECT", Napi::Number::New(env, SCARD_SHARE_DIRECT));
14
+
15
+ // Protocols
16
+ exports.Set("SCARD_PROTOCOL_T0", Napi::Number::New(env, SCARD_PROTOCOL_T0));
17
+ exports.Set("SCARD_PROTOCOL_T1", Napi::Number::New(env, SCARD_PROTOCOL_T1));
18
+ exports.Set("SCARD_PROTOCOL_RAW", Napi::Number::New(env, SCARD_PROTOCOL_RAW));
19
+ exports.Set("SCARD_PROTOCOL_UNDEFINED", Napi::Number::New(env, SCARD_PROTOCOL_UNDEFINED));
20
+
21
+ // Disposition
22
+ exports.Set("SCARD_LEAVE_CARD", Napi::Number::New(env, SCARD_LEAVE_CARD));
23
+ exports.Set("SCARD_RESET_CARD", Napi::Number::New(env, SCARD_RESET_CARD));
24
+ exports.Set("SCARD_UNPOWER_CARD", Napi::Number::New(env, SCARD_UNPOWER_CARD));
25
+ exports.Set("SCARD_EJECT_CARD", Napi::Number::New(env, SCARD_EJECT_CARD));
26
+
27
+ // State flags
28
+ exports.Set("SCARD_STATE_UNAWARE", Napi::Number::New(env, SCARD_STATE_UNAWARE));
29
+ exports.Set("SCARD_STATE_IGNORE", Napi::Number::New(env, SCARD_STATE_IGNORE));
30
+ exports.Set("SCARD_STATE_CHANGED", Napi::Number::New(env, SCARD_STATE_CHANGED));
31
+ exports.Set("SCARD_STATE_UNKNOWN", Napi::Number::New(env, SCARD_STATE_UNKNOWN));
32
+ exports.Set("SCARD_STATE_UNAVAILABLE", Napi::Number::New(env, SCARD_STATE_UNAVAILABLE));
33
+ exports.Set("SCARD_STATE_EMPTY", Napi::Number::New(env, SCARD_STATE_EMPTY));
34
+ exports.Set("SCARD_STATE_PRESENT", Napi::Number::New(env, SCARD_STATE_PRESENT));
35
+ exports.Set("SCARD_STATE_ATRMATCH", Napi::Number::New(env, SCARD_STATE_ATRMATCH));
36
+ exports.Set("SCARD_STATE_EXCLUSIVE", Napi::Number::New(env, SCARD_STATE_EXCLUSIVE));
37
+ exports.Set("SCARD_STATE_INUSE", Napi::Number::New(env, SCARD_STATE_INUSE));
38
+ exports.Set("SCARD_STATE_MUTE", Napi::Number::New(env, SCARD_STATE_MUTE));
39
+ }
40
+
41
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
42
+ // Initialize wrapper classes
43
+ PCSCContext::Init(env, exports);
44
+ PCSCReader::Init(env, exports);
45
+ PCSCCard::Init(env, exports);
46
+ ReaderMonitor::Init(env, exports);
47
+
48
+ // Export constants
49
+ ExportConstants(env, exports);
50
+
51
+ return exports;
52
+ }
53
+
54
+ NODE_API_MODULE(smartcard_napi, Init)
@@ -0,0 +1,234 @@
1
+ #include "async_workers.h"
2
+ #include "pcsc_card.h"
3
+ #include "pcsc_errors.h"
4
+ #include <cstring>
5
+
6
+ // ============================================================================
7
+ // WaitForChangeWorker
8
+ // ============================================================================
9
+
10
+ WaitForChangeWorker::WaitForChangeWorker(
11
+ Napi::Env env,
12
+ SCARDCONTEXT context,
13
+ std::vector<std::string> readerNames,
14
+ std::vector<DWORD> currentStates,
15
+ DWORD timeout,
16
+ Napi::Promise::Deferred deferred)
17
+ : Napi::AsyncWorker(env),
18
+ context_(context),
19
+ readerNames_(std::move(readerNames)),
20
+ timeout_(timeout),
21
+ result_(SCARD_S_SUCCESS),
22
+ deferred_(deferred) {
23
+
24
+ // Initialize reader states
25
+ states_.resize(readerNames_.size());
26
+ for (size_t i = 0; i < readerNames_.size(); i++) {
27
+ memset(&states_[i], 0, sizeof(SCARD_READERSTATE));
28
+ states_[i].szReader = readerNames_[i].c_str();
29
+ states_[i].dwCurrentState = (i < currentStates.size()) ? currentStates[i] : SCARD_STATE_UNAWARE;
30
+ }
31
+ }
32
+
33
+ void WaitForChangeWorker::Execute() {
34
+ // This runs on worker thread - safe to block
35
+ result_ = SCardGetStatusChange(context_, timeout_, states_.data(), states_.size());
36
+ }
37
+
38
+ void WaitForChangeWorker::OnOK() {
39
+ Napi::Env env = Env();
40
+
41
+ if (result_ == SCARD_S_SUCCESS) {
42
+ // Build array of reader states
43
+ Napi::Array changes = Napi::Array::New(env);
44
+
45
+ for (size_t i = 0; i < states_.size(); i++) {
46
+ Napi::Object reader = Napi::Object::New(env);
47
+ reader.Set("name", Napi::String::New(env, readerNames_[i]));
48
+ reader.Set("state", Napi::Number::New(env, states_[i].dwEventState));
49
+ reader.Set("changed", Napi::Boolean::New(env,
50
+ (states_[i].dwEventState & SCARD_STATE_CHANGED) != 0));
51
+
52
+ if (states_[i].cbAtr > 0) {
53
+ reader.Set("atr", Napi::Buffer<uint8_t>::Copy(
54
+ env, states_[i].rgbAtr, states_[i].cbAtr));
55
+ } else {
56
+ reader.Set("atr", env.Null());
57
+ }
58
+
59
+ changes.Set(static_cast<uint32_t>(i), reader);
60
+ }
61
+
62
+ deferred_.Resolve(changes);
63
+ } else if (result_ == SCARD_E_CANCELLED) {
64
+ // Cancelled - resolve with null
65
+ deferred_.Resolve(env.Null());
66
+ } else if (result_ == SCARD_E_TIMEOUT) {
67
+ // Timeout - resolve with empty array
68
+ deferred_.Resolve(Napi::Array::New(env, 0));
69
+ } else {
70
+ deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
71
+ }
72
+ }
73
+
74
+ void WaitForChangeWorker::OnError(const Napi::Error& error) {
75
+ deferred_.Reject(error.Value());
76
+ }
77
+
78
+ // ============================================================================
79
+ // TransmitWorker
80
+ // ============================================================================
81
+
82
+ TransmitWorker::TransmitWorker(
83
+ Napi::Env env,
84
+ SCARDHANDLE card,
85
+ DWORD protocol,
86
+ std::vector<uint8_t> sendBuffer,
87
+ Napi::Promise::Deferred deferred)
88
+ : Napi::AsyncWorker(env),
89
+ card_(card),
90
+ protocol_(protocol),
91
+ sendBuffer_(std::move(sendBuffer)),
92
+ recvLength_(0),
93
+ result_(SCARD_S_SUCCESS),
94
+ deferred_(deferred) {
95
+ // Pre-allocate receive buffer (max APDU response size)
96
+ recvBuffer_.resize(258);
97
+ }
98
+
99
+ void TransmitWorker::Execute() {
100
+ // Select protocol-specific PCI structure
101
+ const SCARD_IO_REQUEST* pioSendPci;
102
+ if (protocol_ == SCARD_PROTOCOL_T0) {
103
+ pioSendPci = SCARD_PCI_T0;
104
+ } else if (protocol_ == SCARD_PROTOCOL_T1) {
105
+ pioSendPci = SCARD_PCI_T1;
106
+ } else {
107
+ pioSendPci = SCARD_PCI_RAW;
108
+ }
109
+
110
+ recvLength_ = static_cast<DWORD>(recvBuffer_.size());
111
+
112
+ result_ = SCardTransmit(
113
+ card_,
114
+ pioSendPci,
115
+ sendBuffer_.data(),
116
+ static_cast<DWORD>(sendBuffer_.size()),
117
+ nullptr,
118
+ recvBuffer_.data(),
119
+ &recvLength_
120
+ );
121
+ }
122
+
123
+ void TransmitWorker::OnOK() {
124
+ Napi::Env env = Env();
125
+
126
+ if (result_ == SCARD_S_SUCCESS) {
127
+ Napi::Buffer<uint8_t> buffer = Napi::Buffer<uint8_t>::Copy(
128
+ env, recvBuffer_.data(), recvLength_);
129
+ deferred_.Resolve(buffer);
130
+ } else {
131
+ deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
132
+ }
133
+ }
134
+
135
+ void TransmitWorker::OnError(const Napi::Error& error) {
136
+ deferred_.Reject(error.Value());
137
+ }
138
+
139
+ // ============================================================================
140
+ // ControlWorker
141
+ // ============================================================================
142
+
143
+ ControlWorker::ControlWorker(
144
+ Napi::Env env,
145
+ SCARDHANDLE card,
146
+ DWORD controlCode,
147
+ std::vector<uint8_t> sendBuffer,
148
+ Napi::Promise::Deferred deferred)
149
+ : Napi::AsyncWorker(env),
150
+ card_(card),
151
+ controlCode_(controlCode),
152
+ sendBuffer_(std::move(sendBuffer)),
153
+ bytesReturned_(0),
154
+ result_(SCARD_S_SUCCESS),
155
+ deferred_(deferred) {
156
+ // Pre-allocate receive buffer
157
+ recvBuffer_.resize(256);
158
+ }
159
+
160
+ void ControlWorker::Execute() {
161
+ result_ = SCardControl(
162
+ card_,
163
+ controlCode_,
164
+ sendBuffer_.empty() ? nullptr : sendBuffer_.data(),
165
+ static_cast<DWORD>(sendBuffer_.size()),
166
+ recvBuffer_.data(),
167
+ static_cast<DWORD>(recvBuffer_.size()),
168
+ &bytesReturned_
169
+ );
170
+ }
171
+
172
+ void ControlWorker::OnOK() {
173
+ Napi::Env env = Env();
174
+
175
+ if (result_ == SCARD_S_SUCCESS) {
176
+ Napi::Buffer<uint8_t> buffer = Napi::Buffer<uint8_t>::Copy(
177
+ env, recvBuffer_.data(), bytesReturned_);
178
+ deferred_.Resolve(buffer);
179
+ } else {
180
+ deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
181
+ }
182
+ }
183
+
184
+ void ControlWorker::OnError(const Napi::Error& error) {
185
+ deferred_.Reject(error.Value());
186
+ }
187
+
188
+ // ============================================================================
189
+ // ConnectWorker
190
+ // ============================================================================
191
+
192
+ ConnectWorker::ConnectWorker(
193
+ Napi::Env env,
194
+ SCARDCONTEXT context,
195
+ std::string readerName,
196
+ DWORD shareMode,
197
+ DWORD preferredProtocols,
198
+ Napi::Promise::Deferred deferred)
199
+ : Napi::AsyncWorker(env),
200
+ context_(context),
201
+ readerName_(std::move(readerName)),
202
+ shareMode_(shareMode),
203
+ preferredProtocols_(preferredProtocols),
204
+ card_(0),
205
+ activeProtocol_(0),
206
+ result_(SCARD_S_SUCCESS),
207
+ deferred_(deferred) {
208
+ }
209
+
210
+ void ConnectWorker::Execute() {
211
+ result_ = SCardConnect(
212
+ context_,
213
+ readerName_.c_str(),
214
+ shareMode_,
215
+ preferredProtocols_,
216
+ &card_,
217
+ &activeProtocol_
218
+ );
219
+ }
220
+
221
+ void ConnectWorker::OnOK() {
222
+ Napi::Env env = Env();
223
+
224
+ if (result_ == SCARD_S_SUCCESS) {
225
+ Napi::Object card = PCSCCard::NewInstance(env, card_, activeProtocol_, readerName_);
226
+ deferred_.Resolve(card);
227
+ } else {
228
+ deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
229
+ }
230
+ }
231
+
232
+ void ConnectWorker::OnError(const Napi::Error& error) {
233
+ deferred_.Reject(error.Value());
234
+ }
@@ -0,0 +1,100 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <vector>
5
+ #include <string>
6
+ #include "platform/pcsc.h"
7
+
8
+ // Async worker for SCardGetStatusChange
9
+ class WaitForChangeWorker : public Napi::AsyncWorker {
10
+ public:
11
+ WaitForChangeWorker(Napi::Env env,
12
+ SCARDCONTEXT context,
13
+ std::vector<std::string> readerNames,
14
+ std::vector<DWORD> currentStates,
15
+ DWORD timeout,
16
+ Napi::Promise::Deferred deferred);
17
+
18
+ void Execute() override;
19
+ void OnOK() override;
20
+ void OnError(const Napi::Error& error) override;
21
+
22
+ private:
23
+ SCARDCONTEXT context_;
24
+ std::vector<std::string> readerNames_;
25
+ std::vector<SCARD_READERSTATE> states_;
26
+ DWORD timeout_;
27
+ LONG result_;
28
+ Napi::Promise::Deferred deferred_;
29
+ };
30
+
31
+ // Async worker for SCardTransmit
32
+ class TransmitWorker : public Napi::AsyncWorker {
33
+ public:
34
+ TransmitWorker(Napi::Env env,
35
+ SCARDHANDLE card,
36
+ DWORD protocol,
37
+ std::vector<uint8_t> sendBuffer,
38
+ Napi::Promise::Deferred deferred);
39
+
40
+ void Execute() override;
41
+ void OnOK() override;
42
+ void OnError(const Napi::Error& error) override;
43
+
44
+ private:
45
+ SCARDHANDLE card_;
46
+ DWORD protocol_;
47
+ std::vector<uint8_t> sendBuffer_;
48
+ std::vector<uint8_t> recvBuffer_;
49
+ DWORD recvLength_;
50
+ LONG result_;
51
+ Napi::Promise::Deferred deferred_;
52
+ };
53
+
54
+ // Async worker for SCardControl
55
+ class ControlWorker : public Napi::AsyncWorker {
56
+ public:
57
+ ControlWorker(Napi::Env env,
58
+ SCARDHANDLE card,
59
+ DWORD controlCode,
60
+ std::vector<uint8_t> sendBuffer,
61
+ Napi::Promise::Deferred deferred);
62
+
63
+ void Execute() override;
64
+ void OnOK() override;
65
+ void OnError(const Napi::Error& error) override;
66
+
67
+ private:
68
+ SCARDHANDLE card_;
69
+ DWORD controlCode_;
70
+ std::vector<uint8_t> sendBuffer_;
71
+ std::vector<uint8_t> recvBuffer_;
72
+ DWORD bytesReturned_;
73
+ LONG result_;
74
+ Napi::Promise::Deferred deferred_;
75
+ };
76
+
77
+ // Async worker for SCardConnect
78
+ class ConnectWorker : public Napi::AsyncWorker {
79
+ public:
80
+ ConnectWorker(Napi::Env env,
81
+ SCARDCONTEXT context,
82
+ std::string readerName,
83
+ DWORD shareMode,
84
+ DWORD preferredProtocols,
85
+ Napi::Promise::Deferred deferred);
86
+
87
+ void Execute() override;
88
+ void OnOK() override;
89
+ void OnError(const Napi::Error& error) override;
90
+
91
+ private:
92
+ SCARDCONTEXT context_;
93
+ std::string readerName_;
94
+ DWORD shareMode_;
95
+ DWORD preferredProtocols_;
96
+ SCARDHANDLE card_;
97
+ DWORD activeProtocol_;
98
+ LONG result_;
99
+ Napi::Promise::Deferred deferred_;
100
+ };