More work on split key protocol.
This commit is contained in:
parent
77debc1d4f
commit
50b13d2941
4 changed files with 153 additions and 36 deletions
|
@ -81,7 +81,7 @@ jQuery(function ($) {
|
||||||
<label for="q1">Q<sub>1</sub>=</label>
|
<label for="q1">Q<sub>1</sub>=</label>
|
||||||
<input id="q1" type="text" readonly="readonly"/>
|
<input id="q1" type="text" readonly="readonly"/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label for="alpha">α=</label>
|
<label for="alpha">α=</label>
|
||||||
<input id="alpha" type="text" readonly="readonly"/>
|
<input id="alpha" type="text" readonly="readonly"/>
|
||||||
|
@ -91,9 +91,34 @@ jQuery(function ($) {
|
||||||
<label for="beta">β=</label>
|
<label for="beta">β=</label>
|
||||||
<input id="beta" type="text" readonly="readonly"/>
|
<input id="beta" type="text" readonly="readonly"/>
|
||||||
</div>
|
</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>
|
||||||
<div class="messageright"><div class="arrow"></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>
|
||||||
<div class="bob">
|
<div class="bob">
|
||||||
<p>Bob validates Q<sub>1</sub> by ensuring that
|
<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>
|
<li>nQ<sub>1</sub> = O</li>
|
||||||
</ol></p>
|
</ol></p>
|
||||||
<p>And verifies the message to be signed</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>
|
<p>He then generates his share k<sub>2</sub> of the private one-time value k</p>
|
||||||
<div>
|
<div>
|
||||||
<label for="k2">k<sub>2</sub>=</label>
|
<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>
|
<label for="q2">Q<sub>2</sub>=</label>
|
||||||
<input id="q2" type="text" readonly="readonly"/>
|
<input id="q2" type="text" readonly="readonly"/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label for="c">c=</label>
|
<label for="B">B=</label>
|
||||||
<input id="c" type="text" readonly="readonly"/>
|
<input id="B" type="text" readonly="readonly"/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label for="sigma">σ=</label>
|
<label for="sigma">σ=</label>
|
||||||
<input id="sigma" type="text" readonly="readonly"/>
|
<input id="sigma" type="text" readonly="readonly"/>
|
||||||
|
@ -153,6 +185,10 @@ Q<sub>2</sub>, r, σ
|
||||||
<input id="s" type="text" readonly="readonly"/>
|
<input id="s" type="text" readonly="readonly"/>
|
||||||
</div>
|
</div>
|
||||||
<p>She verifies the signature using r and the combined public key before publishing.</p>
|
<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>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -39,7 +39,7 @@ function log() {
|
||||||
postMessage({ "cmd": "log", "args": Array.prototype.slice.apply(arguments) });
|
postMessage({ "cmd": "log", "args": Array.prototype.slice.apply(arguments) });
|
||||||
};
|
};
|
||||||
|
|
||||||
self.onmessage = function (event) {
|
function start() {
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
var ecparams = getSECCurveByName("secp256k1");
|
||||||
var rng = new SecureRandom();
|
var rng = new SecureRandom();
|
||||||
|
|
||||||
|
@ -73,6 +73,10 @@ self.onmessage = function (event) {
|
||||||
return this.pub = P.multiply(this.d1).getEncoded();
|
return this.pub = P.multiply(this.d1).getEncoded();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Alice.prototype.getPubShare = function () {
|
||||||
|
return G.multiply(this.d1);
|
||||||
|
};
|
||||||
|
|
||||||
Bob.prototype.getPubShare = function () {
|
Bob.prototype.getPubShare = function () {
|
||||||
return G.multiply(this.d2);
|
return G.multiply(this.d2);
|
||||||
};
|
};
|
||||||
|
@ -87,24 +91,43 @@ self.onmessage = function (event) {
|
||||||
this.z1 = this.k1.modInverse(n);
|
this.z1 = this.k1.modInverse(n);
|
||||||
ff('z1', this.z1);
|
ff('z1', this.z1);
|
||||||
|
|
||||||
var Q1 = G.multiply(this.k1);
|
var Q_1 = G.multiply(this.k1);
|
||||||
ff('q1', Q1);
|
ff('q1', Q_1);
|
||||||
|
|
||||||
var alpha = this.paillier.encrypt(this.z1);
|
var alpha = this.paillier.encrypt(this.z1);
|
||||||
var beta = this.paillier.encrypt(this.d1.multiply(this.z1).mod(n));
|
|
||||||
|
|
||||||
ff('alpha', alpha);
|
ff('alpha', alpha);
|
||||||
|
|
||||||
|
var beta = this.paillier.encrypt(this.d1.multiply(this.z1).mod(n));
|
||||||
ff('beta', beta);
|
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 {
|
return {
|
||||||
message: message,
|
Q_1: Q_1,
|
||||||
e: this.e,
|
P_1: this.getPubShare(),
|
||||||
Q1: Q1,
|
|
||||||
alpha: alpha,
|
alpha: alpha,
|
||||||
beta: beta,
|
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.
|
// does what we want.
|
||||||
|
|
||||||
// Throws exception on error
|
// Throws exception on error
|
||||||
pkg.Q1.validate();
|
pkg.Q_1.validate();
|
||||||
|
|
||||||
var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true});
|
var hash = Crypto.SHA256(Crypto.SHA256(message, {asBytes: true}), {asBytes: true});
|
||||||
this.e = BigInteger.fromByteArrayUnsigned(hash).mod(n);
|
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.paillier = pkg.paillier;
|
||||||
this.alpha = pkg.alpha;
|
this.alpha = pkg.alpha;
|
||||||
this.beta = pkg.beta;
|
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);
|
this.k2 = Bitcoin.ECDSA.getBigRandom(n);
|
||||||
ff('k2', this.k2);
|
ff('k2', this.k2);
|
||||||
|
|
||||||
this.z2 = this.k2.modInverse(n);
|
this.z2 = this.k2.modInverse(n);
|
||||||
ff('z2', this.z2);
|
ff('z2', this.z2);
|
||||||
|
|
||||||
var Q2 = G.multiply(this.k2);
|
var Q_2 = G.multiply(this.k2);
|
||||||
ff('q2', Q2);
|
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);
|
this.r = Q.getX().toBigInteger().mod(n);
|
||||||
ff('r', this.r);
|
ff('r', this.r);
|
||||||
|
|
||||||
|
@ -143,26 +183,26 @@ self.onmessage = function (event) {
|
||||||
throw new Error('r must not be zero.');
|
throw new Error('r must not be zero.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = Bitcoin.ECDSA.getBigRandom(this.paillier.n.divide(n));
|
var B = Bitcoin.ECDSA.getBigRandom(this.paillier.n.divide(n));
|
||||||
ff('c', c);
|
ff('B', B);
|
||||||
|
|
||||||
var p = this.paillier;
|
var p = this.paillier;
|
||||||
var s_a = p.multiply(this.alpha, this.e.multiply(this.z2));
|
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 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);
|
ff('sigma', sigma);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Q2: Q2,
|
Q_2: Q_2,
|
||||||
r: this.r,
|
r: this.r,
|
||||||
sigma: sigma
|
sigma: sigma
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Alice.prototype.step3 = function (pkg) {
|
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);
|
this.r = Q.getX().toBigInteger().mod(n);
|
||||||
|
|
||||||
if (!this.r.equals(pkg.r)) {
|
if (!this.r.equals(pkg.r)) {
|
||||||
|
@ -219,11 +259,25 @@ self.onmessage = function (event) {
|
||||||
log("sig :", hex(sig));
|
log("sig :", hex(sig));
|
||||||
log("sig/CHK:", hex(sigChk));
|
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/CHK:", Bitcoin.ECDSA.verify(hash, sigChk, pub));
|
||||||
log("ver/CTL:", Bitcoin.ECDSA.verify(hash, Bitcoin.ECDSA.sign(hash, dChk), 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);
|
var priv = Bitcoin.ECDSA.getBigRandom(n);
|
||||||
pub = G.multiply(priv).getEncoded();
|
pub = G.multiply(priv).getEncoded();
|
||||||
log("ver/GEN:", Bitcoin.ECDSA.verify(hash, Bitcoin.ECDSA.sign(hash, priv), pub));
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -65,6 +65,14 @@ Bitcoin.Paillier = (function () {
|
||||||
return c.modPow(f, this.nSq);
|
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) {
|
Paillier.PrivateKey = function (n,g,l,m,nSq) {
|
||||||
this.l = l;
|
this.l = l;
|
||||||
this.m = m;
|
this.m = m;
|
||||||
|
@ -73,12 +81,8 @@ Bitcoin.Paillier = (function () {
|
||||||
this.pub = new Paillier.PublicKey(n,g,this.nSq);
|
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) {
|
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);
|
.divide(this.n).multiply(this.m).mod(this.n);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,5 +97,16 @@ Bitcoin.Paillier = (function () {
|
||||||
return rn.modPow(e, this.n);
|
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;
|
return Paillier;
|
||||||
})();
|
})();
|
||||||
|
|
12
test/test.js
12
test/test.js
|
@ -98,3 +98,15 @@ test("Signing and Verifying", function () {
|
||||||
);
|
);
|
||||||
ok(Bitcoin.ECDSA.verify(hash2, sig_c, s2), "Verify constant signature");
|
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");
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue