├── test-wallets ├── classic.wallet ├── classic.aes.wallet ├── mbhd.0.4.1.wallet.aes ├── mbhd.0.5.1.wallet.aes └── classic.key ├── tsconfig.json ├── LICENSE ├── src ├── aes-cbc-decrypter.ts ├── scrypt-encryption-key.ts └── mbexport.ts ├── .gitignore ├── package.json ├── gulpfile.js ├── README.md └── protocol-buffers └── wallet.proto /test-wallets/classic.wallet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidKDeutsch/read-multibit-wallet-file/HEAD/test-wallets/classic.wallet -------------------------------------------------------------------------------- /test-wallets/classic.aes.wallet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidKDeutsch/read-multibit-wallet-file/HEAD/test-wallets/classic.aes.wallet -------------------------------------------------------------------------------- /test-wallets/mbhd.0.4.1.wallet.aes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidKDeutsch/read-multibit-wallet-file/HEAD/test-wallets/mbhd.0.4.1.wallet.aes -------------------------------------------------------------------------------- /test-wallets/mbhd.0.5.1.wallet.aes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidKDeutsch/read-multibit-wallet-file/HEAD/test-wallets/mbhd.0.5.1.wallet.aes -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "lib": ["es6"], 11 | "sourceMap": true, 12 | "pretty": true, 13 | "allowUnreachableCode": false, 14 | "allowUnusedLabels": false, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitUseStrict": false, 18 | "noFallthroughCasesInSwitch": false 19 | } 20 | } -------------------------------------------------------------------------------- /test-wallets/classic.key: -------------------------------------------------------------------------------- 1 | # KEEP YOUR PRIVATE KEYS SAFE ! 2 | # Anyone who can read this file can spend your bitcoin. 3 | # 4 | # Format: 5 | # [[]] 6 | # 7 | # The Base58 encoded private keys are the same format as 8 | # produced by the Satoshi client/ sipa dumpprivkey utility. 9 | # 10 | # Key createdAt is in UTC format as specified by ISO 8601 11 | # e.g: 2011-12-31T16:42:00Z . The century, 'T' and 'Z' are mandatory 12 | # 13 | L5PUQVHfdaHmV8z4u4572ATv2EUiLhZDnMrp5QUBCqiMzJxr5gYL 2017-06-21T19:22:31Z 14 | # End of private keys 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ken Heutmaker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/aes-cbc-decrypter.ts: -------------------------------------------------------------------------------- 1 | import {ScryptEncryptionKey} from "./scrypt-encryption-key"; 2 | import crypto = require('crypto'); 3 | import * as ByteBuffer from "bytebuffer"; 4 | import {Decipher} from "crypto"; 5 | import ScryptParameters = MultibitWallet.ScryptParameters; 6 | 7 | export class Decrypter { 8 | public static factory(passphrase: string, scryptParameters?: ScryptParameters): Decrypter { 9 | let key: ScryptEncryptionKey; 10 | 11 | if (scryptParameters) { 12 | key = new ScryptEncryptionKey(passphrase, (scryptParameters.salt.toBuffer())); 13 | } else { 14 | key = new ScryptEncryptionKey(passphrase); 15 | } 16 | return new Decrypter(key); 17 | } 18 | 19 | private constructor(private key: ScryptEncryptionKey) {} 20 | 21 | public decrypt(data: ByteBuffer, iv: ByteBuffer): Promise { 22 | return this.initialize(iv) 23 | .then((aesCbc: Decipher) => { 24 | let mainBuffer = aesCbc.update(Buffer.from(data.toBuffer())); 25 | let finalBuffer = aesCbc.final(); 26 | let decrypted: Buffer = Buffer.concat([ 27 | mainBuffer, 28 | finalBuffer 29 | ]); 30 | 31 | return ByteBuffer.wrap(decrypted); 32 | }); 33 | } 34 | 35 | private initialize(iv: ByteBuffer): Promise { 36 | return this.key.keyPromise 37 | .then((key: ByteBuffer) => { 38 | return crypto.createDecipheriv('aes-256-cbc', key.toBuffer(), iv.toBuffer()); 39 | }); 40 | } 41 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | ### JetBrains template 41 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 42 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 43 | 44 | # User-specific stuff: 45 | .idea 46 | 47 | ## File-based project format: 48 | *.iws 49 | 50 | ## Plugin-specific files: 51 | 52 | # IntelliJ 53 | /out/ 54 | 55 | # mpeltonen/sbt-idea plugin 56 | .idea_modules/ 57 | 58 | # JIRA plugin 59 | atlassian-ide-plugin.xml 60 | 61 | # Crashlytics plugin (for Android Studio and IntelliJ) 62 | com_crashlytics_export_strings.xml 63 | crashlytics.properties 64 | crashlytics-build.properties 65 | fabric.properties 66 | /typings/ 67 | src/**/*.js 68 | src/**/*.map 69 | -------------------------------------------------------------------------------- /src/scrypt-encryption-key.ts: -------------------------------------------------------------------------------- 1 | import * as ByteBuffer from "bytebuffer"; 2 | var scrypt = require('scrypt-async'); 3 | 4 | const DEFAULT_N = 16384; 5 | const DEFAULT_R = 8; 6 | const DEFAULT_P = 1; 7 | const DEFAULT_DERIVED_KEY_LENGTH = 32; 8 | 9 | const DEFAULT_SALT = new Buffer([ 10 | 0x35, 0x51, 0x03, 0x80, 0x75, 0xa3, 0xb0, 0xc5 11 | ]); 12 | 13 | interface Math { 14 | log2(x: number): number; 15 | } 16 | declare var Math: Math; 17 | 18 | export class ScryptEncryptionKey { 19 | private salt: Buffer; 20 | private n: number; 21 | private r: number; 22 | private p: number; 23 | private derivedKeyLength: number; 24 | 25 | private _passwordBuffer: ByteBuffer; 26 | private get passwordBuffer(): ByteBuffer { 27 | if (!this._passwordBuffer) { 28 | var bytes = []; 29 | 30 | for (var i = 0; i < this.password.length; ++i) { 31 | var charCode = this.password.charCodeAt(i); 32 | bytes.push((charCode & 0xFF00) >> 8); 33 | bytes.push(charCode & 0xFF); 34 | } 35 | this._passwordBuffer = ByteBuffer.wrap(new Uint8Array(bytes)); 36 | } 37 | return this._passwordBuffer; 38 | } 39 | 40 | private _keyPromise: Promise; 41 | public get keyPromise() { 42 | if (!this._keyPromise) { 43 | this._keyPromise = new Promise((resolve, reject) => { 44 | var pw = new Uint8Array(this.passwordBuffer.toBuffer()); 45 | scrypt( 46 | pw, this.salt, 47 | Math.log2(this.n), this.r, this.derivedKeyLength, 48 | (hash: any) => { 49 | resolve(ByteBuffer.wrap(hash)); 50 | } 51 | ); 52 | }); 53 | } 54 | return this._keyPromise; 55 | } 56 | 57 | constructor(private password: string, salt?: Buffer, 58 | n?: number, r?: number, p?: number, 59 | derivedKeyLength?: number) { 60 | this.salt = salt || DEFAULT_SALT; 61 | this.n = n || DEFAULT_N; 62 | this.r = r || DEFAULT_R; 63 | this.p = p || DEFAULT_P; 64 | this.derivedKeyLength = derivedKeyLength || DEFAULT_DERIVED_KEY_LENGTH; 65 | } 66 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mbexport-rd", 3 | "version": "0.0.8", 4 | "description": "Version of mbexport (A utility that exports the private keys or wallet words from Multibit wallet files) with its dependencies locked to the versions that are known to work", 5 | "main": "build/mbexport-rd", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/DavidKDeutsch/read-multibit-wallet-file.git" 9 | }, 10 | "scripts": { 11 | "patch-release": "npm version patch && gulp build && npm publish && git push --follow-tags", 12 | "minor-release": "npm version minor && gulp build && npm publish && git push --follow-tags", 13 | "major-release": "npm version major && gulp build && npm publish && git push --follow-tags" 14 | }, 15 | "author": "David Deutsch (http://reverenddave.com)", 16 | "contributors": [ 17 | "Ken Heutmaker (bgok)" 18 | ], 19 | "license": "MIT", 20 | "bin": { 21 | "mbexport-rd": "build/mbexport-rd" 22 | }, 23 | "homepage": "https://github.com/DavidKDeutsch/read-multibit-wallet-file", 24 | "dependencies": { 25 | "bcoin": "^1.0.0-beta.12", 26 | "bytebuffer": "^5.0.1", 27 | "commander": "^2.9.0", 28 | "prompt": "^1.0.0", 29 | "protobufjs": "^5.0.1", 30 | "scrypt-async": "^1.2.0" 31 | }, 32 | "devDependencies": { 33 | "@types/bytebuffer": "^5.0.33", 34 | "@types/commander": "^2.9.1", 35 | "@types/node": "^7.0.31", 36 | "babel-plugin-transform-runtime": "^6.23.0", 37 | "babel-preset-es2015": "^6.24.1", 38 | "babelify": "^7.3.0", 39 | "del": "^2.2.1", 40 | "gulp": "^3.9.1", 41 | "gulp-chmod": "^2.0.0", 42 | "gulp-if": "^2.0.1", 43 | "gulp-pbjs": "git+https://github.com/keepkey/gulp-pbjs.git", 44 | "gulp-rename": "^1.2.2", 45 | "gulp-replace": "^0.5.4", 46 | "gulp-typescript": "^3.1.7", 47 | "level-js": "^2.2.4", 48 | "proto2typescript": "^2.2.0", 49 | "through2": "^2.0.1", 50 | "typescript": "^2.3.4", 51 | "vinyl-buffer": "^1.0.0", 52 | "vinyl-source-stream": "^1.1.0", 53 | "yargs": "^4.7.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | let args = require('yargs').argv; 2 | let buffer = require('vinyl-buffer'); 3 | let chmod = require('gulp-chmod'); 4 | let del = require('del'); 5 | let gulp = require('gulp'); 6 | let gulpif = require('gulp-if'); 7 | let pbjs = require('gulp-pbjs'); 8 | let source = require('vinyl-source-stream'); 9 | let rename = require('gulp-rename'); 10 | let replace = require('gulp-replace'); 11 | let proto2ts = require('proto2typescript'); 12 | let through = require('through2'); 13 | let ts = require('gulp-typescript'); 14 | 15 | let tsconfig = require('./tsconfig.json').compilerOptions; 16 | 17 | gulp.task('clean', function (cb) { 18 | del([ 19 | 'dist', 20 | '*.zip', 21 | 'build', 22 | 'bin', 23 | 'vendor', 24 | 'node_modules', 25 | 'typings' 26 | ], cb); 27 | }); 28 | 29 | gulp.task('protocolBuffers', function () { 30 | return gulp.src('protocol-buffers/wallet.proto') 31 | .pipe(pbjs()) 32 | .pipe(gulp.dest('build')); 33 | }); 34 | 35 | gulp.task('wallet.json', ['protocolBuffers'], function () { 36 | return gulp.src('build/wallet.js') 37 | .pipe(replace(/^.*\{([\w\W\n\r]+)\}.*$/, '{$1}')) 38 | .pipe(rename('wallet.json')) 39 | .pipe(gulp.dest('build')); 40 | }); 41 | 42 | gulp.task('wallet.d.ts', ['wallet.json'], function (cb) { 43 | return gulp.src('build/wallet.json') 44 | .pipe(through.obj(function (file, enc, cb) { 45 | let protoJson = JSON.parse(file.contents); 46 | protoJson.package = 'MultibitWallet'; 47 | let result = proto2ts(JSON.stringify(protoJson), { 48 | camelCaseGetSet: true, 49 | properties: true, 50 | underscoreGetSet: false 51 | }, function(err, out) { 52 | file.contents = new Buffer(out); 53 | cb(err, file); 54 | }); 55 | })) 56 | .pipe(replace(/delete/g, 'isDelete')) 57 | .pipe(rename('wallet.d.ts')) 58 | .pipe(gulp.dest('build')) 59 | }); 60 | 61 | gulp.task('typescript', ['wallet.d.ts'], function () { 62 | return gulp.src(['src/**/*.ts', '!src/**/*.spec.ts']) 63 | .pipe(ts(tsconfig)) 64 | .pipe(gulp.dest('build')); 65 | }); 66 | 67 | gulp.task('build', ['typescript', 'protocolBuffers'], function() { 68 | return gulp.src('build/mbexport.js') 69 | .pipe(chmod(0o755)) 70 | .pipe(rename('mbexport-rd')) 71 | .pipe(gulp.dest('build')); 72 | }); 73 | 74 | module.exports = gulp; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Preface 2 | ============ 3 | This is a fork of https://github.com/Multibit-Legacy/read-multibit-wallet-file that includes 4 | a fix for the `TypeError: Cannot read property 'fromPrivate' of undefined` error (and maybe a few others) mentioned in [this thread](https://github.com/Multibit-Legacy/read-multibit-wallet-file/pull/1). Although I have a [pull request](https://github.com/Multibit-Legacy/read-multibit-wallet-file/pull/2) pending with this fix, the original project seems to be abandoned. Everything below is from the original project's `readme` except for where I renamed `mbexport` to `mbexport-rd` (as that is the npm package with this fix). 5 | 6 | --- 7 | --- 8 | 9 | A simple command line tool that exports the private Keys from a Multibit 10 | wallet file. The reason this tool exists is because Multibit is out of 11 | date and has known bugs. Exporting the private keys or wallet words from 12 | Multibit and importing them into another wallet may be the only 13 | effective way to access your funds. 14 | 15 | Installation 16 | ============ 17 | 1. Install node version 6 or higher. You can get it from 18 | https://nodejs.org/en/download/. 19 | 2. Open a command prompt and install this utility: 20 | ```npm install -g mbexport-rd``` 21 | 22 | Now you are ready to export the private keys from your wallet. 23 | 24 | 25 | Locating your wallet file 26 | ========================= 27 | 28 | ### Mac OS 29 | 30 | | Version | Wallet file location | 31 | |---|---| 32 | | **Multibit Classic** | `~/Library/Application Support/MultiBit/*.wallet` | 33 | | **MultibitHD** | ~/Library/Application Support/MultiBitHD/``/mbhd.wallet.aes | 34 | 35 | ### Windows 36 | 37 | | Version | Wallet file location | 38 | |---|---| 39 | | **Multibit Classic** | C:\Users\\``\AppData\Roaming\MultiBit\\``.wallet | 40 | | **MultibitHD** | C:\Users\\``\AppData\Roaming\MultiBitHD\\``\mbhd.wallet.aes | 41 | 42 | 43 | `` is a very long, random directory name that acts as a 44 | globally unique identifier for a wallet. It is starts with `mbhd-` 45 | followed by a long string of letters, numbers and dashes. You will have 46 | one directory like this for each wallet. 47 | 48 | `` is the name that you gave the wallet when you created 49 | it. You will have a ```.wallet` file for each wallet. 50 | 51 | `` is your windows user name. 52 | 53 | Once you figure out the name and location of your wallet file, you can 54 | export the keys. 55 | 56 | Exporting Your Keys 57 | =================== 58 | Open a command prompt and type the following command: 59 | 60 | ```mbexport-rd ``` 61 | 62 | For example, if you are using Multibit HD on MacOS, you would type 63 | something like: 64 | 65 | ```mbexport-rd ~/Library/Application\ Support/MultiBitHD/mbhd-aff7bb4a-8a5d9101-e7e97974-f999c7fb-53795c76/mbhd.wallet.aes``` 66 | 67 | TIP: If you can find the wallet file in the file explorer application, 68 | you can type ```mbexport-rd ``` in the command prompt, then drag the file 69 | from explorer to the command prompt. It should fill in the long file 70 | name for you. 71 | 72 | When you run ```mbexport-rd```, it will ask you to enter the passphrase 73 | for your wallet. Once you do will list any private keys and mnemonic 74 | seeds that it finds in the file. If you run ```mbexport-rd``` on a 75 | Multibit Classic file, the output will look simliar to this: 76 | 77 | ``` 78 | multibit classic wallet opened 79 | Enter your passphrase: *** 80 | 81 | L5PUQVHfdaHmV8z4u4572ATv2EUiLhZDnMrp5QUBCqiMzJxr5gYL 82 | ``` 83 | 84 | For Multibit HD, it will look like: 85 | 86 | ``` 87 | MultibitHD wallet opened 88 | Enter your passphrase: *** 89 | 90 | measure swim globe radio reunion awful reflect tail produce treat cluster spot 91 | ``` 92 | 93 | 94 | ```` 95 | This tool and these instructions are distributed under the MIT License 96 | 97 | Copyright (c) 2017 Ken Heutmaker 98 | 99 | Permission is hereby granted, free of charge, to any person obtaining a copy 100 | of this software and associated documentation files (the "Software"), to deal 101 | in the Software without restriction, including without limitation the rights 102 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 103 | copies of the Software, and to permit persons to whom the Software is 104 | furnished to do so, subject to the following conditions: 105 | 106 | The above copyright notice and this permission notice shall be included in all 107 | copies or substantial portions of the Software. 108 | 109 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 110 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 111 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 112 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 113 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 114 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 115 | SOFTWARE. 116 | ```` 117 | -------------------------------------------------------------------------------- /src/mbexport.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /// 4 | 5 | import * as ByteBuffer from "bytebuffer"; 6 | import * as commander from "commander"; 7 | import {Decrypter} from "./aes-cbc-decrypter"; 8 | import fs = require('fs'); 9 | let bcoin = require("bcoin"); 10 | import Type = MultibitWallet.Key.Type; 11 | import ScryptParameters = MultibitWallet.ScryptParameters; 12 | let prompt = require('prompt'); 13 | 14 | const FIXED_IV = ByteBuffer.wrap(new Uint8Array([ 15 | 0xa3, 0x44, 0x39, 0x1f, 0x53, 0x83, 0x11, 0xb3, 16 | 0x29, 0x54, 0x86, 0x16, 0xc4, 0x89, 0x72, 0x3e 17 | ])); 18 | 19 | prompt.message = ''; 20 | prompt.delimiter = ''; 21 | 22 | let Wallet = require('../build/wallet').wallet.Wallet; 23 | 24 | let fileName: string; 25 | 26 | commander 27 | .version(require('../package.json').version) 28 | .arguments('') 29 | .action((file: string) => { 30 | fileName = file; 31 | }); 32 | 33 | commander.parse(process.argv); 34 | 35 | if (!fileName) { 36 | console.log('A wallet file must be specified'); 37 | commander.help(); 38 | } 39 | 40 | let data: Buffer; 41 | try { 42 | data = fs.readFileSync(fileName); 43 | } catch (e) { 44 | console.log('Error opening wallet file'); 45 | process.exit(e.errno); 46 | } 47 | 48 | let pb = ByteBuffer.wrap(data); 49 | let walletPromise: Promise; 50 | 51 | try { 52 | walletPromise = Promise.resolve(Wallet.decode(pb)) 53 | .then((wallet) => { 54 | console.log('multibit classic wallet opened'); 55 | return wallet; 56 | }); 57 | } catch (e) { 58 | console.log('MultibitHD wallet opened'); 59 | 60 | let decrypter: Decrypter; 61 | 62 | walletPromise = getPassphrase() 63 | .then((passphrase: string) => { 64 | decrypter = Decrypter 65 | .factory(passphrase); 66 | 67 | pb.reset(); 68 | return decrypter.decrypt(pb.slice(16), pb.slice(0,16)) 69 | .then((payload) => { 70 | return Wallet.decode(payload); 71 | }); 72 | }) 73 | .catch((e) => { 74 | pb.reset(); 75 | return decrypter.decrypt(pb, FIXED_IV) 76 | .then((payload) => { 77 | return Wallet.decode(payload); 78 | }); 79 | }); 80 | } 81 | 82 | walletPromise.then((wallet: MultibitWallet.Wallet) => { 83 | console.assert(wallet.getKey().length !== 0, 'One or more keys should exist'); 84 | 85 | let keys: Array = wallet.getKey(); 86 | let keyPromises: Array> = []; 87 | let encryptionParameters = wallet.getEncryptionParameters(); 88 | 89 | keys.forEach(function (key) { 90 | switch (key.getType()) { 91 | case Type.ORIGINAL: 92 | let secretBytes = key.getSecretBytes(); 93 | console.assert(secretBytes, 'Secret bytes are not defined'); 94 | 95 | let keyRing = bcoin.keyring.fromPrivate(secretBytes.toBuffer(), 'main'); 96 | keyPromises.push(Promise.resolve(keyRing.toSecret())); 97 | break; 98 | 99 | case Type.ENCRYPTED_SCRYPT_AES: 100 | console.assert(encryptionParameters, 'Encryption parameters undefined'); 101 | keyPromises.push(readEncryptedKey(key, encryptionParameters, (secretBytes) => { 102 | return bcoin.keyring.fromPrivate(secretBytes.toBuffer(), 'main').toSecret(); 103 | })); 104 | break; 105 | 106 | case Type.DETERMINISTIC_MNEMONIC: 107 | console.assert(encryptionParameters, 'Encryption parameters undefined'); 108 | keyPromises.push(readEncryptedKey(key, encryptionParameters, (secretBytes) => { 109 | return secretBytes.toString('utf8'); 110 | })); 111 | break; 112 | 113 | case Type.DETERMINISTIC_KEY: 114 | break; 115 | 116 | default: 117 | throw 'Unknown key type'; 118 | } 119 | }); 120 | 121 | Promise.all(keyPromises) 122 | .then((keys: Array) => { 123 | keys.forEach((key) => { 124 | console.log(key); 125 | }); 126 | }); 127 | }); 128 | 129 | function readEncryptedKey(key: MultibitWallet.Key, 130 | encryptionParameters: ScryptParameters, 131 | formatter: (bytes: ByteBuffer) => string): Promise { 132 | let encryptedKey = key.getEncryptedData(); 133 | console.assert(encryptedKey, 'Encrypted key is not defined'); 134 | 135 | return getPassphrase() 136 | .then((passphrase: string) => { 137 | return Decrypter 138 | .factory(passphrase, encryptionParameters) 139 | .decrypt(encryptedKey.getEncryptedPrivateKey(), encryptedKey.getInitialisationVector()); 140 | }) 141 | .then(formatter); 142 | } 143 | 144 | let passphrase: string; 145 | 146 | function getPassphrase(): Promise { 147 | if (!passphrase) { 148 | return new Promise((resolve, reject) => { 149 | prompt.get([{ 150 | name : 'passphrase', 151 | description: 'Enter your passphrase:', 152 | replace : '*', 153 | hidden : true 154 | }], (err: any, result: any) => { 155 | if (err) { 156 | reject(err); 157 | } else { 158 | passphrase = result.passphrase; 159 | resolve(passphrase); 160 | } 161 | }); 162 | }); 163 | } else { 164 | return Promise.resolve(passphrase); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /protocol-buffers/wallet.proto: -------------------------------------------------------------------------------- 1 | /** Copyright 2013 Google Inc. 2 | * Copyright 2014 Andreas Schildbach 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * Authors: Jim Burton, Miron Cuperman, Andreas Schildbach 19 | */ 20 | 21 | /* Notes: 22 | * - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian 23 | * - To regenerate after editing, run: mvn generate-sources -DupdateProtobuf 24 | */ 25 | 26 | package wallet; 27 | 28 | option java_package = "org.bitcoinj.wallet"; 29 | option java_outer_classname = "Protos"; 30 | 31 | message PeerAddress { 32 | required bytes ip_address = 1; 33 | required uint32 port = 2; 34 | required uint64 services = 3; 35 | } 36 | 37 | message EncryptedData { 38 | required bytes initialisation_vector = 1; // The initialisation vector for the AES encryption (16 bytes) 39 | required bytes encrypted_private_key = 2; // The encrypted private key 40 | } 41 | 42 | /** 43 | * Data attached to a Key message that defines the data needed by the BIP32 deterministic key hierarchy algorithm. 44 | */ 45 | message DeterministicKey { 46 | // Random data that allows us to extend a key. Without this, we can't figure out the next key in the chain and 47 | // should just treat it as a regular ORIGINAL type key. 48 | required bytes chain_code = 1; 49 | 50 | // The path through the key tree. Each number is encoded in the standard form: high bit set for private derivation 51 | // and high bit unset for public derivation. 52 | repeated uint32 path = 2; 53 | 54 | // How many children of this key have been issued, that is, given to the user when they requested a fresh key? 55 | // For the parents of keys being handed out, this is always less than the true number of children: the difference is 56 | // called the lookahead zone. These keys are put into Bloom filters so we can spot transactions made by clones of 57 | // this wallet - for instance when restoring from backup or if the seed was shared between devices. 58 | // 59 | // If this field is missing it means we're not issuing subkeys of this key to users. 60 | optional uint32 issued_subkeys = 3; 61 | optional uint32 lookahead_size = 4; 62 | 63 | /** 64 | * Flag indicating that this key is a root of a following chain. This chain is following the next non-following chain. 65 | * Following/followed chains concept is used for married keychains, where the set of keys combined together to produce 66 | * a single P2SH multisignature address 67 | */ 68 | optional bool isFollowing = 5; 69 | 70 | // Number of signatures required to spend. This field is needed only for married keychains to reconstruct KeyChain 71 | // and represents the N value from N-of-M CHECKMULTISIG script. For regular single keychains it will always be 1. 72 | optional uint32 sigsRequiredToSpend = 6 [default = 1]; 73 | } 74 | 75 | /** 76 | * A key used to control Bitcoin spending. 77 | * 78 | * Either the private key, the public key or both may be present. It is recommended that 79 | * if the private key is provided that the public key is provided too because deriving it is slow. 80 | * 81 | * If only the public key is provided, the key can only be used to watch the blockchain and verify 82 | * transactions, and not for spending. 83 | */ 84 | message Key { 85 | enum Type { 86 | /** Unencrypted - Original bitcoin secp256k1 curve */ 87 | ORIGINAL = 1; 88 | 89 | /** Encrypted with Scrypt and AES - Original bitcoin secp256k1 curve */ 90 | ENCRYPTED_SCRYPT_AES = 2; 91 | 92 | /** 93 | * Not really a key, but rather contains the mnemonic phrase for a deterministic key hierarchy in the private_key field. 94 | * The label and public_key fields are missing. Creation timestamp will exist. 95 | */ 96 | DETERMINISTIC_MNEMONIC = 3; 97 | 98 | /** 99 | * A key that was derived deterministically. Note that the root seed that created it may NOT be present in the 100 | * wallet, for the case of watching wallets. A deterministic key may or may not have the private key bytes present. 101 | * However the public key bytes and the deterministic_key field are guaranteed to exist. In a wallet where there 102 | * is a path from this key up to a key that has (possibly encrypted) private bytes, it's expected that the private 103 | * key can be rederived on the fly. 104 | */ 105 | DETERMINISTIC_KEY = 4; 106 | } 107 | required Type type = 1; 108 | 109 | // Either the private EC key bytes (without any ASN.1 wrapping), or the deterministic root seed. 110 | // If the secret is encrypted, or this is a "watching entry" then this is missing. 111 | optional bytes secret_bytes = 2; 112 | 113 | // If the secret data is encrypted, then secret_bytes is missing and this field is set. 114 | optional EncryptedData encrypted_data = 6; 115 | 116 | // The public EC key derived from the private key. We allow both to be stored to avoid mobile clients having to 117 | // do lots of slow EC math on startup. For DETERMINISTIC_MNEMONIC entries this is missing. 118 | optional bytes public_key = 3; 119 | 120 | // User-provided label associated with the key. 121 | optional string label = 4; 122 | 123 | // Timestamp stored as millis since epoch. Useful for skipping block bodies before this point. Only reason it's 124 | // optional is that some very old wallets don't have this data. 125 | optional int64 creation_timestamp = 5; 126 | 127 | optional DeterministicKey deterministic_key = 7; 128 | 129 | // The seed for a deterministic key hierarchy. Derived from the mnemonic, 130 | // but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry. 131 | optional bytes deterministic_seed = 8; 132 | 133 | // Encrypted version of the seed 134 | optional EncryptedData encrypted_deterministic_seed = 9; 135 | } 136 | 137 | message Script { 138 | required bytes program = 1; 139 | 140 | // Timestamp stored as millis since epoch. Useful for skipping block bodies before this point 141 | // when watching for scripts on the blockchain. 142 | required int64 creation_timestamp = 2; 143 | } 144 | 145 | message TransactionInput { 146 | // Hash of the transaction this input is using. 147 | required bytes transaction_out_point_hash = 1; 148 | // Index of transaction output used by this input. 149 | required uint32 transaction_out_point_index = 2; 150 | // Script that contains the signatures/pubkeys. 151 | required bytes script_bytes = 3; 152 | // Sequence number. Currently unused, but intended for contracts in future. 153 | optional uint32 sequence = 4; 154 | // Value of connected output, if known 155 | optional int64 value = 5; 156 | } 157 | 158 | message TransactionOutput { 159 | required int64 value = 1; 160 | required bytes script_bytes = 2; // script of transaction output 161 | // If spent, the hash of the transaction doing the spend. 162 | optional bytes spent_by_transaction_hash = 3; 163 | // If spent, the index of the transaction input of the transaction doing the spend. 164 | optional int32 spent_by_transaction_index = 4; 165 | } 166 | 167 | /** 168 | * A description of the confidence we have that a transaction cannot be reversed in the future. 169 | * 170 | * Parsing should be lenient, since this could change for different applications yet we should 171 | * maintain backward compatibility. 172 | */ 173 | message TransactionConfidence { 174 | enum Type { 175 | UNKNOWN = 0; 176 | BUILDING = 1; // In best chain. If and only if appeared_at_height is present. 177 | PENDING = 2; // Unconfirmed and sitting in the networks memory pools, waiting to be included in the chain. 178 | NOT_IN_BEST_CHAIN = 3; // Deprecated: equivalent to PENDING. 179 | DEAD = 4; // Either if overriding_transaction is present or transaction is dead coinbase 180 | } 181 | 182 | // This is optional in case we add confidence types to prevent parse errors - backwards compatible. 183 | optional Type type = 1; 184 | 185 | // If type == BUILDING then this is the chain height at which the transaction was included. 186 | optional int32 appeared_at_height = 2; 187 | 188 | // If set, hash of the transaction that double spent this one into oblivion. A transaction can be double spent by 189 | // multiple transactions in the case of several inputs being re-spent by several transactions but we don't 190 | // bother to track them all, just the first. This only makes sense if type = DEAD. 191 | optional bytes overriding_transaction = 3; 192 | 193 | // If type == BUILDING then this is the depth of the transaction in the blockchain. 194 | // Zero confirmations: depth = 0, one confirmation: depth = 1 etc. 195 | optional int32 depth = 4; 196 | 197 | // deprecated - do not recycle this numeric identifier 198 | // optional int64 work_done = 5; 199 | 200 | repeated PeerAddress broadcast_by = 6; 201 | 202 | // Where did we get this transaction from? Knowing the source may help us to risk analyze pending transactions. 203 | enum Source { 204 | SOURCE_UNKNOWN = 0; // We don't know where it came from, or this is a wallet from the future. 205 | SOURCE_NETWORK = 1; // We received it from a network broadcast. This is the normal way to get payments. 206 | SOURCE_SELF = 2; // We made it ourselves, so we know it should be valid. 207 | // In future: 208 | // - direct from trusted counterparty, eg via bluetooth/wifi direct 209 | // - direct from untrusted counterparty 210 | // - from a wallet that uses trusted computing/secure hardware that won't create double spends 211 | } 212 | optional Source source = 7; 213 | } 214 | 215 | /** A bitcoin transaction */ 216 | 217 | message Transaction { 218 | /** 219 | * This is a bitfield oriented enum, with the following bits: 220 | * 221 | * bit 0 - spent 222 | * bit 1 - appears in alt chain 223 | * bit 2 - appears in best chain 224 | * bit 3 - double-spent 225 | * bit 4 - pending (we would like the tx to go into the best chain) 226 | * 227 | * Not all combinations are interesting, just the ones actually used in the enum. 228 | */ 229 | enum Pool { 230 | UNSPENT = 4; // In best chain, not all outputs spent 231 | SPENT = 5; // In best chain, all outputs spent 232 | INACTIVE = 2; // In non-best chain, not our transaction 233 | DEAD = 10; // Double-spent by a transaction in the best chain 234 | PENDING = 16; // Our transaction, not in any chain 235 | PENDING_INACTIVE = 18; // In non-best chain, our transaction 236 | } 237 | 238 | // See Wallet.java for detailed description of pool semantics 239 | required int32 version = 1; 240 | required bytes hash = 2; 241 | 242 | // If pool is not present, that means either: 243 | // - This Transaction is either not in a wallet at all (the proto is re-used elsewhere) 244 | // - Or it is stored but for other purposes, for example, because it is the overriding transaction of a double spend. 245 | // - Or the Pool enum got a new value which your software is too old to parse. 246 | optional Pool pool = 3; 247 | 248 | optional uint32 lock_time = 4; // The nLockTime field is useful for contracts. 249 | optional int64 updated_at = 5; // millis since epoch the transaction was last updated 250 | 251 | repeated TransactionInput transaction_input = 6; 252 | repeated TransactionOutput transaction_output = 7; 253 | 254 | // A list of blocks in which the transaction has been observed (on any chain). Also, a number used to disambiguate 255 | // ordering within a block. 256 | repeated bytes block_hash = 8; 257 | repeated int32 block_relativity_offsets = 11; 258 | 259 | // Data describing where the transaction is in the chain. 260 | optional TransactionConfidence confidence = 9; 261 | 262 | // For what purpose the transaction was created. 263 | enum Purpose { 264 | // Old wallets or the purpose genuinely is a mystery (e.g. imported from some external source). 265 | UNKNOWN = 0; 266 | // Created in response to a user request for payment. This is the normal case. 267 | USER_PAYMENT = 1; 268 | // Created automatically to move money from rotated keys. 269 | KEY_ROTATION = 2; 270 | // Stuff used by Lighthouse. 271 | ASSURANCE_CONTRACT_CLAIM = 3; 272 | ASSURANCE_CONTRACT_PLEDGE = 4; 273 | ASSURANCE_CONTRACT_STUB = 5; 274 | // In future: de/refragmentation, privacy boosting/mixing, child-pays-for-parent fees, etc. 275 | } 276 | optional Purpose purpose = 10 [default = UNKNOWN]; 277 | 278 | // Exchange rate that was valid when the transaction was sent. 279 | optional ExchangeRate exchange_rate = 12; 280 | 281 | // Memo of the transaction. It can be used to record the memo of the payment request that initiated the 282 | // transaction. 283 | optional string memo = 13; 284 | 285 | // Next tag: 14 286 | } 287 | 288 | /** The parameters used in the scrypt key derivation function. 289 | * The default values are taken from http://www.tarsnap.com/scrypt/scrypt-slides.pdf. 290 | * They can be increased - n is the number of iterations performed and 291 | * r and p can be used to tweak the algorithm - see: 292 | * http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors 293 | */ 294 | message ScryptParameters { 295 | required bytes salt = 1; // Salt to use in generation of the wallet password (8 bytes) 296 | optional int64 n = 2 [default = 16384]; // CPU/ memory cost parameter 297 | optional int32 r = 3 [default = 8]; // Block size parameter 298 | optional int32 p = 4 [default = 1]; // Parallelisation parameter 299 | } 300 | 301 | /** An extension to the wallet */ 302 | message Extension { 303 | required string id = 1; // like org.whatever.foo.bar 304 | required bytes data = 2; 305 | // If we do not understand a mandatory extension, abort to prevent data loss. 306 | // For example, this could be applied to a new type of holding, such as a contract, where 307 | // dropping of an extension in a read/write cycle could cause loss of value. 308 | required bool mandatory = 3; 309 | } 310 | 311 | /** 312 | * A simple key->value mapping that has no interpreted content at all. A bit like the extensions mechanism except 313 | * an extension is keyed by the ID of a piece of code that's loaded with the given data, and has the concept of 314 | * being mandatory if that code isn't found. Whereas this is just a blind key/value store. 315 | */ 316 | message Tag { 317 | required string tag = 1; 318 | required bytes data = 2; 319 | } 320 | 321 | /** 322 | * Data required to reconstruct TransactionSigner. 323 | */ 324 | message TransactionSigner { 325 | // fully qualified class name of TransactionSigner implementation 326 | required string class_name = 1; 327 | // arbitrary data required for signer to function 328 | optional bytes data = 2; 329 | } 330 | 331 | /** A bitcoin wallet */ 332 | message Wallet { 333 | /** 334 | * The encryption type of the wallet. 335 | * 336 | * The encryption type is UNENCRYPTED for wallets where the wallet does not support encryption - wallets prior to 337 | * encryption support are grandfathered in as this wallet type. 338 | * When a wallet is ENCRYPTED_SCRYPT_AES the keys are either encrypted with the wallet password or are unencrypted. 339 | */ 340 | enum EncryptionType { 341 | UNENCRYPTED = 1; // All keys in the wallet are unencrypted 342 | ENCRYPTED_SCRYPT_AES = 2; // All keys are encrypted with a passphrase based KDF of scrypt and AES encryption 343 | } 344 | 345 | required string network_identifier = 1; // the network used by this wallet 346 | // org.bitcoin.production = main, production network (Satoshi genesis block) 347 | // org.bitcoin.test = test network (Andresen genesis block) 348 | 349 | // The SHA256 hash of the head of the best chain seen by this wallet. 350 | optional bytes last_seen_block_hash = 2; 351 | // The height in the chain of the last seen block. 352 | optional uint32 last_seen_block_height = 12; 353 | optional int64 last_seen_block_time_secs = 14; 354 | 355 | repeated Key key = 3; 356 | repeated Transaction transaction = 4; 357 | repeated Script watched_script = 15; 358 | 359 | optional EncryptionType encryption_type = 5 [default=UNENCRYPTED]; 360 | optional ScryptParameters encryption_parameters = 6; 361 | 362 | // The version number of the wallet - used to detect wallets that were produced in the future 363 | // (i.e. the wallet may contain some future format this protobuf or parser code does not know about). 364 | // A version that's higher than the default is considered from the future. 365 | optional int32 version = 7 [default = 1]; 366 | 367 | // deprecated - do not recycle this numeric identifier 368 | // optional int32 minor_version = 8; 369 | 370 | repeated Extension extension = 10; 371 | 372 | // A UTF8 encoded text description of the wallet that is intended for end user provided text. 373 | optional string description = 11; 374 | 375 | // (The field number 12 is used by last_seen_block_height) 376 | 377 | // UNIX time in seconds since the epoch. If set, then any keys created before this date are assumed to be no longer 378 | // wanted. Money sent to them will be re-spent automatically to the first key that was created after this time. It 379 | // can be used to recover a compromised wallet, or just as part of preventative defence-in-depth measures. 380 | optional uint64 key_rotation_time = 13; 381 | 382 | repeated Tag tags = 16; 383 | 384 | // transaction signers added to the wallet 385 | repeated TransactionSigner transaction_signers = 17; 386 | 387 | // Next tag: 18 388 | } 389 | 390 | /** An exchange rate between Bitcoin and some fiat currency. */ 391 | message ExchangeRate { 392 | // This much of satoshis (1E-8 fractions)… 393 | required int64 coin_value = 1; 394 | // …is worth this much of fiat (1E-4 fractions). 395 | required int64 fiat_value = 2; 396 | // ISO 4217 currency code (if available) of the fiat currency. 397 | required string fiat_currency_code = 3; 398 | 399 | // Next tag: 4 400 | } 401 | --------------------------------------------------------------------------------