hd: refactor hardening handling.

This commit is contained in:
Christopher Jeffrey 2017-08-11 04:42:44 -07:00
parent e03182367f
commit 1735fe6a6c
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 40 additions and 50 deletions

View File

@ -6,6 +6,7 @@
'use strict'; 'use strict';
const assert = require('assert');
const LRU = require('../utils/lru'); const LRU = require('../utils/lru');
const common = exports; const common = exports;
@ -17,14 +18,6 @@ const common = exports;
common.HARDENED = 0x80000000; common.HARDENED = 0x80000000;
/**
* Max index (u32max + 1).
* @const {Number}
* @default
*/
common.MAX_INDEX = 0x100000000;
/** /**
* Min entropy bits. * Min entropy bits.
* @const {Number} * @const {Number}
@ -52,13 +45,13 @@ common.cache = new LRU(500);
* Parse a derivation path and return an array of indexes. * Parse a derivation path and return an array of indexes.
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki * @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
* @param {String} path * @param {String} path
* @param {Number?} max - Max index. * @param {Boolean} hard
* @returns {Number[]} * @returns {Number[]}
*/ */
common.parsePath = function parsePath(path, max) { common.parsePath = function parsePath(path, hard) {
if (max == null) assert(typeof path === 'string');
max = common.MAX_INDEX; assert(typeof hard === 'boolean');
const parts = path.split('/'); const parts = path.split('/');
const root = parts.shift(); const root = parts.shift();
@ -72,23 +65,28 @@ common.parsePath = function parsePath(path, max) {
const result = []; const result = [];
for (let index of parts) { for (let part of parts) {
const hardened = index[index.length - 1] === '\''; const hardened = part[part.length - 1] === '\'';
if (hardened) if (hardened)
index = index.slice(0, -1); part = part.slice(0, -1);
if (!/^\d+$/.test(index)) if (!/^\d+$/.test(part))
throw new Error('Non-number path index.'); throw new Error('Non-number path index.');
index = parseInt(index, 10); let index = parseInt(part, 10);
if (hardened) if ((index >>> 0) !== index)
index += common.HARDENED;
if (!(index >= 0 && index < max))
throw new Error('Index out of range.'); throw new Error('Index out of range.');
if (hardened) {
index |= common.HARDENED;
index >>>= 0;
}
if (!hard && (index & common.HARDENED))
throw new Error('Cannot derive hardened.');
result.push(index); result.push(index);
} }
@ -116,10 +114,11 @@ common.isMaster = function isMaster(key) {
common.isBIP44 = function isBIP44(key, account) { common.isBIP44 = function isBIP44(key, account) {
if (account != null) { if (account != null) {
if (key.childIndex !== common.HARDENED + account) const index = (common.HARDENED | account) >>> 0;
if (key.childIndex !== index)
return false; return false;
} }
return key.depth === 3 && key.childIndex >= common.HARDENED; return key.depth === 3 && (key.childIndex & common.HARDENED) !== 0;
}; };
/** /**
@ -129,5 +128,5 @@ common.isBIP44 = function isBIP44(key, account) {
*/ */
common.isBIP45 = function isBIP45(key) { common.isBIP45 = function isBIP45(key) {
return key.depth === 1 && key.childIndex === common.HARDENED + 45; return key.depth === 1 && (key.childIndex & ~common.HARDENED) === 45;
}; };

View File

@ -183,13 +183,12 @@ HDPrivateKey.prototype.destroy = function destroy(pub) {
*/ */
HDPrivateKey.prototype.derive = function derive(index, hardened) { HDPrivateKey.prototype.derive = function derive(index, hardened) {
assert(typeof index === 'number'); assert(util.isU32(index));
if (hardened && index < common.HARDENED) if (hardened) {
index += common.HARDENED; index |= common.HARDENED;
index >>>= 0;
if (index < 0 || index >= common.MAX_INDEX) }
throw new Error('Index out of range.');
if (this.depth >= 0xff) if (this.depth >= 0xff)
throw new Error('Depth too high.'); throw new Error('Depth too high.');
@ -202,7 +201,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
const bw = new StaticWriter(37); const bw = new StaticWriter(37);
if (index >= common.HARDENED) { if (index & common.HARDENED) {
bw.writeU8(0); bw.writeU8(0);
bw.writeBytes(this.privateKey); bw.writeBytes(this.privateKey);
bw.writeU32BE(index); bw.writeU32BE(index);
@ -360,16 +359,12 @@ HDPrivateKey.isRaw = function isRaw(data, network) {
/** /**
* Test whether a string is a valid path. * Test whether a string is a valid path.
* @param {String} path * @param {String} path
* @param {Boolean?} hardened
* @returns {Boolean} * @returns {Boolean}
*/ */
HDPrivateKey.isValidPath = function isValidPath(path) { HDPrivateKey.isValidPath = function isValidPath(path) {
if (typeof path !== 'string')
return false;
try { try {
common.parsePath(path, common.MAX_INDEX); common.parsePath(path, true);
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
@ -384,7 +379,8 @@ HDPrivateKey.isValidPath = function isValidPath(path) {
*/ */
HDPrivateKey.prototype.derivePath = function derivePath(path) { HDPrivateKey.prototype.derivePath = function derivePath(path) {
const indexes = common.parsePath(path, common.MAX_INDEX); const indexes = common.parsePath(path, true);
let key = this; let key = this;
for (const index of indexes) for (const index of indexes)

View File

@ -151,14 +151,11 @@ HDPublicKey.prototype.destroy = function destroy() {
*/ */
HDPublicKey.prototype.derive = function derive(index, hardened) { HDPublicKey.prototype.derive = function derive(index, hardened) {
assert(typeof index === 'number'); assert(util.isU32(index));
if (index >= common.HARDENED || hardened) if ((index & common.HARDENED) || hardened)
throw new Error('Cannot derive hardened.'); throw new Error('Cannot derive hardened.');
if (index < 0)
throw new Error('Index out of range.');
if (this.depth >= 0xff) if (this.depth >= 0xff)
throw new Error('Depth too high.'); throw new Error('Depth too high.');
@ -280,11 +277,8 @@ HDPublicKey.prototype.isBIP45 = function isBIP45() {
*/ */
HDPublicKey.isValidPath = function isValidPath(path) { HDPublicKey.isValidPath = function isValidPath(path) {
if (typeof path !== 'string')
return false;
try { try {
common.parsePath(path, common.HARDENED); common.parsePath(path, false);
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
@ -300,7 +294,8 @@ HDPublicKey.isValidPath = function isValidPath(path) {
*/ */
HDPublicKey.prototype.derivePath = function derivePath(path) { HDPublicKey.prototype.derivePath = function derivePath(path) {
const indexes = common.parsePath(path, common.HARDENED); const indexes = common.parsePath(path, false);
let key = this; let key = this;
for (const index of indexes) for (const index of indexes)

View File

@ -767,21 +767,21 @@ Config.prototype.parseConfig = function parseConfig(text) {
if (colon !== -1 && (colon < equal || equal === -1)) { if (colon !== -1 && (colon < equal || equal === -1)) {
if (seen && !colons) if (seen && !colons)
throw new Error(`Expected \`=\` on line ${num}: "${line}".`); throw new Error(`Expected '=' on line ${num}: "${line}".`);
index = colon; index = colon;
seen = true; seen = true;
colons = true; colons = true;
} else if (equal !== -1) { } else if (equal !== -1) {
if (seen && colons) if (seen && colons)
throw new Error(`Expected \`:\` on line ${num}: "${line}".`); throw new Error(`Expected ':' on line ${num}: "${line}".`);
index = equal; index = equal;
seen = true; seen = true;
colons = false; colons = false;
} else { } else {
const symbol = colons ? ':' : '='; const symbol = colons ? ':' : '=';
throw new Error(`Expected \`${symbol}\` on line ${num}: "${line}".`); throw new Error(`Expected '${symbol}' on line ${num}: "${line}".`);
} }
let key = line.substring(0, index).trim(); let key = line.substring(0, index).trim();