var window = this; importScripts( "../src/crypto-js/crypto.js", "../src/crypto-js/sha256.js", "../src/jsbn/prng4.js", "../src/jsbn/rng.js", "../src/jsbn/jsbn.js", "../src/jsbn/jsbn2.js", "../src/jsbn/ec.js", "../src/jsbn/sec.js", "../src/events/eventemitter.js", "../src/bitcoin.js", "../src/util.js", "../src/base58.js", "../src/address.js", "../src/ecdsa.js", "../src/paillier.js" ); function hex(value) { if ("function" === typeof value.getEncoded) { return Crypto.util.bytesToHex(value.getEncoded()); } else if ("function" === typeof value.toByteArrayUnsigned) { return Crypto.util.bytesToHex(value.toByteArrayUnsigned()); } else if (Array.isArray(value)) { return Crypto.util.bytesToHex(value); } return value; }; function ff(field, value) { value = hex(value); postMessage({ "cmd": "ff", "field": field, "value": value }); }; function log() { postMessage({ "cmd": "log", "args": Array.prototype.slice.apply(arguments) }); }; function start() { var ecparams = getSECCurveByName("secp256k1"); var rng = new SecureRandom(); var G = ecparams.getG(); var n = ecparams.getN(); G.validate(); var Alice = function (pubShare) { this.d1 = Bitcoin.ECDSA.getBigRandom(n); ff('d1', this.d1); this.paillier = Bitcoin.Paillier.generate(n.bitLength()*2+ Math.floor(Math.random()*10)); ff('p1_n', this.paillier.pub.n); ff('p1_g', this.paillier.pub.g); ff('p1_l', this.paillier.l); ff('p1_m', this.paillier.m); }; var Bob = function () { this.d2 = Bitcoin.ECDSA.getBigRandom(n); ff('d2', this.d2); }; Alice.prototype.getPub = function (P) { if (this.pub) return this.pub; P.validate(); return this.pub = P.multiply(this.d1).getEncoded(); }; Alice.prototype.getPubShare = function () { return G.multiply(this.d1); }; Bob.prototype.getPubShare = function () { return G.multiply(this.d2); }; Alice.prototype.step1 = function (message) { var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true}); this.e = BigInteger.fromByteArrayUnsigned(hash).mod(n); this.k1 = Bitcoin.ECDSA.getBigRandom(n); ff('k1', this.k1); this.z1 = this.k1.modInverse(n); ff('z1', this.z1); var Q_1 = G.multiply(this.k1); ff('q1', Q_1); var alpha = this.paillier.encrypt(this.z1); ff('alpha', alpha); var beta = this.paillier.encrypt(this.d1.multiply(this.z1).mod(n)); ff('beta', beta); var r_1 = Q_1.getX().toBigInteger().mod(n); var A = this.paillier.encrypt(Bitcoin.ECDSA.getBigRandom(this.paillier.n.divide(n))); ff('A', A); var s_a = this.paillier.multiply(alpha, this.e); var s_b = this.paillier.multiply(beta, r_1); var sigma_1 = this.paillier.addCrypt(this.paillier.addCrypt(s_a, s_b), this.paillier.multiply(A, n)); ff('sigma_1', sigma_1); var e = Crypto.SHA256(sigma_1.toByteArrayUnsigned(), {asBytes: true}); e = BigInteger.fromByteArrayUnsigned(e); var sigma_1n = this.paillier.rerandomize(sigma_1, e); ff('sigma_1n', sigma_1n); var s_1 = this.paillier.decrypt(sigma_1n); ff('s_1', s_1); var v_n = this.paillier.decryptR(sigma_1n, s_1); ff('v_n', v_n); return { Q_1: Q_1, P_1: this.getPubShare(), alpha: alpha, beta: beta, message: message, paillier: this.paillier.pub, A: A, s_1: s_1, v_n: v_n }; }; Bob.prototype.step2 = function (pkg) { // ... In real life we would check that message is a valid transaction and // does what we want. // Throws exception on error pkg.Q_1.validate(); var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true}); this.e = BigInteger.fromByteArrayUnsigned(hash).mod(n); this.paillier = pkg.paillier; this.alpha = pkg.alpha; this.beta = pkg.beta; var r_1 = pkg.Q_1.getX().toBigInteger().mod(n); var testSig = Bitcoin.ECDSA.serializeSig(r_1, pkg.s_1.mod(n)); if (!Bitcoin.ECDSA.verify(hash, testSig, pkg.P_1.getEncoded())) { throw new Error('Verification of s1 failed.'); } // Verify that alpha and beta are valid by generating and verifying sigma_1n var s_a_1 = this.paillier.multiply(this.alpha, this.e); var s_b_1 = this.paillier.multiply(this.beta, r_1); var sigma_1 = this.paillier.addCrypt(this.paillier.addCrypt(s_a_1, s_b_1), this.paillier.multiply(pkg.A, n)); var e = Crypto.SHA256(sigma_1.toByteArrayUnsigned(), {asBytes: true}); e = BigInteger.fromByteArrayUnsigned(e); var sigma_1n = this.paillier.rerandomize(sigma_1, e); ff('sigma_1n_b', sigma_1n); var sigma_1_verify = this.paillier.encrypt(pkg.s_1, pkg.v_n); if (!sigma_1n.equals(sigma_1_verify)) { throw new Error('Sigma ciphertext did not match expected value.'); } this.k2 = Bitcoin.ECDSA.getBigRandom(n); ff('k2', this.k2); this.z2 = this.k2.modInverse(n); ff('z2', this.z2); var Q_2 = G.multiply(this.k2); ff('q2', Q_2); var Q = pkg.Q_1.multiply(this.k2); this.r = Q.getX().toBigInteger().mod(n); ff('r', this.r); if (this.r.equals(BigInteger.ZERO)) { throw new Error('r must not be zero.'); } var B = Bitcoin.ECDSA.getBigRandom(this.paillier.n.divide(n)); ff('B', B); var p = this.paillier; var s_a = p.multiply(this.alpha, this.e.multiply(this.z2)); var s_b = p.multiply(this.beta, this.r.multiply(this.d2).multiply(this.z2)); var sigma = p.add(p.addCrypt(s_a, s_b), B.multiply(n)); ff('sigma', sigma); return { Q_2: Q_2, r: this.r, sigma: sigma }; }; Alice.prototype.step3 = function (pkg) { pkg.Q_2.validate(); var Q = pkg.Q_2.multiply(this.k1); this.r = Q.getX().toBigInteger().mod(n); if (!this.r.equals(pkg.r)) { throw new Error('Could not confirm value for r.'); } if (this.r.equals(BigInteger.ZERO)) { throw new Error('r must not be zero.'); } var s = this.paillier.decrypt(pkg.sigma).mod(n); ff('s', s); var sig = Bitcoin.ECDSA.serializeSig(this.r, s); var hash = this.e.toByteArrayUnsigned(); if (!Bitcoin.ECDSA.verify(hash, sig, this.getPub())) { throw new Error('Signature failed to verify.'); } return { r: this.r, s: s }; }; var message = "testmessage"; var bob = new Bob(); var pubShare = bob.getPubShare(); var alice = new Alice(pubShare); var pub = alice.getPub(pubShare); var pkg1 = alice.step1(message); var pkg2 = bob.step2(pkg1); var pkg3 = alice.step3(pkg2); var sig = Bitcoin.ECDSA.serializeSig(pkg3.r, pkg3.s); var kChk = alice.k1.multiply(bob.k2); var rChk = G.multiply(kChk).getX().toBigInteger(); log("r :", hex(pkg3.r)); log("r/CHK:", hex(rChk)); var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true}); var eChk = BigInteger.fromByteArrayUnsigned(hash).mod(n); var dChk = alice.d1.multiply(bob.d2); var sChk = kChk.modInverse(n).multiply(eChk.add(dChk.multiply(rChk))).mod(n); log("s :", hex(pkg3.s)); log("s/CHK:", hex(sChk)); var sigChk = Bitcoin.ECDSA.serializeSig(rChk, sChk); log("sig :", hex(sig)); log("sig/CHK:", hex(sigChk)); var ver = Bitcoin.ECDSA.verify(hash, sig, pub); log("ver :", ver); log("ver/CHK:", Bitcoin.ECDSA.verify(hash, sigChk, pub)); log("ver/CTL:", Bitcoin.ECDSA.verify(hash, Bitcoin.ECDSA.sign(hash, dChk), pub)); ff("result", ver ? "SIGNATURE VALID" : "SIGNATURE INVALID"); var priv = Bitcoin.ECDSA.getBigRandom(n); pub = G.multiply(priv).getEncoded(); log("ver/GEN:", Bitcoin.ECDSA.verify(hash, Bitcoin.ECDSA.sign(hash, priv), pub)); }; self.onmessage = function (event) { try { start(); } catch(e) { var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '') .replace(/^\s+at\s+/gm, '') .replace(/^Object.\s*\(/gm, '{anonymous}()@') .split('\n'); log(e+'\n'+stack); } };