Skip to content

Commit d54564d

Browse files
author
AJ ONeal
committed
ref: drastically simplify deriveChild
1 parent 2a1ee09 commit d54564d

File tree

1 file changed

+48
-59
lines changed

1 file changed

+48
-59
lines changed

dashhd.js

Lines changed: 48 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* @prop {HDVersions} versions - magic bytes for base58 prefix
3636
* @prop {HDDerivePath} derive - derive a full hd path from the given root
3737
* @prop {HDDeriveChild} deriveChild - get the next child xkey (in a path segment)
38+
* @prop {HDDeriveChild} _deriveChild - helper
3839
* @prop {HDFingerprint} getFingerprint
3940
* @prop {HDMaybeGetString} getPrivateExtendedKey
4041
* @prop {HDMaybeGetBuffer} getPrivateKey
@@ -323,89 +324,77 @@ var DashHd = ("object" === typeof module && exports) || {};
323324
let hardened = c.length > 1 && c[c.length - 1] === "'";
324325
let childIndex = parseInt(c, 10); // & (HARDENED_OFFSET - 1)
325326
assert(childIndex < HARDENED_OFFSET, "Invalid index");
326-
if (hardened) {
327-
childIndex += HARDENED_OFFSET;
328-
}
329327

330-
_hdkey = await _hdkey.deriveChild(childIndex);
328+
_hdkey = await _hdkey.deriveChild(childIndex, hardened);
331329
}
332330

333331
return _hdkey;
334332
};
335333

334+
hdkey.deriveChild = async function (index, hardened) {
335+
for (;;) {
336+
try {
337+
//@ts-ignore
338+
return await hdkey._deriveChild(index, hardened);
339+
} catch (e) {
340+
// In essence:
341+
// if it couldn't produce a public key, just go on the next one
342+
//
343+
// More precisely:
344+
//
345+
// throw if IL >= n || (privateKey + IL) === 0
346+
// In case parse256(IL) >= n or ki == 0,
347+
// one should proceed with the next value for i
348+
349+
// throw if IL >= n || (g**IL + publicKey) is infinity
350+
// In case parse256(IL) >= n or Ki is the point at infinity,
351+
// one should proceed with the next value for i
352+
index += 1;
353+
}
354+
}
355+
};
356+
336357
// IMPORTANT: never allow `await` (or other async) between writing to
337358
// and accessing these! (otherwise the data will be corrupted)
338359
// (stored here for performance - no allocations or garbage collection)
339360
let _indexBuffer = new Uint8Array(4);
340361
let _indexDv = new DataView(_indexBuffer.buffer);
341362

342-
hdkey.deriveChild = async function (index) {
343-
let isHardened = index >= HARDENED_OFFSET;
344-
let offset = 0;
345-
_indexDv.setUint32(offset, index, BUFFER_BE);
346-
347-
let data = new Uint8Array(INDEXED_KEY_SIZE);
348-
349-
if (isHardened) {
350-
// Hardened child
363+
//@ts-ignore
364+
hdkey._deriveChild = async function (index, hardened) {
365+
let seed = new Uint8Array(INDEXED_KEY_SIZE);
366+
if (hardened) {
351367
if (!_privateKey) {
352368
throw new Error("Could not derive hardened child key");
353369
}
354-
355-
// data = 0x00 || ser256(kpar) || ser32(index)
356-
data.set([0], 0); // 1
357-
data.set(_privateKey, 1); // 32
358-
data.set(_indexBuffer, KEY_SIZE);
370+
index += HARDENED_OFFSET;
371+
seed.set([0], 0);
372+
seed.set(_privateKey, 1);
359373
} else {
360-
// Normal child
361-
// data = serP(point(kpar)) || ser32(index)
362-
// = serP(Kpar) || ser32(index)
363-
data.set(hdkey.publicKey, 0);
364-
data.set(_indexBuffer, KEY_SIZE);
374+
seed.set(hdkey.publicKey, 0);
365375
}
376+
_indexDv.setUint32(0, index, BUFFER_BE);
377+
seed.set(_indexBuffer, KEY_SIZE);
366378

367-
let IBuf = await Utils.sha512hmac(hdkey.chainCode, data);
368-
let I = new Uint8Array(IBuf);
379+
let I = await Utils.sha512hmac(hdkey.chainCode, seed);
369380
let IL = I.slice(0, 32);
370381
let IR = I.slice(32);
371382

372-
let hd = DashHd.create(hdkey.versions);
383+
let _hdkey = DashHd.create(hdkey.versions);
384+
_hdkey.depth = hdkey.depth + 1;
385+
_hdkey.parentFingerprint = hdkey.getFingerprint();
386+
_hdkey.index = index;
387+
_hdkey.chainCode = IR;
373388

374-
// Private parent key -> private child key
375389
if (_privateKey) {
376-
// ki = parse256(IL) + kpar (mod n)
377-
try {
378-
let privateKeyCopy = new Uint8Array(_privateKey);
379-
await hd.setPrivateKey(
380-
await Utils.privateKeyTweakAdd(privateKeyCopy, IL),
381-
);
382-
// throw if IL >= n || (privateKey + IL) === 0
383-
} catch (err) {
384-
// In case parse256(IL) >= n or ki == 0, one should proceed with the next value for i
385-
return await hdkey.deriveChild(index + 1);
386-
}
387-
// Public parent key -> public child key
390+
let nextPrivKey = await Utils.privateKeyTweakAdd(_privateKey, IL);
391+
await _hdkey.setPrivateKey(nextPrivKey);
388392
} else {
389-
// Ki = point(parse256(IL)) + Kpar
390-
// = G*IL + Kpar
391-
try {
392-
let publicKeyCopy = new Uint8Array(hdkey.publicKey);
393-
await hd.setPublicKey(
394-
await Utils.publicKeyTweakAdd(publicKeyCopy, IL),
395-
);
396-
// throw if IL >= n || (g**IL + publicKey) is infinity
397-
} catch (err) {
398-
// In case parse256(IL) >= n or Ki is the point at infinity, one should proceed with the next value for i
399-
return await hdkey.deriveChild(index + 1);
400-
}
393+
let nextPubKey = await Utils.publicKeyTweakAdd(hdkey.publicKey, IL);
394+
await _hdkey.setPublicKey(nextPubKey);
401395
}
402396

403-
hd.chainCode = IR;
404-
hd.depth = hdkey.depth + 1;
405-
hd.parentFingerprint = hdkey.getFingerprint();
406-
hd.index = index;
407-
408-
return hd;
397+
return _hdkey;
409398
};
410399

411400
hdkey.wipePrivateData = function () {
@@ -420,8 +409,7 @@ var DashHd = ("object" === typeof module && exports) || {};
420409
};
421410

422411
DashHd.fromMasterSeed = async function (seedBuffer, versions) {
423-
let IBuf = await Utils.sha512hmac(MASTER_SECRET, seedBuffer);
424-
let I = new Uint8Array(IBuf);
412+
let I = await Utils.sha512hmac(MASTER_SECRET, seedBuffer);
425413
let IL = I.subarray(0, 32);
426414
let IR = I.subarray(32);
427415

@@ -549,6 +537,7 @@ if ("object" === typeof module) {
549537
/**
550538
* @callback HDDeriveChild
551539
* @param {Number} index - includes HARDENED_OFFSET, if applicable
540+
* @param {Boolean} hardened
552541
* returns {Promise<HDKey>}
553542
*/
554543

0 commit comments

Comments
 (0)