Ledger Communication toolkit (#4268)
This commit is contained in:
parent
ba0209678b
commit
53bbc76d06
25
js/src/3rdparty/ledger/index.js
vendored
Normal file
25
js/src/3rdparty/ledger/index.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Ledger3 from './vendor/ledger3';
|
||||||
|
import LedgerEth from './vendor/ledger-eth';
|
||||||
|
|
||||||
|
export function create () {
|
||||||
|
const ledger = new Ledger3('w0w');
|
||||||
|
const app = new LedgerEth(ledger);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
11
js/src/3rdparty/ledger/vendor/README.md
vendored
Normal file
11
js/src/3rdparty/ledger/vendor/README.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Description
|
||||||
|
|
||||||
|
Vendor files (c) 2016 [Ledger](https://github.com/LedgerHQ) for [Ledger Nano-S](https://www.ledgerwallet.com/) integration
|
||||||
|
|
||||||
|
# Origin
|
||||||
|
|
||||||
|
Files originally created via [https://github.com/kvhnuke/etherwallet/pull/248/files](https://github.com/kvhnuke/etherwallet/pull/248/files)
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Files in this directory is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) by their original author
|
166
js/src/3rdparty/ledger/vendor/ledger-eth.js
vendored
Normal file
166
js/src/3rdparty/ledger/vendor/ledger-eth.js
vendored
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
* Ledger Communication toolkit
|
||||||
|
* (c) 2016 Ledger
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var LedgerEth = function(comm) {
|
||||||
|
this.comm = comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
LedgerEth.splitPath = function(path) {
|
||||||
|
var result = [];
|
||||||
|
var components = path.split('/');
|
||||||
|
components.forEach(function (element, index) {
|
||||||
|
var number = parseInt(element, 10);
|
||||||
|
if (isNaN(number)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((element.length > 1) && (element[element.length - 1] == "'")) {
|
||||||
|
number += 0x80000000;
|
||||||
|
}
|
||||||
|
result.push(number);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback is function(response, error)
|
||||||
|
LedgerEth.prototype.getAddress = function(path, callback, boolDisplay, boolChaincode) {
|
||||||
|
var splitPath = LedgerEth.splitPath(path);
|
||||||
|
var buffer = new Buffer(5 + 1 + splitPath.length * 4);
|
||||||
|
buffer[0] = 0xe0;
|
||||||
|
buffer[1] = 0x02;
|
||||||
|
buffer[2] = (boolDisplay ? 0x01 : 0x00);
|
||||||
|
buffer[3] = (boolChaincode ? 0x01 : 0x00);
|
||||||
|
buffer[4] = 1 + splitPath.length * 4;
|
||||||
|
buffer[5] = splitPath.length;
|
||||||
|
splitPath.forEach(function (element, index) {
|
||||||
|
buffer.writeUInt32BE(element, 6 + 4 * index);
|
||||||
|
});
|
||||||
|
var self = this;
|
||||||
|
var localCallback = function(response, error) {
|
||||||
|
if (typeof error != "undefined") {
|
||||||
|
callback(undefined, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var result = {};
|
||||||
|
response = new Buffer(response, 'hex');
|
||||||
|
var sw = response.readUInt16BE(response.length - 2);
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
callback(undefined, "Invalid status " + sw.toString(16));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var publicKeyLength = response[0];
|
||||||
|
var addressLength = response[1 + publicKeyLength];
|
||||||
|
result['publicKey'] = response.slice(1, 1 + publicKeyLength).toString('hex');
|
||||||
|
result['address'] = "0x" + response.slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength).toString('ascii');
|
||||||
|
if (boolChaincode) {
|
||||||
|
result['chainCode'] = response.slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32).toString('hex');
|
||||||
|
}
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.comm.exchange(buffer.toString('hex'), localCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback is function(response, error)
|
||||||
|
LedgerEth.prototype.signTransaction = function(path, rawTxHex, callback) {
|
||||||
|
var splitPath = LedgerEth.splitPath(path);
|
||||||
|
var offset = 0;
|
||||||
|
var rawTx = new Buffer(rawTxHex, 'hex');
|
||||||
|
var apdus = [];
|
||||||
|
while (offset != rawTx.length) {
|
||||||
|
var maxChunkSize = (offset == 0 ? (150 - 1 - splitPath.length * 4) : 150)
|
||||||
|
var chunkSize = (offset + maxChunkSize > rawTx.length ? rawTx.length - offset : maxChunkSize);
|
||||||
|
var buffer = new Buffer(offset == 0 ? 5 + 1 + splitPath.length * 4 + chunkSize : 5 + chunkSize);
|
||||||
|
buffer[0] = 0xe0;
|
||||||
|
buffer[1] = 0x04;
|
||||||
|
buffer[2] = (offset == 0 ? 0x00 : 0x80);
|
||||||
|
buffer[3] = 0x00;
|
||||||
|
buffer[4] = (offset == 0 ? 1 + splitPath.length * 4 + chunkSize : chunkSize);
|
||||||
|
if (offset == 0) {
|
||||||
|
buffer[5] = splitPath.length;
|
||||||
|
splitPath.forEach(function (element, index) {
|
||||||
|
buffer.writeUInt32BE(element, 6 + 4 * index);
|
||||||
|
});
|
||||||
|
rawTx.copy(buffer, 6 + 4 * splitPath.length, offset, offset + chunkSize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rawTx.copy(buffer, 5, offset, offset + chunkSize);
|
||||||
|
}
|
||||||
|
apdus.push(buffer.toString('hex'));
|
||||||
|
offset += chunkSize;
|
||||||
|
}
|
||||||
|
var self = this;
|
||||||
|
var localCallback = function(response, error) {
|
||||||
|
if (typeof error != "undefined") {
|
||||||
|
callback(undefined, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = new Buffer(response, 'hex');
|
||||||
|
var sw = response.readUInt16BE(response.length - 2);
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
callback(undefined, "Invalid status " + sw.toString(16));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (apdus.length == 0) {
|
||||||
|
var result = {};
|
||||||
|
result['v'] = response.slice(0, 1).toString('hex');
|
||||||
|
result['r'] = response.slice(1, 1 + 32).toString('hex');
|
||||||
|
result['s'] = response.slice(1 + 32, 1 + 32 + 32).toString('hex');
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.comm.exchange(apdus.shift(), localCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.comm.exchange(apdus.shift(), localCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback is function(response, error)
|
||||||
|
LedgerEth.prototype.getAppConfiguration = function(callback) {
|
||||||
|
var buffer = new Buffer(5);
|
||||||
|
buffer[0] = 0xe0;
|
||||||
|
buffer[1] = 0x06;
|
||||||
|
buffer[2] = 0x00;
|
||||||
|
buffer[3] = 0x00;
|
||||||
|
buffer[4] = 0x00;
|
||||||
|
var localCallback = function(response, error) {
|
||||||
|
if (typeof error != "undefined") {
|
||||||
|
callback(undefined, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = new Buffer(response, 'hex');
|
||||||
|
var result = {};
|
||||||
|
var sw = response.readUInt16BE(response.length - 2);
|
||||||
|
if (sw != 0x9000) {
|
||||||
|
callback(undefined, "Invalid status " + sw.toString(16));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result['arbitraryDataEnabled'] = (response[0] & 0x01);
|
||||||
|
result['version'] = "" + response[1] + '.' + response[2] + '.' + response[3];
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.comm.exchange(buffer.toString('hex'), localCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LedgerEth;
|
||||||
|
|
||||||
|
/* eslint-enable */
|
72
js/src/3rdparty/ledger/vendor/ledger3.js
vendored
Normal file
72
js/src/3rdparty/ledger/vendor/ledger3.js
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
* Ledger Communication toolkit
|
||||||
|
* (c) 2016 Ledger
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Ledger3 = function(scrambleKey, timeoutSeconds) {
|
||||||
|
this.scrambleKey = new Buffer(scrambleKey, 'ascii');
|
||||||
|
this.timeoutSeconds = timeoutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ledger3.wrapApdu = function(apdu, key) {
|
||||||
|
var result = new Buffer(apdu.length);
|
||||||
|
for (var i=0; i<apdu.length; i++) {
|
||||||
|
result[i] = apdu[i] ^ key[i % key.length];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from normal to web-safe, strip trailing "="s
|
||||||
|
Ledger3.webSafe64 = function(base64) {
|
||||||
|
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from web-safe to normal, add trailing "="s
|
||||||
|
Ledger3.normal64 = function(base64) {
|
||||||
|
return base64.replace(/\-/g, '+').replace(/_/g, '/') + '=='.substring(0, (3*base64.length)%4);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ledger3.prototype.u2fCallback = function(response, callback) {
|
||||||
|
if (typeof response['signatureData'] != "undefined") {
|
||||||
|
var data = new Buffer((Ledger3.normal64(response['signatureData'])), 'base64');
|
||||||
|
callback(data.toString('hex', 5));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(undefined, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback is function(response, error)
|
||||||
|
Ledger3.prototype.exchange = function(apduHex, callback) {
|
||||||
|
var apdu = new Buffer(apduHex, 'hex');
|
||||||
|
var keyHandle = Ledger3.wrapApdu(apdu, this.scrambleKey);
|
||||||
|
var challenge = new Buffer("0000000000000000000000000000000000000000000000000000000000000000", 'hex');
|
||||||
|
var key = {};
|
||||||
|
key['version'] = 'U2F_V2';
|
||||||
|
key['keyHandle'] = Ledger3.webSafe64(keyHandle.toString('base64'));
|
||||||
|
var self = this;
|
||||||
|
var localCallback = function(result) {
|
||||||
|
self.u2fCallback(result, callback);
|
||||||
|
}
|
||||||
|
u2f.sign(location.origin, Ledger3.webSafe64(challenge.toString('base64')), [key], localCallback, this.timeoutSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Ledger3
|
||||||
|
|
||||||
|
/* eslint-enable */
|
Loading…
Reference in New Issue
Block a user