UNPKG

@lorena-ssi/zenroom-lib

Version:

zenroom-lib is a javascript library to interact with the Zenroom Virtual Machine

471 lines (446 loc) 16.1 kB
'use strict' const zenroom = require('zenroom') /** * returns the digital Root * * @param {number} n Number * @returns {number} Digital Root (1 digit) */ function digitalRoot (n) { return (n - 1) % 9 + 1 } /** * Javascript Class to interact with Zenroom. */ module.exports = class Zen { constructor (silent = false) { this.isSilent = silent if (this.isSilent) { this.ogWriteStdout = process.stdout.write.bind(process.stdout) this.ogWriteStdErr = process.stderr.write.bind(process.stderr) /* istanbul ignore next */ process.on('uncaughtException', function (err) { /* istanbul ignore next */ console.error((err && err.stack) ? err.stack : err) }) } } /** * Executes Zencode. * * @param {object} keys Input keys. * @param {string} script Zencode to be executed * @returns {Promise} Return a promise with the execution of the script. */ async execute (keys, script) { const options = { verbosity: 0 } return new Promise((resolve, reject) => { const log = [] if (this.isSilent) { /* istanbul ignore next */ this.stdoutWrite = (data) => log.push({ stdout: data }) this.stderrWrite = (data) => log.push({ stderr: data }) process.stdout.write = this.stdoutWrite process.stderr.write = this.stderrWrite // TODO: They need to implement verbosity https://github.com/DECODEproject/Zenroom/blob/master/bindings/javascript/src/wrapper.js } zenroom .init(options) .keys(keys) .script(script) .print((msg) => { resolve(JSON.parse(msg)) }) .error(() => { reject(new Error('Zenroom error' + log)) }) .zencode_exec() if (this.isSilent) { process.stdout.write = this.ogWriteStdout process.stderr.write = this.ogWriteStdErr } }) } /** * Creates a new keypair. * * @param {string} name Holder of the keypair. * @returns {Promise} Return a promise with the execution of the creation. */ async newKeyPair (name) { return this.execute(false, `rule check version 1.0.0 Scenario simple: Create the keypair Given that I am known as '` + name + `' When I create the keypair Then print my data` ) } /** * Creates a new keypair. * * @param {string} name Holder of the keypair. * @param {*} keys to create * @returns {Promise} Return a promise with the execution of the creation. */ async publicKey (name, keys) { return this.execute(keys, `rule check version 1.0.0 Scenario simple Create the keypair Given that I am known as '` + name + `' and I have my valid 'public key' Then print my data` ) } /** * Encrypts (asymmetric) a message with a keypair. * * @param {string} fromName Who's signing the message. * @param {object} fromKeys Keypair for the encrypter (Zencode format) * @param {string} toName Who's receiving the message. * @param {object} toKeys Public Key for the receiver (Zencode format) * @param {string} message Message to be encrypted * @returns {Promise} Return a promise with the execution of the encryption. */ async encryptAsymmetric (fromName, fromKeys, toName, toKeys, message) { // Move to Hex. const msg = Buffer.from(message, 'utf8') return this.execute([fromKeys, toKeys], `Rule check version 1.0.0 Scenario simple: ` + fromName + ' encrypts a message for ' + toName + ` Given that I am known as '` + fromName + `' and I have my valid 'keypair' and I have a valid 'public key' from '` + toName + `' When I write string '${msg.toString('hex')}' in 'message' and I write string 'This is the header' in 'header' and I encrypt the message for '` + toName + `' Then print the 'secret_message'` ) } /** * Decrypts (asymmetric) a message with a keypair. * * @param {string} fromName Who's signing the message. * @param {object} fromKeys Keypair for the encrypter (Zencode format) * @param {string} toName Who's receiving the message. * @param {object} toKeys Public Key for the receiver (Zencode format) * @param {string} message Message to be decrypted * @returns {Promise} Return a promise with the execution of the encryption. */ async decryptAsymmetric (fromName, fromKeys, toName, toKeys, message) { return new Promise((resolve) => { this.execute([fromKeys, toKeys, message], `Rule check version 1.0.0 Scenario simple: ` + toName + ' decrypts the message for ' + fromName + ` Given that I am known as '` + toName + `' and I have my valid 'keypair' and I have a valid 'public key' from '` + fromName + `' and I have a valid 'secret_message' When I decrypt the secret message from '` + fromName + `' Then print as 'string' the 'message' and print as 'string' the 'header' inside 'secret message'` ).then((msg) => { const txt = Buffer.from(msg.message, 'hex') resolve({ message: txt.toString('utf8') }) }) }) } /** * Encrypts (symmetric) a message with a keypair. * * @param {string} password Password to encrypt the message * @param {string} message Message to be encrypted * @param {string} header Header to be included * @returns {Promise} Return a promise with the execution of the encryption. */ async encryptSymmetric (password, message, header) { // Move to Hex. const msg = Buffer.from(message, 'utf8') const hdr = Buffer.from(header, 'utf8') // Encrypt. return this.execute(false, `Rule check version 1.0.0 Scenario simple: Encrypt a message with the password Given nothing When I write string '${password}' in 'password' and I write string '${msg.toString('hex')}' in 'whisper' and I write string '${hdr.toString('hex')}' in 'header' and I encrypt the secret message 'whisper' with 'password' Then print the 'secret message'` ) } /** * Encrypts (symmetric) a message with a keypair. * * @param {string} password Password to decrypt the message * @param {string} msgEncrypted Message to be decrypted * @returns {Promise} Return a promise with the execution of the encryption. */ async decryptSymmetric (password, msgEncrypted) { return new Promise((resolve) => { this.execute([msgEncrypted], `Rule check version 1.0.0 Scenario simple: Decrypt the message with the password Given I have a valid 'secret message' When I write string '${password}' in 'password' and I decrypt the secret message with 'password' Then print as 'string' the 'text' inside 'message' and print as 'string' the 'header' inside 'message'` ).then((msg) => { const txt = Buffer.from(msg.text, 'hex') const hdr = Buffer.from(msg.header, 'hex') resolve({ header: hdr.toString('utf8'), message: txt.toString('utf8') }) }).catch(_e => { resolve(false) }) }) } /** * Signs a message with a keypair. * * @param {string} signer Who's signing the message. * @param {object} keys Keypair for the signer (Zencode format) * @param {string} message Message to be signed * @returns {Promise} Returns a promise with the execution of the signature. */ async signMessage (signer, keys, message) { return this.execute(keys, `Rule check version 1.0.0 Scenario simple: ` + signer + ` signs a message for Recipient Given that I am known as '` + signer + `' and I have my valid 'keypair' When I write string '${message}' in 'draft' and I create the signature of 'draft' Then print my 'signature' and print my 'draft'` ) } /** * Checks signature of a message. * * @param {string} signer Who's signing the message. * @param {object} signerPublic Who's signing the message, public Key. * @param {object} signature Signature of the message. * @param {string} verifier Message to be checked * @returns {Promise} Returns a promise with the execution of the signature. */ async checkSignature (signer, signerPublic, signature, verifier) { const checkScript = ` rule check version 1.0.0 Scenario simple: ` + verifier + ' verifies the signature from ' + signer + ` Given that I am known as '` + verifier + `' and I have a valid 'public key' from '` + signer + `' and I have a valid 'signature' from '` + signer + `' and I have a 'draft' When I verify the 'draft' is signed by '` + signer + `' Then print 'signature' 'correct' as 'string'` const keys = signature keys[signer].public_key = signerPublic[signer].public_key return this.execute(keys, checkScript) } /** * Creates a new Issuer keypair. * * @param {string} name Issuer of the credential keypair. * @returns {Promise} Return a promise with the execution of the creation. */ async newIssuerKeyPair (name) { return this.execute(false, `rule check version 1.0.0 Scenario 'coconut': issuer keygen Given that I am known as '` + name + `' When I create the issuer keypair Then print my 'issuer keypair'`) } /** * ZKP : Get the Verifier to be published. * * @param {string} verifier Who's signing the message. * @param {object} keys Keypair for the signer (Zencode format) * @returns {Promise} Returns a promise with the execution of the signature. */ async publishVerifier (verifier, keys) { return this.execute(keys, `rule check version 1.0.0 Scenario 'coconut': publish verifier Given that I am known as '` + verifier + `' and I have a valid 'verifier' Then print my 'verifier'` ) } /** * Creates a new Credential keypair. * * @param {string} name Holder of the keypair. * @returns {Promise} Return a promise with the execution of the creation. */ async newCredentialKeyPair (name) { const keygenContract = `rule check version 1.0.0 Scenario 'coconut': issuer keygen Given that I am known as '` + name + `' When I create the credential keypair Then print my 'credential keypair'` return this.execute(false, keygenContract) } /** * Creates a new Signature Request. * * @param {string} name Holder of the credential. * @param {object} credentialKeyPair Keypair for the credentials (Zencode format) * @returns {Promise} Return a promise with the execution of the creation. */ async credentialSignatureRequest (name, credentialKeyPair) { return this.execute(credentialKeyPair, `rule check version 1.0.0 Scenario 'coconut': create request Given that I am known as '` + name + `' and I have my valid 'credential keypair' When I create the credential request Then print my 'credential request'`) } /** * Creates a new Credential keypair. * * @param {string} nameIssuer Issuer of the credential. * @param {object} issuerKeyPair Keypair for the Issuer (Zencode format) * @param {object} signatureRequest signature Request by the Credential Holder. * @returns {Promise} Return a promise with the execution of the creation. */ async signCredentialSignatureRequest (nameIssuer, issuerKeyPair, signatureRequest) { return this.execute([issuerKeyPair, signatureRequest], `rule check version 1.0.0 Scenario 'coconut': issuer sign Given that I am known as '` + nameIssuer + `' and I have my valid 'issuer keypair' and I have a valid 'credential request' When I create the credential signature Then print the 'credential signature' and print the 'verifier'` ) } /** * Aggregates signature to the credential Proof. * * @param {string} name Holder of the keypair. * @param {object} credentialKeyPair Keypair for the credentials (Zencode format) * @param {object} credentialSignature Credential Request Signature * @returns {Promise} Return a promise with the execution of the creation. */ async aggregateCredentialSignature (name, credentialKeyPair, credentialSignature) { return this.execute([credentialKeyPair, credentialSignature], `rule check version 1.0.0 Scenario coconut: aggregate signature Given that I am known as '` + name + `' and I have my valid 'credential keypair' and I have a valid 'credential signature' When I create the credentials Then print my 'credentials' and print my 'credential keypair'`) } /** * Creates a new Credential Proof. * * @param {string} name Holder of the credential. * @param {string} nameIssuer Issuer of the credential. * @param {object} credential to use * @param {object} verifier of credential * @returns {Promise} Return a promise with the execution of the creation. */ async createCredentialProof (name, nameIssuer, credential, verifier) { return this.execute([credential, verifier], `rule check version 1.0.0 Scenario coconut: create proof Given that I am known as '` + name + `' and I have my valid 'credential keypair' and I have a valid 'verifier' from '` + nameIssuer + `' and I have my valid 'credentials' When I aggregate the verifiers and I create the credential proof Then print the 'credential proof'`) } /** * Verify a Credential Proof. * * @param {string} nameIssuer Issuer * @param {object} credentialProof to use * @param {object} verifier to use * @returns {Promise} Return a promise with the execution of the creation. */ async verifyCredentialProof (nameIssuer, credentialProof, verifier) { return this.execute([credentialProof, verifier], `rule check version 1.0.0 Scenario coconut: verify proof Given that I have a valid 'verifier' from '` + nameIssuer + `' and I have a valid 'credential proof' When I aggregate the verifiers and I verify the credential proof Then print 'Success' 'OK' as 'string'`) } /** * Create a Random string * * @param {number} length Length of the random string * @returns {Promise} Return a promise with the execution of the creation. */ async random (length = 32) { return new Promise((resolve) => { this.execute(false, `rule check version 1.0.0 Scenario simple: Generate a random password Given nothing When I create the array of '1' random objects of '256' bits Then print the 'array'`).then((rnd) => { var b = Buffer.from(rnd.array[0]) resolve(b.toString('base64').substring(0, length)) }) }) } /** * Creates a random Pin * * @param {number} length Length of the random PIN * @returns {Promise} Return a promise with the execution of the creation. */ async randomPin (length = 6) { return new Promise((resolve) => { let pin = '' this.random(length) .then((rnd) => { for (let i = 0; i < length; i++) { pin += digitalRoot(rnd.charCodeAt(i)) } resolve(pin) }) }) } /** * Creates a random DID * * @returns {Promise} Return a promise with the execution of the creation. */ async randomDID () { return new Promise((resolve) => { this.random(32) .then((rnd) => { var b = Buffer.from(rnd) resolve(b.toString('base64').slice(0, 32)) }) }) } /** * Create a Hash * * @param {string} source to be hashed * @returns {Promise} Return a promise with the execution of the creation. */ async hash (source) { return this.execute(false, `rule output encoding hex Given nothing When I write string '` + source + `' in 'source' and I create the hash of 'source' Then print the 'hash'`) } }