UNPKG

15.3 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11var __generator = (this && this.__generator) || function (thisArg, body) {
12 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14 function verb(n) { return function (v) { return step([n, v]); }; }
15 function step(op) {
16 if (f) throw new TypeError("Generator is already executing.");
17 while (_) try {
18 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19 if (y = 0, t) op = [op[0] & 2, t.value];
20 switch (op[0]) {
21 case 0: case 1: t = op; break;
22 case 4: _.label++; return { value: op[1], done: false };
23 case 5: _.label++; y = op[1]; op = [0]; continue;
24 case 7: op = _.ops.pop(); _.trys.pop(); continue;
25 default:
26 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30 if (t[2]) _.ops.pop();
31 _.trys.pop(); continue;
32 }
33 op = body.call(thisArg, _);
34 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36 }
37};
38var __read = (this && this.__read) || function (o, n) {
39 var m = typeof Symbol === "function" && o[Symbol.iterator];
40 if (!m) return o;
41 var i = m.call(o), r, ar = [], e;
42 try {
43 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
44 }
45 catch (error) { e = { error: error }; }
46 finally {
47 try {
48 if (r && !r.done && (m = i["return"])) m.call(i);
49 }
50 finally { if (e) throw e.error; }
51 }
52 return ar;
53};
54var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
55 if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
56 if (ar || !(i in from)) {
57 if (!ar) ar = Array.prototype.slice.call(from, 0, i);
58 ar[i] = from[i];
59 }
60 }
61 return to.concat(ar || Array.prototype.slice.call(from));
62};
63var __values = (this && this.__values) || function(o) {
64 var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
65 if (m) return m.call(o);
66 if (o && typeof o.length === "number") return {
67 next: function () {
68 if (o && i >= o.length) o = void 0;
69 return { value: o && o[i++], done: !o };
70 }
71 };
72 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
73};
74var __importDefault = (this && this.__importDefault) || function (mod) {
75 return (mod && mod.__esModule) ? mod : { "default": mod };
76};
77exports.__esModule = true;
78exports.getAltStatusMessage = exports.StatusCodes = exports.TransportStatusError = exports.TransportError = void 0;
79var events_1 = __importDefault(require("events"));
80var errors_1 = require("@ledgerhq/errors");
81exports.TransportError = errors_1.TransportError;
82exports.StatusCodes = errors_1.StatusCodes;
83exports.getAltStatusMessage = errors_1.getAltStatusMessage;
84exports.TransportStatusError = errors_1.TransportStatusError;
85/**
86 * Transport defines the generic interface to share between node/u2f impl
87 * A **Descriptor** is a parametric type that is up to be determined for the implementation.
88 * it can be for instance an ID, an file path, a URL,...
89 */
90var Transport = /** @class */ (function () {
91 function Transport() {
92 var _this = this;
93 this.exchangeTimeout = 30000;
94 this.unresponsiveTimeout = 15000;
95 this.deviceModel = null;
96 this._events = new events_1["default"]();
97 /**
98 * wrapper on top of exchange to simplify work of the implementation.
99 * @param cla
100 * @param ins
101 * @param p1
102 * @param p2
103 * @param data
104 * @param statusList is a list of accepted status code (shorts). [0x9000] by default
105 * @return a Promise of response buffer
106 */
107 this.send = function (cla, ins, p1, p2, data, statusList) {
108 if (data === void 0) { data = Buffer.alloc(0); }
109 if (statusList === void 0) { statusList = [errors_1.StatusCodes.OK]; }
110 return __awaiter(_this, void 0, void 0, function () {
111 var response, sw;
112 return __generator(this, function (_a) {
113 switch (_a.label) {
114 case 0:
115 if (data.length >= 256) {
116 throw new errors_1.TransportError("data.length exceed 256 bytes limit. Got: " + data.length, "DataLengthTooBig");
117 }
118 return [4 /*yield*/, this.exchange(Buffer.concat([
119 Buffer.from([cla, ins, p1, p2]),
120 Buffer.from([data.length]),
121 data,
122 ]))];
123 case 1:
124 response = _a.sent();
125 sw = response.readUInt16BE(response.length - 2);
126 if (!statusList.some(function (s) { return s === sw; })) {
127 throw new errors_1.TransportStatusError(sw);
128 }
129 return [2 /*return*/, response];
130 }
131 });
132 });
133 };
134 this.exchangeAtomicImpl = function (f) { return __awaiter(_this, void 0, void 0, function () {
135 var resolveBusy, busyPromise, unresponsiveReached, timeout, res;
136 var _this = this;
137 return __generator(this, function (_a) {
138 switch (_a.label) {
139 case 0:
140 if (this.exchangeBusyPromise) {
141 throw new errors_1.TransportRaceCondition("An action was already pending on the Ledger device. Please deny or reconnect.");
142 }
143 busyPromise = new Promise(function (r) {
144 resolveBusy = r;
145 });
146 this.exchangeBusyPromise = busyPromise;
147 unresponsiveReached = false;
148 timeout = setTimeout(function () {
149 unresponsiveReached = true;
150 _this.emit("unresponsive");
151 }, this.unresponsiveTimeout);
152 _a.label = 1;
153 case 1:
154 _a.trys.push([1, , 3, 4]);
155 return [4 /*yield*/, f()];
156 case 2:
157 res = _a.sent();
158 if (unresponsiveReached) {
159 this.emit("responsive");
160 }
161 return [2 /*return*/, res];
162 case 3:
163 clearTimeout(timeout);
164 if (resolveBusy)
165 resolveBusy();
166 this.exchangeBusyPromise = null;
167 return [7 /*endfinally*/];
168 case 4: return [2 /*return*/];
169 }
170 });
171 }); };
172 this._appAPIlock = null;
173 }
174 /**
175 * low level api to communicate with the device
176 * This method is for implementations to implement but should not be directly called.
177 * Instead, the recommanded way is to use send() method
178 * @param apdu the data to send
179 * @return a Promise of response data
180 */
181 Transport.prototype.exchange = function (_apdu) {
182 throw new Error("exchange not implemented");
183 };
184 /**
185 * set the "scramble key" for the next exchanges with the device.
186 * Each App can have a different scramble key and they internally will set it at instanciation.
187 * @param key the scramble key
188 */
189 Transport.prototype.setScrambleKey = function (_key) { };
190 /**
191 * close the exchange with the device.
192 * @return a Promise that ends when the transport is closed.
193 */
194 Transport.prototype.close = function () {
195 return Promise.resolve();
196 };
197 /**
198 * Listen to an event on an instance of transport.
199 * Transport implementation can have specific events. Here is the common events:
200 * * `"disconnect"` : triggered if Transport is disconnected
201 */
202 Transport.prototype.on = function (eventName, cb) {
203 this._events.on(eventName, cb);
204 };
205 /**
206 * Stop listening to an event on an instance of transport.
207 */
208 Transport.prototype.off = function (eventName, cb) {
209 this._events.removeListener(eventName, cb);
210 };
211 Transport.prototype.emit = function (event) {
212 var _a;
213 var args = [];
214 for (var _i = 1; _i < arguments.length; _i++) {
215 args[_i - 1] = arguments[_i];
216 }
217 (_a = this._events).emit.apply(_a, __spreadArray([event], __read(args), false));
218 };
219 /**
220 * Enable or not logs of the binary exchange
221 */
222 Transport.prototype.setDebugMode = function () {
223 console.warn("setDebugMode is deprecated. use @ledgerhq/logs instead. No logs are emitted in this anymore.");
224 };
225 /**
226 * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F)
227 */
228 Transport.prototype.setExchangeTimeout = function (exchangeTimeout) {
229 this.exchangeTimeout = exchangeTimeout;
230 };
231 /**
232 * Define the delay before emitting "unresponsive" on an exchange that does not respond
233 */
234 Transport.prototype.setExchangeUnresponsiveTimeout = function (unresponsiveTimeout) {
235 this.unresponsiveTimeout = unresponsiveTimeout;
236 };
237 /**
238 * create() allows to open the first descriptor available or
239 * throw if there is none or if timeout is reached.
240 * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase)
241 * @example
242 TransportFoo.create().then(transport => ...)
243 */
244 Transport.create = function (openTimeout, listenTimeout) {
245 var _this = this;
246 if (openTimeout === void 0) { openTimeout = 3000; }
247 return new Promise(function (resolve, reject) {
248 var found = false;
249 var sub = _this.listen({
250 next: function (e) {
251 found = true;
252 if (sub)
253 sub.unsubscribe();
254 if (listenTimeoutId)
255 clearTimeout(listenTimeoutId);
256 _this.open(e.descriptor, openTimeout).then(resolve, reject);
257 },
258 error: function (e) {
259 if (listenTimeoutId)
260 clearTimeout(listenTimeoutId);
261 reject(e);
262 },
263 complete: function () {
264 if (listenTimeoutId)
265 clearTimeout(listenTimeoutId);
266 if (!found) {
267 reject(new errors_1.TransportError(_this.ErrorMessage_NoDeviceFound, "NoDeviceFound"));
268 }
269 }
270 });
271 var listenTimeoutId = listenTimeout
272 ? setTimeout(function () {
273 sub.unsubscribe();
274 reject(new errors_1.TransportError(_this.ErrorMessage_ListenTimeout, "ListenTimeout"));
275 }, listenTimeout)
276 : null;
277 });
278 };
279 Transport.prototype.decorateAppAPIMethods = function (self, methods, scrambleKey) {
280 var e_1, _a;
281 try {
282 for (var methods_1 = __values(methods), methods_1_1 = methods_1.next(); !methods_1_1.done; methods_1_1 = methods_1.next()) {
283 var methodName = methods_1_1.value;
284 self[methodName] = this.decorateAppAPIMethod(methodName, self[methodName], self, scrambleKey);
285 }
286 }
287 catch (e_1_1) { e_1 = { error: e_1_1 }; }
288 finally {
289 try {
290 if (methods_1_1 && !methods_1_1.done && (_a = methods_1["return"])) _a.call(methods_1);
291 }
292 finally { if (e_1) throw e_1.error; }
293 }
294 };
295 Transport.prototype.decorateAppAPIMethod = function (methodName, f, ctx, scrambleKey) {
296 var _this = this;
297 return function () {
298 var args = [];
299 for (var _i = 0; _i < arguments.length; _i++) {
300 args[_i] = arguments[_i];
301 }
302 return __awaiter(_this, void 0, void 0, function () {
303 var _appAPIlock;
304 return __generator(this, function (_a) {
305 switch (_a.label) {
306 case 0:
307 _appAPIlock = this._appAPIlock;
308 if (_appAPIlock) {
309 return [2 /*return*/, Promise.reject(new errors_1.TransportError("Ledger Device is busy (lock " + _appAPIlock + ")", "TransportLocked"))];
310 }
311 _a.label = 1;
312 case 1:
313 _a.trys.push([1, , 3, 4]);
314 this._appAPIlock = methodName;
315 this.setScrambleKey(scrambleKey);
316 return [4 /*yield*/, f.apply(ctx, args)];
317 case 2: return [2 /*return*/, _a.sent()];
318 case 3:
319 this._appAPIlock = null;
320 return [7 /*endfinally*/];
321 case 4: return [2 /*return*/];
322 }
323 });
324 });
325 };
326 };
327 Transport.ErrorMessage_ListenTimeout = "No Ledger device found (timeout)";
328 Transport.ErrorMessage_NoDeviceFound = "No Ledger device found";
329 return Transport;
330}());
331exports["default"] = Transport;
332//# sourceMappingURL=Transport.js.map
\No newline at end of file