diff --git a/demo/demo.css b/demo/demo.css deleted file mode 100644 index 6912bee..0000000 --- a/demo/demo.css +++ /dev/null @@ -1,50 +0,0 @@ -p { - margin: 0.4em 0 0.2em; -} - -input[type=text] { - width: 500px; -} - -.alice, .bob { - margin: 1em; - width: 550px; - padding: 10px; -} - -.alice { - border: 2px solid grey; - border-left-width: 20px; -} - -.bob { - border: 2px solid grey; - border-right-width: 20px; -} - -.messageleft, .messageright { - margin: 1em; - background-color: grey; - height: 30px; - text-align: center; - color: #fff; - line-height: 30px; - width: 590px; -} - -.messageleft .arrow, .messageright .arrow { - border-top: 15px solid #fff; - border-bottom: 15px solid #fff; - width: 0; - height: 0; -} - -.messageright .arrow { - float: right; - border-left: 15px solid grey; -} - -.messageleft .arrow { - float: left; - border-right: 15px solid grey; -} diff --git a/demo/split-key.html b/demo/split-key.html deleted file mode 100644 index 82175bd..0000000 --- a/demo/split-key.html +++ /dev/null @@ -1,194 +0,0 @@ -<!doctype html> -<html> -<head> -<title>Two-party ECDSA signature generation</title> -<link rel="stylesheet" type="text/css" href="demo.css"/> -<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> -<script type="text/javascript"> -jQuery(function ($) { - var worker = new Worker("split-key.js"); - - worker.onmessage = function (event) { - var data = event.data; - - switch (data.cmd) { - case "ff": - $("#"+data.field).val(data.value); - break; - case "log": - if (console && "function" === typeof console.log) { - console.log.apply(console, data.args); - } - break; - } - }; - worker.onerror = function (error) { - console.error(error); - }; - - worker.postMessage("start"); -}); -</script> -</head> -<body> -<h1>Two-party ECDSA signature generation</h1> -<p><strong>Initialization</strong></p> -<div class="alice"> - <p>Alice starts out with her share of the private key d<sub>1</sub></p> - <div> - <label for="d1">d<sub>1</sub>=</label> - <input id="d1" type="text" readonly="readonly"/> - </div> - <p>And a Paillier keypair pk/sk</p> - <div> - <label for="p1_n">n=</label> - <input id="p1_n" type="text" readonly="readonly"/> - </div> - <div> - <label for="p1_g">g=</label> - <input id="p1_g" type="text" readonly="readonly"/> - </div> - <div> - <label for="p1_l">λ=</label> - <input id="p1_l" type="text" readonly="readonly"/> - </div> - <div> - <label for="p1_m">μ=</label> - <input id="p1_m" type="text" readonly="readonly"/> - </div> -</div> -<div class="bob"> - <p>Bob starts out with his share d<sub>2</sub> of the private key d</p> - <div> - <label for="d2">d<sub>2</sub>=</label> - <input id="d2" type="text" readonly="readonly"/> - </div> -</div> -<p><strong>Protocol</strong></p> -<div class="alice"> - <p>First Alice generates her share of the one-time secret k<sub>1</sub></p> - <div> - <label for="k1">k<sub>1</sub>=</label> - <input id="k1" type="text" readonly="readonly"/> - </div> - <p>And its inverse z<sub>1</sub> = (k<sub>1</sub>)<sup>-1</sup> mod n</p> - <div> - <label for="z1">z<sub>1</sub>=</label> - <input id="z1" type="text" readonly="readonly"/> - </div> - <p>She also calculates Q<sub>1</sub> = k<sub>1</sub>G</p> - <div> - <label for="q1">Q<sub>1</sub>=</label> - <input id="q1" type="text" readonly="readonly"/> - </div> - <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"/> - </div> - <p>And β = E<sub>pk</sub>(d<sub>1</sub>z<sub>1</sub> mod n)</p> - <div> - <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, A, s<sub>1</sub>, v' -</div> -<div class="bob"> - <p>Bob validates Q<sub>1</sub> by ensuring that - <ol> - <li>Q<sub>1</sub> ≠ O</li> - <li>x<sub>Q<sub>1</sub></sub> and y<sub>Q<sub>1</sub></sub> are in the interval [1,n - 1]</li> - <li>y<sub>Q<sub>1</sub></sub><sup>2</sup> ≡ x<sub>Q<sub>1</sub></sub><sup>3</sup> + ax<sub>Q<sub>1</sub></sub> + b (mod p)</li> - <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> - <input id="k2" type="text" readonly="readonly"/> - </div> - <p>And its inverse z<sub>2</sub> = (k<sub>2</sub>)<sup>-1</sup> mod n</p> - <div> - <label for="z2">z<sub>2</sub>=</label> - <input id="z2" type="text" readonly="readonly"/> - </div> - <p>He can calculate r = x<sub>Q</sub> where Q(x<sub>Q</sub>, y<sub>Q</sub>) = k<sub>2</sub>Q<sub>1</sub></p> - <div> - <label for="r">r=</label> - <input id="r" type="text" readonly="readonly"/> - </div> - <p>And Q<sub>2</sub> = k<sub>2</sub>G</p> - <div> - <label for="q2">Q<sub>2</sub>=</label> - <input id="q2" type="text" readonly="readonly"/> - </div> - <p>Bob prepares a random value B ∈ [1, n<sub>P</sub>/n<sub>EC</sub>] to use for blinding<p> - <div> - <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>(Bn<sub>EC</sub>)</p> - <div> - <label for="sigma">σ=</label> - <input id="sigma" type="text" readonly="readonly"/> - </div> -</div> -<div class="messageleft"><div class="arrow"></div> -Q<sub>2</sub>, r, σ -</div> -<div class="alice"> - <p>Alice confirms Q<sub>2</sub> is a valid public point - <ol> - <li>Q<sub>2</sub> ≠ O</li> - <li>x<sub>Q<sub>2</sub></sub> and y<sub>Q<sub>2</sub></sub> are in the interval [1,n - 1]</li> - <li>y<sub>Q<sub>2</sub></sub><sup>2</sup> ≡ x<sub>Q<sub>2</sub></sub><sup>3</sup> + ax<sub>Q<sub>2</sub></sub> + b (mod p)</li> - <li>nQ<sub>2</sub> = O</li> - </ol></p> - <p>She now calculates r = x<sub>Q</sub> where Q = k<sub>1</sub>Q<sub>2</sub> and matches it against what Bob claimed</p> - <p>She decrypts σ to receive s = D<sub>sk</sub>(σ)</p> - <div> - <label for="s">s=</label> - <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 deleted file mode 100644 index 0ded297..0000000 --- a/demo/split-key.js +++ /dev/null @@ -1,283 +0,0 @@ -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.<anonymous>\s*\(/gm, '{anonymous}()@') - .split('\n'); - log(e+'\n'+stack); - } -};