UNPKG

@yipsec/rise

Version:

eclipse-resistant network identities

669 lines (579 loc) 28.7 kB
<!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: index.js</title><!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-rise.html">rise</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-rise-EncryptedRiseMessage.html">EncryptedRiseMessage</a></div><div class="sidebar-section-children"><a href="module-rise-RiseIdentity.html">RiseIdentity</a></div><div class="sidebar-section-children"><a href="module-rise-RiseMessage.html">RiseMessage</a></div><div class="sidebar-section-children"><a href="module-rise-RiseSecret.html">RiseSecret</a></div><div class="sidebar-section-children"><a href="module-rise-RiseSolution.html">RiseSolution</a></div><div class="sidebar-section-children"><a href="module-rise-SignedRiseMessage.html">SignedRiseMessage</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">index.js</h1></header><article><pre class="prettyprint source lang-js"><code>/** * Rise is a protocol for decentalized, eclipse-resistant identities. * @module rise * @author chihuahua.rodeo &lt;161chihuahuas@disroot.org> * @license LGPL-2.1 */ 'use strict'; const crypto = require('node:crypto'); const bip39 = require('bip39'); const ecies = require('eciesjs'); const { secp256k1: secp } = require('@noble/curves/secp256k1'); const equihash = require('@yipsec/equihash'); function _hash(alg, input) { return crypto.createHash(alg).update(input).digest(); } function sha256(input) { return _hash('sha256', input); } function rmd160(input) { return _hash('rmd160', input); } class RiseIdentity { /** * Default difficuly setting. Require this many leading zeroes in solution * proofs. */ static get Z() { return 6; } /** * Lowered difficulty for testing. */ static get TEST_Z() { return 0; } /** * Equihash N parameter (width in bits). */ static get N() { return 102; } /** * Lowered width for testing. */ static get TEST_N() { return 90; } /** * Equihash K parameter (length). */ static get K() { return 5; } /** * Lowered length for testing. */ static get TEST_K() { return 5; } /** * Rise magic number. Used as message terminator and protocol identitifier. */ static get MAGIC() { return sha256(Buffer.from('¬«', 'binary')); } /** * Magic number to segment test network. */ static get TEST_MAGIC() { return sha256(Buffer.from('¡', 'binary')); } /** * Rise default salt for pbkdf2 operations. */ static get SALT() { return Buffer.from('\fæ×"\x94ì9\x82BW(i&lt;ªþ`', 'binary'); } /** * Rise *private* identity bundle. This is the primary interface for using * this module. Allows to generate new identities and use them as the * context for protected operations. * @constructor * @param {Uint8Array|buffer} [entropy] - Private key (secp256k1). If absent * a new one will be created. * @param {RiseSolution} [solution] - Equihash solution corresponding to the * given private key. * @param {Uint8Array|buffer} [salt=module:rise~RiseSolution~SALT] - Salt used for local * pbkdf2 operations locking/unlocking this identity. */ constructor(entropy, solution, salt = RiseIdentity.SALT) { entropy = entropy || secp.utils.randomPrivateKey(); /** * Used for local PBKDF2. * @member {Uint8Array|buffer} */ this.salt = salt; /** * BIP39 recovery words. * @member {string} */ this.mnemonic = bip39.entropyToMnemonic(entropy); /** * Underlying secret key. * @member {module:rise~RiseSecret} */ this.secret = new RiseSecret(entropy); /** * Underlying equihash solution. * @member {module:rise~RiseSolution} */ this.solution = solution || {}; } /** * 160 bit solution hash. * @member {buffer} */ get fingerprint() { return this.solution.fingerprint; } /** * Creates a new {@link RiseSolution} for this identity. This method updates * the internal state and will overwrite any previous solution performed in * this context. * @param {number} [n=module:rise~RiseIdentity~N] - Width in bits. * @param {number} [k=module:rise~RiseIdentity~K] - Solution length. * @param {buffer} [epoch=module:rise~RiseIdentity~MAGIC] - Prepended to public key before * hashing. This can be used to segment protocol versions by changing this value * which would render solutions generated with a previous or otherwise different * value invalid and require a new solution. * @returns {Promise&lt;module:rise~RiseSolution>} */ solve(n = RiseIdentity.N, k = RiseIdentity.K, epoch = RiseIdentity.MAGIC) { return new Promise((resolve, reject) => { equihash.solve(sha256( Buffer.concat([epoch, this.secret.publicKey]) ), n, k).then(solution => { this.solution = new RiseSolution(solution.proof, solution.nonce, this.secret.publicKey, epoch); resolve(this.solution); }, reject); }); } /** * Returns a plain object representation of this identity, serializable to JSON. * @returns {object} */ toJSON() { return { secret: Buffer.from(this.secret.privateKey).toString('base64'), salt: this.salt.toString('base64'), version: require('./package.json').version, solution: this.solution ? this.solution.toJSON() : {} }; } /** * Creates an encrypted blob representation of this identity, suitable for * persistance to disk. * @param {string} password - User provided passphrase used to encrypt this * identity. * @returns {buffer} */ lock(password) { const key = crypto.pbkdf2Sync(password, this.salt, 100000, 32, 'sha512'); const iv = key.subarray(0, 16); const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); const encryptedData = Buffer.concat([ cipher.update(JSON.stringify(this.toJSON())), cipher.final() ]); return encryptedData; } /** * Constructs an encrypted and signed {@link module:rise~RiseMessage} for the given * public key. * @param {Uint8Array|buffer} toPublicKey - Recipient identity for encryption. * @param {Object.&lt;string, string>} [body] - Key-value pairs to serialize in the * message. * @param {Object.&lt;string, string>} [head] - Custom headers to include. **Headers * are NOT ENCRYPTED**. Only information that is necessary for routing should be * included here. * @returns {module:rise~SignedRiseMessage} */ message(toPublicKey, body = {}, head = {}) { const clearMsg = new RiseMessage(this.solution, body, head); const cryptMsg = clearMsg.encrypt(toPublicKey); return cryptMsg.sign(this.secret.privateKey); } /** * Decrypts the blob given the password and creates a new instance. * @param {string} password - User supplied passphrase for decryption. * @param {buffer} data - Binary blob of encrypted identity. * @param {buffer} [salt=module:rise~RiseIdentity~SALT] - Salt for pbkdf2. * @returns {modulex:rise~RiseIdentity} */ static unlock(password, data, salt = RiseIdentity.SALT) { const key = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha512'); const iv = key.subarray(0, 16); const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); const decryptedData = Buffer.concat([ decipher.update(data), decipher.final() ]); const json = JSON.parse(decryptedData.toString()); return new RiseIdentity( Buffer.from(json.secret, 'base64'), RiseSolution.fromJSON(json.solution), Buffer.from(json.salt, 'base64') ); } /** * "Mines" a new {@link module:rise~RiseIdentity} and *iteratively* generates * {@link module:rise~RiseSolution}s until one is found that satisfies the stated * difficulty. * @param {number} [zeroes=module:rise~RiseIdentity~Z] - Difficulty level expressed in * number of leading zero bits. * @param {number} [n=module:rise~RiseIdentity~N] - Width in bits. * @param {number} [k=module:rise~RiseIdentity~K] - Solution length. * @param {buffer} [epoch=module:rise~RiseIdentity~MAGIC] - Network magic number. * @returns {module:rise~RiseIdentity} */ static generate(zeroes = RiseIdentity.Z, n, k, epoch) { return new Promise(async (resolve, reject) => { let id, sol; do { id = new RiseIdentity(); try { sol = await id.solve(n, k, epoch); } catch (e) { return reject(e); } } while (sol.difficulty &lt; zeroes) resolve(id); }); } } class RiseSecret { /** * Interface for secp256k1 key pair. If no secret is provided, one will be * generated. * @constructor * @param {Uint8Array|buffer} [secret] - Private key to use. */ constructor(secret) { /** * Underlying private key. * @member {Uint8Array} */ this.privateKey = secret ? Uint8Array.from(secret) : secp.utils.randomPrivateKey(); } /** * Public key derived from private key. * @member {Uint8Array} */ get publicKey() { return secp.getPublicKey(this.privateKey); } /** * Decrypts the given data using the underlying private key. * @param {Uint8Array|buffer} message - Encrypted blob. * @returns {Uint8Array} */ decrypt(message) { return ecies.decrypt(this.privateKey, message); } /** * Creates a digital signature from the provided data. * @param {Uint8Array|buffer} message - Binary blob to sign. * @returns {string} hexSignature */ sign(message) { const buf = message; const msg = sha256(buf); const sig = secp.sign(msg, this.privateKey); return sig.toCompactHex(); } } class RiseMessage { /** * Protocol headers included in every rise message. * @typedef {object} module:rise~RiseMessage~Head * @prop {string} nonce - One-time token to prevent replay attacks. * @prop {string} version - Version of the rise package. Analagous to user agent. * @prop {boolean} ciphertext - Indicates if the message body should be treated * as ciphertext (it is encrypted). * @prop {module:rise~RiseSolution} solution - Sender authentication data. */ /** * Interface allowing for authenticated message exchange. * @constructor * @param {module:rise~RiseSolution} solution - Identity solution data. * @param {Object.&lt;string, string>} [body] - Key-value data to include. * @param {Object.&lt;string, string>} [head] - Additional headers. */ constructor(solution, body = {}, headers = {}) { /** * Default message headers, plus any custom ones supplied. * @member {module:rise~RiseMessage~Head} */ this.head = { nonce: Date.now() + '~' + crypto.randomBytes(8).toString('base64'), version: require('./package.json').version, ciphertext: false, solution, ...headers }; /** * Key-value pairs given for the message. * @member {Object.&lt;string, string>|string} */ this.body = Buffer.isBuffer(body) ? body.toString('base64') : body; } /** * Encrypts the message state for the public key provided. * @param {Uint8Array|buffer} publicKey - Recipient public key. * @returns {module:rise~EncryptedRiseMessage} */ encrypt(publicKey) { const body = this.head.ciphertext ? this.body : JSON.stringify(this.body); return new EncryptedRiseMessage(this.head.solution, ecies.encrypt(publicKey, body), this.head); } /** * Signs the message state using the private key provided. * @param {Uint8Array|buffer} privateKey - Identity to use for signature. * @returns {module:rise~SignedRiseMessage} */ sign(privateKey) { const secret = new RiseSecret(privateKey); const buf = this.toBuffer(); const sig = secret.sign(buf); const msg = new SignedRiseMessage(this.head.solution, this.body, { ...this.head, signature: sig }); return msg; } /** * Returns only the body of this message. * @returns {Object.&lt;string, string|module:rise~RiseMessage|module:rise~EncryptedRiseMessage|module:rise~SignedRiseMessage|string>} */ unwrap() { return this.body; } /** * Ensures the solution header is valid. * @returns {boolean} */ validate() { return this.head.solution.verify(...arguments); } /** * Serializes the message to wire format. * @returns {buffer} */ toBuffer() { const magicStr = RiseIdentity.MAGIC.toString('hex'); const head = JSON.stringify(this.head); const body = this.head.ciphertext ? this.body : JSON.stringify(this.body); const str = [head, body].join(magicStr); return Buffer.from(str); } /** * Creates a new message instance from the serialized message. * @param {buffer} message - Binary blob to deserialize. * @returns {module:rise~RiseMessage|module:rise~EncryptedRiseMessage|module:rise~SignedRiseMessage} */ static fromBuffer(buffer) { const str = buffer.toString(); const magicStr = RiseIdentity.MAGIC.toString('hex'); const [rawHead, rawBody] = str.split(magicStr); const head = JSON.parse(rawHead); const body = head.ciphertext ? rawBody : JSON.parse(rawBody); head.solution = RiseSolution.fromJSON(head.solution); if (head.signature &amp;&amp; head.ciphertext) { return new SignedRiseMessage(head.solution, body, head); } if (head.ciphertext) { return new EncryptedRiseMessage(head.solution, body, head); } return new RiseMessage(head.solution, body, head); } } class EncryptedRiseMessage extends RiseMessage { /** * Interface for an encrypted message. * @constructor * @extends {module:rise~RiseMessage} */ constructor() { super(...arguments); this.head.ciphertext = true; } /** * Decrypts the message using the supplied private key. * @param {Uint8Array|buffer} privateKey - Private key to use. * @returns {module:rise~RiseMessage} */ decrypt(privateKey) { const secret = new RiseSecret(privateKey); let body = JSON.parse( Buffer.from(secret.decrypt(Buffer.from(this.body, 'base64'))) .toString()); if (body.head &amp;&amp; body.body) { let { proof, nonce, epoch, pubkey } = body.head.solution; const sol = new RiseSolution( Buffer.from(proof, 'base64'), parseInt(nonce), Buffer.from(pubkey, 'base64'), Buffer.from(epoch, 'base64') ); body.head.solution = sol; if (body.head.signature &amp;&amp; body.head.ciphertext) { body = new SignedRiseMessage(sol, body.body, body.head); } else if (body.head.ciphertext) { body = new EncryptedRiseMessage(sol, body.body, body.head); } else { body = new RiseMessage(sol, body.body, body.head); } } return new RiseMessage(this.head.solution, body, this.head); } } class SignedRiseMessage extends EncryptedRiseMessage { /** * Interface for digitall signed rise message * @constructor * @extends {module:rise~EncryptedRiseMessage} */ constructor() { super(...arguments); } /** * Ensures that the digita signature is valid. * @returns {boolean} */ verify() { const sig = this.head.signature; delete this.head.signature; const buf = this.toBuffer(); const msg = sha256(buf); const pub = this.head.solution.pubkey; const result = secp.verify(sig, msg, pub); this.head.signature = sig; return result; } } class RiseSolution { /** * Interface for identity solutions. * @param {buffer} proof - Equihash proof value. * @param {number} nonce - Solution nonce. * @param {buffer} pubkey - Public key solution was seeded from. * @param {buffer} epoch - Magic network number prepended to public key. */ constructor(proof, nonce, pubkey, epoch = RiseIdentity.MAGIC) { /** * Equihash proof. * @member {buffer} */ this.proof = proof; /** * Solution nonce. * @member {nuber} */ this.nonce = nonce; /** * Network magic number. * @member {buffer} */ this.epoch = epoch; /** * Public key. * @member {buffer} */ this.pubkey = Buffer.from(pubkey); } /** * RIPEMD-160 hash for SHA-256 hash of serialized solution. * @member {buffer} */ get fingerprint() { return rmd160(sha256(Buffer.from(JSON.stringify(this.toJSON())))); } /** * Number of leading zeroes in the proof. * @member {number} */ get difficulty() { const binStr = this.getProofAsBinaryString(); for (let i = 0; i &lt; binStr.length; i++) { if (binStr[i] !== '0') { return i; } } return binStr.length; } /** * Serilaizes the solution into a JSON object. * @returns {Object.&lt;string, string>} */ toJSON() { return { proof: this.proof.toString('base64'), nonce: this.nonce.toString(), pubkey: this.pubkey.toString('base64'), epoch: this.epoch.toString('base64') }; } /** * Constructs a {@link RiseSolution} from a JSON object. * @param {Object.&lt;string, string>} json - Serialized solution. * @returns {module:rise~RiseSolution} */ static fromJSON(json) { return new RiseSolution(Buffer.from(json.proof, 'base64'), parseInt(json.nonce), Buffer.from(json.pubkey, 'base64'), Buffer.from(json.epoch, 'base64')); } /** * Ensures that the solution is valid. * @param {number} [n=RiseIdentity.N] - Width in bits. * @param {number} [k=RiseIdentity.K] - Proof length. * @returns {Promise&lt;boolean>} */ verify(n = RiseIdentity.N, k = RiseIdentity.K) { return equihash.verify(sha256(Buffer.concat([this.epoch, this.pubkey])), this.proof, this.nonce, n, k); } /** * Represents the proof as a string of 1's and 0's. * @returns {string} */ getProofAsBinaryString() { const mapping = { '0': '0000', '1': '0001', '2': '0010', '3': '0011', '4': '0100', '5': '0101', '6': '0110', '7': '0111', '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111' }; const hexaString = this.proof.toString('hex').toLowerCase(); const bitmaps = []; for (let i = 0; i &lt; hexaString.length; i++) { bitmaps.push(mapping[hexaString[i]]); } return bitmaps.join(''); } } module.exports.Secret = RiseSecret; module.exports.Message = RiseMessage; module.exports.EncryptedMessage = EncryptedRiseMessage; module.exports.SignedMessage = SignedRiseMessage; module.exports.Identity = RiseIdentity; module.exports.Solution = RiseSolution; </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><div class="mobile-nav-links"></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-rise.html">rise</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-rise-EncryptedRiseMessage.html">EncryptedRiseMessage</a></div><div class="sidebar-section-children"><a href="module-rise-RiseIdentity.html">RiseIdentity</a></div><div class="sidebar-section-children"><a href="module-rise-RiseMessage.html">RiseMessage</a></div><div class="sidebar-section-children"><a href="module-rise-RiseSecret.html">RiseSecret</a></div><div class="sidebar-section-children"><a href="module-rise-RiseSolution.html">RiseSolution</a></div><div class="sidebar-section-children"><a href="module-rise-SignedRiseMessage.html">SignedRiseMessage</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>