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';
const assert = require('assert');
const LRU = require('../utils/lru');
const common = exports;
@ -17,14 +18,6 @@ const common = exports;
common.HARDENED = 0x80000000;
/**
* Max index (u32max + 1).
* @const {Number}
* @default
*/
common.MAX_INDEX = 0x100000000;
/**
* Min entropy bits.
* @const {Number}
@ -52,13 +45,13 @@ common.cache = new LRU(500);
* Parse a derivation path and return an array of indexes.
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
* @param {String} path
* @param {Number?} max - Max index.
* @param {Boolean} hard
* @returns {Number[]}
*/
common.parsePath = function parsePath(path, max) {
if (max == null)
max = common.MAX_INDEX;
common.parsePath = function parsePath(path, hard) {
assert(typeof path === 'string');
assert(typeof hard === 'boolean');
const parts = path.split('/');
const root = parts.shift();
@ -72,23 +65,28 @@ common.parsePath = function parsePath(path, max) {
const result = [];
for (let index of parts) {
const hardened = index[index.length - 1] === '\'';
for (let part of parts) {
const hardened = part[part.length - 1] === '\'';
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.');
index = parseInt(index, 10);
let index = parseInt(part, 10);
if (hardened)
index += common.HARDENED;
if (!(index >= 0 && index < max))
if ((index >>> 0) !== index)
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);
}
@ -116,10 +114,11 @@ common.isMaster = function isMaster(key) {
common.isBIP44 = function isBIP44(key, account) {
if (account != null) {
if (key.childIndex !== common.HARDENED + account)
const index = (common.HARDENED | account) >>> 0;
if (key.childIndex !== index)
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) {
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) {
assert(typeof index === 'number');
assert(util.isU32(index));
if (hardened && index < common.HARDENED)
index += common.HARDENED;
if (index < 0 || index >= common.MAX_INDEX)
throw new Error('Index out of range.');
if (hardened) {
index |= common.HARDENED;
index >>>= 0;
}
if (this.depth >= 0xff)
throw new Error('Depth too high.');
@ -202,7 +201,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
const bw = new StaticWriter(37);
if (index >= common.HARDENED) {
if (index & common.HARDENED) {
bw.writeU8(0);
bw.writeBytes(this.privateKey);
bw.writeU32BE(index);
@ -360,16 +359,12 @@ HDPrivateKey.isRaw = function isRaw(data, network) {
/**
* Test whether a string is a valid path.
* @param {String} path
* @param {Boolean?} hardened
* @returns {Boolean}
*/
HDPrivateKey.isValidPath = function isValidPath(path) {
if (typeof path !== 'string')
return false;
try {
common.parsePath(path, common.MAX_INDEX);
common.parsePath(path, true);
return true;
} catch (e) {
return false;
@ -384,7 +379,8 @@ HDPrivateKey.isValidPath = function isValidPath(path) {
*/
HDPrivateKey.prototype.derivePath = function derivePath(path) {
const indexes = common.parsePath(path, common.MAX_INDEX);
const indexes = common.parsePath(path, true);
let key = this;
for (const index of indexes)

View File

@ -151,14 +151,11 @@ HDPublicKey.prototype.destroy = function destroy() {
*/
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.');
if (index < 0)
throw new Error('Index out of range.');
if (this.depth >= 0xff)
throw new Error('Depth too high.');
@ -280,11 +277,8 @@ HDPublicKey.prototype.isBIP45 = function isBIP45() {
*/
HDPublicKey.isValidPath = function isValidPath(path) {
if (typeof path !== 'string')
return false;
try {
common.parsePath(path, common.HARDENED);
common.parsePath(path, false);
return true;
} catch (e) {
return false;
@ -300,7 +294,8 @@ HDPublicKey.isValidPath = function isValidPath(path) {
*/
HDPublicKey.prototype.derivePath = function derivePath(path) {
const indexes = common.parsePath(path, common.HARDENED);
const indexes = common.parsePath(path, false);
let key = this;
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 (seen && !colons)
throw new Error(`Expected \`=\` on line ${num}: "${line}".`);
throw new Error(`Expected '=' on line ${num}: "${line}".`);
index = colon;
seen = true;
colons = true;
} else if (equal !== -1) {
if (seen && colons)
throw new Error(`Expected \`:\` on line ${num}: "${line}".`);
throw new Error(`Expected ':' on line ${num}: "${line}".`);
index = equal;
seen = true;
colons = false;
} else {
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();