Skip to content

Commit 17efef2

Browse files
committed
BIP38 encrypt/decrypt WIFs on mainnet/testnet.
1 parent 7e78f65 commit 17efef2

File tree

7 files changed

+183
-5
lines changed

7 files changed

+183
-5
lines changed

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let cmd = require('node-cmd');
2525
// let ProgressBar = require('progress');
2626

2727
program
28-
.version('1.0.2');
28+
.version('1.0.3');
2929

3030
program
3131
.command('new <name>')

lib/BitcoinCash.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ var _coininfo = require('coininfo');
3434

3535
var _coininfo2 = _interopRequireDefault(_coininfo);
3636

37+
var _bip3 = require('bip38');
38+
39+
var _bip4 = _interopRequireDefault(_bip3);
40+
41+
var _wif = require('wif');
42+
43+
var _wif2 = _interopRequireDefault(_wif);
44+
3745
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3846

3947
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -187,6 +195,27 @@ var BitcoinCash = function () {
187195

188196
return Math.ceil(totalWeight / 4);
189197
}
198+
}, {
199+
key: 'encryptBIP38',
200+
value: function encryptBIP38(privKeyWIF, passphrase) {
201+
var decoded = _wif2.default.decode(privKeyWIF);
202+
203+
return _bip4.default.encrypt(decoded.privateKey, decoded.compressed, passphrase);
204+
}
205+
}, {
206+
key: 'decryptBIP38',
207+
value: function decryptBIP38(encryptedKey, passphrase) {
208+
var network = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'mainnet';
209+
210+
var decryptedKey = _bip4.default.decrypt(encryptedKey, passphrase);
211+
var prefix = void 0;
212+
if (network === 'testnet') {
213+
prefix = 0xEF;
214+
} else {
215+
prefix = 0x80;
216+
}
217+
return _wif2.default.encode(prefix, decryptedKey.privateKey, decryptedKey.compressed);
218+
}
190219
}]);
191220

192221
return BitcoinCash;

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "bitbox-cli",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "A development framework for Bitcoin Cash",
55
"author": "@bitboxearth",
66
"main": "index.js",
77
"scripts": {
8-
"test": "nyc mocha --require babel-core/register",
8+
"test": "nyc mocha --timeout 15000 --require babel-core/register",
99
"build": "babel src/ -d lib/",
1010
"flow": "flow"
1111
},
@@ -28,6 +28,7 @@
2828
"bchaddrjs": "^0.2.1",
2929
"bigi": "^1.4.2",
3030
"bip21": "bigearth/bip21",
31+
"bip38": "^2.0.2",
3132
"bip39": "^2.5.0",
3233
"bip68": "^1.0.4",
3334
"bitcoin-ops": "bigearth/bitcoin-ops",
@@ -54,7 +55,8 @@
5455
"repl.history": "^0.1.4",
5556
"safe-buffer": "^5.1.2",
5657
"satoshi-bitcoin": "^1.0.4",
57-
"touch": "^3.1.0"
58+
"touch": "^3.1.0",
59+
"wif": "^2.0.6"
5860
},
5961
"devDependencies": {
6062
"chai": "^4.1.2",

src/BitcoinCash.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import bitcoinMessage from 'bitcoinjs-message';
55
import bs58 from 'bs58';
66
import bip21 from 'bip21';
77
import coininfo from'coininfo';
8+
import bip38 from 'bip38';
9+
import wif from 'wif';
10+
811
let Buffer = require('safe-buffer').Buffer
912

1013
class BitcoinCash {
@@ -117,6 +120,23 @@ class BitcoinCash {
117120

118121
return Math.ceil(totalWeight / 4)
119122
}
123+
124+
encryptBIP38(privKeyWIF, passphrase) {
125+
let decoded = wif.decode(privKeyWIF);
126+
127+
return bip38.encrypt(decoded.privateKey, decoded.compressed, passphrase);
128+
}
129+
130+
decryptBIP38(encryptedKey, passphrase, network = 'mainnet') {
131+
let decryptedKey = bip38.decrypt(encryptedKey, passphrase);
132+
let prefix;
133+
if(network === 'testnet') {
134+
prefix = 0xEF;
135+
} else {
136+
prefix = 0x80;
137+
}
138+
return wif.encode(prefix, decryptedKey.privateKey, decryptedKey.compressed) ;
139+
}
120140
}
121141

122142
export default BitcoinCash;

test/BitcoinCash.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,38 @@ describe('#BitcoinCash', () => {
186186
});
187187
});
188188
});
189+
190+
describe('#bip38', () => {
191+
describe('#encryptBIP38', () => {
192+
fixtures.bip38.encrypt.mainnet.forEach((fixture) => {
193+
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => {
194+
let encryptedKey = BITBOX.BitcoinCash.encryptBIP38(fixture.wif, fixture.password);
195+
assert.equal(encryptedKey, fixture.encryptedKey);
196+
});
197+
});
198+
199+
fixtures.bip38.encrypt.testnet.forEach((fixture) => {
200+
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on testnet`, () => {
201+
let encryptedKey = BITBOX.BitcoinCash.encryptBIP38(fixture.wif, fixture.password);
202+
assert.equal(encryptedKey, fixture.encryptedKey);
203+
});
204+
});
205+
});
206+
207+
describe('#decryptBIP38', () => {
208+
fixtures.bip38.decrypt.mainnet.forEach((fixture) => {
209+
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => {
210+
let wif = BITBOX.BitcoinCash.decryptBIP38(fixture.encryptedKey, fixture.password, 'mainnet');
211+
assert.equal(wif, fixture.wif);
212+
});
213+
});
214+
215+
fixtures.bip38.decrypt.testnet.forEach((fixture) => {
216+
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on testnet`, () => {
217+
let wif = BITBOX.BitcoinCash.decryptBIP38(fixture.encryptedKey, fixture.password, 'testnet');
218+
assert.equal(wif, fixture.wif);
219+
});
220+
});
221+
});
222+
});
189223
});

