From 50b13d2941bb7195a8f1d4aa4d20b75234b5bfd6 Mon Sep 17 00:00:00 2001 From: Stefan Thomas <justmoon@members.fsf.org> Date: Sat, 3 Sep 2011 19:36:45 +0100 Subject: [PATCH] More work on split key protocol. --- demo/split-key.html | 48 +++++++++++++++++--- demo/split-key.js | 104 +++++++++++++++++++++++++++++++++----------- src/paillier.js | 25 ++++++++--- test/test.js | 12 +++++ 4 files changed, 153 insertions(+), 36 deletions(-) diff --git a/demo/split-key.html b/demo/split-key.html index b3b9915..82175bd 100755 --- a/demo/split-key.html +++ b/demo/split-key.html @@ -81,7 +81,7 @@ jQuery(function ($) { <label for="q1">Q<sub>1</sub>=</label> <input id="q1" type="text" readonly="readonly"/> </div> - <p>She then encrypts z<sub>1</sub> with her Paillier secret to create α = E<sub>pk</sub>(z<sub>1</sub>)</p> + <p>She then encrypts z<sub>1</sub> using Paillier to create α = E<sub>pk</sub>(z<sub>1</sub>)</p> <div> <label for="alpha">α=</label> <input id="alpha" type="text" readonly="readonly"/> @@ -91,9 +91,34 @@ jQuery(function ($) { <label for="beta">β=</label> <input id="beta" type="text" readonly="readonly"/> </div> + <p>And also generates an encrypted blinding factor A = E<sub>pk</sub>(c) for some c ∈ [1, n<sub>P</sub>/n<sub>EC</sub>]</p> + <div> + <label for="A">A=</label> + <input id="A" type="text" readonly="readonly"/> + </div> + <p>Alice composes the encrypted signature σ<sub>1</sub> = (α ×<sub>pk</sub> e) +<sub>pk</sub> (β ×<sub>pk</sub> r) +<sub>pk</sub> (A ×<sub>pk</sub> n)</p> + <div> + <label for="sigma_1">σ<sub>1</sub>=</label> + <input id="sigma_1" type="text" readonly="readonly"/> + </div> + <p>She deterministically rerandomizes it to receive σ<sub>1</sub>' = σ<sub>1</sub>HASH(σ<sub>1</sub>)<sup>n</sub> mod n<sup>2</sup></p> + <div> + <label for="sigma_1n">σ<sub>1</sub>'=</label> + <input id="sigma_1n" type="text" readonly="readonly"/> + </div> + <p>And decrypts σ<sub>1</sub>' to receive s<sub>1</sub></p> + <div> + <label for="s_1">s<sub>1</sub>=</label> + <input id="s_1" type="text" readonly="readonly"/> + </div> + <p>And v', the randomizing factor in σ<sub>1</sub>'</p> + <div> + <label for="v_n">v<sub>'</sub>=</label> + <input id="v_n" type="text" readonly="readonly"/> + </div> </div> <div class="messageright"><div class="arrow"></div> -Q<sub>1</sub>, α, β, message, e, pk +Q<sub>1</sub>, α, β, message, e, pk, A, s<sub>1</sub>, v' </div> <div class="bob"> <p>Bob validates Q<sub>1</sub> by ensuring that @@ -104,6 +129,13 @@ Q<sub>1</sub>, α, β, message, e, pk <li>nQ<sub>1</sub> = O</li> </ol></p> <p>And verifies the message to be signed</p> + <p>He then verifies s<sub>1</sub> as a valid signature</p> + <p>Bob also calculates σ<sub>1</sub>' from α, β and A</p> + <div> + <label for="sigma_1n_b">σ<sub>1</sub>'=</label> + <input id="sigma_1n_b" type="text" readonly="readonly"/> + </div> + <p>And verifies it matches E<sub>pk</sub>(s<sub>1</sub>, v')</p> <p>He then generates his share k<sub>2</sub> of the private one-time value k</p> <div> <label for="k2">k<sub>2</sub>=</label> @@ -124,12 +156,12 @@ Q<sub>1</sub>, α, β, message, e, pk <label for="q2">Q<sub>2</sub>=</label> <input id="q2" type="text" readonly="readonly"/> </div> - <p>Bob prepares a random value c to use for blinding<p> + <p>Bob prepares a random value B ∈ [1, n<sub>P</sub>/n<sub>EC</sub>] to use for blinding<p> <div> - <label for="c">c=</label> - <input id="c" type="text" readonly="readonly"/> + <label for="B">B=</label> + <input id="B" type="text" readonly="readonly"/> </div> - <p>Finally he calculates σ = (α ×<sub>pk</sub> z<sub>2</sub>e) +<sub>pk</sub> (β ×<sub>pk</sub> z<sub>2</sub>d<sub>2</sub>r) +<sub>pk</sub> E<sub>pk</sub>(cn)</p> + <p>Finally he calculates σ = (α ×<sub>pk</sub> z<sub>2</sub>e) +<sub>pk</sub> (β ×<sub>pk</sub> z<sub>2</sub>d<sub>2</sub>r) +<sub>pk</sub> E<sub>pk</sub>(Bn<sub>EC</sub>)</p> <div> <label for="sigma">σ=</label> <input id="sigma" type="text" readonly="readonly"/> @@ -153,6 +185,10 @@ Q<sub>2</sub>, r, σ <input id="s" type="text" readonly="readonly"/> </div> <p>She verifies the signature using r and the combined public key before publishing.</p> + <div> + <label for="result"></label> + <input id="result" type="text" readonly="readonly"/> + </div> </div> </body> </html> diff --git a/demo/split-key.js b/demo/split-key.js index 582e1e9..0ded297 100644 --- a/demo/split-key.js +++ b/demo/split-key.js @@ -39,7 +39,7 @@ function log() { postMessage({ "cmd": "log", "args": Array.prototype.slice.apply(arguments) }); }; -self.onmessage = function (event) { +function start() { var ecparams = getSECCurveByName("secp256k1"); var rng = new SecureRandom(); @@ -73,6 +73,10 @@ self.onmessage = function (event) { 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); }; @@ -87,24 +91,43 @@ self.onmessage = function (event) { this.z1 = this.k1.modInverse(n); ff('z1', this.z1); - var Q1 = G.multiply(this.k1); - ff('q1', Q1); + var Q_1 = G.multiply(this.k1); + ff('q1', Q_1); var alpha = this.paillier.encrypt(this.z1); - var beta = this.paillier.encrypt(this.d1.multiply(this.z1).mod(n)); - ff('alpha', alpha); + + var beta = this.paillier.encrypt(this.d1.multiply(this.z1).mod(n)); ff('beta', beta); - // TODO: Generate a proof that alpha and beta are safe + 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 { - message: message, - e: this.e, - Q1: Q1, + Q_1: Q_1, + P_1: this.getPubShare(), alpha: alpha, beta: beta, - paillier: this.paillier.pub + message: message, + paillier: this.paillier.pub, + A: A, + s_1: s_1, + v_n: v_n }; }; @@ -113,29 +136,46 @@ self.onmessage = function (event) { // does what we want. // Throws exception on error - pkg.Q1.validate(); + pkg.Q_1.validate(); var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true}); this.e = BigInteger.fromByteArrayUnsigned(hash).mod(n); - if (!this.e.equals(pkg.e)) { - throw new Error('We arrived at different values for e.'); - } - 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 Q2 = G.multiply(this.k2); - ff('q2', Q2); + var Q_2 = G.multiply(this.k2); + ff('q2', Q_2); - var Q = pkg.Q1.multiply(this.k2); + var Q = pkg.Q_1.multiply(this.k2); this.r = Q.getX().toBigInteger().mod(n); ff('r', this.r); @@ -143,26 +183,26 @@ self.onmessage = function (event) { throw new Error('r must not be zero.'); } - var c = Bitcoin.ECDSA.getBigRandom(this.paillier.n.divide(n)); - ff('c', c); + 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), c.multiply(n)); + var sigma = p.add(p.addCrypt(s_a, s_b), B.multiply(n)); ff('sigma', sigma); return { - Q2: Q2, + Q_2: Q_2, r: this.r, sigma: sigma }; }; Alice.prototype.step3 = function (pkg) { - pkg.Q2.validate(); + pkg.Q_2.validate(); - var Q = pkg.Q2.multiply(this.k1); + var Q = pkg.Q_2.multiply(this.k1); this.r = Q.getX().toBigInteger().mod(n); if (!this.r.equals(pkg.r)) { @@ -219,11 +259,25 @@ self.onmessage = function (event) { log("sig :", hex(sig)); log("sig/CHK:", hex(sigChk)); - log("ver :", Bitcoin.ECDSA.verify(hash, sig, pub)); + 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.<anonymous>\s*\(/gm, '{anonymous}()@') + .split('\n'); + log(e+'\n'+stack); + } +}; diff --git a/src/paillier.js b/src/paillier.js index 17fada6..32b1735 100644 --- a/src/paillier.js +++ b/src/paillier.js @@ -65,6 +65,14 @@ Bitcoin.Paillier = (function () { return c.modPow(f, this.nSq); }; + Paillier.PublicKey.prototype.rerandomize = function (c, r) { + if (!r) { + var coprimeBitLength = this.n.bitLength() - Math.floor(Math.random()*10); + r = new BigInteger(coprimeBitLength, 1, rng); + } + return c.multiply(r.modPow(this.n, this.nSq)).mod(this.nSq); + }; + Paillier.PrivateKey = function (n,g,l,m,nSq) { this.l = l; this.m = m; @@ -73,12 +81,8 @@ Bitcoin.Paillier = (function () { this.pub = new Paillier.PublicKey(n,g,this.nSq); }; - Paillier.PrivateKey.prototype.encrypt = function (m) { - return this.pub.encrypt(m); - }; - Paillier.PrivateKey.prototype.decrypt = function (c) { - return c.modPow(this.l, this.nSq).mod(this.nSq).subtract(BigInteger.ONE) + return c.modPow(this.l, this.nSq).subtract(BigInteger.ONE) .divide(this.n).multiply(this.m).mod(this.n); }; @@ -93,5 +97,16 @@ Bitcoin.Paillier = (function () { return rn.modPow(e, this.n); }; + function createProxyMethod(name) { + return function () { + return this.pub[name].apply(this.pub, + Array.prototype.slice.apply(arguments)); + }; + }; + var a = ["add", "addCrypt", "multiply", "rerandomize", "encrypt"]; + for (var i = 0, l = a.length; i < l; i++) { + Paillier.PrivateKey.prototype[a[i]] = createProxyMethod(a[i]); + } + return Paillier; })(); diff --git a/test/test.js b/test/test.js index 010dc5c..8d6e21b 100755 --- a/test/test.js +++ b/test/test.js @@ -98,3 +98,15 @@ test("Signing and Verifying", function () { ); ok(Bitcoin.ECDSA.verify(hash2, sig_c, s2), "Verify constant signature"); }); + +// +// Testing Paillier +// ----------------------------------------------------------------------------- +module("paillier"); + +test("Classes", function () { + expect(3); + ok(Bitcoin.Paillier, "Bitcoin.Paillier"); + ok(Bitcoin.Paillier.PublicKey, "Bitcoin.Paillier.PublicKey"); + ok(Bitcoin.Paillier.PrivateKey, "Bitcoin.Paillier.PrivateKey"); +});