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