hd: refactor hardening handling.
This commit is contained in:
parent
e03182367f
commit
1735fe6a6c
@ -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;
|
||||
};
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user