Socket
Socket
Sign inDemoInstall

@ledgerhq/hw-transport-web-ble

Package Overview
Dependencies
Maintainers
11
Versions
284
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ledgerhq/hw-transport-web-ble - npm Package Compare versions

Comparing version 4.35.0 to 4.35.1-beta.28

LICENSE

10

lib/receiveAPDU.js

@@ -8,3 +8,3 @@ "use strict";

var _hwTransport = require("@ledgerhq/hw-transport");
var _errors = require("@ledgerhq/errors");

@@ -28,3 +28,3 @@ var _rxjs = require("rxjs");

complete: function complete() {
o.error(new _hwTransport.TransportError("Disconnected", "Disconnected"));
o.error(new _errors.TransportError("Disconnected", "Disconnected"));
sub.unsubscribe();

@@ -46,3 +46,3 @@ },

if (tag !== TagId) {
o.error(new _hwTransport.TransportError("Invalid tag " + tag.toString(16), "InvalidTag"));
o.error(new _errors.TransportError("Invalid tag " + tag.toString(16), "InvalidTag"));
return;

@@ -52,3 +52,3 @@ }

if (notifiedIndex !== index) {
o.error(new _hwTransport.TransportError("BLE: Invalid sequence number. discontinued chunk. Received " + index + " but expected " + notifiedIndex, "InvalidSequence"));
o.error(new _errors.TransportError("BLE: Invalid sequence number. discontinued chunk. Received " + index + " but expected " + notifiedIndex, "InvalidSequence"));
return;

@@ -64,3 +64,3 @@ }

if (notifiedData.length > notifiedDataLength) {
o.error(new _hwTransport.TransportError("BLE: received too much data. discontinued chunk. Received " + notifiedData.length + " but expected " + notifiedDataLength, "BLETooMuchData"));
o.error(new _errors.TransportError("BLE: received too much data. discontinued chunk. Received " + notifiedData.length + " but expected " + notifiedDataLength, "BLETooMuchData"));
return;

@@ -67,0 +67,0 @@ }

