From 5daf409388fbdd9102e4c99fb1fc65e108147a94 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jan 2023 23:33:25 -0700 Subject: [PATCH 01/22] chore: make Prettier --- .prettierignore | 4 + .prettierrc.json | 9 ++ lib/hdkey.js | 362 +++++++++++++++++++++++++++-------------------- package.json | 11 +- 4 files changed, 225 insertions(+), 161 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..b3e090f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +docs +node_modules +package.json +package-lock.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..a48aa0c --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "singleQuote": false, + "bracketSpacing": true, + "proseWrap": "always", + "semi": true, + "trailingComma": "all" +} diff --git a/lib/hdkey.js b/lib/hdkey.js index 55de104..5dccd7f 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -1,257 +1,307 @@ -var assert = require('assert') -var Buffer = require('safe-buffer').Buffer -var crypto = require('crypto') -var bs58check = require('bs58check') -var RIPEMD160 = require('ripemd160') -var secp256k1 = require('secp256k1') +"use strict"; -var MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8') -var HARDENED_OFFSET = 0x80000000 -var LEN = 78 +var assert = require("assert"); +var Buffer = require("safe-buffer").Buffer; +var crypto = require("crypto"); +var bs58check = require("bs58check"); +var RIPEMD160 = require("ripemd160"); +var secp256k1 = require("secp256k1"); + +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 +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, "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', { +Object.defineProperty(HDKey.prototype, "privateKey", { get: function () { - return this._privateKey + return this._privateKey; }, set: function (value) { - assert.equal(value.length, 32, 'Private key must be 32 bytes.') - assert(secp256k1.privateKeyVerify(value) === true, 'Invalid private key') + assert.equal(value.length, 32, "Private key must be 32 bytes."); + assert(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); - this._privateKey = value - this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)) - this._identifier = hash160(this.publicKey) - this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0) - } -}) + this._privateKey = value; + this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + this._identifier = hash160(this.publicKey); + this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0); + }, +}); -function setPublicKey (hdkey, publicKey) { - hdkey._publicKey = Buffer.from(publicKey) - hdkey._identifier = hash160(publicKey) - hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0) - hdkey._privateKey = null +function setPublicKey(hdkey, publicKey) { + hdkey._publicKey = Buffer.from(publicKey); + hdkey._identifier = hash160(publicKey); + hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); + hdkey._privateKey = null; } -Object.defineProperty(HDKey.prototype, 'publicKey', { +Object.defineProperty(HDKey.prototype, "publicKey", { get: function () { - return this._publicKey + return this._publicKey; }, set: function (value) { - assert(value.length === 33 || value.length === 65, 'Public key must be 33 or 65 bytes.') - assert(secp256k1.publicKeyVerify(value) === true, 'Invalid public key') + assert( + value.length === 33 || value.length === 65, + "Public key must be 33 or 65 bytes.", + ); + assert(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); // force compressed point (performs public key verification) - const publicKey = (value.length === 65) ? secp256k1.publicKeyConvert(value, true) : value - setPublicKey(this, publicKey) - } -}) + const publicKey = + value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; + setPublicKey(this, publicKey); + }, +}); -Object.defineProperty(HDKey.prototype, 'privateExtendedKey', { +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 - } -}) + 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', { +Object.defineProperty(HDKey.prototype, "publicExtendedKey", { get: function () { - return bs58check.encode(serialize(this, this.versions.public, this.publicKey)) - } -}) + 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 + if (path === "m" || path === "M" || path === "m'" || path === "M'") { + return this; } - var entries = path.split('/') - var hdkey = 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 + 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 + 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) - }) + hdkey = hdkey.deriveChild(childIndex); + }); - return hdkey -} + return hdkey; +}; HDKey.prototype.deriveChild = function (index) { - var isHardened = index >= HARDENED_OFFSET - var indexBuffer = Buffer.allocUnsafe(4) - indexBuffer.writeUInt32BE(index, 0) + var isHardened = index >= HARDENED_OFFSET; + var indexBuffer = Buffer.allocUnsafe(4); + indexBuffer.writeUInt32BE(index, 0); - var data + var data; - if (isHardened) { // Hardened child - assert(this.privateKey, 'Could not derive hardened child key') + 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]) + 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 = Buffer.concat([pk, indexBuffer]); + } else { + // Normal child // data = serP(point(kpar)) || ser32(index) // = serP(Kpar) || ser32(index) - data = Buffer.concat([this.publicKey, indexBuffer]) + data = Buffer.concat([this.publicKey, indexBuffer]); } - var I = crypto.createHmac('sha512', this.chainCode).update(data).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) + var I = crypto.createHmac("sha512", this.chainCode).update(data).digest(); + var IL = I.slice(0, 32); + var IR = I.slice(32); - var hd = new HDKey(this.versions) + var hd = new HDKey(this.versions); // Private parent key -> private child key if (this.privateKey) { // ki = parse256(IL) + kpar (mod n) try { - hd.privateKey = Buffer.from(secp256k1.privateKeyTweakAdd(Buffer.from(this.privateKey), IL)) + hd.privateKey = Buffer.from( + secp256k1.privateKeyTweakAdd(Buffer.from(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) + return this.deriveChild(index + 1); } - // Public parent key -> public child key + // Public parent key -> public child key } else { // Ki = point(parse256(IL)) + Kpar // = G*IL + Kpar try { - hd.publicKey = Buffer.from(secp256k1.publicKeyTweakAdd(Buffer.from(this.publicKey), IL, true)) + hd.publicKey = Buffer.from( + secp256k1.publicKeyTweakAdd(Buffer.from(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) + return this.deriveChild(index + 1); } } - hd.chainCode = IR - hd.depth = this.depth + 1 - hd.parentFingerprint = this.fingerprint// .readUInt32BE(0) - hd.index = index + hd.chainCode = IR; + hd.depth = this.depth + 1; + hd.parentFingerprint = this.fingerprint; // .readUInt32BE(0) + hd.index = index; - return hd -} + return hd; +}; HDKey.prototype.sign = function (hash) { - return Buffer.from(secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(this.privateKey)).signature) -} + return Buffer.from( + secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(this.privateKey)) + .signature, + ); +}; HDKey.prototype.verify = function (hash, signature) { return secp256k1.ecdsaVerify( Uint8Array.from(signature), Uint8Array.from(hash), - Uint8Array.from(this.publicKey) - ) -} + Uint8Array.from(this.publicKey), + ); +}; HDKey.prototype.wipePrivateData = function () { - if (this._privateKey) crypto.randomBytes(this._privateKey.length).copy(this._privateKey) - this._privateKey = null - return this -} + if (this._privateKey) + crypto.randomBytes(this._privateKey.length).copy(this._privateKey); + this._privateKey = null; + return this; +}; HDKey.prototype.toJSON = function () { return { xpriv: this.privateExtendedKey, - xpub: this.publicExtendedKey - } -} + xpub: this.publicExtendedKey, + }; +}; HDKey.fromMasterSeed = function (seedBuffer, versions) { - var I = crypto.createHmac('sha512', MASTER_SECRET).update(seedBuffer).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) + var I = crypto + .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 + var hdkey = new HDKey(versions); + hdkey.chainCode = IR; + hdkey.privateKey = IL; - return hdkey -} + return hdkey; +}; HDKey.fromExtendedKey = function (base58key, versions, skipVerification) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) - versions = versions || BITCOIN_VERSIONS - skipVerification = skipVerification || false - 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 + versions = versions || BITCOIN_VERSIONS; + skipVerification = skipVerification || false; + 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') + assert( + version === versions.public, + "Version mismatch: version does not match public", + ); if (skipVerification) { - setPublicKey(hdkey, key) + setPublicKey(hdkey, key); } else { - hdkey.publicKey = key + hdkey.publicKey = key; } } - return hdkey -} + return hdkey; +}; HDKey.fromJSON = function (obj) { - return HDKey.fromExtendedKey(obj.xpriv) -} + return HDKey.fromExtendedKey(obj.xpriv); +}; -function serialize (hdkey, version, key) { +function serialize(hdkey, version, key) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) - var buffer = Buffer.allocUnsafe(LEN) + var buffer = Buffer.allocUnsafe(LEN); - buffer.writeUInt32BE(version, 0) - buffer.writeUInt8(hdkey.depth, 4) + 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) + 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) + hdkey.chainCode.copy(buffer, 13); + key.copy(buffer, 45); - return buffer + return buffer; } -function hash160 (buf) { - var sha = crypto.createHash('sha256').update(buf).digest() - return new RIPEMD160().update(sha).digest() +function hash160(buf) { + var sha = crypto.createHash("sha256").update(buf).digest(); + return new RIPEMD160().update(sha).digest(); } -HDKey.HARDENED_OFFSET = HARDENED_OFFSET -module.exports = HDKey +HDKey.HARDENED_OFFSET = HARDENED_OFFSET; +module.exports = HDKey; diff --git a/package.json b/package.json index 3845b5e..2fb9d72 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,7 @@ "mocha": "^6.1.4", "mocha-lcov-reporter": "0.0.1", "mochify": "^6.3.0", - "secure-random": "^1.0.0", - "standard": "^7.1.1" + "secure-random": "^1.0.0" }, "dependencies": { "bs58check": "^2.1.2", @@ -40,11 +39,13 @@ "secp256k1": "^4.0.0" }, "scripts": { - "lint": "standard", + "lint": "npx -p typescript@4.x -- tsc -p ./jsconfig.json", "browser-test": "mochify --wd -R spec", - "test": "standard && mocha", + "test": "mocha", "unit": "mocha", "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", - "coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info" + "coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info", + "bump": "npm version -m \"chore(release): bump to v%s\"", + "fmt": "npx -p prettier@2.x -- prettier -w '**/*.{js,md}'" } } From e145a5240a49d7537d2c28f58b7900db4d7c6baa Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 11:50:01 -0700 Subject: [PATCH 02/22] chore: make README.md, tests, and CHANGELOG Prettier f: make Prettier --- CHANGELOG.md | 189 ++++++++----- README.md | 87 +++--- test/hdkey.test.js | 653 +++++++++++++++++++++++++-------------------- 3 files changed, 539 insertions(+), 390 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca9cd6..9e0511d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,97 +1,145 @@ -2.1.0 / 2023-01-18 ------------------- +## 2.1.0 / 2023-01-18 -- Add `skipVerification` option to `HDKey.fromExtendedKey()` to allow skipping verification logic for performance ([#53](https://github.com/cryptocoinjs/hdkey/pull/53)) -- Use `ripemd160` package; as Node v18+ `crypto.createHash()` does not support ripemd160 ([#51](https://github.com/cryptocoinjs/hdkey/pull/51)) -- Fix `.sign()` method in some environments with polyfilled `Buffer` implementations ([#50](https://github.com/cryptocoinjs/hdkey/pull/50)) -- Performance improvements ([#52](https://github.com/cryptocoinjs/hdkey/pull/52)) +- Add `skipVerification` option to `HDKey.fromExtendedKey()` to allow skipping + verification logic for performance + ([#53](https://github.com/cryptocoinjs/hdkey/pull/53)) +- Use `ripemd160` package; as Node v18+ `crypto.createHash()` does not support + ripemd160 ([#51](https://github.com/cryptocoinjs/hdkey/pull/51)) +- Fix `.sign()` method in some environments with polyfilled `Buffer` + implementations ([#50](https://github.com/cryptocoinjs/hdkey/pull/50)) +- Performance improvements + ([#52](https://github.com/cryptocoinjs/hdkey/pull/52)) -2.0.1 / 2020-05-30 ------------------- + 2.0.1 / 2020-05-30 -- Bugfix: prevent mutating buffers passed in ([#39](https://github.com/cryptocoinjs/hdkey/pull/39)) +--- -2.0.0 / 2020-05-29 ------------------- +- Bugfix: prevent mutating buffers passed in + ([#39](https://github.com/cryptocoinjs/hdkey/pull/39)) -- **BREAKING:** Require Node.js v10+ ([#38](https://github.com/cryptocoinjs/hdkey/pull/38)) -- Upgrade `secp256k1` dependency; to use N-API ([#32](https://github.com/cryptocoinjs/hdkey/pull/32)) + 2.0.0 / 2020-05-29 -1.1.2 / 2020-04-16 ------------------- +--- -- Fix extremely rare types bug ([#33](https://github.com/cryptocoinjs/hdkey/pull/33)) -- Use `bs58check` dependency instead of `coinstring` ([#30](https://github.com/cryptocoinjs/hdkey/pull/30)) -- Don't publish test files ([#27](https://github.com/cryptocoinjs/hdkey/issues/27), [#34](https://github.com/cryptocoinjs/hdkey/pull/34)) +- **BREAKING:** Require Node.js v10+ + ([#38](https://github.com/cryptocoinjs/hdkey/pull/38)) +- Upgrade `secp256k1` dependency; to use N-API + ([#32](https://github.com/cryptocoinjs/hdkey/pull/32)) -1.1.1 / 2019-02-09 ------------------- + 1.1.2 / 2020-04-16 -- Fix Electron v4 support. No changes to external API. ([#26](https://github.com/cryptocoinjs/hdkey/pull/26)) +--- -1.1.0 / 2018-08-14 ------------------- +- Fix extremely rare types bug + ([#33](https://github.com/cryptocoinjs/hdkey/pull/33)) +- Use `bs58check` dependency instead of `coinstring` + ([#30](https://github.com/cryptocoinjs/hdkey/pull/30)) +- Don't publish test files + ([#27](https://github.com/cryptocoinjs/hdkey/issues/27), + [#34](https://github.com/cryptocoinjs/hdkey/pull/34)) -- Add `wipePrivateData()` method ([#22](https://github.com/cryptocoinjs/hdkey/pull/22)) -- Add missing LICENSE file ([#21](https://github.com/cryptocoinjs/hdkey/pull/21)) + 1.1.1 / 2019-02-09 -1.0.0 / 2018-05-24 ------------------- +--- + +- Fix Electron v4 support. No changes to external API. + ([#26](https://github.com/cryptocoinjs/hdkey/pull/26)) + + 1.1.0 / 2018-08-14 + +--- + +- Add `wipePrivateData()` method + ([#22](https://github.com/cryptocoinjs/hdkey/pull/22)) +- Add missing LICENSE file + ([#21](https://github.com/cryptocoinjs/hdkey/pull/21)) + + 1.0.0 / 2018-05-24 + +--- - drop support for all Node.js versions 4 and earlier -- fix `derive()` path validation ([#20](https://github.com/cryptocoinjs/hdkey/pull/20)) +- fix `derive()` path validation + ([#20](https://github.com/cryptocoinjs/hdkey/pull/20)) + + 0.8.0 / 2018-02-06 + +--- -0.8.0 / 2018-02-06 ------------------- - add `sign()` and `verify()` - upgrade to `safe-buffer` -0.7.1 / 2016-05-26 ------------------- -- fix bug when `privateKey` is `null`, `privateExtendedKey` should not throw, and return `null` [#7][#7] + 0.7.1 / 2016-05-26 + +--- + +- fix bug when `privateKey` is `null`, `privateExtendedKey` should not throw, + and return `null` [#7][#7] + + 0.7.0 / 2016-03-22 + +--- -0.7.0 / 2016-03-22 ------------------- - upgrade from `ecurve` to `secp256k1`. [#5][#5] -0.6.0 / 2015-07-02 ------------------- -- **breaking** (same day though, haha). Changed `publicExtendedKey`/`privateExtendedKey` in `JSON` methods to `xpub`/`xpriv` + 0.6.0 / 2015-07-02 + +--- + +- **breaking** (same day though, haha). Changed + `publicExtendedKey`/`privateExtendedKey` in `JSON` methods to `xpub`/`xpriv` - export `HARDENED_OFFSET` -0.5.0 / 2015-07-02 ------------------- + 0.5.0 / 2015-07-02 + +--- + - JavaScript Standard Style - fix rare condition for BIP32 consistency: [#1][#1] - added `toJSON()/fromJSON()` -0.4.0 / 2014-09-24 ------------------- -- dropped `sha512` dependency and upgraded to crypto-browserify that supports sha512 + 0.4.0 / 2014-09-24 + +--- + +- dropped `sha512` dependency and upgraded to crypto-browserify that supports + sha512 + + 0.3.1 / 2014-07-11 + +--- -0.3.1 / 2014-07-11 ------------------- - removed superfluous code `this._privateKeyBigInteger` -0.3.0 / 2014-06-29 ------------------- -- bugfix: if private key was less than 32 bytes, pad out to 32 bytes with leading zeros (this happens in derive) -- changed behavior of `privateExtendedKey()` and `publicExtendedKey()` to return base 58 encoded `string` instead of `Buffer` -- changed behavior of `fromExtendedKey()` from accepting a type of `Buffer` bytes to base58 `string` + 0.3.0 / 2014-06-29 + +--- + +- bugfix: if private key was less than 32 bytes, pad out to 32 bytes with + leading zeros (this happens in derive) +- changed behavior of `privateExtendedKey()` and `publicExtendedKey()` to return + base 58 encoded `string` instead of `Buffer` +- changed behavior of `fromExtendedKey()` from accepting a type of `Buffer` + bytes to base58 `string` + + 0.2.0 / 2014-06-25 + +--- -0.2.0 / 2014-06-25 ------------------- - upgraded `"ecurve": "^0.8.0"` to `"ecurve": "^1.0.0"` - added functionality to derive public to public child keys -0.1.0 / 2014-06-16 ------------------- + 0.1.0 / 2014-06-16 + +--- + - removed semicolons per http://cryptocoinjs.com/about/contributing/#semicolons - removed `ECKey` dep - added `ecurve` dep - removed `terst` dev dep for `assert` - added method `fromMasterSeed(seedBuffer, [versions])` -- changed constructor from `new HDKey(masterSeed, [versions])` to `new HDKey([versions])` +- changed constructor from `new HDKey(masterSeed, [versions])` to + `new HDKey([versions])` - added properties: `privateKey` and `publicKey` - removed method `getIdentifier()`, added property `identifier` - removed method `getFingerprint()`, added property `fingerprint` @@ -99,15 +147,28 @@ - renamed `public` to `publicExtendedKey` - added method `fromExtendedKey()` -0.0.1 / 2014-05-29 ------------------- + 0.0.1 / 2014-05-29 + +--- + - initial release -[#7]: https://github.com/cryptocoinjs/hdkey/issues/7 "privateExtendedKey error handling" -[#6]: https://github.com/cryptocoinjs/hdkey/pull/6 "hdkey: use bippath for BIP32 path parsing and validation" -[#5]: https://github.com/cryptocoinjs/hdkey/pull/5 "hdkey: use the secp256k1 package for crypto" -[#4]: https://github.com/cryptocoinjs/hdkey/issues/4 "Is this library still maintained?" -[#3]: https://github.com/cryptocoinjs/hdkey/pull/3 "Update hdkey.js" -[#2]: https://github.com/cryptocoinjs/hdkey/pull/2 "Update hdkey.js" -[#1]: https://github.com/cryptocoinjs/hdkey/issues/1 "rare condition needed for bip consistency" + +[#7]: + https://github.com/cryptocoinjs/hdkey/issues/7 + "privateExtendedKey error handling" +[#6]: + https://github.com/cryptocoinjs/hdkey/pull/6 + "hdkey: use bippath for BIP32 path parsing and validation" +[#5]: + https://github.com/cryptocoinjs/hdkey/pull/5 + "hdkey: use the secp256k1 package for crypto" +[#4]: + https://github.com/cryptocoinjs/hdkey/issues/4 + "Is this library still maintained?" +[#3]: https://github.com/cryptocoinjs/hdkey/pull/3 "Update hdkey.js" +[#2]: https://github.com/cryptocoinjs/hdkey/pull/2 "Update hdkey.js" +[#1]: + https://github.com/cryptocoinjs/hdkey/issues/1 + "rare condition needed for bip consistency" diff --git a/README.md b/README.md index 425bd71..4f5cf3e 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,64 @@ -hdkey -===== +# hdkey [![NPM Package](https://img.shields.io/npm/v/hdkey.svg?style=flat-square)](https://www.npmjs.org/package/hdkey) [![build status](https://secure.travis-ci.org/cryptocoinjs/hdkey.svg)](http://travis-ci.org/cryptocoinjs/hdkey) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) -A JavaScript component for [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)(hierarchical deterministic keys). +A JavaScript component for +[BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)(hierarchical +deterministic keys). - -Installation ------------- +## Installation ```bash npm i --save hdkey ``` - -Usage ------ +## Usage **example:** ```js -var HDKey = require('hdkey') -var seed = 'a0c42a9c3ac6abf2ba6a9946ae83af18f51bf1c9fa7dacc4c92513cc4dd015834341c775dcd4c0fac73547c5662d81a9e9361a0aac604a73a321bd9103bce8af' -var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) -console.log(hdkey.privateExtendedKey) +var HDKey = require("hdkey"); +var seed = + "a0c42a9c3ac6abf2ba6a9946ae83af18f51bf1c9fa7dacc4c92513cc4dd015834341c775dcd4c0fac73547c5662d81a9e9361a0aac604a73a321bd9103bce8af"; +var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); +console.log(hdkey.privateExtendedKey); // => 'xprv9s21ZrQH143K2SKJK9EYRW3Vsg8tWVHRS54hAJasj1eGsQXeWDHLeuu5hpLHRbeKedDJM4Wj9wHHMmuhPF8dQ3bzyup6R7qmMQ1i1FtzNEW' -console.log(hdkey.publicExtendedKey) +console.log(hdkey.publicExtendedKey); // => 'xpub661MyMwAqRbcEvPmRAmYndzERhyNux1GoHzHxgzVHMBFkCro3kbbCiDZZ5XabZDyXPj5mH3hktvkjhhUdCQxie5e1g4t2GuAWNbPmsSfDp2' ``` - ### `HDKey.fromMasterSeed(seedBuffer[, versions])` -Creates an `hdkey` object from a master seed buffer. Accepts an optional `versions` object. +Creates an `hdkey` object from a master seed buffer. Accepts an optional +`versions` object. ```js -var seed = 'a0c42a9c3ac6abf2ba6a9946ae83af18f51bf1c9fa7dacc4c92513cc4dd015834341c775dcd4c0fac73547c5662d81a9e9361a0aac604a73a321bd9103bce8af' -var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) +var seed = + "a0c42a9c3ac6abf2ba6a9946ae83af18f51bf1c9fa7dacc4c92513cc4dd015834341c775dcd4c0fac73547c5662d81a9e9361a0aac604a73a321bd9103bce8af"; +var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); ``` ### `HDKey.fromExtendedKey(extendedKey[, versions, skipVerification])` -Creates an `hdkey` object from a `xprv` or `xpub` extended key string. Accepts an optional `versions` object & an optional `skipVerification` boolean. If `skipVerification` is set to true, then the provided public key's x (and y if uncompressed) coordinate will not will be verified to be on the curve. +Creates an `hdkey` object from a `xprv` or `xpub` extended key string. Accepts +an optional `versions` object & an optional `skipVerification` boolean. If +`skipVerification` is set to true, then the provided public key's x (and y if +uncompressed) coordinate will not will be verified to be on the curve. ```js -var key = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j' -var hdkey = HDKey.fromExtendedKey(key) +var key = + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; +var hdkey = HDKey.fromExtendedKey(key); ``` **or** ```js -var key = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt' -var hdkey = HDKey.fromExtendedKey(key) +var key = + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; +var hdkey = HDKey.fromExtendedKey(key); ``` ### `HDKey.fromJSON(obj)` @@ -68,13 +72,14 @@ Creates an `hdkey` object from an object created via `hdkey.toJSON()`. Derives the `hdkey` at `path` from the current `hdkey`. ```js -var seed = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542' -var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) -var childkey = hdkey.derive("m/0/2147483647'/1") +var seed = + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; +var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); +var childkey = hdkey.derive("m/0/2147483647'/1"); -console.log(childkey.privateExtendedKey) +console.log(childkey.privateExtendedKey); // -> "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef" -console.log(childkey.publicExtendedKey) +console.log(childkey.publicExtendedKey); // -> "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon" ``` @@ -87,25 +92,31 @@ var childkey = hdkey.derive("m/44'/0'/0'/0/0"); ### `hdkey.sign(hash)` -Signs the buffer `hash` with the private key using `secp256k1` and returns the signature as a buffer. +Signs the buffer `hash` with the private key using `secp256k1` and returns the +signature as a buffer. ### `hdkey.verify(hash, signature)` -Verifies that the `signature` is valid for `hash` and the `hdkey`'s public key using `secp256k1`. Returns `true` for valid, `false` for invalid. Throws if the `hash` or `signature` is the wrong length. +Verifies that the `signature` is valid for `hash` and the `hdkey`'s public key +using `secp256k1`. Returns `true` for valid, `false` for invalid. Throws if the +`hash` or `signature` is the wrong length. ### `hdkey.wipePrivateData()` -Wipes all record of the private key from the `hdkey` instance. After calling this method, the instance will behave as if it was created via `HDKey.fromExtendedKey(xpub)`. +Wipes all record of the private key from the `hdkey` instance. After calling +this method, the instance will behave as if it was created via +`HDKey.fromExtendedKey(xpub)`. ### `hdkey.toJSON()` Serializes the `hdkey` to an object that can be `JSON.stringify()`ed. ```js -var seed = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542' -var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) +var seed = + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; +var hdkey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); -console.log(hdkey.toJSON()) +console.log(hdkey.toJSON()); // -> { // xpriv: 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U', // xpub: 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB' @@ -128,16 +139,14 @@ Getter/Setter of the `hdkey`'s `xprv`, stored as a string. Getter/Setter of the `hdkey`'s `xpub`, stored as a string. -References ----------- +## References + - https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/hdnode.js - http://bip32.org/ - http://blog.richardkiss.com/?p=313 - https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki - http://bitcoinmagazine.com/8396/deterministic-wallets-advantages-flaw/ - -License -------- +## License MIT diff --git a/test/hdkey.test.js b/test/hdkey.test.js index 6c87505..90bd5cb 100644 --- a/test/hdkey.test.js +++ b/test/hdkey.test.js @@ -1,311 +1,390 @@ -var assert = require('assert') -var BigInteger = require('bigi') -var Buffer = require('safe-buffer').Buffer -var ecurve = require('ecurve') -var secureRandom = require('secure-random') -var curve = ecurve.getCurveByName('secp256k1') -var HDKey = require('../') -var fixtures = require('./fixtures/hdkey') +var assert = require("assert"); +var BigInteger = require("bigi"); +var Buffer = require("safe-buffer").Buffer; +var ecurve = require("ecurve"); +var secureRandom = require("secure-random"); +var curve = ecurve.getCurveByName("secp256k1"); +var HDKey = require("../"); +var fixtures = require("./fixtures/hdkey"); // trinity: mocha /* global describe it */ -describe('hdkey', function () { - describe('+ fromMasterSeed', function () { +describe("hdkey", function () { + describe("+ fromMasterSeed", function () { fixtures.valid.forEach(function (f) { - it('should properly derive the chain path: ' + f.path, function () { - var hdkey = HDKey.fromMasterSeed(Buffer.from(f.seed, 'hex')) - var childkey = hdkey.derive(f.path) + it("should properly derive the chain path: " + f.path, function () { + var hdkey = HDKey.fromMasterSeed(Buffer.from(f.seed, "hex")); + var childkey = hdkey.derive(f.path); - assert.equal(childkey.privateExtendedKey, f.private) - assert.equal(childkey.publicExtendedKey, f.public) - }) + assert.equal(childkey.privateExtendedKey, f.private); + assert.equal(childkey.publicExtendedKey, f.public); + }); - describe('> ' + f.path + ' toJSON() / fromJSON()', function () { - it('should return an object read for JSON serialization', function () { - var hdkey = HDKey.fromMasterSeed(Buffer.from(f.seed, 'hex')) - var childkey = hdkey.derive(f.path) + describe("> " + f.path + " toJSON() / fromJSON()", function () { + it("should return an object read for JSON serialization", function () { + var hdkey = HDKey.fromMasterSeed(Buffer.from(f.seed, "hex")); + var childkey = hdkey.derive(f.path); var obj = { xpriv: f.private, - xpub: f.public - } - - assert.deepEqual(childkey.toJSON(), obj) - - var newKey = HDKey.fromJSON(obj) - assert.strictEqual(newKey.privateExtendedKey, f.private) - assert.strictEqual(newKey.publicExtendedKey, f.public) - }) - }) - }) - }) - - describe('- privateKey', function () { - it('should throw an error if incorrect key size', function () { - var hdkey = new HDKey() + xpub: f.public, + }; + + assert.deepEqual(childkey.toJSON(), obj); + + var newKey = HDKey.fromJSON(obj); + assert.strictEqual(newKey.privateExtendedKey, f.private); + assert.strictEqual(newKey.publicExtendedKey, f.public); + }); + }); + }); + }); + + describe("- privateKey", function () { + it("should throw an error if incorrect key size", function () { + var hdkey = new HDKey(); assert.throws(function () { - hdkey.privateKey = Buffer.from([1, 2, 3, 4]) - }, /key must be 32/) - }) - }) + hdkey.privateKey = Buffer.from([1, 2, 3, 4]); + }, /key must be 32/); + }); + }); - describe('- publicKey', function () { - it('should throw an error if incorrect key size', function () { + describe("- publicKey", function () { + it("should throw an error if incorrect key size", function () { assert.throws(function () { - var hdkey = new HDKey() - hdkey.publicKey = Buffer.from([1, 2, 3, 4]) - }, /key must be 33 or 65/) - }) - - it('should not throw if key is 33 bytes (compressed)', function () { - var priv = secureRandom.randomBuffer(32) - var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true) - assert.equal(pub.length, 33) - var hdkey = new HDKey() - hdkey.publicKey = pub - }) - - it('should not throw if key is 65 bytes (not compressed)', function () { - var priv = secureRandom.randomBuffer(32) - var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false) - assert.equal(pub.length, 65) - var hdkey = new HDKey() - hdkey.publicKey = pub - }) - }) - - describe('+ fromExtendedKey()', function () { - describe('> when private', function () { - it('should parse it', function () { + var hdkey = new HDKey(); + hdkey.publicKey = Buffer.from([1, 2, 3, 4]); + }, /key must be 33 or 65/); + }); + + it("should not throw if key is 33 bytes (compressed)", function () { + var priv = secureRandom.randomBuffer(32); + var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true); + assert.equal(pub.length, 33); + var hdkey = new HDKey(); + hdkey.publicKey = pub; + }); + + it("should not throw if key is 65 bytes (not compressed)", function () { + var priv = secureRandom.randomBuffer(32); + var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false); + assert.equal(pub.length, 65); + var hdkey = new HDKey(); + hdkey.publicKey = pub; + }); + }); + + describe("+ fromExtendedKey()", function () { + describe("> when private", function () { + it("should parse it", function () { // m/0/2147483647'/1/2147483646'/2 - var key = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j' - var hdkey = HDKey.fromExtendedKey(key) - assert.equal(hdkey.versions.private, 0x0488ade4) - assert.equal(hdkey.versions.public, 0x0488b21e) - assert.equal(hdkey.depth, 5) - assert.equal(hdkey.parentFingerprint, 0x31a507b8) - assert.equal(hdkey.index, 2) - assert.equal(hdkey.chainCode.toString('hex'), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') - assert.equal(hdkey.privateKey.toString('hex'), 'bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') - assert.equal(hdkey.publicKey.toString('hex'), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(hdkey.identifier.toString('hex'), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') - }) - }) - - describe('> when public', function () { - it('should parse it', function () { + var key = + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; + var hdkey = HDKey.fromExtendedKey(key); + assert.equal(hdkey.versions.private, 0x0488ade4); + assert.equal(hdkey.versions.public, 0x0488b21e); + assert.equal(hdkey.depth, 5); + assert.equal(hdkey.parentFingerprint, 0x31a507b8); + assert.equal(hdkey.index, 2); + assert.equal( + hdkey.chainCode.toString("hex"), + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", + ); + assert.equal( + hdkey.privateKey.toString("hex"), + "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", + ); + assert.equal( + hdkey.publicKey.toString("hex"), + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", + ); + assert.equal( + hdkey.identifier.toString("hex"), + "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", + ); + }); + }); + + describe("> when public", function () { + it("should parse it", function () { // m/0/2147483647'/1/2147483646'/2 - var key = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt' - var hdkey = HDKey.fromExtendedKey(key) - assert.equal(hdkey.versions.private, 0x0488ade4) - assert.equal(hdkey.versions.public, 0x0488b21e) - assert.equal(hdkey.depth, 5) - assert.equal(hdkey.parentFingerprint, 0x31a507b8) - assert.equal(hdkey.index, 2) - assert.equal(hdkey.chainCode.toString('hex'), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') - assert.equal(hdkey.privateKey, null) - assert.equal(hdkey.publicKey.toString('hex'), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(hdkey.identifier.toString('hex'), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') - }) - - it('should parse it without verification', function () { + var key = + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; + var hdkey = HDKey.fromExtendedKey(key); + assert.equal(hdkey.versions.private, 0x0488ade4); + assert.equal(hdkey.versions.public, 0x0488b21e); + assert.equal(hdkey.depth, 5); + assert.equal(hdkey.parentFingerprint, 0x31a507b8); + assert.equal(hdkey.index, 2); + assert.equal( + hdkey.chainCode.toString("hex"), + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", + ); + assert.equal(hdkey.privateKey, null); + assert.equal( + hdkey.publicKey.toString("hex"), + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", + ); + assert.equal( + hdkey.identifier.toString("hex"), + "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", + ); + }); + + it("should parse it without verification", function () { // m/0/2147483647'/1/2147483646'/2 - var key = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt' - var hdkey = HDKey.fromExtendedKey(key, null, false) - assert.equal(hdkey.versions.private, 0x0488ade4) - assert.equal(hdkey.versions.public, 0x0488b21e) - assert.equal(hdkey.depth, 5) - assert.equal(hdkey.parentFingerprint, 0x31a507b8) - assert.equal(hdkey.index, 2) - assert.equal(hdkey.chainCode.toString('hex'), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') - assert.equal(hdkey.privateKey, null) - assert.equal(hdkey.publicKey.toString('hex'), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(hdkey.identifier.toString('hex'), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') - }) - }) - }) - - describe('> when signing', function () { - it('should work', function () { - var key = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j' - var hdkey = HDKey.fromExtendedKey(key) - - var ma = Buffer.alloc(32, 0) - var mb = Buffer.alloc(32, 8) - var a = hdkey.sign(ma) - var b = hdkey.sign(mb) - assert.equal(a.toString('hex'), '6ba4e554457ce5c1f1d7dbd10459465e39219eb9084ee23270688cbe0d49b52b7905d5beb28492be439a3250e9359e0390f844321b65f1a88ce07960dd85da06') - assert.equal(b.toString('hex'), 'dfae85d39b73c9d143403ce472f7c4c8a5032c13d9546030044050e7d39355e47a532e5c0ae2a25392d97f5e55ab1288ef1e08d5c034bad3b0956fbbab73b381') - assert.equal(hdkey.verify(ma, a), true) - assert.equal(hdkey.verify(mb, b), true) - assert.equal(hdkey.verify(Buffer.alloc(32), Buffer.alloc(64)), false) - assert.equal(hdkey.verify(ma, b), false) - assert.equal(hdkey.verify(mb, a), false) + var key = + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; + var hdkey = HDKey.fromExtendedKey(key, null, false); + assert.equal(hdkey.versions.private, 0x0488ade4); + assert.equal(hdkey.versions.public, 0x0488b21e); + assert.equal(hdkey.depth, 5); + assert.equal(hdkey.parentFingerprint, 0x31a507b8); + assert.equal(hdkey.index, 2); + assert.equal( + hdkey.chainCode.toString("hex"), + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", + ); + assert.equal(hdkey.privateKey, null); + assert.equal( + hdkey.publicKey.toString("hex"), + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", + ); + assert.equal( + hdkey.identifier.toString("hex"), + "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", + ); + }); + }); + }); + + describe("> when signing", function () { + it("should work", function () { + var key = + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; + var hdkey = HDKey.fromExtendedKey(key); + + var ma = Buffer.alloc(32, 0); + var mb = Buffer.alloc(32, 8); + var a = hdkey.sign(ma); + var b = hdkey.sign(mb); + assert.equal( + a.toString("hex"), + "6ba4e554457ce5c1f1d7dbd10459465e39219eb9084ee23270688cbe0d49b52b7905d5beb28492be439a3250e9359e0390f844321b65f1a88ce07960dd85da06", + ); + assert.equal( + b.toString("hex"), + "dfae85d39b73c9d143403ce472f7c4c8a5032c13d9546030044050e7d39355e47a532e5c0ae2a25392d97f5e55ab1288ef1e08d5c034bad3b0956fbbab73b381", + ); + assert.equal(hdkey.verify(ma, a), true); + assert.equal(hdkey.verify(mb, b), true); + assert.equal(hdkey.verify(Buffer.alloc(32), Buffer.alloc(64)), false); + assert.equal(hdkey.verify(ma, b), false); + assert.equal(hdkey.verify(mb, a), false); assert.throws(function () { - hdkey.verify(Buffer.alloc(99), a) - }, /message.*length/) + hdkey.verify(Buffer.alloc(99), a); + }, /message.*length/); assert.throws(function () { - hdkey.verify(ma, Buffer.alloc(99)) - }, /signature.*length/) - }) - }) - - describe('> when deriving public key', function () { - it('should work', function () { - var key = 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8' - var hdkey = HDKey.fromExtendedKey(key) - - var path = 'm/3353535/2223/0/99424/4/33' - var derivedHDKey = hdkey.derive(path) - - var expected = 'xpub6JdKdVJtdx6sC3nh87pDvnGhotXuU5Kz6Qy7Piy84vUAwWSYShsUGULE8u6gCivTHgz7cCKJHiXaaMeieB4YnoFVAsNgHHKXJ2mN6jCMbH1' - assert.equal(derivedHDKey.publicExtendedKey, expected) - }) - }) - - describe('> when private key integer is less than 32 bytes', function () { - it('should work', function () { - var seed = '000102030405060708090a0b0c0d0e0f' - var masterKey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) - - var newKey = masterKey.derive("m/44'/6'/4'") - var expected = 'xprv9ymoag6W7cR6KBcJzhCM6qqTrb3rRVVwXKzwNqp1tDWcwierEv3BA9if3ARHMhMPh9u2jNoutcgpUBLMfq3kADDo7LzfoCnhhXMRGX3PXDx' - assert.equal(newKey.privateExtendedKey, expected) - }) - }) - - describe('HARDENED_OFFSET', function () { - it('should be set', function () { - assert(HDKey.HARDENED_OFFSET) - }) - }) - - describe('> when private key has leading zeros', function () { - it('will include leading zeros when hashing to derive child', function () { - var key = 'xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr' - var hdkey = HDKey.fromExtendedKey(key) - assert.equal(hdkey.privateKey.toString('hex'), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd') - var derived = hdkey.derive("m/44'/0'/0'/0/0'") - assert.equal(derived.privateKey.toString('hex'), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb') - }) - }) - - describe('> when private key is null', function () { - it('privateExtendedKey should return null and not throw', function () { - var seed = '000102030405060708090a0b0c0d0e0f' - var masterKey = HDKey.fromMasterSeed(Buffer.from(seed, 'hex')) - - assert.ok(masterKey.privateExtendedKey, 'xpriv is truthy') - masterKey._privateKey = null + hdkey.verify(ma, Buffer.alloc(99)); + }, /signature.*length/); + }); + }); + + describe("> when deriving public key", function () { + it("should work", function () { + var key = + "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; + var hdkey = HDKey.fromExtendedKey(key); + + var path = "m/3353535/2223/0/99424/4/33"; + var derivedHDKey = hdkey.derive(path); + + var expected = + "xpub6JdKdVJtdx6sC3nh87pDvnGhotXuU5Kz6Qy7Piy84vUAwWSYShsUGULE8u6gCivTHgz7cCKJHiXaaMeieB4YnoFVAsNgHHKXJ2mN6jCMbH1"; + assert.equal(derivedHDKey.publicExtendedKey, expected); + }); + }); + + describe("> when private key integer is less than 32 bytes", function () { + it("should work", function () { + var seed = "000102030405060708090a0b0c0d0e0f"; + var masterKey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); + + var newKey = masterKey.derive("m/44'/6'/4'"); + var expected = + "xprv9ymoag6W7cR6KBcJzhCM6qqTrb3rRVVwXKzwNqp1tDWcwierEv3BA9if3ARHMhMPh9u2jNoutcgpUBLMfq3kADDo7LzfoCnhhXMRGX3PXDx"; + assert.equal(newKey.privateExtendedKey, expected); + }); + }); + + describe("HARDENED_OFFSET", function () { + it("should be set", function () { + assert(HDKey.HARDENED_OFFSET); + }); + }); + + describe("> when private key has leading zeros", function () { + it("will include leading zeros when hashing to derive child", function () { + var key = + "xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr"; + var hdkey = HDKey.fromExtendedKey(key); + assert.equal( + hdkey.privateKey.toString("hex"), + "00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd", + ); + var derived = hdkey.derive("m/44'/0'/0'/0/0'"); + assert.equal( + derived.privateKey.toString("hex"), + "3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb", + ); + }); + }); + + describe("> when private key is null", function () { + it("privateExtendedKey should return null and not throw", function () { + var seed = "000102030405060708090a0b0c0d0e0f"; + var masterKey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); + + assert.ok(masterKey.privateExtendedKey, "xpriv is truthy"); + masterKey._privateKey = null; assert.doesNotThrow(function () { - masterKey.privateExtendedKey - }) - - assert.ok(!masterKey.privateExtendedKey, 'xpriv is falsy') - }) - }) - - describe(' - when the path given to derive contains only the master extended key', function () { - const hdKeyInstance = HDKey.fromMasterSeed(Buffer.from(fixtures.valid[0].seed, 'hex')) - - it('should return the same hdkey instance', function () { - assert.equal(hdKeyInstance.derive('m'), hdKeyInstance) - assert.equal(hdKeyInstance.derive('M'), hdKeyInstance) - assert.equal(hdKeyInstance.derive("m'"), hdKeyInstance) - assert.equal(hdKeyInstance.derive("M'"), hdKeyInstance) - }) - }) - - describe(' - when the path given to derive does not begin with master extended key', function () { - it('should throw an error', function () { + masterKey.privateExtendedKey; + }); + + assert.ok(!masterKey.privateExtendedKey, "xpriv is falsy"); + }); + }); + + describe(" - when the path given to derive contains only the master extended key", function () { + const hdKeyInstance = HDKey.fromMasterSeed( + Buffer.from(fixtures.valid[0].seed, "hex"), + ); + + it("should return the same hdkey instance", function () { + assert.equal(hdKeyInstance.derive("m"), hdKeyInstance); + assert.equal(hdKeyInstance.derive("M"), hdKeyInstance); + assert.equal(hdKeyInstance.derive("m'"), hdKeyInstance); + assert.equal(hdKeyInstance.derive("M'"), hdKeyInstance); + }); + }); + + describe(" - when the path given to derive does not begin with master extended key", function () { + it("should throw an error", function () { assert.throws(function () { - HDKey.prototype.derive('123') - }, /Path must start with "m" or "M"/) - }) - }) - - describe('- after wipePrivateData()', function () { - it('should not have private data', function () { - const hdkey = HDKey.fromMasterSeed(Buffer.from(fixtures.valid[6].seed, 'hex')).wipePrivateData() - assert.equal(hdkey.privateKey, null) - assert.equal(hdkey.privateExtendedKey, null) - assert.throws(() => hdkey.sign(Buffer.alloc(32)), "shouldn't be able to sign") - const childKey = hdkey.derive('m/0') - assert.equal(childKey.publicExtendedKey, fixtures.valid[7].public) - assert.equal(childKey.privateKey, null) - assert.equal(childKey.privateExtendedKey, null) - }) - - it('should have correct data', function () { + HDKey.prototype.derive("123"); + }, /Path must start with "m" or "M"/); + }); + }); + + describe("- after wipePrivateData()", function () { + it("should not have private data", function () { + const hdkey = HDKey.fromMasterSeed( + Buffer.from(fixtures.valid[6].seed, "hex"), + ).wipePrivateData(); + assert.equal(hdkey.privateKey, null); + assert.equal(hdkey.privateExtendedKey, null); + assert.throws( + () => hdkey.sign(Buffer.alloc(32)), + "shouldn't be able to sign", + ); + const childKey = hdkey.derive("m/0"); + assert.equal(childKey.publicExtendedKey, fixtures.valid[7].public); + assert.equal(childKey.privateKey, null); + assert.equal(childKey.privateExtendedKey, null); + }); + + it("should have correct data", function () { // m/0/2147483647'/1/2147483646'/2 - const key = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j' - const hdkey = HDKey.fromExtendedKey(key).wipePrivateData() - assert.equal(hdkey.versions.private, 0x0488ade4) - assert.equal(hdkey.versions.public, 0x0488b21e) - assert.equal(hdkey.depth, 5) - assert.equal(hdkey.parentFingerprint, 0x31a507b8) - assert.equal(hdkey.index, 2) - assert.equal(hdkey.chainCode.toString('hex'), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') - assert.equal(hdkey.publicKey.toString('hex'), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(hdkey.identifier.toString('hex'), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') - }) - - it('should be able to verify signatures', function () { - const fullKey = HDKey.fromMasterSeed(fixtures.valid[0].seed) + const key = + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; + const hdkey = HDKey.fromExtendedKey(key).wipePrivateData(); + assert.equal(hdkey.versions.private, 0x0488ade4); + assert.equal(hdkey.versions.public, 0x0488b21e); + assert.equal(hdkey.depth, 5); + assert.equal(hdkey.parentFingerprint, 0x31a507b8); + assert.equal(hdkey.index, 2); + assert.equal( + hdkey.chainCode.toString("hex"), + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", + ); + assert.equal( + hdkey.publicKey.toString("hex"), + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", + ); + assert.equal( + hdkey.identifier.toString("hex"), + "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", + ); + }); + + it("should be able to verify signatures", function () { + const fullKey = HDKey.fromMasterSeed(fixtures.valid[0].seed); // using JSON methods to clone before mutating - const wipedKey = HDKey.fromJSON(fullKey.toJSON()).wipePrivateData() - - const hash = Buffer.alloc(32, 8) - assert.ok(wipedKey.verify(hash, fullKey.sign(hash))) - }) - - it('should not throw if called on hdkey without private data', function () { - const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].public) - assert.doesNotThrow(() => hdkey.wipePrivateData()) - assert.equal(hdkey.publicExtendedKey, fixtures.valid[0].public) - }) - }) - - describe('Deriving a child key does not mutate the internal state', function () { - it('should not mutate it when deriving with a private key', function () { - const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].private) - const path = 'm/123' - const privateKeyBefore = hdkey.privateKey.toString('hex') - - const child = hdkey.derive(path) - assert.equal(hdkey.privateKey.toString('hex'), privateKeyBefore) - - const child2 = hdkey.derive(path) - assert.equal(hdkey.privateKey.toString('hex'), privateKeyBefore) - - const child3 = hdkey.derive(path) - assert.equal(hdkey.privateKey.toString('hex'), privateKeyBefore) - - assert.equal(child.privateKey.toString('hex'), child2.privateKey.toString('hex')) - assert.equal(child2.privateKey.toString('hex'), child3.privateKey.toString('hex')) - }) - - it('should not mutate it when deriving without a private key', function () { - const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].private) - const path = 'm/123/123/123' - hdkey.wipePrivateData() - - const publicKeyBefore = hdkey.publicKey.toString('hex') - - const child = hdkey.derive(path) - assert.equal(hdkey.publicKey.toString('hex'), publicKeyBefore) - - const child2 = hdkey.derive(path) - assert.equal(hdkey.publicKey.toString('hex'), publicKeyBefore) - - const child3 = hdkey.derive(path) - assert.equal(hdkey.publicKey.toString('hex'), publicKeyBefore) - - assert.equal(child.publicKey.toString('hex'), child2.publicKey.toString('hex')) - assert.equal(child2.publicKey.toString('hex'), child3.publicKey.toString('hex')) - }) - }) -}) + const wipedKey = HDKey.fromJSON(fullKey.toJSON()).wipePrivateData(); + + const hash = Buffer.alloc(32, 8); + assert.ok(wipedKey.verify(hash, fullKey.sign(hash))); + }); + + it("should not throw if called on hdkey without private data", function () { + const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].public); + assert.doesNotThrow(() => hdkey.wipePrivateData()); + assert.equal(hdkey.publicExtendedKey, fixtures.valid[0].public); + }); + }); + + describe("Deriving a child key does not mutate the internal state", function () { + it("should not mutate it when deriving with a private key", function () { + const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].private); + const path = "m/123"; + const privateKeyBefore = hdkey.privateKey.toString("hex"); + + const child = hdkey.derive(path); + assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + + const child2 = hdkey.derive(path); + assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + + const child3 = hdkey.derive(path); + assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + + assert.equal( + child.privateKey.toString("hex"), + child2.privateKey.toString("hex"), + ); + assert.equal( + child2.privateKey.toString("hex"), + child3.privateKey.toString("hex"), + ); + }); + + it("should not mutate it when deriving without a private key", function () { + const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].private); + const path = "m/123/123/123"; + hdkey.wipePrivateData(); + + const publicKeyBefore = hdkey.publicKey.toString("hex"); + + const child = hdkey.derive(path); + assert.equal(hdkey.publicKey.toString("hex"), publicKeyBefore); + + const child2 = hdkey.derive(path); + assert.equal(hdkey.publicKey.toString("hex"), publicKeyBefore); + + const child3 = hdkey.derive(path); + assert.equal(hdkey.publicKey.toString("hex"), publicKeyBefore); + + assert.equal( + child.publicKey.toString("hex"), + child2.publicKey.toString("hex"), + ); + assert.equal( + child2.publicKey.toString("hex"), + child3.publicKey.toString("hex"), + ); + }); + }); +}); From c21b8b25dba7fb6988ea57b5a115ee7c41f7014c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:03:46 -0700 Subject: [PATCH 03/22] chore: add lint config --- .jshintrc | 23 +++++++++++ jsconfig.json | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 .jshintrc create mode 100644 jsconfig.json diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..589cec4 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,23 @@ +{ + "browser": true, + "node": true, + "esversion": 11, + "curly": true, + "sub": true, + + "bitwise": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "immed": true, + "latedef": "nofunc", + "nonbsp": true, + "nonew": true, + "plusplus": true, + "undef": true, + "unused": "vars", + "strict": true, + "maxdepth": 4, + "maxstatements": 100, + "maxcomplexity": 20 +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..7ba9292 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,110 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + "typeRoots": ["./typings","./node_modules/@types"], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "*.js", + "bin/**/*.js", + "lib/**/*.js", + "src/**/*.js" +], + "exclude": ["node_modules"] +} \ No newline at end of file From 7df4a14b5b242a4f06c375e950948265205e89ec Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:00:05 -0700 Subject: [PATCH 04/22] ref!: IIFE wrap, indent, and use HDKEY as export shim --- lib/hdkey.js | 557 ++++++++++++++++++++++++++------------------------- 1 file changed, 284 insertions(+), 273 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 5dccd7f..ca6b604 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -1,307 +1,318 @@ -"use strict"; - -var assert = require("assert"); -var Buffer = require("safe-buffer").Buffer; -var crypto = require("crypto"); -var bs58check = require("bs58check"); -var RIPEMD160 = require("ripemd160"); -var secp256k1 = require("secp256k1"); - -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; -} +var HDKEY = ("object" === typeof module && exports) || {}; +(function (window, HDKEY) { + "use strict"; + + var assert = require("assert"); + var Buffer = require("safe-buffer").Buffer; + var crypto = require("crypto"); + var bs58check = require("bs58check"); + var RIPEMD160 = require("ripemd160"); + var secp256k1 = require("secp256k1"); + + 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 }; + + HDKEY.create = function (versions) { + return new HDKey(versions); + }; -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(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); - - this._privateKey = value; - this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); - this._identifier = hash160(this.publicKey); - this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0); - }, -}); - -function setPublicKey(hdkey, publicKey) { - hdkey._publicKey = Buffer.from(publicKey); - hdkey._identifier = hash160(publicKey); - hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); - hdkey._privateKey = null; -} + 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, "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(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); - // force compressed point (performs public key verification) - const publicKey = - value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; - setPublicKey(this, publicKey); - }, -}); - -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), - ); - }, -}); + 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; + }, + }); -HDKey.prototype.derive = function (path) { - if (path === "m" || path === "M" || path === "m'" || path === "M'") { - return this; - } + 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(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); + + this._privateKey = value; + this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + this._identifier = hash160(this.publicKey); + this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0); + }, + }); - 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; - } + 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(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); + // force compressed point (performs public key verification) + const publicKey = + value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; + setPublicKey(this, publicKey); + }, + }); - 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; + 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; + }, + }); - hdkey = hdkey.deriveChild(childIndex); + Object.defineProperty(HDKey.prototype, "publicExtendedKey", { + get: function () { + return bs58check.encode( + serialize(this, this.versions.public, this.publicKey), + ); + }, }); - return hdkey; -}; + HDKey.prototype.derive = function (path) { + if (path === "m" || path === "M" || path === "m'" || path === "M'") { + return this; + } -HDKey.prototype.deriveChild = function (index) { - var isHardened = index >= HARDENED_OFFSET; - var indexBuffer = Buffer.allocUnsafe(4); - indexBuffer.writeUInt32BE(index, 0); + 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 data; + 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; - if (isHardened) { - // Hardened child - assert(this.privateKey, "Could not derive hardened child key"); + hdkey = hdkey.deriveChild(childIndex); + }); - var pk = this.privateKey; - var zb = Buffer.alloc(1, 0); - pk = Buffer.concat([zb, pk]); + return hdkey; + }; - // 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]); - } + HDKey.prototype.deriveChild = function (index) { + var isHardened = index >= HARDENED_OFFSET; + var indexBuffer = Buffer.allocUnsafe(4); + indexBuffer.writeUInt32BE(index, 0); - var I = crypto.createHmac("sha512", this.chainCode).update(data).digest(); - var IL = I.slice(0, 32); - var IR = I.slice(32); + var data; - var hd = new HDKey(this.versions); + if (isHardened) { + // Hardened child + assert(this.privateKey, "Could not derive hardened child key"); - // Private parent key -> private child key - if (this.privateKey) { - // ki = parse256(IL) + kpar (mod n) - try { - hd.privateKey = Buffer.from( - secp256k1.privateKeyTweakAdd(Buffer.from(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); + 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]); } - // Public parent key -> public child key - } else { - // Ki = point(parse256(IL)) + Kpar - // = G*IL + Kpar - try { - hd.publicKey = Buffer.from( - secp256k1.publicKeyTweakAdd(Buffer.from(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); + + var I = crypto.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 = Buffer.from( + secp256k1.privateKeyTweakAdd(Buffer.from(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 = Buffer.from( + secp256k1.publicKeyTweakAdd(Buffer.from(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 Buffer.from( - secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(this.privateKey)) - .signature, - ); -}; - -HDKey.prototype.verify = function (hash, signature) { - return secp256k1.ecdsaVerify( - Uint8Array.from(signature), - Uint8Array.from(hash), - Uint8Array.from(this.publicKey), - ); -}; - -HDKey.prototype.wipePrivateData = function () { - if (this._privateKey) - crypto.randomBytes(this._privateKey.length).copy(this._privateKey); - this._privateKey = null; - return this; -}; - -HDKey.prototype.toJSON = function () { - return { - xpriv: this.privateExtendedKey, - xpub: this.publicExtendedKey, + hd.chainCode = IR; + hd.depth = this.depth + 1; + hd.parentFingerprint = this.fingerprint; // .readUInt32BE(0) + hd.index = index; + + return hd; }; -}; - -HDKey.fromMasterSeed = function (seedBuffer, versions) { - var I = crypto - .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, skipVerification) { - // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) - versions = versions || BITCOIN_VERSIONS; - skipVerification = skipVerification || false; - 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.prototype.sign = function (hash) { + return Buffer.from( + secp256k1.ecdsaSign( + Uint8Array.from(hash), + Uint8Array.from(this.privateKey), + ).signature, + ); + }; + + HDKey.prototype.verify = function (hash, signature) { + return secp256k1.ecdsaVerify( + Uint8Array.from(signature), + Uint8Array.from(hash), + Uint8Array.from(this.publicKey), ); - hdkey.privateKey = key.slice(1); // cut off first 0x0 byte - } else { + }; + + HDKey.prototype.wipePrivateData = function () { + if (this._privateKey) + crypto.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 = crypto + .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, skipVerification) { + // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) + versions = versions || BITCOIN_VERSIONS; + skipVerification = skipVerification || false; + var hdkey = new HDKey(versions); + + var keyBuffer = bs58check.decode(base58key); + + var version = keyBuffer.readUInt32BE(0); assert( - version === versions.public, - "Version mismatch: version does not match public", + version === versions.private || version === versions.public, + "Version mismatch: does not match private or public", ); - if (skipVerification) { - setPublicKey(hdkey, key); + + 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 { - hdkey.publicKey = key; + assert( + version === versions.public, + "Version mismatch: version does not match public", + ); + if (skipVerification) { + setPublicKey(hdkey, key); + } else { + hdkey.publicKey = key; + } } - } - return hdkey; -}; + return hdkey; + }; + + HDKEY.fromJSON = function (obj) { + return HDKEY.fromExtendedKey(obj.xpriv); + }; -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); -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); - 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); - 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); - hdkey.chainCode.copy(buffer, 13); - key.copy(buffer, 45); + return buffer; + } - return buffer; -} + function setPublicKey(hdkey, publicKey) { + hdkey._publicKey = Buffer.from(publicKey); + hdkey._identifier = hash160(publicKey); + hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); + hdkey._privateKey = null; + } -function hash160(buf) { - var sha = crypto.createHash("sha256").update(buf).digest(); - return new RIPEMD160().update(sha).digest(); -} + function hash160(buf) { + var sha = crypto.createHash("sha256").update(buf).digest(); + return new RIPEMD160().update(sha).digest(); + } -HDKey.HARDENED_OFFSET = HARDENED_OFFSET; -module.exports = HDKey; + HDKEY.HARDENED_OFFSET = HARDENED_OFFSET; +})(("object" === typeof window && window) || {}, HDKEY); +if ("object" === typeof module) { + module.exports = HDKEY; +} From 365b915b8f82fcb067ba327426199716169c57b3 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:09:06 -0700 Subject: [PATCH 05/22] ref!: nest HDKey under HDKEY, new HDKey(...) => HDKEY.create(...) --- lib/hdkey.js | 359 ++++++++++++++++++++++++++------------------------- 1 file changed, 181 insertions(+), 178 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index ca6b604..11548e8 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -17,208 +17,211 @@ var HDKEY = ("object" === typeof module && exports) || {}; var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; HDKEY.create = function (versions) { - return new HDKey(versions); - }; + 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; + } - 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, "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(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); - - this._privateKey = value; - this._publicKey = Buffer.from(secp256k1.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(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); - // force compressed point (performs public key verification) - const publicKey = - value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; - setPublicKey(this, publicKey); - }, - }); - - 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]), - ), + 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( + secp256k1.privateKeyVerify(value) === true, + "Invalid private key", ); - 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; - } + this._privateKey = value; + this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + this._identifier = hash160(this.publicKey); + this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0); + }, + }); - 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; - } + 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(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); + // force compressed point (performs public key verification) + const publicKey = + value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; + setPublicKey(this, publicKey); + }, + }); - 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; + 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; + }, + }); - hdkey = hdkey.deriveChild(childIndex); + Object.defineProperty(HDKey.prototype, "publicExtendedKey", { + get: function () { + return bs58check.encode( + serialize(this, this.versions.public, this.publicKey), + ); + }, }); - return hdkey; - }; + HDKey.prototype.derive = function (path) { + if (path === "m" || path === "M" || path === "m'" || path === "M'") { + return this; + } - HDKey.prototype.deriveChild = function (index) { - var isHardened = index >= HARDENED_OFFSET; - var indexBuffer = Buffer.allocUnsafe(4); - indexBuffer.writeUInt32BE(index, 0); + 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 data; + 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; - if (isHardened) { - // Hardened child - assert(this.privateKey, "Could not derive hardened child key"); + hdkey = hdkey.deriveChild(childIndex); + }); - var pk = this.privateKey; - var zb = Buffer.alloc(1, 0); - pk = Buffer.concat([zb, pk]); + return hdkey; + }; - // 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]); - } + HDKey.prototype.deriveChild = function (index) { + var isHardened = index >= HARDENED_OFFSET; + var indexBuffer = Buffer.allocUnsafe(4); + indexBuffer.writeUInt32BE(index, 0); - var I = crypto.createHmac("sha512", this.chainCode).update(data).digest(); - var IL = I.slice(0, 32); - var IR = I.slice(32); + var data; - var hd = new HDKey(this.versions); + if (isHardened) { + // Hardened child + assert(this.privateKey, "Could not derive hardened child key"); - // Private parent key -> private child key - if (this.privateKey) { - // ki = parse256(IL) + kpar (mod n) - try { - hd.privateKey = Buffer.from( - secp256k1.privateKeyTweakAdd(Buffer.from(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); + 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]); } - // Public parent key -> public child key - } else { - // Ki = point(parse256(IL)) + Kpar - // = G*IL + Kpar - try { - hd.publicKey = Buffer.from( - secp256k1.publicKeyTweakAdd(Buffer.from(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); + + var I = crypto.createHmac("sha512", this.chainCode).update(data).digest(); + var IL = I.slice(0, 32); + var IR = I.slice(32); + + var hd = HDKEY.create(this.versions); + + // Private parent key -> private child key + if (this.privateKey) { + // ki = parse256(IL) + kpar (mod n) + try { + hd.privateKey = Buffer.from( + secp256k1.privateKeyTweakAdd(Buffer.from(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 = Buffer.from( + secp256k1.publicKeyTweakAdd(Buffer.from(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; + hd.chainCode = IR; + hd.depth = this.depth + 1; + hd.parentFingerprint = this.fingerprint; // .readUInt32BE(0) + hd.index = index; - return hd; - }; + return hd; + }; - HDKey.prototype.sign = function (hash) { - return Buffer.from( - secp256k1.ecdsaSign( - Uint8Array.from(hash), - Uint8Array.from(this.privateKey), - ).signature, - ); - }; + HDKey.prototype.sign = function (hash) { + return Buffer.from( + secp256k1.ecdsaSign( + Uint8Array.from(hash), + Uint8Array.from(this.privateKey), + ).signature, + ); + }; - HDKey.prototype.verify = function (hash, signature) { - return secp256k1.ecdsaVerify( - Uint8Array.from(signature), - Uint8Array.from(hash), - Uint8Array.from(this.publicKey), - ); - }; + HDKey.prototype.verify = function (hash, signature) { + return secp256k1.ecdsaVerify( + Uint8Array.from(signature), + Uint8Array.from(hash), + Uint8Array.from(this.publicKey), + ); + }; - HDKey.prototype.wipePrivateData = function () { - if (this._privateKey) - crypto.randomBytes(this._privateKey.length).copy(this._privateKey); - this._privateKey = null; - return this; - }; + HDKey.prototype.wipePrivateData = function () { + if (this._privateKey) + crypto.randomBytes(this._privateKey.length).copy(this._privateKey); + this._privateKey = null; + return this; + }; - HDKey.prototype.toJSON = function () { - return { - xpriv: this.privateExtendedKey, - xpub: this.publicExtendedKey, + HDKey.prototype.toJSON = function () { + return { + xpriv: this.privateExtendedKey, + xpub: this.publicExtendedKey, + }; }; + + return new HDKey(versions); }; HDKEY.fromMasterSeed = function (seedBuffer, versions) { @@ -229,7 +232,7 @@ var HDKEY = ("object" === typeof module && exports) || {}; var IL = I.slice(0, 32); var IR = I.slice(32); - var hdkey = new HDKey(versions); + var hdkey = HDKEY.create(versions); hdkey.chainCode = IR; hdkey.privateKey = IL; @@ -240,7 +243,7 @@ var HDKEY = ("object" === typeof module && exports) || {}; // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) versions = versions || BITCOIN_VERSIONS; skipVerification = skipVerification || false; - var hdkey = new HDKey(versions); + var hdkey = HDKEY.create(versions); var keyBuffer = bs58check.decode(base58key); From cd4fcc9eee2cbfa683c323e5e359a7ac45bb665b Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:23:11 -0700 Subject: [PATCH 06/22] ref!: move HDKey.prototype, this => hdkey --- lib/hdkey.js | 129 ++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 11548e8..3d29eb9 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -17,36 +17,36 @@ var HDKEY = ("object" === typeof module && exports) || {}; var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; HDKEY.create = function (versions) { - 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; - } + let hdkey = {}; + + hdkey.versions = versions || BITCOIN_VERSIONS; + hdkey.depth = 0; + hdkey.index = 0; + hdkey._privateKey = null; + hdkey._publicKey = null; + hdkey.chainCode = null; + hdkey._fingerprint = 0; + hdkey.parentFingerprint = 0; - Object.defineProperty(HDKey.prototype, "fingerprint", { + Object.defineProperty(hdkey, "fingerprint", { get: function () { - return this._fingerprint; + return hdkey._fingerprint; }, }); - Object.defineProperty(HDKey.prototype, "identifier", { + Object.defineProperty(hdkey, "identifier", { get: function () { - return this._identifier; + return hdkey._identifier; }, }); - Object.defineProperty(HDKey.prototype, "pubKeyHash", { + Object.defineProperty(hdkey, "pubKeyHash", { get: function () { - return this.identifier; + return hdkey.identifier; }, }); - Object.defineProperty(HDKey.prototype, "privateKey", { + Object.defineProperty(hdkey, "privateKey", { get: function () { - return this._privateKey; + return hdkey._privateKey; }, set: function (value) { assert.equal(value.length, 32, "Private key must be 32 bytes."); @@ -55,16 +55,16 @@ var HDKEY = ("object" === typeof module && exports) || {}; "Invalid private key", ); - this._privateKey = value; - this._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); - this._identifier = hash160(this.publicKey); - this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0); + hdkey._privateKey = value; + hdkey._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + hdkey._identifier = hash160(hdkey.publicKey); + hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); }, }); - Object.defineProperty(HDKey.prototype, "publicKey", { + Object.defineProperty(hdkey, "publicKey", { get: function () { - return this._publicKey; + return hdkey._publicKey; }, set: function (value) { assert( @@ -75,39 +75,39 @@ var HDKEY = ("object" === typeof module && exports) || {}; // force compressed point (performs public key verification) const publicKey = value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; - setPublicKey(this, publicKey); + setPublicKey(hdkey, publicKey); }, }); - Object.defineProperty(HDKey.prototype, "privateExtendedKey", { + Object.defineProperty(hdkey, "privateExtendedKey", { get: function () { - if (this._privateKey) + if (hdkey._privateKey) return bs58check.encode( serialize( - this, - this.versions.private, - Buffer.concat([Buffer.alloc(1, 0), this.privateKey]), + hdkey, + hdkey.versions.private, + Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), ), ); else return null; }, }); - Object.defineProperty(HDKey.prototype, "publicExtendedKey", { + Object.defineProperty(hdkey, "publicExtendedKey", { get: function () { return bs58check.encode( - serialize(this, this.versions.public, this.publicKey), + serialize(hdkey, hdkey.versions.public, hdkey.publicKey), ); }, }); - HDKey.prototype.derive = function (path) { + hdkey.derive = function (path) { if (path === "m" || path === "M" || path === "m'" || path === "M'") { - return this; + return hdkey; } var entries = path.split("/"); - var hdkey = this; + var _hdkey = hdkey; entries.forEach(function (c, i) { if (i === 0) { assert(/^[mM]{1}/.test(c), 'Path must start with "m" or "M"'); @@ -119,13 +119,13 @@ var HDKEY = ("object" === typeof module && exports) || {}; assert(childIndex < HARDENED_OFFSET, "Invalid index"); if (hardened) childIndex += HARDENED_OFFSET; - hdkey = hdkey.deriveChild(childIndex); + _hdkey = _hdkey.deriveChild(childIndex); }); - return hdkey; + return _hdkey; }; - HDKey.prototype.deriveChild = function (index) { + hdkey.deriveChild = function (index) { var isHardened = index >= HARDENED_OFFSET; var indexBuffer = Buffer.allocUnsafe(4); indexBuffer.writeUInt32BE(index, 0); @@ -134,9 +134,9 @@ var HDKEY = ("object" === typeof module && exports) || {}; if (isHardened) { // Hardened child - assert(this.privateKey, "Could not derive hardened child key"); + assert(hdkey.privateKey, "Could not derive hardened child key"); - var pk = this.privateKey; + var pk = hdkey.privateKey; var zb = Buffer.alloc(1, 0); pk = Buffer.concat([zb, pk]); @@ -146,26 +146,29 @@ var HDKEY = ("object" === typeof module && exports) || {}; // Normal child // data = serP(point(kpar)) || ser32(index) // = serP(Kpar) || ser32(index) - data = Buffer.concat([this.publicKey, indexBuffer]); + data = Buffer.concat([hdkey.publicKey, indexBuffer]); } - var I = crypto.createHmac("sha512", this.chainCode).update(data).digest(); + var I = crypto + .createHmac("sha512", hdkey.chainCode) + .update(data) + .digest(); var IL = I.slice(0, 32); var IR = I.slice(32); - var hd = HDKEY.create(this.versions); + var hd = HDKEY.create(hdkey.versions); // Private parent key -> private child key - if (this.privateKey) { + if (hdkey.privateKey) { // ki = parse256(IL) + kpar (mod n) try { hd.privateKey = Buffer.from( - secp256k1.privateKeyTweakAdd(Buffer.from(this.privateKey), IL), + secp256k1.privateKeyTweakAdd(Buffer.from(hdkey.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); + return hdkey.deriveChild(index + 1); } // Public parent key -> public child key } else { @@ -173,55 +176,55 @@ var HDKEY = ("object" === typeof module && exports) || {}; // = G*IL + Kpar try { hd.publicKey = Buffer.from( - secp256k1.publicKeyTweakAdd(Buffer.from(this.publicKey), IL, true), + secp256k1.publicKeyTweakAdd(Buffer.from(hdkey.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); + return hdkey.deriveChild(index + 1); } } hd.chainCode = IR; - hd.depth = this.depth + 1; - hd.parentFingerprint = this.fingerprint; // .readUInt32BE(0) + hd.depth = hdkey.depth + 1; + hd.parentFingerprint = hdkey.fingerprint; // .readUInt32BE(0) hd.index = index; return hd; }; - HDKey.prototype.sign = function (hash) { + hdkey.sign = function (hash) { return Buffer.from( secp256k1.ecdsaSign( Uint8Array.from(hash), - Uint8Array.from(this.privateKey), + Uint8Array.from(hdkey.privateKey), ).signature, ); }; - HDKey.prototype.verify = function (hash, signature) { + hdkey.verify = function (hash, signature) { return secp256k1.ecdsaVerify( Uint8Array.from(signature), Uint8Array.from(hash), - Uint8Array.from(this.publicKey), + Uint8Array.from(hdkey.publicKey), ); }; - HDKey.prototype.wipePrivateData = function () { - if (this._privateKey) - crypto.randomBytes(this._privateKey.length).copy(this._privateKey); - this._privateKey = null; - return this; + hdkey.wipePrivateData = function () { + if (hdkey._privateKey) + crypto.randomBytes(hdkey._privateKey.length).copy(hdkey._privateKey); + hdkey._privateKey = null; + return hdkey; }; - HDKey.prototype.toJSON = function () { + hdkey.toJSON = function () { return { - xpriv: this.privateExtendedKey, - xpub: this.publicExtendedKey, + xpriv: hdkey.privateExtendedKey, + xpub: hdkey.publicExtendedKey, }; }; - return new HDKey(versions); + return hdkey; }; HDKEY.fromMasterSeed = function (seedBuffer, versions) { From 945e409c51e696ab82641d6c3feb4c737362b275 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:43:24 -0700 Subject: [PATCH 07/22] ref: restore HDKEY to HDKey --- lib/hdkey.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 3d29eb9..c222006 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -1,5 +1,5 @@ -var HDKEY = ("object" === typeof module && exports) || {}; -(function (window, HDKEY) { +var HDKey = ("object" === typeof module && exports) || {}; +(function (window, HDKey) { "use strict"; var assert = require("assert"); @@ -16,8 +16,8 @@ var HDKEY = ("object" === typeof module && exports) || {}; // Bitcoin hardcoded by default, can use package `coininfo` for others var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; - HDKEY.create = function (versions) { - let hdkey = {}; + HDKey.create = function (versions) { + var hdkey = {}; hdkey.versions = versions || BITCOIN_VERSIONS; hdkey.depth = 0; @@ -156,7 +156,7 @@ var HDKEY = ("object" === typeof module && exports) || {}; var IL = I.slice(0, 32); var IR = I.slice(32); - var hd = HDKEY.create(hdkey.versions); + var hd = HDKey.create(hdkey.versions); // Private parent key -> private child key if (hdkey.privateKey) { @@ -227,7 +227,7 @@ var HDKEY = ("object" === typeof module && exports) || {}; return hdkey; }; - HDKEY.fromMasterSeed = function (seedBuffer, versions) { + HDKey.fromMasterSeed = function (seedBuffer, versions) { var I = crypto .createHmac("sha512", MASTER_SECRET) .update(seedBuffer) @@ -235,18 +235,18 @@ var HDKEY = ("object" === typeof module && exports) || {}; var IL = I.slice(0, 32); var IR = I.slice(32); - var hdkey = HDKEY.create(versions); + var hdkey = HDKey.create(versions); hdkey.chainCode = IR; hdkey.privateKey = IL; return hdkey; }; - HDKEY.fromExtendedKey = function (base58key, versions, skipVerification) { + HDKey.fromExtendedKey = function (base58key, versions, skipVerification) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) versions = versions || BITCOIN_VERSIONS; skipVerification = skipVerification || false; - var hdkey = HDKEY.create(versions); + var hdkey = HDKey.create(versions); var keyBuffer = bs58check.decode(base58key); @@ -284,8 +284,8 @@ var HDKEY = ("object" === typeof module && exports) || {}; return hdkey; }; - HDKEY.fromJSON = function (obj) { - return HDKEY.fromExtendedKey(obj.xpriv); + HDKey.fromJSON = function (obj) { + return HDKey.fromExtendedKey(obj.xpriv); }; function serialize(hdkey, version, key) { @@ -317,8 +317,8 @@ var HDKEY = ("object" === typeof module && exports) || {}; return new RIPEMD160().update(sha).digest(); } - HDKEY.HARDENED_OFFSET = HARDENED_OFFSET; -})(("object" === typeof window && window) || {}, HDKEY); + HDKey.HARDENED_OFFSET = HARDENED_OFFSET; +})(("object" === typeof window && window) || {}, HDKey); if ("object" === typeof module) { - module.exports = HDKEY; + module.exports = HDKey; } From 35c44d133d05a47fde9abdf042adf12683232a64 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:56:29 -0700 Subject: [PATCH 08/22] ref!: defineProperty(privateKey, ...) => privateKey, setPrivateKey --- lib/hdkey.js | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index c222006..e4874ac 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -22,7 +22,7 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.versions = versions || BITCOIN_VERSIONS; hdkey.depth = 0; hdkey.index = 0; - hdkey._privateKey = null; + hdkey.privateKey = null; hdkey._publicKey = null; hdkey.chainCode = null; hdkey._fingerprint = 0; @@ -44,23 +44,15 @@ var HDKey = ("object" === typeof module && exports) || {}; }, }); - Object.defineProperty(hdkey, "privateKey", { - get: function () { - return hdkey._privateKey; - }, - set: function (value) { - assert.equal(value.length, 32, "Private key must be 32 bytes."); - assert( - secp256k1.privateKeyVerify(value) === true, - "Invalid private key", - ); + hdkey.setPrivateKey = function (value) { + assert.equal(value.length, 32, "Private key must be 32 bytes."); + assert(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); - hdkey._privateKey = value; - hdkey._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); - hdkey._identifier = hash160(hdkey.publicKey); - hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); - }, - }); + hdkey.privateKey = value; + hdkey._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + hdkey._identifier = hash160(hdkey.publicKey); + hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); + }; Object.defineProperty(hdkey, "publicKey", { get: function () { @@ -81,7 +73,7 @@ var HDKey = ("object" === typeof module && exports) || {}; Object.defineProperty(hdkey, "privateExtendedKey", { get: function () { - if (hdkey._privateKey) + if (hdkey.privateKey) return bs58check.encode( serialize( hdkey, @@ -162,8 +154,10 @@ var HDKey = ("object" === typeof module && exports) || {}; if (hdkey.privateKey) { // ki = parse256(IL) + kpar (mod n) try { - hd.privateKey = Buffer.from( - secp256k1.privateKeyTweakAdd(Buffer.from(hdkey.privateKey), IL), + hd.setPrivateKey( + Buffer.from( + secp256k1.privateKeyTweakAdd(Buffer.from(hdkey.privateKey), IL), + ), ); // throw if IL >= n || (privateKey + IL) === 0 } catch (err) { @@ -211,9 +205,9 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.wipePrivateData = function () { - if (hdkey._privateKey) - crypto.randomBytes(hdkey._privateKey.length).copy(hdkey._privateKey); - hdkey._privateKey = null; + if (hdkey.privateKey) + crypto.randomBytes(hdkey.privateKey.length).copy(hdkey.privateKey); + hdkey.privateKey = null; return hdkey; }; @@ -237,7 +231,7 @@ var HDKey = ("object" === typeof module && exports) || {}; var hdkey = HDKey.create(versions); hdkey.chainCode = IR; - hdkey.privateKey = IL; + hdkey.setPrivateKey(IL); return hdkey; }; @@ -268,7 +262,7 @@ var HDKey = ("object" === typeof module && exports) || {}; version === versions.private, "Version mismatch: version does not match private", ); - hdkey.privateKey = key.slice(1); // cut off first 0x0 byte + hdkey.setPrivateKey(key.slice(1)); // cut off first 0x0 byte } else { assert( version === versions.public, @@ -309,7 +303,7 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey._publicKey = Buffer.from(publicKey); hdkey._identifier = hash160(publicKey); hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); - hdkey._privateKey = null; + hdkey.privateKey = null; } function hash160(buf) { From f9ae62fbc097a002da890868ea4693d5ef323b62 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 00:58:14 -0700 Subject: [PATCH 09/22] ref!: defineProperty(publicKey, ...) => publicKey, setPublicKey --- lib/hdkey.js | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index e4874ac..d2838e3 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -23,7 +23,7 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.depth = 0; hdkey.index = 0; hdkey.privateKey = null; - hdkey._publicKey = null; + hdkey.publicKey = null; hdkey.chainCode = null; hdkey._fingerprint = 0; hdkey.parentFingerprint = 0; @@ -49,27 +49,22 @@ var HDKey = ("object" === typeof module && exports) || {}; assert(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); hdkey.privateKey = value; - hdkey._publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); + hdkey.publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); hdkey._identifier = hash160(hdkey.publicKey); hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); }; - Object.defineProperty(hdkey, "publicKey", { - get: function () { - return hdkey._publicKey; - }, - set: function (value) { - assert( - value.length === 33 || value.length === 65, - "Public key must be 33 or 65 bytes.", - ); - assert(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); - // force compressed point (performs public key verification) - const publicKey = - value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; - setPublicKey(hdkey, publicKey); - }, - }); + hdkey.setPublicKey = function (value) { + assert( + value.length === 33 || value.length === 65, + "Public key must be 33 or 65 bytes.", + ); + assert(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); + // force compressed point (performs public key verification) + const publicKey = + value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; + setPublicKey(hdkey, publicKey); + }; Object.defineProperty(hdkey, "privateExtendedKey", { get: function () { @@ -169,8 +164,14 @@ var HDKey = ("object" === typeof module && exports) || {}; // Ki = point(parse256(IL)) + Kpar // = G*IL + Kpar try { - hd.publicKey = Buffer.from( - secp256k1.publicKeyTweakAdd(Buffer.from(hdkey.publicKey), IL, true), + hd.setPublicKey( + Buffer.from( + secp256k1.publicKeyTweakAdd( + Buffer.from(hdkey.publicKey), + IL, + true, + ), + ), ); // throw if IL >= n || (g**IL + publicKey) is infinity } catch (err) { @@ -271,7 +272,7 @@ var HDKey = ("object" === typeof module && exports) || {}; if (skipVerification) { setPublicKey(hdkey, key); } else { - hdkey.publicKey = key; + hdkey.setPublicKey(key); } } @@ -300,7 +301,7 @@ var HDKey = ("object" === typeof module && exports) || {}; } function setPublicKey(hdkey, publicKey) { - hdkey._publicKey = Buffer.from(publicKey); + hdkey.publicKey = Buffer.from(publicKey); hdkey._identifier = hash160(publicKey); hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); hdkey.privateKey = null; From eb0662f917d318d74263be2caa1d7f20cfffe59c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:06:37 -0700 Subject: [PATCH 10/22] ref!: _identfier, identifier, defineProperty(pubKeyHash, ...) => identifier --- lib/hdkey.js | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index d2838e3..8ed328f 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -24,6 +24,9 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.index = 0; hdkey.privateKey = null; hdkey.publicKey = null; + // note: BIP-32 "Key Identifier" is the same as "Pub Key Hash" + // but used for a different purpose, hence the semantic name + hdkey.identifier = null; hdkey.chainCode = null; hdkey._fingerprint = 0; hdkey.parentFingerprint = 0; @@ -33,16 +36,6 @@ var HDKey = ("object" === typeof module && exports) || {}; return hdkey._fingerprint; }, }); - Object.defineProperty(hdkey, "identifier", { - get: function () { - return hdkey._identifier; - }, - }); - Object.defineProperty(hdkey, "pubKeyHash", { - get: function () { - return hdkey.identifier; - }, - }); hdkey.setPrivateKey = function (value) { assert.equal(value.length, 32, "Private key must be 32 bytes."); @@ -50,8 +43,8 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.privateKey = value; hdkey.publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); - hdkey._identifier = hash160(hdkey.publicKey); - hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); + hdkey.identifier = hash160(hdkey.publicKey); + hdkey._fingerprint = hdkey.pubKeyHash.slice(0, 4).readUInt32BE(0); }; hdkey.setPublicKey = function (value) { @@ -302,8 +295,8 @@ var HDKey = ("object" === typeof module && exports) || {}; function setPublicKey(hdkey, publicKey) { hdkey.publicKey = Buffer.from(publicKey); - hdkey._identifier = hash160(publicKey); - hdkey._fingerprint = hdkey._identifier.slice(0, 4).readUInt32BE(0); + hdkey.identifier = hash160(publicKey); + hdkey._fingerprint = hdkey.pubKeyHash.slice(0, 4).readUInt32BE(0); hdkey.privateKey = null; } From a3f17800d32619c2460ec4142d96312133da5c9e Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:14:03 -0700 Subject: [PATCH 11/22] ref!: move fingerprint to getFingerprint (it's just a slice, and only used once) --- lib/hdkey.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 8ed328f..d0645fc 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -28,14 +28,11 @@ var HDKey = ("object" === typeof module && exports) || {}; // but used for a different purpose, hence the semantic name hdkey.identifier = null; hdkey.chainCode = null; - hdkey._fingerprint = 0; hdkey.parentFingerprint = 0; - Object.defineProperty(hdkey, "fingerprint", { - get: function () { - return hdkey._fingerprint; - }, - }); + hdkey.getFingerprint = function () { + return hdkey.identifier.slice(0, 4).readUInt32BE(0); + }; hdkey.setPrivateKey = function (value) { assert.equal(value.length, 32, "Private key must be 32 bytes."); @@ -44,7 +41,6 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.privateKey = value; hdkey.publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); hdkey.identifier = hash160(hdkey.publicKey); - hdkey._fingerprint = hdkey.pubKeyHash.slice(0, 4).readUInt32BE(0); }; hdkey.setPublicKey = function (value) { @@ -175,7 +171,7 @@ var HDKey = ("object" === typeof module && exports) || {}; hd.chainCode = IR; hd.depth = hdkey.depth + 1; - hd.parentFingerprint = hdkey.fingerprint; // .readUInt32BE(0) + hd.parentFingerprint = hdkey.getFingerprint(); hd.index = index; return hd; @@ -296,7 +292,6 @@ var HDKey = ("object" === typeof module && exports) || {}; function setPublicKey(hdkey, publicKey) { hdkey.publicKey = Buffer.from(publicKey); hdkey.identifier = hash160(publicKey); - hdkey._fingerprint = hdkey.pubKeyHash.slice(0, 4).readUInt32BE(0); hdkey.privateKey = null; } From b85240bf6093b332ced5b44e6cf64398dd516e85 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:20:55 -0700 Subject: [PATCH 12/22] ref!: (private|public)ExtendedKey => get(Private|Public)ExtendedKey --- lib/hdkey.js | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index d0645fc..07c0dbd 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -55,27 +55,23 @@ var HDKey = ("object" === typeof module && exports) || {}; setPublicKey(hdkey, publicKey); }; - Object.defineProperty(hdkey, "privateExtendedKey", { - get: function () { - if (hdkey.privateKey) - return bs58check.encode( - serialize( - hdkey, - hdkey.versions.private, - Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), - ), - ); - else return null; - }, - }); - - Object.defineProperty(hdkey, "publicExtendedKey", { - get: function () { + hdkey.getPrivateExtendedKey = function () { + if (hdkey.privateKey) return bs58check.encode( - serialize(hdkey, hdkey.versions.public, hdkey.publicKey), + serialize( + hdkey, + hdkey.versions.private, + Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), + ), ); - }, - }); + else return null; + }; + + hdkey.getPublicExtendedKey = function () { + return bs58check.encode( + serialize(hdkey, hdkey.versions.public, hdkey.publicKey), + ); + }; hdkey.derive = function (path) { if (path === "m" || path === "M" || path === "m'" || path === "M'") { @@ -203,8 +199,8 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.toJSON = function () { return { - xpriv: hdkey.privateExtendedKey, - xpub: hdkey.publicExtendedKey, + xpriv: hdkey.getPrivateExtendedKey(), + xpub: hdkey.getPublicExtendedKey(), }; }; From 02ae1a794cc07ee629db14c7e861fb388bdb2ac4 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:24:01 -0700 Subject: [PATCH 13/22] chore(lint): always use curlies around if, etc --- lib/hdkey.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 07c0dbd..ff307c6 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -50,21 +50,23 @@ var HDKey = ("object" === typeof module && exports) || {}; ); assert(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); // force compressed point (performs public key verification) - const publicKey = + var publicKey = value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; setPublicKey(hdkey, publicKey); }; hdkey.getPrivateExtendedKey = function () { - if (hdkey.privateKey) - return bs58check.encode( - serialize( - hdkey, - hdkey.versions.private, - Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), - ), - ); - else return null; + if (!hdkey.privateKey) { + return null; + } + + return bs58check.encode( + serialize( + hdkey, + hdkey.versions.private, + Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), + ), + ); }; hdkey.getPublicExtendedKey = function () { @@ -89,7 +91,9 @@ var HDKey = ("object" === typeof module && exports) || {}; 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; + if (hardened) { + childIndex += HARDENED_OFFSET; + } _hdkey = _hdkey.deriveChild(childIndex); }); @@ -191,8 +195,9 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.wipePrivateData = function () { - if (hdkey.privateKey) + if (hdkey.privateKey) { crypto.randomBytes(hdkey.privateKey.length).copy(hdkey.privateKey); + } hdkey.privateKey = null; return hdkey; }; From a38744127ee3950208269de2c1a4b4c593b48306 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 01:36:06 -0700 Subject: [PATCH 14/22] ref!: make privateKey more private (won't log in console) --- lib/hdkey.js | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index ff307c6..d08e7de 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -18,15 +18,16 @@ var HDKey = ("object" === typeof module && exports) || {}; HDKey.create = function (versions) { var hdkey = {}; + var _privateKey = null; hdkey.versions = versions || BITCOIN_VERSIONS; hdkey.depth = 0; hdkey.index = 0; - hdkey.privateKey = null; hdkey.publicKey = null; // note: BIP-32 "Key Identifier" is the same as "Pub Key Hash" // but used for a different purpose, hence the semantic name hdkey.identifier = null; + // note: chainCode is public hdkey.chainCode = null; hdkey.parentFingerprint = 0; @@ -34,11 +35,14 @@ var HDKey = ("object" === typeof module && exports) || {}; return hdkey.identifier.slice(0, 4).readUInt32BE(0); }; + hdkey.getPrivateKey = function () { + return _privateKey; + }; hdkey.setPrivateKey = function (value) { assert.equal(value.length, 32, "Private key must be 32 bytes."); assert(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); - hdkey.privateKey = value; + _privateKey = value; hdkey.publicKey = Buffer.from(secp256k1.publicKeyCreate(value, true)); hdkey.identifier = hash160(hdkey.publicKey); }; @@ -52,11 +56,17 @@ var HDKey = ("object" === typeof module && exports) || {}; // force compressed point (performs public key verification) var publicKey = value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; - setPublicKey(hdkey, publicKey); + hdkey._setPublicKey(publicKey); + }; + + hdkey._setPublicKey = function (publicKey) { + hdkey.publicKey = Buffer.from(publicKey); + hdkey.identifier = hash160(publicKey); + _privateKey = null; }; hdkey.getPrivateExtendedKey = function () { - if (!hdkey.privateKey) { + if (!_privateKey) { return null; } @@ -64,7 +74,7 @@ var HDKey = ("object" === typeof module && exports) || {}; serialize( hdkey, hdkey.versions.private, - Buffer.concat([Buffer.alloc(1, 0), hdkey.privateKey]), + Buffer.concat([Buffer.alloc(1, 0), _privateKey]), ), ); }; @@ -110,9 +120,9 @@ var HDKey = ("object" === typeof module && exports) || {}; if (isHardened) { // Hardened child - assert(hdkey.privateKey, "Could not derive hardened child key"); + assert(_privateKey, "Could not derive hardened child key"); - var pk = hdkey.privateKey; + var pk = _privateKey; var zb = Buffer.alloc(1, 0); pk = Buffer.concat([zb, pk]); @@ -135,12 +145,12 @@ var HDKey = ("object" === typeof module && exports) || {}; var hd = HDKey.create(hdkey.versions); // Private parent key -> private child key - if (hdkey.privateKey) { + if (_privateKey) { // ki = parse256(IL) + kpar (mod n) try { hd.setPrivateKey( Buffer.from( - secp256k1.privateKeyTweakAdd(Buffer.from(hdkey.privateKey), IL), + secp256k1.privateKeyTweakAdd(Buffer.from(_privateKey), IL), ), ); // throw if IL >= n || (privateKey + IL) === 0 @@ -179,10 +189,8 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.sign = function (hash) { return Buffer.from( - secp256k1.ecdsaSign( - Uint8Array.from(hash), - Uint8Array.from(hdkey.privateKey), - ).signature, + secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(_privateKey)) + .signature, ); }; @@ -195,10 +203,10 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.wipePrivateData = function () { - if (hdkey.privateKey) { - crypto.randomBytes(hdkey.privateKey.length).copy(hdkey.privateKey); + if (_privateKey) { + crypto.randomBytes(_privateKey.length).copy(_privateKey); } - hdkey.privateKey = null; + _privateKey = null; return hdkey; }; @@ -260,7 +268,7 @@ var HDKey = ("object" === typeof module && exports) || {}; "Version mismatch: version does not match public", ); if (skipVerification) { - setPublicKey(hdkey, key); + hdkey._setPublicKey(key); } else { hdkey.setPublicKey(key); } @@ -290,12 +298,6 @@ var HDKey = ("object" === typeof module && exports) || {}; return buffer; } - function setPublicKey(hdkey, publicKey) { - hdkey.publicKey = Buffer.from(publicKey); - hdkey.identifier = hash160(publicKey); - hdkey.privateKey = null; - } - function hash160(buf) { var sha = crypto.createHash("sha256").update(buf).digest(); return new RIPEMD160().update(sha).digest(); From 14caabb84df1653ee053304b442fd5feca9e8154 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 12:42:43 -0700 Subject: [PATCH 15/22] ref: remove 'assert' node lock-in --- lib/hdkey.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index d08e7de..17a3281 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -2,7 +2,6 @@ var HDKey = ("object" === typeof module && exports) || {}; (function (window, HDKey) { "use strict"; - var assert = require("assert"); var Buffer = require("safe-buffer").Buffer; var crypto = require("crypto"); var bs58check = require("bs58check"); @@ -39,7 +38,7 @@ var HDKey = ("object" === typeof module && exports) || {}; return _privateKey; }; hdkey.setPrivateKey = function (value) { - assert.equal(value.length, 32, "Private key must be 32 bytes."); + assert(value.length === 32, "Private key must be 32 bytes."); assert(secp256k1.privateKeyVerify(value) === true, "Invalid private key"); _privateKey = value; @@ -120,7 +119,7 @@ var HDKey = ("object" === typeof module && exports) || {}; if (isHardened) { // Hardened child - assert(_privateKey, "Could not derive hardened child key"); + assert(Boolean(_privateKey), "Could not derive hardened child key"); var pk = _privateKey; var zb = Buffer.alloc(1, 0); @@ -281,6 +280,12 @@ var HDKey = ("object" === typeof module && exports) || {}; return HDKey.fromExtendedKey(obj.xpriv); }; + function assert(assertion, message) { + if (!assertion) { + throw new Error(message); + } + } + function serialize(hdkey, version, key) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) var buffer = Buffer.allocUnsafe(LEN); From 1cac79c65db0e08b7def3d757831e4f205a6b6de Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 14:42:23 -0700 Subject: [PATCH 16/22] ref: remove safe-buffer in favor of Node's now-safer Buffer --- lib/hdkey.js | 1 - package.json | 4 +--- test/hdkey.test.js | 7 +++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 17a3281..d1e2966 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -2,7 +2,6 @@ var HDKey = ("object" === typeof module && exports) || {}; (function (window, HDKey) { "use strict"; - var Buffer = require("safe-buffer").Buffer; var crypto = require("crypto"); var bs58check = require("bs58check"); var RIPEMD160 = require("ripemd160"); diff --git a/package.json b/package.json index 2fb9d72..a421f68 100644 --- a/package.json +++ b/package.json @@ -29,13 +29,11 @@ "istanbul": "^0.4.5", "mocha": "^6.1.4", "mocha-lcov-reporter": "0.0.1", - "mochify": "^6.3.0", - "secure-random": "^1.0.0" + "mochify": "^6.3.0" }, "dependencies": { "bs58check": "^2.1.2", "ripemd160": "^2.0.2", - "safe-buffer": "^5.1.1", "secp256k1": "^4.0.0" }, "scripts": { diff --git a/test/hdkey.test.js b/test/hdkey.test.js index 90bd5cb..8009f58 100644 --- a/test/hdkey.test.js +++ b/test/hdkey.test.js @@ -1,8 +1,7 @@ var assert = require("assert"); var BigInteger = require("bigi"); -var Buffer = require("safe-buffer").Buffer; +var crypto = require("crypto"); var ecurve = require("ecurve"); -var secureRandom = require("secure-random"); var curve = ecurve.getCurveByName("secp256k1"); var HDKey = require("../"); var fixtures = require("./fixtures/hdkey"); @@ -59,7 +58,7 @@ describe("hdkey", function () { }); it("should not throw if key is 33 bytes (compressed)", function () { - var priv = secureRandom.randomBuffer(32); + var priv = crypto.randomBytes(32); var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true); assert.equal(pub.length, 33); var hdkey = new HDKey(); @@ -67,7 +66,7 @@ describe("hdkey", function () { }); it("should not throw if key is 65 bytes (not compressed)", function () { - var priv = secureRandom.randomBuffer(32); + var priv = crypto.randomBytes(32); var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false); assert.equal(pub.length, 65); var hdkey = new HDKey(); From 75c42a622a771acc254ca6de2eced30034e8e614 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 12:05:21 -0700 Subject: [PATCH 17/22] fix(test): update all tests with new APIs --- test/hdkey.test.js | 77 +++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/test/hdkey.test.js b/test/hdkey.test.js index 8009f58..07581bd 100644 --- a/test/hdkey.test.js +++ b/test/hdkey.test.js @@ -16,8 +16,8 @@ describe("hdkey", function () { var hdkey = HDKey.fromMasterSeed(Buffer.from(f.seed, "hex")); var childkey = hdkey.derive(f.path); - assert.equal(childkey.privateExtendedKey, f.private); - assert.equal(childkey.publicExtendedKey, f.public); + assert.equal(childkey.getPrivateExtendedKey(), f.private); + assert.equal(childkey.getPublicExtendedKey(), f.public); }); describe("> " + f.path + " toJSON() / fromJSON()", function () { @@ -33,8 +33,8 @@ describe("hdkey", function () { assert.deepEqual(childkey.toJSON(), obj); var newKey = HDKey.fromJSON(obj); - assert.strictEqual(newKey.privateExtendedKey, f.private); - assert.strictEqual(newKey.publicExtendedKey, f.public); + assert.strictEqual(newKey.getPrivateExtendedKey(), f.private); + assert.strictEqual(newKey.getPublicExtendedKey(), f.public); }); }); }); @@ -42,9 +42,9 @@ describe("hdkey", function () { describe("- privateKey", function () { it("should throw an error if incorrect key size", function () { - var hdkey = new HDKey(); + var hdkey = HDKey.create(); assert.throws(function () { - hdkey.privateKey = Buffer.from([1, 2, 3, 4]); + hdkey.setPrivateKey(Buffer.from([1, 2, 3, 4])); }, /key must be 32/); }); }); @@ -52,8 +52,8 @@ describe("hdkey", function () { describe("- publicKey", function () { it("should throw an error if incorrect key size", function () { assert.throws(function () { - var hdkey = new HDKey(); - hdkey.publicKey = Buffer.from([1, 2, 3, 4]); + var hdkey = HDKey.create(); + hdkey.setPublicKey(Buffer.from([1, 2, 3, 4])); }, /key must be 33 or 65/); }); @@ -61,16 +61,16 @@ describe("hdkey", function () { var priv = crypto.randomBytes(32); var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true); assert.equal(pub.length, 33); - var hdkey = new HDKey(); - hdkey.publicKey = pub; + var hdkey = HDKey.create(); + hdkey.setPublicKey(pub); }); it("should not throw if key is 65 bytes (not compressed)", function () { var priv = crypto.randomBytes(32); var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false); assert.equal(pub.length, 65); - var hdkey = new HDKey(); - hdkey.publicKey = pub; + var hdkey = HDKey.create(); + hdkey.setPublicKey(pub); }); }); @@ -91,7 +91,7 @@ describe("hdkey", function () { "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", ); assert.equal( - hdkey.privateKey.toString("hex"), + hdkey.getPrivateKey().toString("hex"), "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", ); assert.equal( @@ -120,7 +120,7 @@ describe("hdkey", function () { hdkey.chainCode.toString("hex"), "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", ); - assert.equal(hdkey.privateKey, null); + assert.equal(hdkey.getPrivateKey(), null); assert.equal( hdkey.publicKey.toString("hex"), "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", @@ -145,7 +145,7 @@ describe("hdkey", function () { hdkey.chainCode.toString("hex"), "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", ); - assert.equal(hdkey.privateKey, null); + assert.equal(hdkey.getPrivateKey(), null); assert.equal( hdkey.publicKey.toString("hex"), "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", @@ -202,7 +202,7 @@ describe("hdkey", function () { var expected = "xpub6JdKdVJtdx6sC3nh87pDvnGhotXuU5Kz6Qy7Piy84vUAwWSYShsUGULE8u6gCivTHgz7cCKJHiXaaMeieB4YnoFVAsNgHHKXJ2mN6jCMbH1"; - assert.equal(derivedHDKey.publicExtendedKey, expected); + assert.equal(derivedHDKey.getPublicExtendedKey(), expected); }); }); @@ -214,7 +214,7 @@ describe("hdkey", function () { var newKey = masterKey.derive("m/44'/6'/4'"); var expected = "xprv9ymoag6W7cR6KBcJzhCM6qqTrb3rRVVwXKzwNqp1tDWcwierEv3BA9if3ARHMhMPh9u2jNoutcgpUBLMfq3kADDo7LzfoCnhhXMRGX3PXDx"; - assert.equal(newKey.privateExtendedKey, expected); + assert.equal(newKey.getPrivateExtendedKey(), expected); }); }); @@ -230,12 +230,12 @@ describe("hdkey", function () { "xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr"; var hdkey = HDKey.fromExtendedKey(key); assert.equal( - hdkey.privateKey.toString("hex"), + hdkey.getPrivateKey().toString("hex"), "00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd", ); var derived = hdkey.derive("m/44'/0'/0'/0/0'"); assert.equal( - derived.privateKey.toString("hex"), + derived.getPrivateKey().toString("hex"), "3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb", ); }); @@ -246,14 +246,14 @@ describe("hdkey", function () { var seed = "000102030405060708090a0b0c0d0e0f"; var masterKey = HDKey.fromMasterSeed(Buffer.from(seed, "hex")); - assert.ok(masterKey.privateExtendedKey, "xpriv is truthy"); - masterKey._privateKey = null; + assert.ok(masterKey.getPrivateExtendedKey(), "xpriv is truthy"); + masterKey.wipePrivateData(); assert.doesNotThrow(function () { - masterKey.privateExtendedKey; + masterKey.getPrivateExtendedKey(); }); - assert.ok(!masterKey.privateExtendedKey, "xpriv is falsy"); + assert.ok(!masterKey.getPrivateExtendedKey(), "xpriv is falsy"); }); }); @@ -273,7 +273,8 @@ describe("hdkey", function () { describe(" - when the path given to derive does not begin with master extended key", function () { it("should throw an error", function () { assert.throws(function () { - HDKey.prototype.derive("123"); + const hdkey = HDKey.create(); + hdkey.derive("123"); }, /Path must start with "m" or "M"/); }); }); @@ -283,16 +284,16 @@ describe("hdkey", function () { const hdkey = HDKey.fromMasterSeed( Buffer.from(fixtures.valid[6].seed, "hex"), ).wipePrivateData(); - assert.equal(hdkey.privateKey, null); - assert.equal(hdkey.privateExtendedKey, null); + assert.equal(hdkey.getPrivateKey(), null); + assert.equal(hdkey.getPrivateExtendedKey(), null); assert.throws( () => hdkey.sign(Buffer.alloc(32)), "shouldn't be able to sign", ); const childKey = hdkey.derive("m/0"); - assert.equal(childKey.publicExtendedKey, fixtures.valid[7].public); - assert.equal(childKey.privateKey, null); - assert.equal(childKey.privateExtendedKey, null); + assert.equal(childKey.getPublicExtendedKey(), fixtures.valid[7].public); + assert.equal(childKey.getPrivateKey(), null); + assert.equal(childKey.getPrivateExtendedKey(), null); }); it("should have correct data", function () { @@ -331,7 +332,7 @@ describe("hdkey", function () { it("should not throw if called on hdkey without private data", function () { const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].public); assert.doesNotThrow(() => hdkey.wipePrivateData()); - assert.equal(hdkey.publicExtendedKey, fixtures.valid[0].public); + assert.equal(hdkey.getPublicExtendedKey(), fixtures.valid[0].public); }); }); @@ -339,24 +340,24 @@ describe("hdkey", function () { it("should not mutate it when deriving with a private key", function () { const hdkey = HDKey.fromExtendedKey(fixtures.valid[0].private); const path = "m/123"; - const privateKeyBefore = hdkey.privateKey.toString("hex"); + const privateKeyBefore = hdkey.getPrivateKey().toString("hex"); const child = hdkey.derive(path); - assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + assert.equal(hdkey.getPrivateKey().toString("hex"), privateKeyBefore); const child2 = hdkey.derive(path); - assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + assert.equal(hdkey.getPrivateKey().toString("hex"), privateKeyBefore); const child3 = hdkey.derive(path); - assert.equal(hdkey.privateKey.toString("hex"), privateKeyBefore); + assert.equal(hdkey.getPrivateKey().toString("hex"), privateKeyBefore); assert.equal( - child.privateKey.toString("hex"), - child2.privateKey.toString("hex"), + child.getPrivateKey().toString("hex"), + child2.getPrivateKey().toString("hex"), ); assert.equal( - child2.privateKey.toString("hex"), - child3.privateKey.toString("hex"), + child2.getPrivateKey().toString("hex"), + child3.getPrivateKey().toString("hex"), ); }); From 986587db474f1afc7e0988371361de274537a9c1 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 3 Feb 2023 02:16:30 -0700 Subject: [PATCH 18/22] security: update mocha and lazy install dev deps --- package.json | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index a421f68..0508ec0 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,9 @@ "files": [], "devDependencies": { "bigi": "^1.1.0", - "coveralls": "^3.0.4", "ecurve": "^1.0.0", - "istanbul": "^0.4.5", - "mocha": "^6.1.4", - "mocha-lcov-reporter": "0.0.1", - "mochify": "^6.3.0" + "mocha": "^10.2.0", + "mocha-lcov-reporter": "0.0.1" }, "dependencies": { "bs58check": "^2.1.2", @@ -38,11 +35,11 @@ }, "scripts": { "lint": "npx -p typescript@4.x -- tsc -p ./jsconfig.json", - "browser-test": "mochify --wd -R spec", + "browser-test": "npx mochify@6 --wd -R spec", "test": "mocha", "unit": "mocha", - "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", - "coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info", + "coverage": "npx istanbul@0.4 cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", + "coveralls": "npm run-script coverage && npx coveralls@3 < coverage/lcov.info", "bump": "npm version -m \"chore(release): bump to v%s\"", "fmt": "npx -p prettier@2.x -- prettier -w '**/*.{js,md}'" } From 92dccc832826cb6f5d62c3df439789e1f0acdacc Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 3 Feb 2023 04:05:11 -0700 Subject: [PATCH 19/22] chore: remove node v10 because mocha no longer supports it --- .github/workflows/node.js.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 12420f6..ea5876a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [10.x, 12.x, 14.x, 16.x, 18.x, latest] + node-version: [12.x, 14.x, 16.x, 18.x, latest] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} From 7472aadd39e042a9258ae1f77274df720c4b5739 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 15:24:15 -0700 Subject: [PATCH 20/22] chore: add types and minor changes for type checking --- lib/hdkey.js | 184 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 7 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index d1e2966..e849d38 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -1,3 +1,44 @@ +/** + * @typedef HDKey + * @prop {HDCreate} create + * @prop {HDFromSeed} fromMasterSeed + * @prop {HDFromXKey} fromExtendedKey + * @prop {HDFromJSON} fromJSON + * @prop {Number} HARDENED_OFFSET - 0x80000000 + */ + +/** + * @callback HDCreate + * @param {HDVersions} [versions] + * @returns {hdkey} + */ + +/** + * @typedef hdkey + * @prop {Buffer} chainCode - extra 32-bytes of shared entropy for xkeys + * @prop {Number} depth - of hd path - typically 0 is seed, 1-3 hardened, 4-5 are not + * @prop {Buffer} identifier - same bytes as pubKeyHash, but used for id + * @prop {Number} index - the final segment of an HD Path, the index of the wif/addr + * @prop {Number} parentFingerprint - 32-bit int, slice of id, stored in child xkeys + * @prop {Buffer} publicKey + * @prop {HDVersions} versions - magic bytes for base58 prefix + * @prop {HDDerivePath} derive - derive a full hd path from the given root + * @prop {HDDeriveChild} deriveChild - get the next child xkey (in a path segment) + * @prop {HDFingerprint} getFingerprint + * @prop {HDMaybeGetString} getPrivateExtendedKey + * @prop {HDMaybeGetBuffer} getPrivateKey + * @prop {HDGetString} getPublicExtendedKey + * @prop {HDSetBuffer} setPublicKey + * @prop {HDSetBuffer} setPrivateKey + * @prop {HDSign} sign + * @prop {HDVerify} verify + * @prop {HDToJSON} toJSON + * @prop {HDWipePrivates} wipePrivateData - randomizes private key buffer in-place + * @prop {Function} _setPublicKey + */ + +/** @type {HDKey} */ +//@ts-ignore var HDKey = ("object" === typeof module && exports) || {}; (function (window, HDKey) { "use strict"; @@ -15,21 +56,23 @@ var HDKey = ("object" === typeof module && exports) || {}; var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; HDKey.create = function (versions) { + /** @type {hdkey} */ var hdkey = {}; + /** @type {Buffer?} */ var _privateKey = null; hdkey.versions = versions || BITCOIN_VERSIONS; hdkey.depth = 0; hdkey.index = 0; - hdkey.publicKey = null; - // note: BIP-32 "Key Identifier" is the same as "Pub Key Hash" - // but used for a different purpose, hence the semantic name - hdkey.identifier = null; - // note: chainCode is public - hdkey.chainCode = null; + //hdkey.publicKey = null; + //hdkey.identifier = null; + //hdkey.chainCode = null; hdkey.parentFingerprint = 0; hdkey.getFingerprint = function () { + if (!hdkey.identifier) { + throw new Error("Public key has not been set"); + } return hdkey.identifier.slice(0, 4).readUInt32BE(0); }; @@ -57,6 +100,9 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey._setPublicKey(publicKey); }; + /** + * @param {Buffer} publicKey + */ hdkey._setPublicKey = function (publicKey) { hdkey.publicKey = Buffer.from(publicKey); hdkey.identifier = hash160(publicKey); @@ -78,6 +124,10 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.getPublicExtendedKey = function () { + if (!hdkey.publicKey) { + throw new Error("Missing public key"); + } + return bs58check.encode( serialize(hdkey, hdkey.versions.public, hdkey.publicKey), ); @@ -118,7 +168,9 @@ var HDKey = ("object" === typeof module && exports) || {}; if (isHardened) { // Hardened child - assert(Boolean(_privateKey), "Could not derive hardened child key"); + if (!_privateKey) { + throw new Error("Could not derive hardened child key"); + } var pk = _privateKey; var zb = Buffer.alloc(1, 0); @@ -186,6 +238,10 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.sign = function (hash) { + if (!_privateKey) { + throw new Error("Private Key must be set"); + } + return Buffer.from( secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(_privateKey)) .signature, @@ -279,12 +335,21 @@ var HDKey = ("object" === typeof module && exports) || {}; return HDKey.fromExtendedKey(obj.xpriv); }; + /** + * @param {Boolean} assertion + * @param {String} message + */ function assert(assertion, message) { if (!assertion) { throw new Error(message); } } + /** + * @param {hdkey} hdkey - TODO attach to hdkey + * @param {Number} version + * @param {Buffer} key + */ function serialize(hdkey, version, key) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) var buffer = Buffer.allocUnsafe(LEN); @@ -302,6 +367,10 @@ var HDKey = ("object" === typeof module && exports) || {}; return buffer; } + /** + * @param {Buffer} buf + * @returns {Buffer} + */ function hash160(buf) { var sha = crypto.createHash("sha256").update(buf).digest(); return new RIPEMD160().update(sha).digest(); @@ -312,3 +381,104 @@ var HDKey = ("object" === typeof module && exports) || {}; if ("object" === typeof module) { module.exports = HDKey; } + +// Type Definitions + +/** + * @typedef HDVersions + * @prop {Number} private - 32-bit int (encodes to 'xprv' in base58) + * @prop {Number} public - 32-bit int (encodes to 'xpub' in base58) + */ + +/** + * @typedef HDJSON + * @prop {String?} xpriv - base58check-encoded extended private key + * @prop {String} xpub - base58check-encoded extended public key + */ + +// Function Definitions + +/** + * @callback HDDeriveChild + * @param {Number} index - includes HARDENED_OFFSET, if applicable + */ + +/** + * @callback HDDerivePath + * @param {String} path + */ + +/** + * @callback HDFingerprint + * @returns {Number} + */ + +/** + * @callback HDFromXKey + * @param {String} base58key - base58check-encoded xkey + * @param {HDVersions} [versions] + * @param {Boolean} [skipVerification] + * returns {hdkey} + */ + +/** + * @callback HDFromJSON + * @param {HDFromJSONOpts} opts + * returns {hdkey} + * + * @typedef HDFromJSONOpts + * @prop {String} xpriv + */ + +/** + * @callback HDFromSeed + * @param {Buffer} seedBuffer + * @param {HDVersions} [versions] + */ + +/** + * @callback HDGetBuffer + * @returns {Buffer} + */ + +/** + * @callback HDGetString + * @returns {String} + */ + +/** + * @callback HDMaybeGetBuffer + * @returns {Buffer?} + */ + +/** + * @callback HDMaybeGetString + * @returns {String?} + */ + +/** + * @callback HDSetBuffer + * @param {Buffer} buf + */ + +/** + * @callback HDSign + * @param {Buffer} hash + * @returns {Buffer} - signature + */ + +/** + * @callback HDToJSON + * @returns {HDJSON} + */ + +/** + * @callback HDVerify + * @param {Buffer} hash + * @param {Buffer} signature + * @returns {Boolean} + */ + +/** + * @callback HDWipePrivates + */ From 2d19b288c49e5b88fae525e4dd2243d0b04e3ab9 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 26 Jan 2023 15:33:17 -0700 Subject: [PATCH 21/22] ref: var => let (for upcoming `forEach` => async `for` changes) --- lib/hdkey.js | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index e849d38..04e93ba 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -43,23 +43,23 @@ var HDKey = ("object" === typeof module && exports) || {}; (function (window, HDKey) { "use strict"; - var crypto = require("crypto"); - var bs58check = require("bs58check"); - var RIPEMD160 = require("ripemd160"); - var secp256k1 = require("secp256k1"); + let crypto = require("crypto"); + let bs58check = require("bs58check"); + let RIPEMD160 = require("ripemd160"); + let secp256k1 = require("secp256k1"); - var MASTER_SECRET = Buffer.from("Bitcoin seed", "utf8"); - var HARDENED_OFFSET = 0x80000000; - var LEN = 78; + let MASTER_SECRET = Buffer.from("Bitcoin seed", "utf8"); + let HARDENED_OFFSET = 0x80000000; + let LEN = 78; // Bitcoin hardcoded by default, can use package `coininfo` for others - var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; + let BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; HDKey.create = function (versions) { /** @type {hdkey} */ - var hdkey = {}; + let hdkey = {}; /** @type {Buffer?} */ - var _privateKey = null; + let _privateKey = null; hdkey.versions = versions || BITCOIN_VERSIONS; hdkey.depth = 0; @@ -95,7 +95,7 @@ var HDKey = ("object" === typeof module && exports) || {}; ); assert(secp256k1.publicKeyVerify(value) === true, "Invalid public key"); // force compressed point (performs public key verification) - var publicKey = + let publicKey = value.length === 65 ? secp256k1.publicKeyConvert(value, true) : value; hdkey._setPublicKey(publicKey); }; @@ -138,16 +138,16 @@ var HDKey = ("object" === typeof module && exports) || {}; return hdkey; } - var entries = path.split("/"); - var _hdkey = hdkey; + let entries = path.split("/"); + let _hdkey = hdkey; 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) + let hardened = c.length > 1 && c[c.length - 1] === "'"; + let childIndex = parseInt(c, 10); // & (HARDENED_OFFSET - 1) assert(childIndex < HARDENED_OFFSET, "Invalid index"); if (hardened) { childIndex += HARDENED_OFFSET; @@ -160,11 +160,11 @@ var HDKey = ("object" === typeof module && exports) || {}; }; hdkey.deriveChild = function (index) { - var isHardened = index >= HARDENED_OFFSET; - var indexBuffer = Buffer.allocUnsafe(4); + let isHardened = index >= HARDENED_OFFSET; + let indexBuffer = Buffer.allocUnsafe(4); indexBuffer.writeUInt32BE(index, 0); - var data; + let data; if (isHardened) { // Hardened child @@ -172,8 +172,8 @@ var HDKey = ("object" === typeof module && exports) || {}; throw new Error("Could not derive hardened child key"); } - var pk = _privateKey; - var zb = Buffer.alloc(1, 0); + let pk = _privateKey; + let zb = Buffer.alloc(1, 0); pk = Buffer.concat([zb, pk]); // data = 0x00 || ser256(kpar) || ser32(index) @@ -185,14 +185,14 @@ var HDKey = ("object" === typeof module && exports) || {}; data = Buffer.concat([hdkey.publicKey, indexBuffer]); } - var I = crypto + let I = crypto .createHmac("sha512", hdkey.chainCode) .update(data) .digest(); - var IL = I.slice(0, 32); - var IR = I.slice(32); + let IL = I.slice(0, 32); + let IR = I.slice(32); - var hd = HDKey.create(hdkey.versions); + let hd = HDKey.create(hdkey.versions); // Private parent key -> private child key if (_privateKey) { @@ -275,14 +275,14 @@ var HDKey = ("object" === typeof module && exports) || {}; }; HDKey.fromMasterSeed = function (seedBuffer, versions) { - var I = crypto + let I = crypto .createHmac("sha512", MASTER_SECRET) .update(seedBuffer) .digest(); - var IL = I.slice(0, 32); - var IR = I.slice(32); + let IL = I.slice(0, 32); + let IR = I.slice(32); - var hdkey = HDKey.create(versions); + let hdkey = HDKey.create(versions); hdkey.chainCode = IR; hdkey.setPrivateKey(IL); @@ -293,11 +293,11 @@ var HDKey = ("object" === typeof module && exports) || {}; // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) versions = versions || BITCOIN_VERSIONS; skipVerification = skipVerification || false; - var hdkey = HDKey.create(versions); + let hdkey = HDKey.create(versions); - var keyBuffer = bs58check.decode(base58key); + let keyBuffer = bs58check.decode(base58key); - var version = keyBuffer.readUInt32BE(0); + let version = keyBuffer.readUInt32BE(0); assert( version === versions.private || version === versions.public, "Version mismatch: does not match private or public", @@ -308,7 +308,7 @@ var HDKey = ("object" === typeof module && exports) || {}; hdkey.index = keyBuffer.readUInt32BE(9); hdkey.chainCode = keyBuffer.slice(13, 45); - var key = keyBuffer.slice(45); + let key = keyBuffer.slice(45); if (key.readUInt8(0) === 0) { // private assert( @@ -352,12 +352,12 @@ var HDKey = ("object" === typeof module && exports) || {}; */ function serialize(hdkey, version, key) { // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) - var buffer = Buffer.allocUnsafe(LEN); + let buffer = Buffer.allocUnsafe(LEN); buffer.writeUInt32BE(version, 0); buffer.writeUInt8(hdkey.depth, 4); - var fingerprint = hdkey.depth ? hdkey.parentFingerprint : 0x00000000; + let fingerprint = hdkey.depth ? hdkey.parentFingerprint : 0x00000000; buffer.writeUInt32BE(fingerprint, 5); buffer.writeUInt32BE(hdkey.index, 9); @@ -372,7 +372,7 @@ var HDKey = ("object" === typeof module && exports) || {}; * @returns {Buffer} */ function hash160(buf) { - var sha = crypto.createHash("sha256").update(buf).digest(); + let sha = crypto.createHash("sha256").update(buf).digest(); return new RIPEMD160().update(sha).digest(); } From c19206d2fd917b6efad364bff696d78fc08261e0 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 27 Jan 2023 04:14:18 -0700 Subject: [PATCH 22/22] ref: replace forEach() with for() {} as prep for async --- lib/hdkey.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/hdkey.js b/lib/hdkey.js index 04e93ba..09e07a7 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -140,10 +140,11 @@ var HDKey = ("object" === typeof module && exports) || {}; let entries = path.split("/"); let _hdkey = hdkey; - entries.forEach(function (c, i) { + for (let i = 0; i < entries.length; i += 1) { + let c = entries[i]; if (i === 0) { assert(/^[mM]{1}/.test(c), 'Path must start with "m" or "M"'); - return; + continue; } let hardened = c.length > 1 && c[c.length - 1] === "'"; @@ -154,7 +155,7 @@ var HDKey = ("object" === typeof module && exports) || {}; } _hdkey = _hdkey.deriveChild(childIndex); - }); + } return _hdkey; };