smartcard 1.0.46 → 2.0.0
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/LICENSE +1 -1
- package/README.md +387 -0
- package/binding.gyp +45 -0
- package/lib/devices.js +251 -0
- package/lib/errors.js +72 -0
- package/lib/index.d.ts +247 -0
- package/lib/index.js +76 -14
- package/package.json +56 -39
- package/src/addon.cpp +54 -0
- package/src/async_workers.cpp +234 -0
- package/src/async_workers.h +100 -0
- package/src/pcsc_card.cpp +248 -0
- package/src/pcsc_card.h +41 -0
- package/src/pcsc_context.cpp +224 -0
- package/src/pcsc_context.h +32 -0
- package/src/pcsc_errors.h +49 -0
- package/src/pcsc_reader.cpp +89 -0
- package/src/pcsc_reader.h +39 -0
- package/src/platform/pcsc.h +36 -0
- package/src/reader_monitor.cpp +344 -0
- package/src/reader_monitor.h +57 -0
- package/.prettierrc +0 -3
- package/README.MD +0 -371
- package/babel.config.json +0 -3
- package/demo/device-activated-promise.js +0 -12
- package/demo/device-activated.js +0 -12
- package/demo/device-deactivated-promise.js +0 -12
- package/demo/device-deactivated.js +0 -12
- package/demo/smartcard-demo.js +0 -100
- package/lib/Card.js +0 -129
- package/lib/CommandApdu.js +0 -109
- package/lib/Device.js +0 -138
- package/lib/Devices.js +0 -134
- package/lib/Iso7816Application.js +0 -169
- package/lib/ResponseApdu.js +0 -129
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#include "pcsc_card.h"
|
|
2
|
+
#include "pcsc_errors.h"
|
|
3
|
+
#include "async_workers.h"
|
|
4
|
+
#include <cstring>
|
|
5
|
+
|
|
6
|
+
Napi::FunctionReference PCSCCard::constructor;
|
|
7
|
+
|
|
8
|
+
Napi::Object PCSCCard::Init(Napi::Env env, Napi::Object exports) {
|
|
9
|
+
Napi::Function func = DefineClass(env, "Card", {
|
|
10
|
+
InstanceAccessor("protocol", &PCSCCard::GetProtocolValue, nullptr),
|
|
11
|
+
InstanceAccessor("connected", &PCSCCard::GetConnectedValue, nullptr),
|
|
12
|
+
InstanceAccessor("atr", &PCSCCard::GetAtr, nullptr),
|
|
13
|
+
InstanceMethod("transmit", &PCSCCard::Transmit),
|
|
14
|
+
InstanceMethod("control", &PCSCCard::Control),
|
|
15
|
+
InstanceMethod("getStatus", &PCSCCard::GetStatus),
|
|
16
|
+
InstanceMethod("disconnect", &PCSCCard::Disconnect),
|
|
17
|
+
InstanceMethod("reconnect", &PCSCCard::Reconnect),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
constructor = Napi::Persistent(func);
|
|
21
|
+
constructor.SuppressDestruct();
|
|
22
|
+
|
|
23
|
+
exports.Set("Card", func);
|
|
24
|
+
return exports;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Napi::Object PCSCCard::NewInstance(Napi::Env env, SCARDHANDLE card,
|
|
28
|
+
DWORD protocol, const std::string& readerName) {
|
|
29
|
+
Napi::Object obj = constructor.New({});
|
|
30
|
+
PCSCCard* cardObj = Napi::ObjectWrap<PCSCCard>::Unwrap(obj);
|
|
31
|
+
cardObj->card_ = card;
|
|
32
|
+
cardObj->protocol_ = protocol;
|
|
33
|
+
cardObj->readerName_ = readerName;
|
|
34
|
+
cardObj->connected_ = true;
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
PCSCCard::PCSCCard(const Napi::CallbackInfo& info)
|
|
39
|
+
: Napi::ObjectWrap<PCSCCard>(info),
|
|
40
|
+
card_(0),
|
|
41
|
+
protocol_(SCARD_PROTOCOL_UNDEFINED),
|
|
42
|
+
connected_(false) {
|
|
43
|
+
// Properties set via NewInstance
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
PCSCCard::~PCSCCard() {
|
|
47
|
+
if (connected_ && card_ != 0) {
|
|
48
|
+
SCardDisconnect(card_, SCARD_LEAVE_CARD);
|
|
49
|
+
connected_ = false;
|
|
50
|
+
card_ = 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Napi::Value PCSCCard::GetProtocolValue(const Napi::CallbackInfo& info) {
|
|
55
|
+
return Napi::Number::New(info.Env(), protocol_);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Napi::Value PCSCCard::GetConnectedValue(const Napi::CallbackInfo& info) {
|
|
59
|
+
return Napi::Boolean::New(info.Env(), connected_);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Napi::Value PCSCCard::GetAtr(const Napi::CallbackInfo& info) {
|
|
63
|
+
Napi::Env env = info.Env();
|
|
64
|
+
|
|
65
|
+
if (!connected_) {
|
|
66
|
+
return env.Null();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get ATR via SCardStatus
|
|
70
|
+
DWORD readerLen = 0;
|
|
71
|
+
DWORD state = 0;
|
|
72
|
+
DWORD protocol = 0;
|
|
73
|
+
BYTE atr[MAX_ATR_SIZE];
|
|
74
|
+
DWORD atrLen = sizeof(atr);
|
|
75
|
+
|
|
76
|
+
LONG result = SCardStatus(card_, nullptr, &readerLen, &state, &protocol, atr, &atrLen);
|
|
77
|
+
|
|
78
|
+
if (result != SCARD_S_SUCCESS) {
|
|
79
|
+
return env.Null();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return Napi::Buffer<uint8_t>::Copy(env, atr, atrLen);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Napi::Value PCSCCard::Transmit(const Napi::CallbackInfo& info) {
|
|
86
|
+
Napi::Env env = info.Env();
|
|
87
|
+
|
|
88
|
+
if (!connected_) {
|
|
89
|
+
Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
|
|
90
|
+
return env.Null();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (info.Length() < 1) {
|
|
94
|
+
Napi::TypeError::New(env, "Expected command buffer").ThrowAsJavaScriptException();
|
|
95
|
+
return env.Null();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
std::vector<uint8_t> sendBuffer;
|
|
99
|
+
|
|
100
|
+
if (info[0].IsBuffer()) {
|
|
101
|
+
Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
|
|
102
|
+
sendBuffer.assign(buffer.Data(), buffer.Data() + buffer.Length());
|
|
103
|
+
} else if (info[0].IsArray()) {
|
|
104
|
+
Napi::Array arr = info[0].As<Napi::Array>();
|
|
105
|
+
sendBuffer.reserve(arr.Length());
|
|
106
|
+
for (uint32_t i = 0; i < arr.Length(); i++) {
|
|
107
|
+
sendBuffer.push_back(static_cast<uint8_t>(arr.Get(i).As<Napi::Number>().Uint32Value()));
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
Napi::TypeError::New(env, "Expected Buffer or Array").ThrowAsJavaScriptException();
|
|
111
|
+
return env.Null();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Create promise for async transmit
|
|
115
|
+
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
|
|
116
|
+
|
|
117
|
+
TransmitWorker* worker = new TransmitWorker(
|
|
118
|
+
env, card_, protocol_, sendBuffer, deferred);
|
|
119
|
+
worker->Queue();
|
|
120
|
+
|
|
121
|
+
return deferred.Promise();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Napi::Value PCSCCard::Control(const Napi::CallbackInfo& info) {
|
|
125
|
+
Napi::Env env = info.Env();
|
|
126
|
+
|
|
127
|
+
if (!connected_) {
|
|
128
|
+
Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
|
|
129
|
+
return env.Null();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
133
|
+
Napi::TypeError::New(env, "Expected control code").ThrowAsJavaScriptException();
|
|
134
|
+
return env.Null();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
DWORD controlCode = info[0].As<Napi::Number>().Uint32Value();
|
|
138
|
+
|
|
139
|
+
std::vector<uint8_t> sendBuffer;
|
|
140
|
+
if (info.Length() > 1) {
|
|
141
|
+
if (info[1].IsBuffer()) {
|
|
142
|
+
Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
|
|
143
|
+
sendBuffer.assign(buffer.Data(), buffer.Data() + buffer.Length());
|
|
144
|
+
} else if (info[1].IsArray()) {
|
|
145
|
+
Napi::Array arr = info[1].As<Napi::Array>();
|
|
146
|
+
sendBuffer.reserve(arr.Length());
|
|
147
|
+
for (uint32_t i = 0; i < arr.Length(); i++) {
|
|
148
|
+
sendBuffer.push_back(static_cast<uint8_t>(arr.Get(i).As<Napi::Number>().Uint32Value()));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Create promise for async control
|
|
154
|
+
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
|
|
155
|
+
|
|
156
|
+
ControlWorker* worker = new ControlWorker(
|
|
157
|
+
env, card_, controlCode, sendBuffer, deferred);
|
|
158
|
+
worker->Queue();
|
|
159
|
+
|
|
160
|
+
return deferred.Promise();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Napi::Value PCSCCard::GetStatus(const Napi::CallbackInfo& info) {
|
|
164
|
+
Napi::Env env = info.Env();
|
|
165
|
+
|
|
166
|
+
if (!connected_) {
|
|
167
|
+
Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
|
|
168
|
+
return env.Null();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
DWORD readerLen = 0;
|
|
172
|
+
DWORD state = 0;
|
|
173
|
+
DWORD protocol = 0;
|
|
174
|
+
BYTE atr[MAX_ATR_SIZE];
|
|
175
|
+
DWORD atrLen = sizeof(atr);
|
|
176
|
+
|
|
177
|
+
// First call to get reader name length
|
|
178
|
+
LONG result = SCardStatus(card_, nullptr, &readerLen, &state, &protocol, atr, &atrLen);
|
|
179
|
+
|
|
180
|
+
if (result != SCARD_S_SUCCESS) {
|
|
181
|
+
Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
|
|
182
|
+
return env.Null();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Napi::Object status = Napi::Object::New(env);
|
|
186
|
+
status.Set("state", Napi::Number::New(env, state));
|
|
187
|
+
status.Set("protocol", Napi::Number::New(env, protocol));
|
|
188
|
+
status.Set("atr", Napi::Buffer<uint8_t>::Copy(env, atr, atrLen));
|
|
189
|
+
|
|
190
|
+
return status;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
Napi::Value PCSCCard::Disconnect(const Napi::CallbackInfo& info) {
|
|
194
|
+
Napi::Env env = info.Env();
|
|
195
|
+
|
|
196
|
+
if (!connected_) {
|
|
197
|
+
return env.Undefined();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
DWORD disposition = SCARD_LEAVE_CARD;
|
|
201
|
+
if (info.Length() > 0 && info[0].IsNumber()) {
|
|
202
|
+
disposition = info[0].As<Napi::Number>().Uint32Value();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
LONG result = SCardDisconnect(card_, disposition);
|
|
206
|
+
connected_ = false;
|
|
207
|
+
card_ = 0;
|
|
208
|
+
|
|
209
|
+
if (result != SCARD_S_SUCCESS) {
|
|
210
|
+
Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return env.Undefined();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
Napi::Value PCSCCard::Reconnect(const Napi::CallbackInfo& info) {
|
|
217
|
+
Napi::Env env = info.Env();
|
|
218
|
+
|
|
219
|
+
if (!connected_) {
|
|
220
|
+
Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
|
|
221
|
+
return env.Null();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
DWORD shareMode = SCARD_SHARE_SHARED;
|
|
225
|
+
DWORD preferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
|
|
226
|
+
DWORD initialization = SCARD_LEAVE_CARD;
|
|
227
|
+
|
|
228
|
+
if (info.Length() > 0 && info[0].IsNumber()) {
|
|
229
|
+
shareMode = info[0].As<Napi::Number>().Uint32Value();
|
|
230
|
+
}
|
|
231
|
+
if (info.Length() > 1 && info[1].IsNumber()) {
|
|
232
|
+
preferredProtocols = info[1].As<Napi::Number>().Uint32Value();
|
|
233
|
+
}
|
|
234
|
+
if (info.Length() > 2 && info[2].IsNumber()) {
|
|
235
|
+
initialization = info[2].As<Napi::Number>().Uint32Value();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
DWORD activeProtocol = 0;
|
|
239
|
+
LONG result = SCardReconnect(card_, shareMode, preferredProtocols, initialization, &activeProtocol);
|
|
240
|
+
|
|
241
|
+
if (result != SCARD_S_SUCCESS) {
|
|
242
|
+
Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
|
|
243
|
+
return env.Null();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
protocol_ = activeProtocol;
|
|
247
|
+
return Napi::Number::New(env, activeProtocol);
|
|
248
|
+
}
|
package/src/pcsc_card.h
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <napi.h>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include "platform/pcsc.h"
|
|
7
|
+
|
|
8
|
+
class PCSCCard : public Napi::ObjectWrap<PCSCCard> {
|
|
9
|
+
public:
|
|
10
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
11
|
+
static Napi::Object NewInstance(Napi::Env env, SCARDHANDLE card,
|
|
12
|
+
DWORD protocol, const std::string& readerName);
|
|
13
|
+
|
|
14
|
+
PCSCCard(const Napi::CallbackInfo& info);
|
|
15
|
+
~PCSCCard();
|
|
16
|
+
|
|
17
|
+
// Accessors
|
|
18
|
+
SCARDHANDLE GetHandle() const { return card_; }
|
|
19
|
+
DWORD GetProtocol() const { return protocol_; }
|
|
20
|
+
bool IsConnected() const { return connected_; }
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
static Napi::FunctionReference constructor;
|
|
24
|
+
|
|
25
|
+
SCARDHANDLE card_;
|
|
26
|
+
DWORD protocol_;
|
|
27
|
+
std::string readerName_;
|
|
28
|
+
bool connected_;
|
|
29
|
+
|
|
30
|
+
// JavaScript-exposed methods
|
|
31
|
+
Napi::Value Transmit(const Napi::CallbackInfo& info);
|
|
32
|
+
Napi::Value Control(const Napi::CallbackInfo& info);
|
|
33
|
+
Napi::Value GetStatus(const Napi::CallbackInfo& info);
|
|
34
|
+
Napi::Value Disconnect(const Napi::CallbackInfo& info);
|
|
35
|
+
Napi::Value Reconnect(const Napi::CallbackInfo& info);
|
|
36
|
+
|
|
37
|
+
// Getters
|
|
38
|
+
Napi::Value GetProtocolValue(const Napi::CallbackInfo& info);
|
|
39
|
+
Napi::Value GetConnectedValue(const Napi::CallbackInfo& info);
|
|
40
|
+
Napi::Value GetAtr(const Napi::CallbackInfo& info);
|
|
41
|
+
};
|