@@ -7,6 +7,178 @@ "use strict";

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _open = function () {
var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(deviceOrId, needsReconnect) {
var device, bluetooth, _ref5, _ref6, service, infos, deviceModel, writeUuid, notifyUuid, _ref7, _ref8, writeC, notifyC, notifyObservable, notif, transport, onDisconnect, beforeMTUTime, afterMTUTime;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
device = void 0;
if (!(typeof deviceOrId === "string")) {
_context2.next = 11;
break;
}
if (!transportsCache[deviceOrId]) {
_context2.next = 5;
break;
}
_debug.logSubject.next({
type: "verbose",
message: "Transport in cache, using that."
});
return _context2.abrupt("return", transportsCache[deviceOrId]);
case 5:
bluetooth = requiresBluetooth();
// TODO instead we should "query" the device by its ID
_context2.next = 8;
return bluetooth.requestDevice(requestDeviceParam());
case 8:
device = _context2.sent;
_context2.next = 12;
break;
case 11:
device = deviceOrId;
case 12:
if (device.gatt.connected) {
_context2.next = 16;
break;
}
_debug.logSubject.next({
type: "verbose",
message: "not connected. connecting..."
});
_context2.next = 16;
return device.gatt.connect();
case 16:
_context2.next = 18;
return retrieveService(device);
case 18:
_ref5 = _context2.sent;
_ref6 = _slicedToArray(_ref5, 2);
service = _ref6[0];
infos = _ref6[1];
deviceModel = infos.deviceModel, writeUuid = infos.writeUuid, notifyUuid = infos.notifyUuid;
_context2.next = 25;
return Promise.all([service.getCharacteristic(writeUuid), service.getCharacteristic(notifyUuid)]);
case 25:
_ref7 = _context2.sent;
_ref8 = _slicedToArray(_ref7, 2);
writeC = _ref8[0];
notifyC = _ref8[1];
notifyObservable = (0, _monitorCharacteristic.monitorCharacteristic)(notifyC).pipe((0, _operators.tap)(function (value) {
_debug.logSubject.next({
type: "ble-frame-read",
message: value.toString("hex")
});
}), (0, _operators.share)());
notif = notifyObservable.subscribe();
transport = new BluetoothTransport(device, writeC, notifyObservable, deviceModel);
if (device.gatt.connected) {
_context2.next = 34;
break;
}
throw new _errors.DisconnectedDevice();
case 34:
transportsCache[transport.id] = transport;
onDisconnect = function onDisconnect(e) {
delete transportsCache[transport.id];
transport.notYetDisconnected = false;
notif.unsubscribe();
device.removeEventListener("gattserverdisconnected", onDisconnect);
_debug.logSubject.next({
type: "verbose",
message: "BleTransport(" + transport.id + ") disconnected"
});
transport.emit("disconnect", e);
};
device.addEventListener("gattserverdisconnected", onDisconnect);
beforeMTUTime = Date.now();
_context2.prev = 38;
_context2.next = 41;
return transport.inferMTU();
case 41:
_context2.prev = 41;
afterMTUTime = Date.now();
// workaround for #279: we need to open() again if we come the first time here,
// to make sure we do a disconnect() after the first pairing time
// because of a firmware bug
if (afterMTUTime - beforeMTUTime < 500) {
needsReconnect = false; // (optim) there is likely no new pairing done because mtu answer was fast.
}
if (!needsReconnect) {
_context2.next = 49;
break;
}
_context2.next = 47;
return device.gatt.disconnect();
case 47:
_context2.next = 49;
return new Promise(function (s) {
return setTimeout(s, 500);
});
case 49:
return _context2.finish(41);
case 50:
if (!needsReconnect) {
_context2.next = 52;
break;
}
return _context2.abrupt("return", _open(device, false));
case 52:
return _context2.abrupt("return", transport);
case 53:
case "end":
return _context2.stop();
}
}
}, _callee2, this, [[38,, 41, 50]]);
}));
return function _open(_x2, _x3) {
return _ref4.apply(this, arguments);
};
}();
/**
* react-native bluetooth BLE implementation
* @example
* import BluetoothTransport from "@ledgerhq/hw-transport-web-ble";
*/
var _hwTransport = require("@ledgerhq/hw-transport");

@@ -16,2 +188,6 @@

var _errors = require("@ledgerhq/errors");
var _devices = require("@ledgerhq/devices");
var _rxjs = require("rxjs");

@@ -31,4 +207,2 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

@@ -39,8 +213,6 @@

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
/* eslint-disable prefer-template */
var ServiceUuid = "d973f2e0-b19e-11e2-9e96-0800200c9a66";
var WriteCharacteristicUuid = "d973f2e2-b19e-11e2-9e96-0800200c9a66";
var NotifyCharacteristicUuid = "d973f2e1-b19e-11e2-9e96-0800200c9a66";
var requiresBluetooth = function requiresBluetooth() {

@@ -70,7 +242,5 @@ // $FlowFixMe

});
return {
unsubscribe: function unsubscribe() {
unsubscribed = true;
bluetooth.removeEventListener("availabilitychanged", onAvailabilityChanged);
}
return function () {
unsubscribed = true;
bluetooth.removeEventListener("availabilitychanged", onAvailabilityChanged);
};

@@ -82,8 +252,69 @@ });

