diff --git a/js/src/3rdparty/ledger/index.js b/js/src/3rdparty/ledger/index.js new file mode 100644 index 000000000..332d431dd --- /dev/null +++ b/js/src/3rdparty/ledger/index.js @@ -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 . + +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; +} diff --git a/js/src/3rdparty/ledger/vendor/README.md b/js/src/3rdparty/ledger/vendor/README.md new file mode 100644 index 000000000..a44c3521b --- /dev/null +++ b/js/src/3rdparty/ledger/vendor/README.md @@ -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 diff --git a/js/src/3rdparty/ledger/vendor/ledger-eth.js b/js/src/3rdparty/ledger/vendor/ledger-eth.js new file mode 100644 index 000000000..80ff0d0a6 --- /dev/null +++ b/js/src/3rdparty/ledger/vendor/ledger-eth.js @@ -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 */ diff --git a/js/src/3rdparty/ledger/vendor/ledger3.js b/js/src/3rdparty/ledger/vendor/ledger3.js new file mode 100644 index 000000000..9601279ad --- /dev/null +++ b/js/src/3rdparty/ledger/vendor/ledger3.js @@ -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