diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1e097b3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Riskless
+### Undergraduate dissertation
+
+**Paper: https://judesouthworth.xyz/Dissertation.pdf**
+
+## Running the demo
+
+1. `pip install -r requirements.txt`
+2. `python3 app.py`
+3. Navigate browser to `http://localhost:5000`
+
+### Running the benchmarks
+
+1. Press F12
+2. Type the name of the benchmark to run:
+ - `RSABench`
+ - `PaillierBench`
+ - `ZeroProofBench`
+ - `ZeroProofVerifierBench`
+ - `ZeroProofSizeBench`
+ - `Protocol4Bench`
+ - `Protocol4VerifierBench`
+ - `Protocol4SizeBench`
+3. Wait for results to output in milliseconds
+
+### Changing the key size
+
+The key size is determined by the variable `KEY_SIZE` in `random_primes.js`. This
+variable dictates the size of _each_ prime used in the modulus, so the total key size
+is twice this value. Recommend selecting one of 512, 1024, or 2048. In fact, I recommend
+selecting 512 if you want the program to run in less than 3-5 business days.
diff --git a/static/js/lz-string.js b/static/js/lz-string.js
new file mode 100644
index 0000000..7bb03c2
--- /dev/null
+++ b/static/js/lz-string.js
@@ -0,0 +1,309 @@
+var LZString = (function () {
+ var r = String.fromCharCode,
+ o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+ n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",
+ e = {};
+ function t(r, o) {
+ if (!e[r]) {
+ e[r] = {};
+ for (var n = 0; n < r.length; n++) e[r][r.charAt(n)] = n;
+ }
+ return e[r][o];
+ }
+ var i = {
+ compressToBase64: function (r) {
+ if (null == r) return "";
+ var n = i._compress(r, 6, function (r) {
+ return o.charAt(r);
+ });
+ switch (n.length % 4) {
+ default:
+ case 0:
+ return n;
+ case 1:
+ return n + "===";
+ case 2:
+ return n + "==";
+ case 3:
+ return n + "=";
+ }
+ },
+ decompressFromBase64: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 32, function (n) {
+ return t(o, r.charAt(n));
+ });
+ },
+ compressToUTF16: function (o) {
+ return null == o
+ ? ""
+ : i._compress(o, 15, function (o) {
+ return r(o + 32);
+ }) + " ";
+ },
+ decompressFromUTF16: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 16384, function (o) {
+ return r.charCodeAt(o) - 32;
+ });
+ },
+ compressToUint8Array: function (r) {
+ for (
+ var o = i.compress(r),
+ n = new Uint8Array(2 * o.length),
+ e = 0,
+ t = o.length;
+ e < t;
+ e++
+ ) {
+ var s = o.charCodeAt(e);
+ (n[2 * e] = s >>> 8), (n[2 * e + 1] = s % 256);
+ }
+ return n;
+ },
+ decompressFromUint8Array: function (o) {
+ if (null == o) return i.decompress(o);
+ for (var n = new Array(o.length / 2), e = 0, t = n.length; e < t; e++)
+ n[e] = 256 * o[2 * e] + o[2 * e + 1];
+ var s = [];
+ return (
+ n.forEach(function (o) {
+ s.push(r(o));
+ }),
+ i.decompress(s.join(""))
+ );
+ },
+ compressToEncodedURIComponent: function (r) {
+ return null == r
+ ? ""
+ : i._compress(r, 6, function (r) {
+ return n.charAt(r);
+ });
+ },
+ decompressFromEncodedURIComponent: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : ((r = r.replace(/ /g, "+")),
+ i._decompress(r.length, 32, function (o) {
+ return t(n, r.charAt(o));
+ }));
+ },
+ compress: function (o) {
+ return i._compress(o, 16, function (o) {
+ return r(o);
+ });
+ },
+ _compress: function (r, o, n) {
+ if (null == r) return "";
+ var e,
+ t,
+ i,
+ s = {},
+ u = {},
+ a = "",
+ p = "",
+ c = "",
+ l = 2,
+ f = 3,
+ h = 2,
+ d = [],
+ m = 0,
+ v = 0;
+ for (i = 0; i < r.length; i += 1)
+ if (
+ ((a = r.charAt(i)),
+ Object.prototype.hasOwnProperty.call(s, a) ||
+ ((s[a] = f++), (u[a] = !0)),
+ (p = c + a),
+ Object.prototype.hasOwnProperty.call(s, p))
+ )
+ c = p;
+ else {
+ if (Object.prototype.hasOwnProperty.call(u, c)) {
+ if (c.charCodeAt(0) < 256) {
+ for (e = 0; e < h; e++)
+ (m <<= 1),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++;
+ for (t = c.charCodeAt(0), e = 0; e < 8; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ } else {
+ for (t = 1, e = 0; e < h; e++)
+ (m = (m << 1) | t),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t = 0);
+ for (t = c.charCodeAt(0), e = 0; e < 16; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ }
+ 0 == --l && ((l = Math.pow(2, h)), h++), delete u[c];
+ } else
+ for (t = s[c], e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ 0 == --l && ((l = Math.pow(2, h)), h++),
+ (s[p] = f++),
+ (c = String(a));
+ }
+ if ("" !== c) {
+ if (Object.prototype.hasOwnProperty.call(u, c)) {
+ if (c.charCodeAt(0) < 256) {
+ for (e = 0; e < h; e++)
+ (m <<= 1),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++;
+ for (t = c.charCodeAt(0), e = 0; e < 8; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ } else {
+ for (t = 1, e = 0; e < h; e++)
+ (m = (m << 1) | t),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t = 0);
+ for (t = c.charCodeAt(0), e = 0; e < 16; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ }
+ 0 == --l && ((l = Math.pow(2, h)), h++), delete u[c];
+ } else
+ for (t = s[c], e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ 0 == --l && ((l = Math.pow(2, h)), h++);
+ }
+ for (t = 2, e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ for (;;) {
+ if (((m <<= 1), v == o - 1)) {
+ d.push(n(m));
+ break;
+ }
+ v++;
+ }
+ return d.join("");
+ },
+ decompress: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 32768, function (o) {
+ return r.charCodeAt(o);
+ });
+ },
+ _decompress: function (o, n, e) {
+ var t,
+ i,
+ s,
+ u,
+ a,
+ p,
+ c,
+ l = [],
+ f = 4,
+ h = 4,
+ d = 3,
+ m = "",
+ v = [],
+ g = { val: e(0), position: n, index: 1 };
+ for (t = 0; t < 3; t += 1) l[t] = t;
+ for (s = 0, a = Math.pow(2, 2), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ switch (s) {
+ case 0:
+ for (s = 0, a = Math.pow(2, 8), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ c = r(s);
+ break;
+ case 1:
+ for (s = 0, a = Math.pow(2, 16), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ c = r(s);
+ break;
+ case 2:
+ return "";
+ }
+ for (l[3] = c, i = c, v.push(c); ; ) {
+ if (g.index > o) return "";
+ for (s = 0, a = Math.pow(2, d), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ switch ((c = s)) {
+ case 0:
+ for (s = 0, a = Math.pow(2, 8), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position &&
+ ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ (l[h++] = r(s)), (c = h - 1), f--;
+ break;
+ case 1:
+ for (s = 0, a = Math.pow(2, 16), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position &&
+ ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ (l[h++] = r(s)), (c = h - 1), f--;
+ break;
+ case 2:
+ return v.join("");
+ }
+ if ((0 == f && ((f = Math.pow(2, d)), d++), l[c])) m = l[c];
+ else {
+ if (c !== h) return null;
+ m = i + i.charAt(0);
+ }
+ v.push(m),
+ (l[h++] = i + m.charAt(0)),
+ (i = m),
+ 0 == --f && ((f = Math.pow(2, d)), d++);
+ }
+ },
+ };
+ return i;
+})();
+"function" == typeof define && define.amd
+ ? define(function () {
+ return LZString;
+ })
+ : "undefined" != typeof module && null != module
+ ? (module.exports = LZString)
+ : "undefined" != typeof angular &&
+ null != angular &&
+ angular.module("LZString", []).factory("LZString", function () {
+ return LZString;
+ });
diff --git a/static/js/modules/crypto/paillier.js b/static/js/modules/crypto/paillier.js
index fead085..07aa1c8 100644
--- a/static/js/modules/crypto/paillier.js
+++ b/static/js/modules/crypto/paillier.js
@@ -4,6 +4,25 @@ import { gcd, mod_exp } from "./math.js";
const PAILLIER = 0;
const JURIK = 1;
+function RSTransform(g, a, p) {
+ let plainText = p.toString(16);
+ if (plainText.length % 2 !== 0) {
+ plainText = "0" + plainText;
+ }
+
+ let aStr = a.toString(16);
+ if (aStr.length % 2 !== 0) {
+ aStr = "0" + aStr;
+ }
+
+ let hasher = new jsSHA("SHAKE256", "HEX");
+ hasher.update(g.toString(16));
+ hasher.update(plainText);
+ hasher.update(aStr);
+
+ return BigInt("0x" + hasher.getHash("HEX", { outputLen: 2048 }));
+}
+
class Ciphertext {
constructor(key, plainText, r, set) {
if (set !== undefined) {
@@ -93,28 +112,12 @@ class Ciphertext {
}
let a = mod_exp(rp, this.pubKey.n, this.pubKey.n2);
- let hasher = new jsSHA("SHAKE256", "HEX");
- let plainText = this.plainText.toString(16);
- if (plainText.length % 2 !== 0) {
- plainText = "0" + plainText;
- }
-
- let aStr = a.toString(16);
- if (aStr.length % 2 !== 0) {
- aStr = "0" + aStr;
- }
-
- hasher.update(this.pubKey.g.toString(16));
- hasher.update(plainText);
- hasher.update(aStr);
-
- let challenge = BigInt("0x" + hasher.getHash("HEX", { outputLen: 2048 }));
+ let challenge = RSTransform(this.pubKey.g, a, this.plainText);
return {
plainText: "0x" + this.plainText.toString(16),
a: "0x" + a.toString(16),
- challenge: "0x" + challenge.toString(16),
proof:
"0x" +
(
@@ -195,11 +198,17 @@ export class ReadOnlyCiphertext {
}
verifyNI(statement) {
+ let challenge = RSTransform(
+ this.pubKey.g,
+ BigInt(statement.a),
+ BigInt(statement.plainText)
+ );
+
let verifier = new ValueProofSessionVerifier(
this,
BigInt(statement.plainText),
BigInt(statement.a),
- BigInt(statement.challenge)
+ challenge
);
if (verifier.verify(BigInt(statement.proof))) {
diff --git a/static/js/modules/crypto/random_primes.js b/static/js/modules/crypto/random_primes.js
index 93628b5..e970f94 100644
--- a/static/js/modules/crypto/random_primes.js
+++ b/static/js/modules/crypto/random_primes.js
@@ -1,6 +1,6 @@
import { mod_exp } from "./math.js";
-export const KEY_SIZE = 512;
+export const KEY_SIZE = 2048;
export function cryptoRandom(bits) {
if (bits === undefined) {
diff --git a/static/js/modules/interface/proofs.js b/static/js/modules/interface/proofs.js
index 196810d..0667bfb 100644
--- a/static/js/modules/interface/proofs.js
+++ b/static/js/modules/interface/proofs.js
@@ -135,7 +135,6 @@ export function verifyRegions(obj, key) {
let plaintext = c.verifyNI(verification.zeroProofs[regionName]);
if (plaintext !== 0n) {
- console.log(plaintext);
return false;
}
}
diff --git a/templates/index.html b/templates/index.html
index f80a0bd..11e00b3 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -7,7 +7,8 @@
-
+
+
@@ -188,6 +189,8 @@
function RSABench() {
console.log("Warming up")
+ const ROUNDS = 250;
+
for (let i = 0n; i < 100n; i++) {
window.rsa.pubKey.encrypt(i);
}
@@ -195,17 +198,19 @@
console.log("Benching")
performance.mark("rsa-start")
- for (let i = 0n; i < 250n; i++) {
+ for (let i = 0n; i < BigInt(ROUNDS); i++) {
window.rsa.pubKey.encrypt(i);
}
performance.mark("rsa-end")
- console.log(`Bench done. Duration: ${performance.measure("rsa-duration", "rsa-start", "rsa-end").duration}`)
+ console.log(`Bench done. Time per encrypt: ${performance.measure("rsa-duration", "rsa-start", "rsa-end").duration / ROUNDS}`)
}
function PaillierBench() {
console.log("Warming up")
+ const ROUNDS = 250
+
for (let i = 0n; i < 100n; i++) {
window.paillier.pubKey.encrypt(i);
}
@@ -213,12 +218,162 @@
console.log("Benching")
performance.mark("paillier-start")
- for (let i = 0n; i < 250n; i++) {
+ for (let i = 0n; i < BigInt(ROUNDS); i++) {
window.paillier.pubKey.encrypt(i);
}
performance.mark("paillier-end")
- console.log(`Bench done. Duration: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration}`)
+ console.log(`Bench done. Time per encrypt: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration / ROUNDS}`)
+ }
+
+ function ZeroProofBench() {
+ console.log("Warming up")
+
+ const cipherText = paillier.pubKey.encrypt(0n)
+ const ROUNDS = 20;
+
+ for (let i = 0; i < 5; i++) {
+ cipherText.proveNI();
+ }
+
+ console.log("Benching")
+
+ performance.mark("paillier-start")
+ for (let i = 0; i < ROUNDS; i++) {
+ cipherText.proveNI()
+ }
+ performance.mark("paillier-end")
+
+ console.log(`Bench done. Time per proof: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration / ROUNDS}`)
+ }
+
+ function ZeroProofVerifierBench() {
+ console.log("Warming up")
+ const ROUNDS = 20
+
+ const cipherText = paillier.pubKey.encrypt(1n)
+ const readOnly = cipherText.asReadOnlyCiphertext();
+ const proof = cipherText.proveNI()
+
+ for (let i = 0; i < 5; i++) {
+ readOnly.verifyNI(proof)
+ }
+
+ console.log("Benching")
+
+ performance.mark("paillier-start")
+ for (let i = 0; i < ROUNDS; i++) {
+ readOnly.verifyNI(proof)
+ }
+ performance.mark("paillier-end")
+
+ console.log(`Bench done. Time per verification: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration / ROUNDS}`)
+ }
+
+ function Protocol4Bench() {
+ console.log("Warming up")
+
+ const regions = {
+ A: paillier.pubKey.encrypt(0n),
+ B: paillier.pubKey.encrypt(1n),
+ C: paillier.pubKey.encrypt(0n),
+ D: paillier.pubKey.encrypt(0n),
+ E: paillier.pubKey.encrypt(0n)
+ }
+ const ROUNDS = 20;
+
+ for (let i = 0; i < 5; i++) {
+ proveRegions(regions)
+ }
+
+ console.log("Benching")
+
+ performance.mark("paillier-start")
+ for (let i = 0; i < ROUNDS; i++) {
+ proveRegions(regions)
+ }
+ performance.mark("paillier-end")
+
+ console.log(`Bench done. Time per proof: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration / ROUNDS}`)
+ }
+
+ function Protocol4VerifierBench() {
+ console.log("Warming up")
+
+ const ROUNDS = 20;
+ const regions = {
+ A: paillier.pubKey.encrypt(0n),
+ B: paillier.pubKey.encrypt(1n),
+ C: paillier.pubKey.encrypt(0n),
+ D: paillier.pubKey.encrypt(0n),
+ E: paillier.pubKey.encrypt(0n)
+ }
+ let proof = proveRegions(regions)
+
+ for (let i = 0; i < 5; i++) {
+ verifyRegions(proof, paillier.pubKey)
+ }
+
+ console.log("Benching")
+
+ performance.mark("paillier-start")
+ for (let i = 0; i < ROUNDS; i++) {
+ verifyRegions(proof, paillier.pubKey)
+ }
+ performance.mark("paillier-end")
+
+ console.log(`Bench done. Time per verification: ${performance.measure("paillier-duration", "paillier-start", "paillier-end").duration / ROUNDS}`)
+ }
+
+ // https://gist.github.com/kawanet/352a2ed1d1656816b2bc
+ function string_to_buffer(src) {
+ return (new Uint16Array([].map.call(src, function(c) {
+ return c.charCodeAt(0)
+ }))).buffer;
+ }
+
+ function Protocol4Size() {
+ const regions = {
+ A: paillier.pubKey.encrypt(0n),
+ B: paillier.pubKey.encrypt(1n),
+ C: paillier.pubKey.encrypt(0n),
+ D: paillier.pubKey.encrypt(0n),
+ E: paillier.pubKey.encrypt(0n)
+ }
+ let ROUNDS = 10;
+
+ let size = 0;
+ let compressedSize = 0;
+
+ for (let x = 0; x < ROUNDS; x++) {
+ let s = JSON.stringify(proveRegions(regions));
+ size += string_to_buffer(s).byteLength;
+ compressedSize += LZString.compressToUint8Array(s).length;
+ }
+
+ return {
+ size: size / ROUNDS,
+ compressedSize: compressedSize / ROUNDS
+ };
+ }
+
+ function ZeroProofSize() {
+ const ROUNDS = 100;
+ const cipherText = paillier.pubKey.encrypt(0n)
+
+ let size = 0;
+ let compressedSize = 0;
+
+ for (let x = 0; x < ROUNDS; x++) {
+ let s = JSON.stringify(cipherText.proveNI());
+ size += string_to_buffer(s).byteLength;
+ compressedSize += LZString.compressToUint8Array(s).length;
+ }
+
+ return {
+ size: size / ROUNDS,
+ compressedSize: compressedSize / ROUNDS
+ };
}