/**
* react-native bluetooth BLE implementation
* @example
* import BluetoothTransport from "@ledgerhq/hw-transport-web-ble";
*/
var requestDeviceParam = function requestDeviceParam() {
return {
filters: (0, _devices.getBluetoothServiceUuids)().map(function (uuid) {
return {
services: [uuid]
};
})
};
};
var retrieveService = function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(device) {
var _ref2, _ref3, service, infos;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (device.gatt) {
_context.next = 2;
break;
}
throw new Error("bluetooth gatt not found");
case 2:
_context.next = 4;
return device.gatt.getPrimaryServices();
case 4:
_ref2 = _context.sent;
_ref3 = _slicedToArray(_ref2, 1);
service = _ref3[0];
if (service) {
_context.next = 9;
break;
}
throw new Error("bluetooth service not found");
case 9:
infos = (0, _devices.getInfosForServiceUuid)(service.uuid);
if (infos) {
_context.next = 12;
break;
}
throw new Error("bluetooth service infos not found");
case 12:
return _context.abrupt("return", [service, infos]);
case 13:
case "end":
return _context.stop();
}
}
}, _callee, undefined);
}));
return function retrieveService(_x) {
return _ref.apply(this, arguments);
};
}();
var BluetoothTransport = function (_Transport) {

@@ -102,2 +333,4 @@ _inherits(BluetoothTransport, _Transport);

value: function listen(observer) {
var _this2 = this;
_debug.logSubject.next({

@@ -112,12 +345,28 @@ type: "verbose",

bluetooth.requestDevice({
filters: [{
services: [ServiceUuid]
}]
}).then(function (device) {
if (!unsubscribed) {
observer.next({ type: "add", descriptor: device, device: device });
observer.complete();
}
});
bluetooth.requestDevice(requestDeviceParam()).then(function () {
var _ref9 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(device) {
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (!unsubscribed) {
observer.next({
type: "add",
descriptor: device
});
observer.complete();
}
case 1:
case "end":
return _context3.stop();
}
}
}, _callee3, _this2);
}));
return function (_x4) {
return _ref9.apply(this, arguments);
};
}());
function unsubscribe() {

@@ -131,112 +380,19 @@ unsubscribed = true;

value: function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(deviceOrId) {
var device, bluetooth, service, _ref2, _ref3, writeC, notifyC, notifyObservable, notif, transport, onDisconnect;
return regeneratorRuntime.wrap(function _callee$(_context) {
var _ref10 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(deviceOrId) {
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context.prev = _context.next) {
switch (_context4.prev = _context4.next) {
case 0:
device = void 0;
return _context4.abrupt("return", _open(deviceOrId, true));
if (!(typeof deviceOrId === "string")) {
_context.next = 11;
break;
}
if (!transportsCache[deviceOrId]) {
_context.next = 5;
break;
}
_debug.logSubject.next({
type: "verbose",
message: "Transport in cache, using that."
});
return _context.abrupt("return", transportsCache[deviceOrId]);
case 5:
bluetooth = requiresBluetooth();
// TODO instead we should "query" the device by its ID
_context.next = 8;
return bluetooth.requestDevice({
filters: [{
services: [ServiceUuid]
}]
});
case 8:
device = _context.sent;
_context.next = 12;
break;
case 11:
device = deviceOrId;
case 12:
if (device.gatt.connected) {
_context.next = 16;
break;
}
_debug.logSubject.next({
type: "verbose",
message: "not connected. connecting..."
});
_context.next = 16;
return device.gatt.connect();
case 16:
_context.next = 18;
return device.gatt.getPrimaryService(ServiceUuid);
case 18:
service = _context.sent;
_context.next = 21;
return Promise.all([service.getCharacteristic(WriteCharacteristicUuid), service.getCharacteristic(NotifyCharacteristicUuid)]);
case 21:
_ref2 = _context.sent;
_ref3 = _slicedToArray(_ref2, 2);
writeC = _ref3[0];
notifyC = _ref3[1];
notifyObservable = (0, _monitorCharacteristic.monitorCharacteristic)(notifyC).pipe((0, _operators.tap)(function (value) {
_debug.logSubject.next({
type: "ble-frame-read",
message: value.toString("hex")
});
}), (0, _operators.share)());
notif = notifyObservable.subscribe();
transport = new BluetoothTransport(device, writeC, notifyObservable);
transportsCache[transport.id] = transport;
onDisconnect = function onDisconnect(e) {
delete transportsCache[transport.id];
transport.notYetDisconnected = false;
notif.unsubscribe();
device.removeEventListener("gattserverdisconnected", onDisconnect);
_debug.logSubject.next({
type: "verbose",
message: "BleTransport(" + transport.id + ") disconnected"
});
transport.emit("disconnect", e);
};
device.addEventListener("gattserverdisconnected", onDisconnect);
return _context.abrupt("return", transport);
case 32:
case 1:
case "end":
return _context.stop();
return _context4.stop();
}
}
}, _callee, this);
}, _callee4, this);
}));
function open(_x) {
return _ref.apply(this, arguments);
function open(_x5) {
return _ref10.apply(this, arguments);
}

@@ -248,4 +404,4 @@

function BluetoothTransport(device, writeCharacteristic, notifyObservable) {
var _this2 = this;
function BluetoothTransport(device, writeCharacteristic, notifyObservable, deviceModel) {
var _this3 = this;

@@ -260,9 +416,9 @@ _classCallCheck(this, BluetoothTransport);

_this.exchange = function (apdu) {
return _this.atomic(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
return _this.exchangeAtomicImpl(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {
var debug, msgIn, data, msgOut;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
return regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) {
switch (_context2.prev = _context2.next) {
switch (_context5.prev = _context5.next) {
case 0:
_context2.prev = 0;
_context5.prev = 0;
debug = _this.debug;

@@ -274,7 +430,7 @@ msgIn = apdu.toString("hex");

_context2.next = 7;
_context5.next = 7;
return (0, _rxjs.merge)(_this.notifyObservable.pipe(_receiveAPDU.receiveAPDU), (0, _sendAPDU.sendAPDU)(_this.write, apdu, _this.mtuSize)).toPromise();
case 7:
data = _context2.sent;
data = _context5.sent;
msgOut = data.toString("hex");

@@ -285,11 +441,11 @@

return _context2.abrupt("return", data);
return _context5.abrupt("return", data);
case 14:
_context2.prev = 14;
_context2.t0 = _context2["catch"](0);
_context5.prev = 14;
_context5.t0 = _context5["catch"](0);
_debug.logSubject.next({
type: "ble-error",
message: "exchange got " + String(_context2.t0)
message: "exchange got " + String(_context5.t0)
});

@@ -300,10 +456,10 @@ if (_this.notYetDisconnected) {

}
throw _context2.t0;
throw _context5.t0;
case 19:
case "end":
return _context2.stop();
return _context5.stop();
}
}
}, _callee2, _this2, [[0, 14]]);
}, _callee5, _this3, [[0, 14]]);
})));

