Beta 7 with some repo names changed.
This commit is contained in:
1 parent
0f7b40cca7
commit
963c19493a
3408 files changed
+462217
-15516
No files matched your search
Generated
Vendored
+255
@@ -0,0 +1,255 @@
|
||||
'use strict';
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var assert = _interopDefault(require('assert'));
|
||||
var safeBuffer = _interopDefault(require('safe-buffer'));
|
||||
var bs58check = _interopDefault(require('bs58check'));
|
||||
|
||||
var cryptoShim = require("../shims/hdkey-crypto");
|
||||
|
||||
var secp256k1Shim = require("../shims/hdkey-secp256k1v3");
|
||||
|
||||
var Buffer = safeBuffer.Buffer;
|
||||
|
||||
|
||||
|
||||
|
||||
var MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8');
|
||||
var HARDENED_OFFSET = 0x80000000;
|
||||
var LEN = 78;
|
||||
|
||||
// Bitcoin hardcoded by default, can use package `coininfo` for others
|
||||
var BITCOIN_VERSIONS = {private: 0x0488ADE4, public: 0x0488B21E};
|
||||
|
||||
function HDKey (versions) {
|
||||
this.versions = versions || BITCOIN_VERSIONS;
|
||||
this.depth = 0;
|
||||
this.index = 0;
|
||||
this._privateKey = null;
|
||||
this._publicKey = null;
|
||||
this.chainCode = null;
|
||||
this._fingerprint = 0;
|
||||
this.parentFingerprint = 0;
|
||||
}
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'fingerprint', { get: function () { return this._fingerprint } });
|
||||
Object.defineProperty(HDKey.prototype, 'identifier', { get: function () { return this._identifier } });
|
||||
Object.defineProperty(HDKey.prototype, 'pubKeyHash', { get: function () { return this.identifier } });
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'privateKey', {
|
||||
get: function () {
|
||||
return this._privateKey
|
||||
},
|
||||
set: function (value) {
|
||||
assert.equal(value.length, 32, 'Private key must be 32 bytes.');
|
||||
assert(secp256k1Shim.privateKeyVerify(value) === true, 'Invalid private key');
|
||||
|
||||
this._privateKey = value;
|
||||
this._publicKey = secp256k1Shim.publicKeyCreate(value, true);
|
||||
this._identifier = hash160(this.publicKey);
|
||||
this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'publicKey', {
|
||||
get: function () {
|
||||
return this._publicKey
|
||||
},
|
||||
set: function (value) {
|
||||
assert(value.length === 33 || value.length === 65, 'Public key must be 33 or 65 bytes.');
|
||||
assert(secp256k1Shim.publicKeyVerify(value) === true, 'Invalid public key');
|
||||
|
||||
this._publicKey = secp256k1Shim.publicKeyConvert(value, true); // force compressed point
|
||||
this._identifier = hash160(this.publicKey);
|
||||
this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0);
|
||||
this._privateKey = null;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'privateExtendedKey', {
|
||||
get: function () {
|
||||
if (this._privateKey) return bs58check.encode(serialize(this, this.versions.private, Buffer.concat([Buffer.alloc(1, 0), this.privateKey])))
|
||||
else return null
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'publicExtendedKey', {
|
||||
get: function () {
|
||||
return bs58check.encode(serialize(this, this.versions.public, this.publicKey))
|
||||
}
|
||||
});
|
||||
|
||||
HDKey.prototype.derive = function (path) {
|
||||
if (path === 'm' || path === 'M' || path === "m'" || path === "M'") {
|
||||
return this
|
||||
}
|
||||
|
||||
var entries = path.split('/');
|
||||
var hdkey = this;
|
||||
entries.forEach(function (c, i) {
|
||||
if (i === 0) {
|
||||
assert(/^[mM]{1}/.test(c), 'Path must start with "m" or "M"');
|
||||
return
|
||||
}
|
||||
|
||||
var hardened = (c.length > 1) && (c[c.length - 1] === "'");
|
||||
var childIndex = parseInt(c, 10); // & (HARDENED_OFFSET - 1)
|
||||
assert(childIndex < HARDENED_OFFSET, 'Invalid index');
|
||||
if (hardened) childIndex += HARDENED_OFFSET;
|
||||
|
||||
hdkey = hdkey.deriveChild(childIndex);
|
||||
});
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.prototype.deriveChild = function (index) {
|
||||
var isHardened = index >= HARDENED_OFFSET;
|
||||
var indexBuffer = Buffer.allocUnsafe(4);
|
||||
indexBuffer.writeUInt32BE(index, 0);
|
||||
|
||||
var data;
|
||||
|
||||
if (isHardened) { // Hardened child
|
||||
assert(this.privateKey, 'Could not derive hardened child key');
|
||||
|
||||
var pk = this.privateKey;
|
||||
var zb = Buffer.alloc(1, 0);
|
||||
pk = Buffer.concat([zb, pk]);
|
||||
|
||||
// data = 0x00 || ser256(kpar) || ser32(index)
|
||||
data = Buffer.concat([pk, indexBuffer]);
|
||||
} else { // Normal child
|
||||
// data = serP(point(kpar)) || ser32(index)
|
||||
// = serP(Kpar) || ser32(index)
|
||||
data = Buffer.concat([this.publicKey, indexBuffer]);
|
||||
}
|
||||
|
||||
var I = cryptoShim.createHmac('sha512', this.chainCode).update(data).digest();
|
||||
var IL = I.slice(0, 32);
|
||||
var IR = I.slice(32);
|
||||
|
||||
var hd = new HDKey(this.versions);
|
||||
|
||||
// Private parent key -> private child key
|
||||
if (this.privateKey) {
|
||||
// ki = parse256(IL) + kpar (mod n)
|
||||
try {
|
||||
hd.privateKey = secp256k1Shim.privateKeyTweakAdd(this.privateKey, IL);
|
||||
// throw if IL >= n || (privateKey + IL) === 0
|
||||
} catch (err) {
|
||||
// In case parse256(IL) >= n or ki == 0, one should proceed with the next value for i
|
||||
return this.deriveChild(index + 1)
|
||||
}
|
||||
// Public parent key -> public child key
|
||||
} else {
|
||||
// Ki = point(parse256(IL)) + Kpar
|
||||
// = G*IL + Kpar
|
||||
try {
|
||||
hd.publicKey = secp256k1Shim.publicKeyTweakAdd(this.publicKey, IL, true);
|
||||
// throw if IL >= n || (g**IL + publicKey) is infinity
|
||||
} catch (err) {
|
||||
// In case parse256(IL) >= n or Ki is the point at infinity, one should proceed with the next value for i
|
||||
return this.deriveChild(index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
hd.chainCode = IR;
|
||||
hd.depth = this.depth + 1;
|
||||
hd.parentFingerprint = this.fingerprint;// .readUInt32BE(0)
|
||||
hd.index = index;
|
||||
|
||||
return hd
|
||||
};
|
||||
|
||||
HDKey.prototype.sign = function (hash) {
|
||||
return secp256k1Shim.sign(hash, this.privateKey).signature
|
||||
};
|
||||
|
||||
HDKey.prototype.verify = function (hash, signature) {
|
||||
return secp256k1Shim.verify(hash, signature, this.publicKey)
|
||||
};
|
||||
|
||||
HDKey.prototype.wipePrivateData = function () {
|
||||
if (this._privateKey) cryptoShim.randomBytes(this._privateKey.length).copy(this._privateKey);
|
||||
this._privateKey = null;
|
||||
return this
|
||||
};
|
||||
|
||||
HDKey.prototype.toJSON = function () {
|
||||
return {
|
||||
xpriv: this.privateExtendedKey,
|
||||
xpub: this.publicExtendedKey
|
||||
}
|
||||
};
|
||||
|
||||
HDKey.fromMasterSeed = function (seedBuffer, versions) {
|
||||
var I = cryptoShim.createHmac('sha512', MASTER_SECRET).update(seedBuffer).digest();
|
||||
var IL = I.slice(0, 32);
|
||||
var IR = I.slice(32);
|
||||
|
||||
var hdkey = new HDKey(versions);
|
||||
hdkey.chainCode = IR;
|
||||
hdkey.privateKey = IL;
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.fromExtendedKey = function (base58key, versions) {
|
||||
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
|
||||
versions = versions || BITCOIN_VERSIONS;
|
||||
var hdkey = new HDKey(versions);
|
||||
|
||||
var keyBuffer = bs58check.decode(base58key);
|
||||
|
||||
var version = keyBuffer.readUInt32BE(0);
|
||||
assert(version === versions.private || version === versions.public, 'Version mismatch: does not match private or public');
|
||||
|
||||
hdkey.depth = keyBuffer.readUInt8(4);
|
||||
hdkey.parentFingerprint = keyBuffer.readUInt32BE(5);
|
||||
hdkey.index = keyBuffer.readUInt32BE(9);
|
||||
hdkey.chainCode = keyBuffer.slice(13, 45);
|
||||
|
||||
var key = keyBuffer.slice(45);
|
||||
if (key.readUInt8(0) === 0) { // private
|
||||
assert(version === versions.private, 'Version mismatch: version does not match private');
|
||||
hdkey.privateKey = key.slice(1); // cut off first 0x0 byte
|
||||
} else {
|
||||
assert(version === versions.public, 'Version mismatch: version does not match public');
|
||||
hdkey.publicKey = key;
|
||||
}
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.fromJSON = function (obj) {
|
||||
return HDKey.fromExtendedKey(obj.xpriv)
|
||||
};
|
||||
|
||||
function serialize (hdkey, version, key) {
|
||||
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
|
||||
var buffer = Buffer.allocUnsafe(LEN);
|
||||
|
||||
buffer.writeUInt32BE(version, 0);
|
||||
buffer.writeUInt8(hdkey.depth, 4);
|
||||
|
||||
var fingerprint = hdkey.depth ? hdkey.parentFingerprint : 0x00000000;
|
||||
buffer.writeUInt32BE(fingerprint, 5);
|
||||
buffer.writeUInt32BE(hdkey.index, 9);
|
||||
|
||||
hdkey.chainCode.copy(buffer, 13);
|
||||
key.copy(buffer, 45);
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
function hash160 (buf) {
|
||||
var sha = cryptoShim.createHash('sha256').update(buf).digest();
|
||||
return cryptoShim.createHash('ripemd160').update(sha).digest()
|
||||
}
|
||||
|
||||
HDKey.HARDENED_OFFSET = HARDENED_OFFSET;
|
||||
var hdkey = HDKey;
|
||||
|
||||
module.exports = hdkey;
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var createHash = _interopDefault(require('create-hash'));
|
||||
var pbkdf2 = _interopDefault(require('pbkdf2'));
|
||||
var randombytes = _interopDefault(require('randombytes'));
|
||||
|
||||
function unwrapExports (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
function createCommonjsModule(fn, module) {
|
||||
return module = { exports: {} }, fn(module, module.exports), module.exports;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var emptyModule = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null
|
||||
});
|
||||
|
||||
var src = createCommonjsModule(function (module, exports) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
|
||||
|
||||
|
||||
let DEFAULT_WORDLIST = emptyModule._default;
|
||||
const INVALID_MNEMONIC = 'Invalid mnemonic';
|
||||
const INVALID_ENTROPY = 'Invalid entropy';
|
||||
const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
|
||||
const WORDLIST_REQUIRED = 'A wordlist is required but a default could not be found.\n' +
|
||||
'Please explicitly pass a 2048 word array explicitly.';
|
||||
function lpad(str, padString, length) {
|
||||
while (str.length < length)
|
||||
str = padString + str;
|
||||
return str;
|
||||
}
|
||||
function binaryToByte(bin) {
|
||||
return parseInt(bin, 2);
|
||||
}
|
||||
function bytesToBinary(bytes) {
|
||||
return bytes.map(x => lpad(x.toString(2), '0', 8)).join('');
|
||||
}
|
||||
function deriveChecksumBits(entropyBuffer) {
|
||||
const ENT = entropyBuffer.length * 8;
|
||||
const CS = ENT / 32;
|
||||
const hash = createHash('sha256')
|
||||
.update(entropyBuffer)
|
||||
.digest();
|
||||
return bytesToBinary([...hash]).slice(0, CS);
|
||||
}
|
||||
function salt(password) {
|
||||
return 'mnemonic' + (password || '');
|
||||
}
|
||||
function mnemonicToSeedSync(mnemonic, password) {
|
||||
const mnemonicBuffer = Buffer.from((mnemonic || '').normalize('NFKD'), 'utf8');
|
||||
const saltBuffer = Buffer.from(salt((password || '').normalize('NFKD')), 'utf8');
|
||||
return pbkdf2.pbkdf2Sync(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512');
|
||||
}
|
||||
exports.mnemonicToSeedSync = mnemonicToSeedSync;
|
||||
function mnemonicToSeed(mnemonic, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const mnemonicBuffer = Buffer.from((mnemonic || '').normalize('NFKD'), 'utf8');
|
||||
const saltBuffer = Buffer.from(salt((password || '').normalize('NFKD')), 'utf8');
|
||||
pbkdf2.pbkdf2(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512', (err, data) => {
|
||||
if (err)
|
||||
return reject(err);
|
||||
else
|
||||
return resolve(data);
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.mnemonicToSeed = mnemonicToSeed;
|
||||
function mnemonicToEntropy(mnemonic, wordlist) {
|
||||
wordlist = wordlist || DEFAULT_WORDLIST;
|
||||
if (!wordlist) {
|
||||
throw new Error(WORDLIST_REQUIRED);
|
||||
}
|
||||
const words = (mnemonic || '').normalize('NFKD').split(' ');
|
||||
if (words.length % 3 !== 0)
|
||||
throw new Error(INVALID_MNEMONIC);
|
||||
// convert word indices to 11 bit binary strings
|
||||
const bits = words
|
||||
.map(word => {
|
||||
const index = wordlist.indexOf(word);
|
||||
if (index === -1)
|
||||
throw new Error(INVALID_MNEMONIC);
|
||||
return lpad(index.toString(2), '0', 11);
|
||||
})
|
||||
.join('');
|
||||
// split the binary string into ENT/CS
|
||||
const dividerIndex = Math.floor(bits.length / 33) * 32;
|
||||
const entropyBits = bits.slice(0, dividerIndex);
|
||||
const checksumBits = bits.slice(dividerIndex);
|
||||
// calculate the checksum and compare
|
||||
const entropyBytes = entropyBits.match(/(.{1,8})/g).map(binaryToByte);
|
||||
if (entropyBytes.length < 16)
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
if (entropyBytes.length > 32)
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
if (entropyBytes.length % 4 !== 0)
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
const entropy = Buffer.from(entropyBytes);
|
||||
const newChecksum = deriveChecksumBits(entropy);
|
||||
if (newChecksum !== checksumBits)
|
||||
throw new Error(INVALID_CHECKSUM);
|
||||
return entropy.toString('hex');
|
||||
}
|
||||
exports.mnemonicToEntropy = mnemonicToEntropy;
|
||||
function entropyToMnemonic(entropy, wordlist) {
|
||||
if (!Buffer.isBuffer(entropy))
|
||||
entropy = Buffer.from(entropy, 'hex');
|
||||
wordlist = wordlist || DEFAULT_WORDLIST;
|
||||
if (!wordlist) {
|
||||
throw new Error(WORDLIST_REQUIRED);
|
||||
}
|
||||
// 128 <= ENT <= 256
|
||||
if (entropy.length < 16)
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
if (entropy.length > 32)
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
if (entropy.length % 4 !== 0)
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
const entropyBits = bytesToBinary([...entropy]);
|
||||
const checksumBits = deriveChecksumBits(entropy);
|
||||
const bits = entropyBits + checksumBits;
|
||||
const chunks = bits.match(/(.{1,11})/g);
|
||||
const words = chunks.map(binary => {
|
||||
const index = binaryToByte(binary);
|
||||
return wordlist[index];
|
||||
});
|
||||
return wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093' // Japanese wordlist
|
||||
? words.join('\u3000')
|
||||
: words.join(' ');
|
||||
}
|
||||
exports.entropyToMnemonic = entropyToMnemonic;
|
||||
function generateMnemonic(strength, rng, wordlist) {
|
||||
strength = strength || 128;
|
||||
if (strength % 32 !== 0)
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
rng = rng || randombytes;
|
||||
return entropyToMnemonic(rng(strength / 8), wordlist);
|
||||
}
|
||||
exports.generateMnemonic = generateMnemonic;
|
||||
function validateMnemonic(mnemonic, wordlist) {
|
||||
try {
|
||||
mnemonicToEntropy(mnemonic, wordlist);
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
exports.validateMnemonic = validateMnemonic;
|
||||
function setDefaultWordlist(language) {
|
||||
const result = emptyModule.wordlists[language];
|
||||
if (result)
|
||||
DEFAULT_WORDLIST = result;
|
||||
else
|
||||
throw new Error('Could not find wordlist for language "' + language + '"');
|
||||
}
|
||||
exports.setDefaultWordlist = setDefaultWordlist;
|
||||
function getDefaultWordlist() {
|
||||
if (!DEFAULT_WORDLIST)
|
||||
throw new Error('No Default Wordlist set');
|
||||
return Object.keys(emptyModule.wordlists).filter(lang => {
|
||||
if (lang === 'JA' || lang === 'EN')
|
||||
return false;
|
||||
return emptyModule.wordlists[lang].every((word, index) => word === DEFAULT_WORDLIST[index]);
|
||||
})[0];
|
||||
}
|
||||
exports.getDefaultWordlist = getDefaultWordlist;
|
||||
var _wordlists_2 = emptyModule;
|
||||
exports.wordlists = _wordlists_2.wordlists;
|
||||
});
|
||||
|
||||
var index = unwrapExports(src);
|
||||
var src_1 = src.mnemonicToSeedSync;
|
||||
var src_2 = src.mnemonicToSeed;
|
||||
var src_3 = src.mnemonicToEntropy;
|
||||
var src_4 = src.entropyToMnemonic;
|
||||
var src_5 = src.generateMnemonic;
|
||||
var src_6 = src.validateMnemonic;
|
||||
var src_7 = src.setDefaultWordlist;
|
||||
var src_8 = src.getDefaultWordlist;
|
||||
var src_9 = src.wordlists;
|
||||
|
||||
exports.default = index;
|
||||
exports.entropyToMnemonic = src_4;
|
||||
exports.generateMnemonic = src_5;
|
||||
exports.getDefaultWordlist = src_8;
|
||||
exports.mnemonicToEntropy = src_3;
|
||||
exports.mnemonicToSeed = src_2;
|
||||
exports.mnemonicToSeedSync = src_1;
|
||||
exports.setDefaultWordlist = src_7;
|
||||
exports.validateMnemonic = src_6;
|
||||
exports.wordlists = src_9;
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
'use strict';
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var assert = _interopDefault(require('assert'));
|
||||
var safeBuffer = _interopDefault(require('safe-buffer'));
|
||||
var bs58check = _interopDefault(require('bs58check'));
|
||||
|
||||
var cryptoShim = require("../shims/hdkey-crypto");
|
||||
|
||||
var secp256k1Shim = require("../shims/hdkey-secp256k1v3");
|
||||
|
||||
var Buffer = safeBuffer.Buffer;
|
||||
|
||||
|
||||
|
||||
|
||||
var MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8');
|
||||
var HARDENED_OFFSET = 0x80000000;
|
||||
var LEN = 78;
|
||||
|
||||
// Bitcoin hardcoded by default, can use package `coininfo` for others
|
||||
var BITCOIN_VERSIONS = {private: 0x0488ADE4, public: 0x0488B21E};
|
||||
|
||||
function HDKey (versions) {
|
||||
this.versions = versions || BITCOIN_VERSIONS;
|
||||
this.depth = 0;
|
||||
this.index = 0;
|
||||
this._privateKey = null;
|
||||
this._publicKey = null;
|
||||
this.chainCode = null;
|
||||
this._fingerprint = 0;
|
||||
this.parentFingerprint = 0;
|
||||
}
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'fingerprint', { get: function () { return this._fingerprint } });
|
||||
Object.defineProperty(HDKey.prototype, 'identifier', { get: function () { return this._identifier } });
|
||||
Object.defineProperty(HDKey.prototype, 'pubKeyHash', { get: function () { return this.identifier } });
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'privateKey', {
|
||||
get: function () {
|
||||
return this._privateKey
|
||||
},
|
||||
set: function (value) {
|
||||
assert.equal(value.length, 32, 'Private key must be 32 bytes.');
|
||||
assert(secp256k1Shim.privateKeyVerify(value) === true, 'Invalid private key');
|
||||
|
||||
this._privateKey = value;
|
||||
this._publicKey = secp256k1Shim.publicKeyCreate(value, true);
|
||||
this._identifier = hash160(this.publicKey);
|
||||
this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'publicKey', {
|
||||
get: function () {
|
||||
return this._publicKey
|
||||
},
|
||||
set: function (value) {
|
||||
assert(value.length === 33 || value.length === 65, 'Public key must be 33 or 65 bytes.');
|
||||
assert(secp256k1Shim.publicKeyVerify(value) === true, 'Invalid public key');
|
||||
|
||||
this._publicKey = secp256k1Shim.publicKeyConvert(value, true); // force compressed point
|
||||
this._identifier = hash160(this.publicKey);
|
||||
this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0);
|
||||
this._privateKey = null;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'privateExtendedKey', {
|
||||
get: function () {
|
||||
if (this._privateKey) return bs58check.encode(serialize(this, this.versions.private, Buffer.concat([Buffer.alloc(1, 0), this.privateKey])))
|
||||
else return null
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(HDKey.prototype, 'publicExtendedKey', {
|
||||
get: function () {
|
||||
return bs58check.encode(serialize(this, this.versions.public, this.publicKey))
|
||||
}
|
||||
});
|
||||
|
||||
HDKey.prototype.derive = function (path) {
|
||||
if (path === 'm' || path === 'M' || path === "m'" || path === "M'") {
|
||||
return this
|
||||
}
|
||||
|
||||
var entries = path.split('/');
|
||||
var hdkey = this;
|
||||
entries.forEach(function (c, i) {
|
||||
if (i === 0) {
|
||||
assert(/^[mM]{1}/.test(c), 'Path must start with "m" or "M"');
|
||||
return
|
||||
}
|
||||
|
||||
var hardened = (c.length > 1) && (c[c.length - 1] === "'");
|
||||
var childIndex = parseInt(c, 10); // & (HARDENED_OFFSET - 1)
|
||||
assert(childIndex < HARDENED_OFFSET, 'Invalid index');
|
||||
if (hardened) childIndex += HARDENED_OFFSET;
|
||||
|
||||
hdkey = hdkey.deriveChild(childIndex);
|
||||
});
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.prototype.deriveChild = function (index) {
|
||||
var isHardened = index >= HARDENED_OFFSET;
|
||||
var indexBuffer = Buffer.allocUnsafe(4);
|
||||
indexBuffer.writeUInt32BE(index, 0);
|
||||
|
||||
var data;
|
||||
|
||||
if (isHardened) { // Hardened child
|
||||
assert(this.privateKey, 'Could not derive hardened child key');
|
||||
|
||||
var pk = this.privateKey;
|
||||
var zb = Buffer.alloc(1, 0);
|
||||
pk = Buffer.concat([zb, pk]);
|
||||
|
||||
// data = 0x00 || ser256(kpar) || ser32(index)
|
||||
data = Buffer.concat([pk, indexBuffer]);
|
||||
} else { // Normal child
|
||||
// data = serP(point(kpar)) || ser32(index)
|
||||
// = serP(Kpar) || ser32(index)
|
||||
data = Buffer.concat([this.publicKey, indexBuffer]);
|
||||
}
|
||||
|
||||
var I = cryptoShim.createHmac('sha512', this.chainCode).update(data).digest();
|
||||
var IL = I.slice(0, 32);
|
||||
var IR = I.slice(32);
|
||||
|
||||
var hd = new HDKey(this.versions);
|
||||
|
||||
// Private parent key -> private child key
|
||||
if (this.privateKey) {
|
||||
// ki = parse256(IL) + kpar (mod n)
|
||||
try {
|
||||
hd.privateKey = secp256k1Shim.privateKeyTweakAdd(this.privateKey, IL);
|
||||
// throw if IL >= n || (privateKey + IL) === 0
|
||||
} catch (err) {
|
||||
// In case parse256(IL) >= n or ki == 0, one should proceed with the next value for i
|
||||
return this.deriveChild(index + 1)
|
||||
}
|
||||
// Public parent key -> public child key
|
||||
} else {
|
||||
// Ki = point(parse256(IL)) + Kpar
|
||||
// = G*IL + Kpar
|
||||
try {
|
||||
hd.publicKey = secp256k1Shim.publicKeyTweakAdd(this.publicKey, IL, true);
|
||||
// throw if IL >= n || (g**IL + publicKey) is infinity
|
||||
} catch (err) {
|
||||
// In case parse256(IL) >= n or Ki is the point at infinity, one should proceed with the next value for i
|
||||
return this.deriveChild(index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
hd.chainCode = IR;
|
||||
hd.depth = this.depth + 1;
|
||||
hd.parentFingerprint = this.fingerprint;// .readUInt32BE(0)
|
||||
hd.index = index;
|
||||
|
||||
return hd
|
||||
};
|
||||
|
||||
HDKey.prototype.sign = function (hash) {
|
||||
return secp256k1Shim.sign(hash, this.privateKey).signature
|
||||
};
|
||||
|
||||
HDKey.prototype.verify = function (hash, signature) {
|
||||
return secp256k1Shim.verify(hash, signature, this.publicKey)
|
||||
};
|
||||
|
||||
HDKey.prototype.wipePrivateData = function () {
|
||||
if (this._privateKey) cryptoShim.randomBytes(this._privateKey.length).copy(this._privateKey);
|
||||
this._privateKey = null;
|
||||
return this
|
||||
};
|
||||
|
||||
HDKey.prototype.toJSON = function () {
|
||||
return {
|
||||
xpriv: this.privateExtendedKey,
|
||||
xpub: this.publicExtendedKey
|
||||
}
|
||||
};
|
||||
|
||||
HDKey.fromMasterSeed = function (seedBuffer, versions) {
|
||||
var I = cryptoShim.createHmac('sha512', MASTER_SECRET).update(seedBuffer).digest();
|
||||
var IL = I.slice(0, 32);
|
||||
var IR = I.slice(32);
|
||||
|
||||
var hdkey = new HDKey(versions);
|
||||
hdkey.chainCode = IR;
|
||||
hdkey.privateKey = IL;
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.fromExtendedKey = function (base58key, versions) {
|
||||
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
|
||||
versions = versions || BITCOIN_VERSIONS;
|
||||
var hdkey = new HDKey(versions);
|
||||
|
||||
var keyBuffer = bs58check.decode(base58key);
|
||||
|
||||
var version = keyBuffer.readUInt32BE(0);
|
||||
assert(version === versions.private || version === versions.public, 'Version mismatch: does not match private or public');
|
||||
|
||||
hdkey.depth = keyBuffer.readUInt8(4);
|
||||
hdkey.parentFingerprint = keyBuffer.readUInt32BE(5);
|
||||
hdkey.index = keyBuffer.readUInt32BE(9);
|
||||
hdkey.chainCode = keyBuffer.slice(13, 45);
|
||||
|
||||
var key = keyBuffer.slice(45);
|
||||
if (key.readUInt8(0) === 0) { // private
|
||||
assert(version === versions.private, 'Version mismatch: version does not match private');
|
||||
hdkey.privateKey = key.slice(1); // cut off first 0x0 byte
|
||||
} else {
|
||||
assert(version === versions.public, 'Version mismatch: version does not match public');
|
||||
hdkey.publicKey = key;
|
||||
}
|
||||
|
||||
return hdkey
|
||||
};
|
||||
|
||||
HDKey.fromJSON = function (obj) {
|
||||
return HDKey.fromExtendedKey(obj.xpriv)
|
||||
};
|
||||
|
||||
function serialize (hdkey, version, key) {
|
||||
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
|
||||
var buffer = Buffer.allocUnsafe(LEN);
|
||||
|
||||
buffer.writeUInt32BE(version, 0);
|
||||
buffer.writeUInt8(hdkey.depth, 4);
|
||||
|
||||
var fingerprint = hdkey.depth ? hdkey.parentFingerprint : 0x00000000;
|
||||
buffer.writeUInt32BE(fingerprint, 5);
|
||||
buffer.writeUInt32BE(hdkey.index, 9);
|
||||
|
||||
hdkey.chainCode.copy(buffer, 13);
|
||||
key.copy(buffer, 45);
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
function hash160 (buf) {
|
||||
var sha = cryptoShim.createHash('sha256').update(buf).digest();
|
||||
return cryptoShim.createHash('ripemd160').update(sha).digest()
|
||||
}
|
||||
|
||||
HDKey.HARDENED_OFFSET = HARDENED_OFFSET;
|
||||
var hdkey = HDKey;
|
||||
|
||||
module.exports = hdkey;
|
||||
Reference in new issue
Block a user