diff --git a/README.md b/README.md index a8c853e..9b67f77 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,8 @@ However, production code will look more like this: RECEIVE, CHANGE async fromSeed(seedBytes, opts) // depth-0 hdkey (Wallet) async fromXKey(xprv||xpub, opts) // depth-4 hdkey (XKey) + async toPublic(xKey) + async wipePrivateData(xKey) async toWif(privBytes, opts) async toAddr(pubBytes, opts) async toXPrv(xprvKey, opts) @@ -415,6 +417,22 @@ let xkey = await DashHd.fromXKey(xprvOrXPub, options); } ``` +### `toPublic(xkey)` + +Creates a copy of the HD Key with `privateKey` set to `null`. + +```js +let xpubKey = await DashHd.toPublic(xprvKey); +``` + +### `wipePrivateData(xkey)` + +Performs an in-place secure erase of the private key memory. + +```js +await DashHd.wipePrivateData(xprvKey); +``` + ### `toWif(privBytes, opts)` Wrapper around `DashKeys.encodeKey(keyBytes, options)` to Base58Check-encode a diff --git a/dashhd.js b/dashhd.js index 04d84c0..60f35e4 100644 --- a/dashhd.js +++ b/dashhd.js @@ -6,6 +6,8 @@ * @prop {HDFingerprint} _fingerprint * @prop {HDFromSeed} fromSeed * @prop {HDFromXKey} fromXKey + * @prop {HDToId} toId + * @prop {HDToIdBytes} toIdBytes * @prop {HDToAddr} toAddr * @prop {HDToWif} toWif * @prop {HDToXPrv} toXPrv @@ -14,6 +16,7 @@ * @prop {HDToXKeyBytes} toXPubBytes * @prop {HDUtils} utils * @prop {HDWipePrivates} wipePrivateData - randomizes private key buffer in-place + * @prop {HDToPublic} toPublic - returns public key * @prop {Number} HARDENED_OFFSET - 0x80000000 * @prop {HDVersions} MAINNET - 'xprv' & 'xpub' * @prop {HDVersions} TESTNET - 'tprv' & 'tpub' @@ -35,6 +38,7 @@ * @prop {HDHasher} ripemd160sum * @prop {HDHasher} sha256sum * @prop {HDHasher} sha512hmac + * @prop {HDBase64Url} bytesToBase64Url * @prop {HDKeyToKey} toPublicKey */ @@ -218,6 +222,23 @@ var DashHd = ("object" === typeof module && exports) || {}; return new Uint8Array(sig); }; + /** @type {HDBase64Url} */ + Utils.bytesToBase64Url = function (bytes) { + let bins = []; + + for (let i = 0; i < bytes.length; i += 1) { + let b = bytes[i]; + let s = String.fromCodePoint(b); + bins.push(s); + } + + let str = bins.join(""); + let b64 = btoa(str); + let b64url = b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + + return b64url; + }; + /** @type {HDSecureErase} */ Utils.secureErase = function (buf) { if (!Crypto.getRandomValues) { @@ -248,6 +269,8 @@ var DashHd = ("object" === typeof module && exports) || {}; let XKEY_SIZE = 74; let XKEY_DEPTH = 4; // m/44'/5'/0'/<0>[/0] + DashHd._utils = Utils; + // Bitcoin defaults hard-coded by default. // Use package `coininfo` for others. DashHd.MAINNET = { private: 0x0488ade4, public: 0x0488b21e }; @@ -651,6 +674,29 @@ var DashHd = ("object" === typeof module && exports) || {}; return hdkey; }; + DashHd.toId = async function (hdkey) { + let idBytes = await DashHd.toIdBytes(hdkey); + let id = Utils.bytesToBase64Url(idBytes); + + return id; + }; + + DashHd.toIdBytes = async function (hdkey) { + let xpubBytes = await DashHd.toXPubBytes(hdkey); + + let hashBuffer = await Crypto.subtle.digest("SHA-256", xpubBytes); + let idBuffer = hashBuffer.slice(0, 8); + let idBytes = new Uint8Array(idBuffer); + + return idBytes; + }; + + DashHd.toPublic = function (_hdkey) { + let hdkey = Object.assign({}, _hdkey); + hdkey.privateKey = null; + return hdkey; + }; + DashHd.wipePrivateData = function (hdkey) { if (hdkey.privateKey) { Utils.secureErase(hdkey.privateKey); @@ -659,6 +705,12 @@ var DashHd = ("object" === typeof module && exports) || {}; return hdkey; }; + DashHd.toPublic = function (_hdkey) { + let hdkey = Object.assign({}, _hdkey); + hdkey.privateKey = null; + return hdkey; + }; + /** * @param {Boolean} assertion * @param {String} message @@ -828,6 +880,18 @@ if ("object" === typeof module) { * @returns {Promise} */ +/** + * @callback HDToId + * @param {HDKey} hdkey + * @returns {Promise} + */ + +/** + * @callback HDToIdBytes + * @param {HDKey} hdkey + * @returns {Promise} + */ + /** * @callback HDToXKeyBytes * @param {HDKey} hdkey @@ -871,6 +935,12 @@ if ("object" === typeof module) { * @returns {Promise} */ +/** + * @callback HDBase64Url + * @param {Uint8Array} bytes + * @returns {String} - URL-Safe Base64 Encoding + */ + /** * @callback HDHasher * @param {Uint8Array} bytes @@ -908,6 +978,12 @@ if ("object" === typeof module) { * @param {Uint8Array} buf */ +/** + * @callback HDToPublic + * @param {HDKey} hdkey + * @returns {HDKey} + */ + /** * @callback HDWipePrivates * @param {HDKey} hdkey diff --git a/package-lock.json b/package-lock.json index 9723bc3..af1ace6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dashhd", - "version": "3.1.0", + "version": "3.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dashhd", - "version": "3.1.0", + "version": "3.2.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "dashkeys": "^1.0.0" diff --git a/package.json b/package.json index d806595..2309cb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dashhd", - "version": "3.1.0", + "version": "3.2.0", "description": "Manage HD Keys from HD Wallet Seed and Extended (xprv, xpub) Key Paths. Part of $DASH Tools.", "main": "dashhd.js", "browser": {