@@ -313,6 +469,6 @@ };

_this.write = function () {
var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(buffer) {
return regeneratorRuntime.wrap(function _callee3$(_context3) {
var _ref12 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6(buffer) {
return regeneratorRuntime.wrap(function _callee6$(_context6) {
while (1) {
switch (_context3.prev = _context3.next) {
switch (_context6.prev = _context6.next) {
case 0:

@@ -323,3 +479,3 @@ _debug.logSubject.next({

});
_context3.next = 3;
_context6.next = 3;
return _this.writeCharacteristic.writeValue(buffer);

@@ -329,75 +485,108 @@

case "end":
return _context3.stop();
return _context6.stop();
}
}
}, _callee3, _this2);
}, _callee6, _this3);
}));
return function (_x2) {
return _ref5.apply(this, arguments);
return function (_x6) {
return _ref12.apply(this, arguments);
};
}();
_this.atomic = function () {
var _ref6 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(f) {
var resolveBusy, busyPromise, res;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
_this.id = device.id;
_this.device = device;
_this.writeCharacteristic = writeCharacteristic;
_this.notifyObservable = notifyObservable;
_this.deviceModel = deviceModel;
_debug.logSubject.next({
type: "verbose",
message: "BleTransport(" + String(_this.id) + ") new instance"
});
return _this;
}
_createClass(BluetoothTransport, [{
key: "inferMTU",
value: function () {
var _ref13 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {
var _this4 = this;
var mtu, mtuSize;
return regeneratorRuntime.wrap(function _callee8$(_context8) {
while (1) {
switch (_context4.prev = _context4.next) {
switch (_context8.prev = _context8.next) {
case 0:
if (!_this.busy) {
_context4.next = 2;
break;
}
mtu = 23;
_context8.next = 3;
return this.exchangeAtomicImpl(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() {
return regeneratorRuntime.wrap(function _callee7$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
_context7.prev = 0;
_context7.next = 3;
return (0, _rxjs.merge)(_this4.notifyObservable.pipe((0, _operators.first)(function (buffer) {
return buffer.readUInt8(0) === 0x08;
}), (0, _operators.map)(function (buffer) {
return buffer.readUInt8(5);
}), (0, _operators.timeout)(30000)), (0, _rxjs.defer)(function () {
return (0, _rxjs.from)(_this4.write(Buffer.from([0x08, 0, 0, 0, 0])));
}).pipe((0, _operators.ignoreElements)())).toPromise();
throw new _hwTransport.TransportError("Transport race condition", "RaceCondition");
case 3:
_context7.t0 = _context7.sent;
mtu = _context7.t0 + 3;
_context7.next = 12;
break;
case 2:
resolveBusy = void 0;
busyPromise = new Promise(function (r) {
resolveBusy = r;
});
case 7:
_context7.prev = 7;
_context7.t1 = _context7["catch"](0);
_this.busy = busyPromise;
_context4.prev = 5;
_context4.next = 8;
return f();
_debug.logSubject.next({
type: "ble-error",
message: "inferMTU got " + String(_context7.t1)
});
_this4.device.gatt.disconnect();
throw _context7.t1;
case 8:
res = _context4.sent;
return _context4.abrupt("return", res);
case 12:
case "end":
return _context7.stop();
}
}
}, _callee7, _this4, [[0, 7]]);
})));
case 10:
_context4.prev = 10;
case 3:
if (resolveBusy) resolveBusy();
_this.busy = null;
return _context4.finish(10);
if (mtu > 23) {
mtuSize = mtu - 3;
case 14:
_debug.logSubject.next({
type: "verbose",
message: "BleTransport(" + String(this.id) + ") mtu set to " + String(mtuSize)
});
this.mtuSize = mtuSize;
}
return _context8.abrupt("return", this.mtuSize);
case 5:
case "end":
return _context4.stop();
return _context8.stop();
}
}
}, _callee4, _this2, [[5,, 10, 14]]);
}, _callee8, this);
}));
return function (_x3) {
return _ref6.apply(this, arguments);
};
}();
function inferMTU() {
return _ref13.apply(this, arguments);
}
_this.id = device.id;
_this.device = device;
_this.writeCharacteristic = writeCharacteristic;
_this.notifyObservable = notifyObservable;
_debug.logSubject.next({
type: "verbose",
message: "BleTransport(" + String(_this.id) + ") new instance"
});
return _this;
}
_createClass(BluetoothTransport, [{
return inferMTU;
}()
}, {
key: "setScrambleKey",

@@ -408,25 +597,25 @@ value: function setScrambleKey() {}

value: function () {
var _ref7 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() {
return regeneratorRuntime.wrap(function _callee5$(_context5) {
var _ref15 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee9() {
return regeneratorRuntime.wrap(function _callee9$(_context9) {
while (1) {
switch (_context5.prev = _context5.next) {
switch (_context9.prev = _context9.next) {
case 0:
if (!this.busy) {
_context5.next = 3;
if (!this.exchangeBusyPromise) {
_context9.next = 3;
break;
}
_context5.next = 3;
return this.busy;
_context9.next = 3;
return this.exchangeBusyPromise;
case 3:
case "end":
return _context5.stop();
return _context9.stop();
}
}
}, _callee5, this);
}, _callee9, this);
}));
function close() {
return _ref7.apply(this, arguments);
return _ref15.apply(this, arguments);
}

@@ -458,7 +647,7 @@

BluetoothTransport.disconnect = function () {
var _ref8 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6(id) {
var _ref16 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee10(id) {
var transport;
return regeneratorRuntime.wrap(function _callee6$(_context6) {
return regeneratorRuntime.wrap(function _callee10$(_context10) {
while (1) {
switch (_context6.prev = _context6.next) {
switch (_context10.prev = _context10.next) {
case 0:

@@ -477,10 +666,10 @@ _debug.logSubject.next({

case "end":
return _context6.stop();
return _context10.stop();
}
}
}, _callee6, undefined);
}, _callee10, undefined);
}));
return function (_x4) {
return _ref8.apply(this, arguments);
return function (_x7) {
return _ref16.apply(this, arguments);
};

@@ -487,0 +676,0 @@ }();

{
"name": "@ledgerhq/hw-transport-web-ble",
"version": "4.35.0",
"version": "4.35.1-beta.28+0f83986",
"description": "Ledger Hardware Wallet Web Bluetooth implementation of the communication layer",

@@ -28,7 +28,9 @@ "keywords": [

"dependencies": {
"@ledgerhq/hw-transport": "^4.35.0",
"rxjs": "^6.3.3"
"@ledgerhq/devices": "^4.35.1-beta.28+0f83986",
"@ledgerhq/errors": "^4.35.1-beta.28+0f83986",
"@ledgerhq/hw-transport": "^4.35.1-beta.28+0f83986",
"rxjs": "^6.4.0"
},
"devDependencies": {
"flow-bin": "^0.66.0",
"flow-bin": "^0.92.1",
"flow-typed": "^2.3.0"

@@ -38,6 +40,8 @@ },

"flow": "flow",
"clean": "rm -rf lib/",
"build": "cd ../.. && export PATH=$(yarn bin):$PATH && cd - && babel --source-maps -d lib src && flow-copy-source -v src lib",
"watch": "cd ../.. && export PATH=$(yarn bin):$PATH && cd - && babel --watch --source-maps -d lib src & flow-copy-source -w -v src lib"
}
"clean": "bash ../../script/clean.sh",
"build": "bash ../../script/build.sh",
"watch": "bash ../../script/watch.sh",
"doc": "bash ../../script/doc.sh"
},
"gitHead": "0f839862fcbb7b74581b74162c1534c9897463cf"
}
<img src="https://user-images.githubusercontent.com/211411/34776833-6f1ef4da-f618-11e7-8b13-f0697901d6a8.png" height="100" />
[Github](https://github.com/LedgerHQ/ledgerjs/),
[API Doc](http://ledgerhq.github.io/ledgerjs/),
[Ledger Devs Slack](https://ledger-dev.slack.com/)
## @ledgerhq/hw-transport-web-ble
Library for Ledger Hardware Wallets.
Allows to communicate with Ledger Hardware Wallets.
[Github](https://github.com/LedgerHQ/ledgerjs/),
[API Doc](http://ledgerhq.github.io/ledgerjs/),
[Ledger Devs Slack](https://ledger-dev.slack.com/)
**[Web]** **(Bluetooth)** – [check browser support](https://caniuse.com/web-bluetooth).
## API
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
#### Table of Contents
- [BluetoothTransport](#bluetoothtransport)
- [Parameters](#parameters)
- [Examples](#examples)
- [observeAvailability](#observeavailability)
- [Parameters](#parameters-1)
### BluetoothTransport
**Extends Transport**
react-native bluetooth BLE implementation
#### Parameters
- `device` **Device**
- `writeCharacteristic` **Characteristic**
- `notifyObservable` **Observable&lt;any>**
- `deviceModel` **DeviceModel**
#### Examples
```javascript
import BluetoothTransport from "@ledgerhq/hw-transport-web-ble";
```
#### observeAvailability
TODO could add this concept in all transports
observe event with { available: bool, type: string } // available is generic, type is specific
an event is emit once and then each time it changes
##### Parameters
- `observer` **any**
// @flow
import { TransportError } from "@ledgerhq/hw-transport";
import { TransportError } from "@ledgerhq/errors";
import { Observable } from "rxjs";

@@ -5,0 +5,0 @@ import { logSubject } from "./debug";

@@ -27,3 +27,3 @@ // @flow

mtuSize: number
) => {
): Observable<void> => {
const chunks = chunkBuffer(apdu, i => mtuSize - (i === 0 ? 5 : 3)).map(

@@ -30,0 +30,0 @@ (buffer, i) => {

// @flow
/* eslint-disable prefer-template */
import Transport, { TransportError } from "@ledgerhq/hw-transport";
import { Observable, merge } from "rxjs";
import { share, tap } from "rxjs/operators";
import Transport from "@ledgerhq/hw-transport";
import { DisconnectedDevice } from "@ledgerhq/errors";
import {
getBluetoothServiceUuids,
getInfosForServiceUuid
} from "@ledgerhq/devices";
import type { DeviceModel } from "@ledgerhq/devices";
import { Observable, defer, merge, from } from "rxjs";
import {
share,
ignoreElements,
first,
map,
tap,
timeout
} from "rxjs/operators";
import { logSubject } from "./debug";

@@ -13,6 +26,2 @@ import type { Device, Characteristic } from "./types";

const ServiceUuid = "d973f2e0-b19e-11e2-9e96-0800200c9a66";
const WriteCharacteristicUuid = "d973f2e2-b19e-11e2-9e96-0800200c9a66";
const NotifyCharacteristicUuid = "d973f2e1-b19e-11e2-9e96-0800200c9a66";
const requiresBluetooth = () => {

@@ -27,3 +36,3 @@ // $FlowFixMe

const availability = () =>
const availability = (): Observable<boolean> =>
Observable.create(observer => {

@@ -41,10 +50,8 @@ const bluetooth = requiresBluetooth();

});
return {
unsubscribe: () => {
unsubscribed = true;
bluetooth.removeEventListener(
"availabilitychanged",
onAvailabilityChanged
);
}
return () => {
unsubscribed = true;
bluetooth.removeEventListener(
"availabilitychanged",
onAvailabilityChanged
);
};

@@ -55,2 +62,116 @@ });

const requestDeviceParam = () => ({
filters: getBluetoothServiceUuids().map(uuid => ({
services: [uuid]
}))
});
const retrieveService = async device => {
if (!device.gatt) throw new Error("bluetooth gatt not found");
const [service] = await device.gatt.getPrimaryServices();
if (!service) throw new Error("bluetooth service not found");
const infos = getInfosForServiceUuid(service.uuid);
if (!infos) throw new Error("bluetooth service infos not found");
return [service, infos];
};
async function open(deviceOrId: Device | string, needsReconnect: boolean) {
let device;
if (typeof deviceOrId === "string") {
if (transportsCache[deviceOrId]) {
logSubject.next({
type: "verbose",
message: "Transport in cache, using that."
});
return transportsCache[deviceOrId];
}
const bluetooth = requiresBluetooth();
// TODO instead we should "query" the device by its ID
device = await bluetooth.requestDevice(requestDeviceParam());
} else {
device = deviceOrId;
}
if (!device.gatt.connected) {
logSubject.next({
type: "verbose",
message: "not connected. connecting..."
});
await device.gatt.connect();
}
const [service, infos] = await retrieveService(device);
const { deviceModel, writeUuid, notifyUuid } = infos;
const [writeC, notifyC] = await Promise.all([
service.getCharacteristic(writeUuid),
service.getCharacteristic(notifyUuid)
]);
const notifyObservable = monitorCharacteristic(notifyC).pipe(
tap(value => {
logSubject.next({
type: "ble-frame-read",
message: value.toString("hex")
});
}),
share()
);
const notif = notifyObservable.subscribe();
const transport = new BluetoothTransport(
device,
writeC,
notifyObservable,
deviceModel
);
if (!device.gatt.connected) {
throw new DisconnectedDevice();
}
transportsCache[transport.id] = transport;
const onDisconnect = e => {
delete transportsCache[transport.id];
transport.notYetDisconnected = false;
notif.unsubscribe();
device.removeEventListener("gattserverdisconnected", onDisconnect);
logSubject.next({
type: "verbose",
message: `BleTransport(${transport.id}) disconnected`
});
transport.emit("disconnect", e);
};
device.addEventListener("gattserverdisconnected", onDisconnect);
let beforeMTUTime = Date.now();
try {
await transport.inferMTU();
} finally {
let afterMTUTime = Date.now();
// workaround for #279: we need to open() again if we come the first time here,
// to make sure we do a disconnect() after the first pairing time
// because of a firmware bug
if (afterMTUTime - beforeMTUTime < 500) {
needsReconnect = false; // (optim) there is likely no new pairing done because mtu answer was fast.
}
if (needsReconnect) {
await device.gatt.disconnect();
// necessary time for the bonding workaround
await new Promise(s => setTimeout(s, 500));
}
}
if (needsReconnect) {
return open(device, false);
}
return transport;
}
/**

@@ -87,16 +208,11 @@ * react-native bluetooth BLE implementation

bluetooth
.requestDevice({
filters: [
{
services: [ServiceUuid]
}
]
})
.then(device => {
if (!unsubscribed) {
observer.next({ type: "add", descriptor: device, device });
observer.complete();
}
});
bluetooth.requestDevice(requestDeviceParam()).then(async device => {
if (!unsubscribed) {
observer.next({
type: "add",
descriptor: device
});
observer.complete();
}
});
function unsubscribe() {

@@ -109,69 +225,3 @@ unsubscribed = true;

static async open(deviceOrId: Device | string) {
let device;
if (typeof deviceOrId === "string") {
if (transportsCache[deviceOrId]) {
logSubject.next({
type: "verbose",
message: "Transport in cache, using that."
});
return transportsCache[deviceOrId];
}
const bluetooth = requiresBluetooth();
// TODO instead we should "query" the device by its ID
device = await bluetooth.requestDevice({
filters: [
{
services: [ServiceUuid]
}
]
});
} else {
device = deviceOrId;
}
if (!device.gatt.connected) {
logSubject.next({
type: "verbose",
message: "not connected. connecting..."
});
await device.gatt.connect();
}
const service = await device.gatt.getPrimaryService(ServiceUuid);
const [writeC, notifyC] = await Promise.all([
service.getCharacteristic(WriteCharacteristicUuid),
service.getCharacteristic(NotifyCharacteristicUuid)
]);
const notifyObservable = monitorCharacteristic(notifyC).pipe(
tap(value => {
logSubject.next({
type: "ble-frame-read",
message: value.toString("hex")
});
}),
share()
);
const notif = notifyObservable.subscribe();
const transport = new BluetoothTransport(device, writeC, notifyObservable);
transportsCache[transport.id] = transport;
const onDisconnect = e => {
delete transportsCache[transport.id];
transport.notYetDisconnected = false;
notif.unsubscribe();
device.removeEventListener("gattserverdisconnected", onDisconnect);
logSubject.next({
type: "verbose",
message: `BleTransport(${transport.id}) disconnected`
});
transport.emit("disconnect", e);
};
device.addEventListener("gattserverdisconnected", onDisconnect);
return transport;
return open(deviceOrId, true);
}

@@ -202,6 +252,9 @@

deviceModel: DeviceModel;
constructor(
device: Device,
writeCharacteristic: Characteristic,
notifyObservable: Observable<*>
notifyObservable: Observable<*>,
deviceModel: DeviceModel
) {

@@ -213,2 +266,3 @@ super();

this.notifyObservable = notifyObservable;
this.deviceModel = deviceModel;

@@ -221,4 +275,44 @@ logSubject.next({

async inferMTU() {
let mtu = 23;
await this.exchangeAtomicImpl(async () => {
try {
mtu =
(await merge(
this.notifyObservable.pipe(
first(buffer => buffer.readUInt8(0) === 0x08),
map(buffer => buffer.readUInt8(5)),
timeout(30000)
),
defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(
ignoreElements()
)
).toPromise()) + 3;
} catch (e) {
logSubject.next({
type: "ble-error",
message: "inferMTU got " + String(e)
});
this.device.gatt.disconnect();
throw e;
}
});
if (mtu > 23) {
const mtuSize = mtu - 3;
logSubject.next({
type: "verbose",
message: `BleTransport(${String(this.id)}) mtu set to ${String(
mtuSize
)}`
});
this.mtuSize = mtuSize;
}
return this.mtuSize;
}
exchange = (apdu: Buffer): Promise<Buffer> =>
this.atomic(async () => {
this.exchangeAtomicImpl(async () => {
try {

@@ -264,26 +358,7 @@ const { debug } = this;

busy: ?Promise<void>;
atomic = async (f: *) => {
if (this.busy) {
throw new TransportError("Transport race condition", "RaceCondition");
}
let resolveBusy;
const busyPromise = new Promise(r => {
resolveBusy = r;
});
this.busy = busyPromise;
try {
const res = await f();
return res;
} finally {
if (resolveBusy) resolveBusy();
this.busy = null;
}
};
async close() {
if (this.busy) {
await this.busy;
if (this.exchangeBusyPromise) {
await this.exchangeBusyPromise;
}
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc