diff --git a/README.md b/README.md index f2a3aef..85bfd8a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ paste sensitive information into. So consider yourself warned. * [Transaction Creator page](https://guggero.github.io/cryptography-toolkit/#!/transaction-creator) * [aezeed Cipher Seed Scheme page](https://guggero.github.io/cryptography-toolkit/#!/aezeed) * [Macaroons page](https://guggero.github.io/cryptography-toolkit/#!/macaroon) +* [Wallet Import helper page](https://guggero.github.io/cryptography-toolkit/#!/wallet-import) ## Send Thanks diff --git a/app.html b/app.html index 80ce2f2..198c4f7 100644 --- a/app.html +++ b/app.html @@ -31,6 +31,9 @@
  • Transaction Creator
  • +
  • + Wallet Import helper +
  • Transaction Creator page
  • aezeed Cipher Seed Scheme page
  • Macaroons page
  • +
  • Wallet Import helper page
  • diff --git a/pages/wallet-import/wallet-import.html b/pages/wallet-import/wallet-import.html new file mode 100644 index 0000000..f966f9c --- /dev/null +++ b/pages/wallet-import/wallet-import.html @@ -0,0 +1,110 @@ +

    Import HD wallet into Bitcoin Core

    + +
    +
    +

    + Explanation +

    +
    +
    +
    + Currently, there is no easy way to import addresses from a HD seed that has been created by another software into Bitcoin Core.
    + This tool helps you do that. +
    +
    +
    + +

    Import HD wallet

    +
    +
    + + +
    + +
    + + <-- paste here to import. +
    +
    + + +
    + +
    + + + + +
    Method
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    +
    Import type
    + +
    Start Path
    + +
    +
    +
    Change (_chg_): Start value
    + +
    End value
    + +
    +
    +
    Index (_idx_): Start value
    + +
    End value
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + +
    +
    +
    +
    + diff --git a/pages/wallet-import/wallet-import.js b/pages/wallet-import/wallet-import.js new file mode 100644 index 0000000..882e566 --- /dev/null +++ b/pages/wallet-import/wallet-import.js @@ -0,0 +1,230 @@ +angular + .module('app') + .component('walletImportPage', { + templateUrl: 'pages/wallet-import/wallet-import.html', + controller: WalletImportPageController, + controllerAs: 'vm', + bindings: {} + }); + +function WalletImportPageController(lodash, bitcoinNetworks) { + const vm = this; + + const PBKDF2_SALT = 'Digital Bitbox', + PBKDF2_HMACLEN = 64, + PBKDF2_ROUNDS_APP = 20480; + const METHOD_NONE = 0, + METHOD_PBKDF2 = 1, + METHOD_COINOMI = 2; + const BITCOIN = lodash.find(bitcoinNetworks, ['label', 'BTC (Bitcoin)']); + const BITCOIN_TESTNET = lodash.find(bitcoinNetworks, ['label', 'BTC (Bitcoin Testnet)']); + const SCHEMES = [ + { + label: "Bitcoin xprv (P2PKH/P2SH, m/44'/0')", + id: 'xprv', + prv: 0x0488ade4, + pub: 0x0488b21e, + path: "m/44'/0'/0'/_chg_/_idx_", + config: BITCOIN.config + }, + { + label: "Bitcoin yprv (P2WPKH in P2SH, m/49'/0')", + id: 'yprv', + prv: 0x049d7878, + pub: 0x049d7cb2, + path: "m/49'/0'/0'/_chg_/_idx_", + config: BITCOIN.config + }, + { + label: "Bitcoin zprv (P2WPKH, m/84'/0')", + id: 'zprv', + prv: 0x04b2430c, + pub: 0x04b24746, + path: "m/84'/0'/0'/_chg_/_idx_", + config: BITCOIN.config + }, + { + label: "Bitcoin Testnet tprv (P2PKH/P2SH, m/44'/1')", + id: 'tprv', + prv: 0x04358394, + pub: 0x043587cf, + path: "m/44'/1'/0'/_chg_/_idx_", + config: BITCOIN_TESTNET.config + }, + { + label: "Bitcoin Testnet uprv (P2WPKH in P2SH, m/49'/1')", + id: 'uprv', + prv: 0x044a4e28, + pub: 0x044a5262, + path: "m/49'/1'/0'/_chg_/_idx_", + config: BITCOIN_TESTNET.config + }, + { + label: "Bitcoin Testnet vprv (P2WPKH, m/84'/1')", + id: 'vprv', + prv: 0x045f18bc, + pub: 0x045f1cf6, + path: "m/84'/1'/0'/_chg_/_idx_", + config: BITCOIN_TESTNET.config + }, + ]; + const TYPES = [ + {label: 'Wallet Dump format (importwallet)', id: 'dump'}, + {label: 'bitcoin-cli importprivkey', id: 'importprivkey'}, + {label: 'bitcoin-cli importpubkey', id: 'importpubkey'}, + ]; + + vm.schemes = SCHEMES; + vm.scheme = SCHEMES[0]; + vm.importTypes = TYPES; + vm.importType = TYPES[0]; + vm.mnemonic = null; + vm.asPassword = true; + vm.passphrase = null; + vm.seed = null; + vm.seedHex = null; + vm.node = null; + vm.nodeBase58 = null; + vm.changeStart = 0; + vm.changeEnd = 1; + vm.indexStart = 0; + vm.indexEnd = 50; + vm.path = ''; + vm.strenghteningMethods = [ + {label: 'BIP39 default (like Coinomi)', id: METHOD_COINOMI}, + {label: 'BIP39 custom (passhprase to hex)', id: METHOD_NONE}, + {label: 'PBKDF2 (Digital Bitbox)', id: METHOD_PBKDF2} + + ]; + vm.strenghtening = vm.strenghteningMethods[0]; + vm.result = ''; + + vm.$onInit = function () { + vm.mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + vm.fromMnemonic(); + }; + + vm.fromMnemonic = function () { + var pw = null; + if (vm.passphrase) { + if (vm.strenghtening.id === METHOD_PBKDF2) { + pw = bitcoin.pbkdf2.pbkdf2Sync( + bitcoin.Buffer.from(vm.passphrase, 'utf8'), + PBKDF2_SALT, + PBKDF2_ROUNDS_APP, + PBKDF2_HMACLEN, + 'sha512' + ).toString('hex'); + } else if (vm.strenghtening.id === METHOD_COINOMI) { + pw = vm.passphrase; + } else { + pw = bitcoin.Buffer.from(vm.passphrase, 'utf8').toString('hex'); + } + } + vm.seed = bitcoin.bip39.mnemonicToSeed(vm.mnemonic, pw); + vm.fromSeed(); + }; + + vm.fromSeed = function () { + if (vm.seed) { + vm.seedHex = vm.seed.toString('hex'); + + vm.node = bitcoin.HDNode.fromSeedBuffer(vm.seed, vm.getConfig()); + vm.nodeBase58 = vm.node.toBase58(); + } + vm.path = vm.scheme.path; + }; + + vm.getConfig = function () { + return angular.extend({}, vm.scheme.config, {bip32: {public: vm.scheme.pub, private: vm.scheme.prv}}); + }; + + vm.getPath = function (change, index) { + let path = vm.path; + path = path.replace(/\/_chg_/, change === null ? '' : '/' + change); + return path.replace(/\/_idx_/, index === null ? '' : '/' + index); + }; + + vm.createExport = function () { + const basePath = vm.path.substring(0, vm.path.indexOf('_') - 1); + vm.baseKey = vm.node.derivePath(basePath); + vm.result = vm['getResultAs' + lodash.capitalize(vm.importType.id)](vm.node, basePath, vm.getConfig()); + }; + + vm.getResultAsDump = function (rootNode, basePath, network) { + const date = new Date().toISOString(); + const baseKey = rootNode.derivePath(basePath); + let str = `# Wallet dump created by cryptography toolkit +# * Created on ${date} + +# extended private masterkey: ${baseKey.toBase58()} + +# You might need to replace the timestamp for every address you see with the +# timestamp when the wallet was originally created for the funds to be registered +# with Bitcoin Core or do a full wallet rescan after importing this dump. +`; + for (let change = vm.changeStart; change <= vm.changeEnd; change++) { + const changePath = `${change}${vm.path.indexOf('_chg_\'') >= 0 ? '\'' : ''}`; + const changeKey = baseKey.derivePath(changePath); + for (let index = vm.indexStart; index <= vm.indexEnd; index++) { + const indexPath = `${index}${vm.path.indexOf('_idx_\'') >= 0 ? '\'' : ''}`; + const key = changeKey.derivePath(indexPath); + const addr = vm.getAddress(key.keyPair, network); + str += `${key.keyPair.toWIF()} ${date} reserve=0 # addr=${addr} hdkeypath=${basePath}/${changePath}/${indexPath}\n`; + } + } + return str; + }; + + vm.getResultAsImportprivkey = function (rootNode, basePath, network) { + const date = new Date().toISOString(); + const baseKey = rootNode.derivePath(basePath); + let str = `# Paste the following lines into a command line window. +# You might want to adjust the block number to rescan from at the bottom of the +# file if the wallet was originally created before 2017-12-18 18:35:25. +`; + for (let change = vm.changeStart; change <= vm.changeEnd; change++) { + const changePath = `${change}${vm.path.indexOf('_chg_\'') >= 0 ? '\'' : ''}`; + const changeKey = baseKey.derivePath(changePath); + for (let index = vm.indexStart; index <= vm.indexEnd; index++) { + const indexPath = `${index}${vm.path.indexOf('_idx_\'') >= 0 ? '\'' : ''}`; + const key = changeKey.derivePath(indexPath); + const addr = vm.getAddress(key.keyPair, network); + str += `bitcoin-cli importprivkey ${key.keyPair.toWIF()} "${basePath}/${changePath}/${indexPath}" false\n`; + } + } + str += 'bitcoin-cli rescanblockchain 500000\n'; + return str; + }; + + vm.getResultAsImportpubkey = function (rootNode, basePath, network) { + const date = new Date().toISOString(); + const baseKey = rootNode.derivePath(basePath); + let str = `# Paste the following lines into a command line window. +# You might want to adjust the block number to rescan from at the bottom of the +# file if the wallet was originally created before 2017-12-18 18:35:25. +`; + for (let change = vm.changeStart; change <= vm.changeEnd; change++) { + const changePath = `${change}${vm.path.indexOf('_chg_\'') >= 0 ? '\'' : ''}`; + const changeKey = baseKey.derivePath(changePath); + for (let index = vm.indexStart; index <= vm.indexEnd; index++) { + const indexPath = `${index}${vm.path.indexOf('_idx_\'') >= 0 ? '\'' : ''}`; + const key = changeKey.derivePath(indexPath); + const addr = vm.getAddress(key.keyPair, network); + str += `bitcoin-cli importpubkey ${key.keyPair.getPublicKeyBuffer().toString('hex')} "${basePath}/${changePath}/${indexPath}" false\n`; + } + } + str += 'bitcoin-cli rescanblockchain 500000\n'; + return str; + }; + + vm.getAddress = function (keyPair, network) { + if (vm.scheme.id === 'xprv' || vm.scheme.id === 'tprv') { + return keyPair.getAddress(); + } else if (vm.scheme.id === 'yprv' || vm.scheme.id === 'uprv') { + return getNestedP2WPKHAddress(keyPair, network); + } else { + return getP2WPKHAddress(keyPair, network); + } + } +}