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 &alpha; = E<sub>pk</sub>(z<sub>1</sub>)</p>
+  <p>She then encrypts z<sub>1</sub> using Paillier to create &alpha; = E<sub>pk</sub>(z<sub>1</sub>)</p>
   <div>
     <label for="alpha">&alpha;=</label>
     <input id="alpha" type="text" readonly="readonly"/>
@@ -91,9 +91,34 @@ jQuery(function ($) {
     <label for="beta">&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 &in; [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 &sigma;<sub>1</sub> = (&alpha; &times;<sub>pk</sub> e) +<sub>pk</sub> (&beta; &times;<sub>pk</sub> r) +<sub>pk</sub> (A &times;<sub>pk</sub> n)</p>
+  <div>
+    <label for="sigma_1">&sigma;<sub>1</sub>=</label>
+    <input id="sigma_1" type="text" readonly="readonly"/>
+  </div>
+  <p>She deterministically rerandomizes it to receive &sigma;<sub>1</sub>' = &sigma;<sub>1</sub>HASH(&sigma;<sub>1</sub>)<sup>n</sub> mod n<sup>2</sup></p>
+  <div>
+    <label for="sigma_1n">&sigma;<sub>1</sub>'=</label>
+    <input id="sigma_1n" type="text" readonly="readonly"/>
+  </div>
+  <p>And decrypts &sigma;<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 &sigma;<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>, &alpha;, &beta;, message, e, pk
+Q<sub>1</sub>, &alpha;, &beta;, 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>, &alpha;, &beta;, 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 &sigma;<sub>1</sub>' from &alpha;, &beta; and A</p>
+  <div>
+    <label for="sigma_1n_b">&sigma;<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>, &alpha;, &beta;, 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 &in; [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 &sigma; = (&alpha; &times;<sub>pk</sub> z<sub>2</sub>e) +<sub>pk</sub> (&beta; &times;<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 &sigma; = (&alpha; &times;<sub>pk</sub> z<sub>2</sub>e) +<sub>pk</sub> (&beta; &times;<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">&sigma;=</label>
     <input id="sigma" type="text" readonly="readonly"/>
@@ -153,6 +185,10 @@ Q<sub>2</sub>, r, &sigma;
     <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");
+});