test/fixtures/BitcoinCash.json

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,5 +348,79 @@
348348
"P2SH":1
349349
}
350350
}
351-
]
351+
],
352+
"bip38": {
353+
"encrypt": {
354+
"mainnet": [
355+
{
356+
"wif": "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR",
357+
"password": "TestingOneTwoThree",
358+
"encryptedKey": "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg"
359+
},
360+
{
361+
"wif": "L1XHKhaBAfkr2FJQn3pTfCMxz652WYfmvKj8xDCHCEDV9tWGcbYj",
362+
"password": "1EBPIyj55eR8bVUov9",
363+
"encryptedKey": "6PYWWnBNfNpSqEJZKcfwbrYgTTdb9PNiGjQJ8r9V6cvsZNKLfcZD8YefQc"
364+
},
365+
{
366+
"wif": "L1phBREbhL4vb1uHHHCAse8bdGE5c7ic2PFjRxMawLzQCsiFVbvu",
367+
"password": "9GKVkabAHBMyAf",
368+
"encryptedKey": "6PYU2fDHRVF2194gKDGkbFbeu4mFgkWtVvg2RPd2Sp6KmZx3RCHFpgBB2G"
369+
}
370+
],
371+
"testnet": [
372+
{
373+
"wif": "cRQeGr3Wv91dUF94zy2EEP7rgwVsqNnZa3MtM7Civxdk74gNEPNn",
374+
"password": "TestingOneTwoThree",
375+
"encryptedKey": "6PYXFe1CdGMuVrLCvTo47JUpqcp7sEK7QxE9m4zHHkzUbgqVXVuTUAJh9T"
376+
},
377+
{
378+
"wif": "cSx7KzdH9EcvDEireu2WYpGnXdFYpta7sJUNt5kVCJgA7kcAU8Gm",
379+
"password": "1EBPIyj55eR8bVUov9",
380+
"encryptedKey": "6PYUAPLwLSEjWSAfoe9NTSPkMZXnJA8j8EFJtKaeSnP18RCouutBrS2735"
381+
},
382+
{
383+
"wif": "cRgunCa2z1gCN6nNapTwKLdo58FdgTeAiJSaXx6RZeWSabQHkQKG",
384+
"password": "9GKVkabAHBMyAf",
385+
"encryptedKey": "6PYTNQJ1dYhLg6X1Xqm62ceuyfxCUYvV4LfhFfzzBaTuV4cFhgS5Xe8t1Y"
386+
}
387+
]
388+
},
389+
"decrypt": {
390+
"mainnet": [
391+
{
392+
"wif": "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR",
393+
"password": "TestingOneTwoThree",
394+
"encryptedKey": "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg"
395+
},
396+
{
397+
"wif": "L1XHKhaBAfkr2FJQn3pTfCMxz652WYfmvKj8xDCHCEDV9tWGcbYj",
398+
"password": "1EBPIyj55eR8bVUov9",
399+
"encryptedKey": "6PYWWnBNfNpSqEJZKcfwbrYgTTdb9PNiGjQJ8r9V6cvsZNKLfcZD8YefQc"
400+
},
401+
{
402+
"wif": "L1phBREbhL4vb1uHHHCAse8bdGE5c7ic2PFjRxMawLzQCsiFVbvu",
403+
"password": "9GKVkabAHBMyAf",
404+
"encryptedKey": "6PYU2fDHRVF2194gKDGkbFbeu4mFgkWtVvg2RPd2Sp6KmZx3RCHFpgBB2G"
405+
}
406+
],
407+
"testnet": [
408+
{
409+
"wif": "cRQeGr3Wv91dUF94zy2EEP7rgwVsqNnZa3MtM7Civxdk74gNEPNn",
410+
"password": "TestingOneTwoThree",
411+
"encryptedKey": "6PYXFe1CdGMuVrLCvTo47JUpqcp7sEK7QxE9m4zHHkzUbgqVXVuTUAJh9T"
412+
},
413+
{
414+
"wif": "cSx7KzdH9EcvDEireu2WYpGnXdFYpta7sJUNt5kVCJgA7kcAU8Gm",
415+
"password": "1EBPIyj55eR8bVUov9",
416+
"encryptedKey": "6PYUAPLwLSEjWSAfoe9NTSPkMZXnJA8j8EFJtKaeSnP18RCouutBrS2735"
417+
},
418+
{
419+
"wif": "cRgunCa2z1gCN6nNapTwKLdo58FdgTeAiJSaXx6RZeWSabQHkQKG",
420+
"password": "9GKVkabAHBMyAf",
421+
"encryptedKey": "6PYTNQJ1dYhLg6X1Xqm62ceuyfxCUYvV4LfhFfzzBaTuV4cFhgS5Xe8t1Y"
422+
}
423+
]
424+
}
425+
}
352426
}

0 commit comments

Comments
 (0)