Skip to content

Commit 68f9083

Browse files
author
Arik Sosman
committed
allowing randomized derivation paths for user-provided keys
Reviewers: andrew, robert Reviewed By: robert Subscribers: ben Differential Revision: https://phabricator.bitgo.com/D6073
1 parent 0a6b6ad commit 68f9083

File tree

5 files changed

+57
-2
lines changed

5 files changed

+57
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bitgo",
3-
"version": "3.4.9",
3+
"version": "3.4.10",
44
"description": "BitGo Javascript SDK",
55
"main": "./src/index.js",
66
"keywords": [

src/v2/baseCoin.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ BaseCoin.prototype.toJSON = function() {
105105
return undefined;
106106
};
107107

108+
BaseCoin.prototype.deriveKeyWithSeed = function({ key, seed }) {
109+
const derivationPathInput = bitcoin.crypto.hash256(`${seed}`).toString('hex');
110+
const derivationPathParts = [
111+
parseInt(derivationPathInput.slice(0, 7), 16),
112+
parseInt(derivationPathInput.slice(7, 14), 16)
113+
];
114+
const derivationPath = 'm/999999/' + derivationPathParts.join('/');
115+
const keyNode = bitcoin.HDNode.fromBase58(key);
116+
const derivedKeyNode = bitcoin.hdPath(keyNode).derive(derivationPath);
117+
return {
118+
key: derivedKeyNode.toBase58(),
119+
derivationPath: derivationPath
120+
};
121+
};
122+
108123
BaseCoin.prototype.initiateRecovery = function(params) {
109124
const keys = [];
110125
const userKey = params.userKey; // Box A

src/v2/wallet.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,14 @@ Wallet.prototype.signTransaction = function(params, callback) {
522522
if (!txPrebuild || typeof txPrebuild !== 'object') {
523523
throw new Error('txPrebuild must be an object');
524524
}
525-
var userPrv = params.prv;
525+
let userPrv = params.prv;
526526
if (userPrv && typeof userPrv !== 'string') {
527527
throw new Error('prv must be a string');
528+
}
529+
if (userPrv && params.coldDerivationSeed) {
530+
// the derivation only makes sense when a key already exists
531+
const derivation = this.baseCoin.deriveKeyWithSeed({ key: userPrv, seed: params.coldDerivationSeed });
532+
userPrv = derivation.key;
528533
} else if (!userPrv) {
529534
if (!userKeychain || typeof userKeychain !== 'object') {
530535
throw new Error('keychain must be an object');

src/v2/wallets.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Wallets.prototype.generateWallet = function(params, callback) {
183183
let backupKeychain;
184184
let bitgoKeychain;
185185
let userKeychainParams;
186+
let derivationPath;
186187

187188
const passphrase = params.passphrase;
188189
const canEncrypt = (!!passphrase && typeof passphrase === 'string');
@@ -195,6 +196,12 @@ Wallets.prototype.generateWallet = function(params, callback) {
195196
if (params.userKey) {
196197
userKeychain = { 'pub': params.userKey };
197198
userKeychainParams = userKeychain;
199+
if (params.coldDerivationSeed) {
200+
// the derivation only makes sense when a key already exists
201+
const derivation = self.baseCoin.deriveKeyWithSeed({ key: params.userKey, seed: params.coldDerivationSeed });
202+
derivationPath = derivation.derivationPath;
203+
userKeychain.pub = derivation.key;
204+
}
198205
} else {
199206
if (!canEncrypt) {
200207
throw new Error('cannot generate user keypair without passphrase');
@@ -296,6 +303,10 @@ Wallets.prototype.generateWallet = function(params, callback) {
296303
result.warning = 'Be sure to backup the backup keychain -- it is not stored anywhere else!';
297304
}
298305

306+
if (derivationPath) {
307+
userKeychain.derivationPath = derivationPath;
308+
}
309+
299310
return result;
300311
})
301312
.nodeify(callback);

test/v2/wallets.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,30 @@ describe('V2 Wallets:', function() {
171171
});
172172
});
173173

174+
it('should make wallet with provided user key and custom derivation path', function() {
175+
var userXpub = keychains.create().pub; // random xpub
176+
var params = {
177+
label: label,
178+
userKey: userXpub,
179+
coldDerivationSeed: 'custom-derivation-seed',
180+
passphrase: passphrase
181+
};
182+
183+
return wallets.generateWallet(params)
184+
.then(function(res) {
185+
res.should.have.property('wallet');
186+
res.should.have.property('userKeychain');
187+
res.should.have.property('backupKeychain');
188+
res.should.have.property('bitgoKeychain');
189+
190+
res.userKeychain.should.have.property('pub');
191+
res.userKeychain.should.have.property('derivationPath');
192+
res.userKeychain.derivationPath.should.equal('m/999999/112305623/88990619');
193+
res.userKeychain.should.not.have.property('prv');
194+
res.userKeychain.should.not.have.property('encryptedPrv');
195+
});
196+
});
197+
174198
it('should generate wallet and freeze it', function() {
175199
var backupXpub = keychains.create().pub; // random xpub
176200
var userXpub = keychains.create().pub; // random xpub

0 commit comments

Comments
 (0)