mirror of
https://github.com/transatoshi-mw/grin-web-wallet.git
synced 2025-10-06 15:52:47 +00:00
1164 lines
30 KiB
JavaScript
Executable File
1164 lines
30 KiB
JavaScript
Executable File
// Use strict
|
|
"use strict";
|
|
|
|
|
|
// Classes
|
|
|
|
// HardwareWallet USB transport class
|
|
class HardwareWalletUsbTransport {
|
|
|
|
// Public
|
|
|
|
// Constructor
|
|
constructor(device, interfaceNumber) {
|
|
|
|
// Set device
|
|
this.device = device;
|
|
|
|
// Set interface number
|
|
this.interfaceNumber = interfaceNumber;
|
|
|
|
// Set allow disconnect event to true
|
|
this.allowDisconnectEvent = true;
|
|
|
|
// Set product name
|
|
var productName = device["manufacturerName"] + " " + device["productName"];
|
|
|
|
// Go through all devices
|
|
for(var i = 0; i < HardwareWalletUsbTransport.DEVICES["length"]; ++i) {
|
|
|
|
// Check if device's vendor ID matches the device's
|
|
if(device["vendorId"] === HardwareWalletUsbTransport.DEVICES[i]["Vendor ID"]) {
|
|
|
|
// Set type to device's type
|
|
this.type = HardwareWalletUsbTransport.DEVICES[i]["Type"];
|
|
|
|
// Check if device's product ID matches the device's
|
|
if("Product ID" in HardwareWalletUsbTransport.DEVICES[i] === false || device["productId"] === HardwareWalletUsbTransport.DEVICES[i]["Product ID"]) {
|
|
|
|
// Check if device has a product name
|
|
if("Product Name" in HardwareWalletUsbTransport.DEVICES[i] === true) {
|
|
|
|
// Set product name
|
|
productName = HardwareWalletUsbTransport.DEVICES[i]["Product Name"];
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set device model
|
|
this["deviceModel"] = {
|
|
|
|
// Product name
|
|
"productName": productName
|
|
};
|
|
}
|
|
|
|
// On
|
|
on(event, callback) {
|
|
|
|
// Check event
|
|
switch(event) {
|
|
|
|
// Disconnect
|
|
case "disconnect":
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Create callback once
|
|
var callbackOnce = function(event) {
|
|
|
|
// Check if device was disconnected
|
|
if(event["device"] === self.device) {
|
|
|
|
// Remove USB disconnect event
|
|
navigator["usb"].removeEventListener("disconnect", callbackOnce);
|
|
|
|
// Check if disconnect event is allowed
|
|
if(self.allowDisconnectEvent === true) {
|
|
|
|
// Call callback
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
|
|
// USB disconnect event
|
|
navigator["usb"].addEventListener("disconnect", callbackOnce);
|
|
|
|
// Return callback once
|
|
return callbackOnce;
|
|
}
|
|
}
|
|
|
|
// Off
|
|
off(event, callback) {
|
|
|
|
// Check event
|
|
switch(event) {
|
|
|
|
// Disconnect
|
|
case "disconnect":
|
|
|
|
// Remove USB disconnect event
|
|
navigator["usb"].removeEventListener("disconnect", callback);
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Close
|
|
close() {
|
|
|
|
// Clear allow disconnect event
|
|
this.allowDisconnectEvent = false;
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return releasing device interface and catch errors
|
|
return self.device.releaseInterface(self.interfaceNumber).catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Return resetting device and catch errors
|
|
return self.device.reset().catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Return closing device and catch errors
|
|
return self.device.close().then(function() {
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Send
|
|
send(messageType, parameterOne, parameterTwo, data) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check type
|
|
switch(self.type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Create header
|
|
var header = new Uint8Array([messageType >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, messageType, parameterOne, parameterTwo, data["length"]]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Create header
|
|
var header = new Uint8Array([messageType >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, messageType, data["length"] >>> (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 3), data["length"] >>> (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 2), data["length"] >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, data["length"]]);
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Create message
|
|
var message = new Uint8Array(header["length"] + data["length"]);
|
|
message.set(header);
|
|
message.set(data, header["length"]);
|
|
|
|
// Return sending message to the device
|
|
return HardwareWalletUsbTransport.sendRequest(self.device, self.type, message).then(function(response) {
|
|
|
|
// Check if response contains a message type
|
|
if(response["length"] >= HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH) {
|
|
|
|
// Get message type
|
|
var messageType = (response[response["length"] - HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | response[response["length"] - (HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH - 1)];
|
|
|
|
// Resolve
|
|
resolve({
|
|
|
|
// Message type
|
|
"Message Type": messageType,
|
|
|
|
// Data
|
|
"Data": response.subarray(0, response["length"] - HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH)
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Securely clear response
|
|
response.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Check if error is that the device was disconnected
|
|
if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWalletUsbTransport.NOT_FOUND_ERROR_CODE) || ("name" in error === true && error["name"] === "NotFoundError"))) {
|
|
|
|
// Reject error
|
|
reject(new DOMException("", "NetworkError"));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Request
|
|
static request(device = HardwareWalletUsbTransport.NO_DEVICE) {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Get device
|
|
var getDevice = function() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check if no device was provided
|
|
if(device === HardwareWalletUsbTransport.NO_DEVICE) {
|
|
|
|
// Return requesting USB device
|
|
return navigator["usb"].requestDevice({
|
|
|
|
// Filters
|
|
"filters": HardwareWalletUsbTransport.DEVICES.map(function(device) {
|
|
|
|
// Check if device has a product ID
|
|
if("Product ID" in device === true) {
|
|
|
|
// Return device's vendor ID and product ID
|
|
return {
|
|
|
|
// Vendor ID
|
|
"vendorId": device["Vendor ID"],
|
|
|
|
// Product ID
|
|
"productId": device["Product ID"]
|
|
};
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return device's vendor ID
|
|
return {
|
|
|
|
// Vendor ID
|
|
"vendorId": device["Vendor ID"]
|
|
};
|
|
}
|
|
})
|
|
|
|
}).then(function(device) {
|
|
|
|
// Check if device isn't opened
|
|
if(device["opened"] === false) {
|
|
|
|
// Resolve device
|
|
resolve(device);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Reject error
|
|
reject(new DOMException("", "InvalidStateError"));
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Initialize device applicable
|
|
var deviceApplicable = false;
|
|
|
|
// Go through all devices
|
|
for(var i = 0; i < HardwareWalletUsbTransport.DEVICES["length"]; ++i) {
|
|
|
|
// Check if device's vendor ID and product ID match the device's
|
|
if(device["vendorId"] === HardwareWalletUsbTransport.DEVICES[i]["Vendor ID"] && ("Product ID" in HardwareWalletUsbTransport.DEVICES[i] === false || device["productId"] === HardwareWalletUsbTransport.DEVICES[i]["Product ID"])) {
|
|
|
|
// Set device applicable
|
|
deviceApplicable = true;
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if device is applicable
|
|
if(deviceApplicable === true) {
|
|
|
|
// Check if device isn't opened
|
|
if(device["opened"] === false) {
|
|
|
|
// Resolve device
|
|
resolve(device);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Reject error
|
|
reject(new DOMException("", "InvalidStateError"));
|
|
}
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// Return getting device
|
|
return getDevice().then(function(device) {
|
|
|
|
// Return opening device
|
|
return device.open().then(function() {
|
|
|
|
// Return selecting device's configuration
|
|
return device.selectConfiguration(HardwareWalletUsbTransport.CONFIGURATION).then(function() {
|
|
|
|
// Return resetting device and catch errors
|
|
return device.reset().catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Initialize interface found
|
|
var interfaceFound = false;
|
|
|
|
// Go through all the configuration's interfaces
|
|
for(var i = 0; i < device["configurations"][0]["interfaces"]["length"] && interfaceFound === false; ++i) {
|
|
|
|
// Go through all of the interface's alternates
|
|
for(var j = 0; j < device["configurations"][0]["interfaces"][i]["alternates"]["length"]; ++j) {
|
|
|
|
// Check if alternates is for WebUSB
|
|
if(device["configurations"][0]["interfaces"][i]["alternates"][j]["interfaceClass"] === HardwareWalletUsbTransport.WEBUSB_INTERFACE_CLASS) {
|
|
|
|
// Set interface found
|
|
interfaceFound = true;
|
|
|
|
// Set interface number
|
|
var interfaceNumber = device["configurations"][0]["interfaces"][i]["interfaceNumber"];
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if interface was found
|
|
if(interfaceFound === true) {
|
|
|
|
// Return claiming interface
|
|
return device.claimInterface(interfaceNumber).then(function() {
|
|
|
|
// Create transport for the device
|
|
var transport = new HardwareWalletUsbTransport(device, interfaceNumber);
|
|
|
|
// Resolve transport
|
|
resolve(transport);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return closing device and catch errors
|
|
return device.close().catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return closing device and catch errors
|
|
return device.close().catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Reject
|
|
reject();
|
|
});
|
|
}
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return closing device and catch errors
|
|
return device.close().catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// List
|
|
static list() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return getting attached USB devices
|
|
return navigator["usb"].getDevices().then(function(devices) {
|
|
|
|
// Initialize applicable devices
|
|
var applicableDevices = [];
|
|
|
|
// Go through all devices
|
|
for(var i = 0; i < devices["length"]; ++i) {
|
|
|
|
// Check if device isn't opened
|
|
if(devices[i]["opened"] === false) {
|
|
|
|
// Initialize device applicable
|
|
var deviceApplicable = false;
|
|
|
|
// Go through all devices
|
|
for(var j = 0; j < HardwareWalletUsbTransport.DEVICES["length"]; ++j) {
|
|
|
|
// Check if device's vendor ID and product ID match the device's
|
|
if(devices[i]["vendorId"] === HardwareWalletUsbTransport.DEVICES[j]["Vendor ID"] && ("Product ID" in HardwareWalletUsbTransport.DEVICES[j] === false || devices[i]["productId"] === HardwareWalletUsbTransport.DEVICES[j]["Product ID"])) {
|
|
|
|
// Set device aapplicable
|
|
deviceApplicable = true;
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if device is applicable
|
|
if(deviceApplicable === true) {
|
|
|
|
// Append device to list
|
|
applicableDevices.push(devices[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resolve applicable devices
|
|
resolve(applicableDevices);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Private
|
|
|
|
// Create packets
|
|
static createPackets(channel, type, payload = HardwareWalletUsbTransport.NO_PAYLOAD) {
|
|
|
|
// Initialize packets
|
|
var packets = [];
|
|
|
|
// Check if payload doesn't exist
|
|
if(payload === HardwareWalletUsbTransport.NO_PAYLOAD) {
|
|
|
|
// Set payload to an empty array
|
|
payload = new Uint8Array([]);
|
|
}
|
|
|
|
// Check type
|
|
switch(type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Create padded payload
|
|
var numberOfPackets = Math.ceil((HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + payload["length"]) / (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH));
|
|
var paddedPayload = (new Uint8Array(numberOfPackets * (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH))).fill(0);
|
|
paddedPayload.set(new Uint8Array([payload["length"] >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, payload["length"]]));
|
|
paddedPayload.set(payload, Uint16Array["BYTES_PER_ELEMENT"]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Check if more than one packet will be used
|
|
if(payload["length"] > HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"]) {
|
|
|
|
// Create padded payload
|
|
var numberOfPackets = Math.ceil((payload["length"] - (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"])) / (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]));
|
|
var paddedPayload = (new Uint8Array(HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"] + numberOfPackets * (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]))).fill(0);
|
|
paddedPayload.set(payload);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Create padded payload
|
|
var paddedPayload = (new Uint8Array(HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"])).fill(0);
|
|
paddedPayload.set(payload);
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Set payload to padded payload
|
|
payload = paddedPayload;
|
|
|
|
// Initialize payload offset
|
|
var payloadOffset = 0;
|
|
|
|
// Go through all packets required to send the payload
|
|
for(var i = 0; payloadOffset !== payload["length"]; ++i) {
|
|
|
|
// Check type
|
|
switch(type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Create header
|
|
var header = new Uint8Array([channel >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, channel, HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_TAG, i >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, i]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Check if at the first packet
|
|
if(i === 0) {
|
|
|
|
// Create header
|
|
var header = HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER;
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Create header
|
|
var header = HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER;
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Get payload part length
|
|
var payloadPartLength = HardwareWalletUsbTransport.PACKET_SIZE - header["length"];
|
|
|
|
// Create packet
|
|
var packet = new Uint8Array(header["length"] + payloadPartLength);
|
|
packet.set(header);
|
|
packet.set(payload.subarray(payloadOffset, payloadOffset + payloadPartLength), header["length"]);
|
|
|
|
// Append packet to list
|
|
packets.push(packet);
|
|
|
|
// Update payload offset
|
|
payloadOffset += payloadPartLength;
|
|
}
|
|
|
|
// Return packets
|
|
return packets;
|
|
}
|
|
|
|
// Send request
|
|
static sendRequest(device, type, payload = HardwareWalletUsbTransport.NO_PAYLOAD) {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Create random channel
|
|
var channel = Math.floor(Math.random() * HardwareWalletUsbTransport.MAX_CHANNEL);
|
|
|
|
// Get packets
|
|
var packets = HardwareWalletUsbTransport.createPackets(channel, type, payload);
|
|
|
|
// Check type
|
|
switch(type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Set endpoint
|
|
var endpoint = HardwareWalletUsbTransport.LEDGER_ENDPOINT;
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Set endpoint
|
|
var endpoint = HardwareWalletUsbTransport.TREZOR_ENDPOINT;
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Send packet
|
|
var sendPacket = new Promise(function(resolve, reject) {
|
|
|
|
// Resolve
|
|
resolve();
|
|
});
|
|
|
|
// Initialize sending packets
|
|
var sendingPackets = [sendPacket];
|
|
|
|
// Go through all packets
|
|
for(var i = 0; i < packets["length"]; ++i) {
|
|
|
|
// Get packet
|
|
let packet = packets[i];
|
|
|
|
// Send next pack after previous packet is send
|
|
sendPacket = sendPacket.then(function() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return transfering out packet
|
|
return device.transferOut(endpoint, packet).then(function() {
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Append sending packet to list
|
|
sendingPackets.push(sendPacket);
|
|
}
|
|
|
|
// Return sending all packets
|
|
return Promise.all(sendingPackets).then(function() {
|
|
|
|
// Receive packet
|
|
var receivePacket = function(expectedSequenceIndex) {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return transfering in packet
|
|
return device.transferIn(endpoint, HardwareWalletUsbTransport.PACKET_SIZE).then(function(response) {
|
|
|
|
// Get packet from response
|
|
var packet = new Uint8Array(response["data"]["buffer"]);
|
|
|
|
// Check if packet's size is correct
|
|
if(packet["length"] === HardwareWalletUsbTransport.PACKET_SIZE) {
|
|
|
|
// Check type
|
|
switch(type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Get response channel
|
|
var responseChannel = (packet[0] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | packet[1];
|
|
|
|
// Check if response channel is correct
|
|
if(responseChannel === channel) {
|
|
|
|
// Check if tag is correct
|
|
if(packet[Uint16Array["BYTES_PER_ELEMENT"]] === HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_TAG) {
|
|
|
|
// Get sequence index
|
|
var sequenceIndex = (packet[Uint16Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | packet[Uint16Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + 1];
|
|
|
|
// Check if sequence index is correct
|
|
if(sequenceIndex === expectedSequenceIndex) {
|
|
|
|
// Resolve packet's payload
|
|
resolve(packet.subarray(HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Check if at the first packet
|
|
if(expectedSequenceIndex === 0) {
|
|
|
|
// Get magic numbers
|
|
var magicNumbers = packet.subarray(0, HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"]);
|
|
|
|
// Go through all magic numbers
|
|
for(var i = 0; i < magicNumbers["length"]; ++i) {
|
|
|
|
// Check if magic number isn't correct
|
|
if(magicNumbers[i] !== HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER[i]) {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Resolve packet's payload
|
|
resolve(packet.subarray(HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"]));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Get magic numbers
|
|
var magicNumbers = packet.subarray(0, HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]);
|
|
|
|
// Go through all magic numbers
|
|
for(var i = 0; i < magicNumbers["length"]; ++i) {
|
|
|
|
// Check if magic number isn't correct
|
|
if(magicNumbers[i] !== HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER[i]) {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Resolve packet's payload
|
|
resolve(packet.subarray(HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]));
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Securely clear packet
|
|
packet.fill(0);
|
|
|
|
// Reject
|
|
reject();
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
};
|
|
|
|
// Return receiving first packet
|
|
return receivePacket(0).then(function(responsePart) {
|
|
|
|
// Check type
|
|
switch(type) {
|
|
|
|
// Ledger type
|
|
case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE:
|
|
|
|
// Get message type
|
|
var messageType = new Uint8Array([]);
|
|
|
|
// Get response size
|
|
var responseSize = (responsePart[0] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | responsePart[1];
|
|
|
|
// Set response
|
|
var response = responsePart.subarray(Uint16Array["BYTES_PER_ELEMENT"]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Trezor type
|
|
case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE:
|
|
|
|
// Get message type
|
|
var messageType = responsePart.subarray(0, HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH);
|
|
|
|
// Get response size
|
|
var responseSize = (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH] << (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 3)) | (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 1] << (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 2)) | (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 2] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 3];
|
|
|
|
// Set response
|
|
var response = responsePart.subarray(HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + Uint32Array["BYTES_PER_ELEMENT"]);
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Set next sequence index
|
|
var nextSequenceIndex = 1;
|
|
|
|
// Get next response part
|
|
var getNextResponsePart = function() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check if the entire response hasn't been received
|
|
if(response["length"] < responseSize) {
|
|
|
|
// Return receiving next packet
|
|
return receivePacket(nextSequenceIndex).then(function(responsePart) {
|
|
|
|
// Append response part to response
|
|
var currentResponse = new Uint8Array(response["length"] + responsePart["length"]);
|
|
currentResponse.set(response);
|
|
currentResponse.set(responsePart, response["length"]);
|
|
response.fill(0);
|
|
responsePart.fill(0);
|
|
response = currentResponse;
|
|
|
|
// Increment next sequence index
|
|
++nextSequenceIndex;
|
|
|
|
// Return getting next response part
|
|
return getNextResponsePart().then(function() {
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Return getting next response part
|
|
return getNextResponsePart().then(function() {
|
|
|
|
// Append message type to response
|
|
var finalResponse = new Uint8Array(responseSize + messageType["length"]);
|
|
finalResponse.set(response.subarray(0, responseSize));
|
|
finalResponse.set(messageType, responseSize);
|
|
response.fill(0);
|
|
|
|
// Resolve final response
|
|
resolve(finalResponse);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Securely clear response
|
|
response.fill(0);
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Catch error
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// No payload
|
|
static get NO_PAYLOAD() {
|
|
|
|
// Return no payload
|
|
return null;
|
|
}
|
|
|
|
// No device
|
|
static get NO_DEVICE() {
|
|
|
|
// Return no device
|
|
return null;
|
|
}
|
|
|
|
// Devices
|
|
static get DEVICES() {
|
|
|
|
// Return devices
|
|
return [
|
|
|
|
// Ledger
|
|
{
|
|
|
|
// Type
|
|
"Type": HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE,
|
|
|
|
// Vendor ID
|
|
"Vendor ID": 0x2C97
|
|
},
|
|
|
|
// Trezor
|
|
{
|
|
|
|
// Type
|
|
"Type": HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE,
|
|
|
|
// Product name
|
|
"Product Name": "Trezor",
|
|
|
|
// Vendor ID
|
|
"Vendor ID": 0x1209,
|
|
|
|
// Product ID
|
|
"Product ID": 0x53C1
|
|
}
|
|
];
|
|
}
|
|
|
|
// WebUSB interface class
|
|
static get WEBUSB_INTERFACE_CLASS() {
|
|
|
|
// Return WebUSB interface class
|
|
return 0xFF;
|
|
}
|
|
|
|
// Configuration
|
|
static get CONFIGURATION() {
|
|
|
|
// Return configuration
|
|
return 0x01;
|
|
}
|
|
|
|
// Packet size
|
|
static get PACKET_SIZE() {
|
|
|
|
// Return packet size
|
|
return 64;
|
|
}
|
|
|
|
// Message type length
|
|
static get MESSAGE_TYPE_LENGTH() {
|
|
|
|
// Return message type length
|
|
return Uint16Array["BYTES_PER_ELEMENT"];
|
|
}
|
|
|
|
// Bits in a byte
|
|
static get BITS_IN_A_BYTE() {
|
|
|
|
// Rerurn bits in a byte
|
|
return 8;
|
|
}
|
|
|
|
// Ledger endpoint
|
|
static get LEDGER_ENDPOINT() {
|
|
|
|
// Return Ledger endpoint
|
|
return 0x03;
|
|
}
|
|
|
|
// Trezor endpoint
|
|
static get TREZOR_ENDPOINT() {
|
|
|
|
// Return Trezor endpoint
|
|
return 0x01;
|
|
}
|
|
|
|
// Not found error code
|
|
static get NOT_FOUND_ERROR_CODE() {
|
|
|
|
// Return not found error code
|
|
return 8;
|
|
}
|
|
|
|
// Ledger packet header length
|
|
static get LEDGER_PACKET_HEADER_LENGTH() {
|
|
|
|
// Return Ledger packet header length
|
|
return 5;
|
|
}
|
|
|
|
// Ledger packet header tag
|
|
static get LEDGER_PACKET_HEADER_TAG() {
|
|
|
|
// Return Ledger packet header tag
|
|
return 0x05;
|
|
}
|
|
|
|
// Trezor first packet header
|
|
static get TREZOR_FIRST_PACKET_HEADER() {
|
|
|
|
// Return Trezor packet header
|
|
return new Uint8Array([0x3F, 0x23, 0x23]);
|
|
}
|
|
|
|
// Trezor next packets header
|
|
static get TREZOR_NEXT_PACKETS_HEADER() {
|
|
|
|
// Return Trezor next packets header
|
|
return new Uint8Array([0x3F]);
|
|
}
|
|
|
|
// Max channel
|
|
static get MAX_CHANNEL() {
|
|
|
|
// Return max channel
|
|
return 0xFFFF;
|
|
}
|
|
}
|
|
|
|
|
|
// Main function
|
|
|
|
// Set global object's hardware wallet USB transport
|
|
globalThis["HardwareWalletUsbTransport"] = HardwareWalletUsbTransport;
|