├── .gitignore
├── jigg
├── .eslintignore
├── .gitignore
├── src
│ ├── jigg.js
│ ├── parse
│ │ ├── label.js
│ │ ├── circuit.js
│ │ └── parse.js
│ ├── modules
│ │ ├── circuit.js
│ │ ├── gate.js
│ │ └── label.js
│ ├── util
│ │ ├── hexutils.js
│ │ └── crypto.js
│ ├── comm
│ │ ├── clientSocket.js
│ │ ├── serverSocket.js
│ │ └── ot.js
│ ├── jiggServer.js
│ ├── jiggClient.js
│ ├── evaluate.js
│ └── garble.js
├── demo
│ ├── client
│ │ ├── worker.js
│ │ └── client.js
│ ├── party.js
│ ├── server.js
│ └── client.html
├── jsdoc.conf.json
├── LICENSE
├── .eslintrc
├── jsdoc.layout.tmpl
├── package.json
└── README.md
├── app
├── .eslintrc.json
├── public
│ ├── favicon.ico
│ └── circuits
│ │ ├── job_matching.txt
│ │ └── 32bit_add.txt
├── laconic
│ ├── laconic_ot_bg.wasm
│ ├── package.json
│ ├── laconic_ot_bg.wasm.d.ts
│ ├── laconic_ot.d.ts
│ └── index.html
├── README.md
├── postcss.config.mjs
├── next.config.ts
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ └── hello.ts
│ └── index.tsx
├── styles
│ └── globals.css
├── tailwind.config.ts
├── tsconfig.json
├── .gitignore
├── package.json
├── LICENSE.md
├── utils
│ └── jobMatchingUtils.ts
├── components
│ ├── JobMatchingForm.tsx
│ └── JobMatching.tsx
└── test
│ └── HIRING.md
├── laconic
├── pkg
│ ├── laconic_ot_bg.wasm
│ ├── package.json
│ ├── laconic_ot_bg.wasm.d.ts
│ ├── README.md
│ ├── laconic_ot.d.ts
│ └── index.html
├── .gitignore
├── src
│ ├── lib.rs
│ ├── kzg.rs
│ ├── wasm_bindings.rs
│ ├── kzg_types.rs
│ ├── kzg_fk_open.rs
│ ├── laconic_ot.rs
│ └── kzg_utils.rs
├── Cargo.toml
├── LICENSE.md
├── table.py
├── README.md
└── benches
│ └── laconic_ot.rs
├── circuits
├── src
│ ├── main.rs
│ ├── demo.rs
│ ├── hiring_original.rs
│ └── hiring.rs
├── Cargo.toml
├── job_matching_two_input.txt
├── job_matching.txt
└── Cargo.lock
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
--------------------------------------------------------------------------------
/jigg/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules/
2 |
--------------------------------------------------------------------------------
/app/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/jigg/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | node_modules
4 | package-lock.json
5 | circuits/gg
6 |
--------------------------------------------------------------------------------
/app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cursive-team/trinity-v0/HEAD/app/public/favicon.ico
--------------------------------------------------------------------------------
/app/laconic/laconic_ot_bg.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cursive-team/trinity-v0/HEAD/app/laconic/laconic_ot_bg.wasm
--------------------------------------------------------------------------------
/laconic/pkg/laconic_ot_bg.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cursive-team/trinity-v0/HEAD/laconic/pkg/laconic_ot_bg.wasm
--------------------------------------------------------------------------------
/circuits/src/main.rs:
--------------------------------------------------------------------------------
1 | mod demo;
2 | mod hiring;
3 | mod hiring_original;
4 |
5 | fn main() {
6 | hiring::hiring();
7 | }
8 |
--------------------------------------------------------------------------------
/app/README.md:
--------------------------------------------------------------------------------
1 | # Trinity demo app
2 |
3 | Demoing trinity circuits in a Next.js app, a mix of fully local and half-server, half-client approaches.
4 |
--------------------------------------------------------------------------------
/laconic/.gitignore:
--------------------------------------------------------------------------------
1 | */target
2 | **/*.rs.bk
3 | Cargo.lock
4 | notes.md
5 | scrap.rs
6 | kzg10.rs
7 | design.md
8 | refactor_notes
9 | *.out
10 | .DS_Store
11 |
--------------------------------------------------------------------------------
/app/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/app/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | reactStrictMode: true,
6 | };
7 |
8 | export default nextConfig;
9 |
--------------------------------------------------------------------------------
/app/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/globals.css";
2 | import type { AppProps } from "next/app";
3 |
4 | export default function App({ Component, pageProps }: AppProps) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/jigg/src/jigg.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main module: exposes client and server API. This is the module that is exposed when
3 | * requiring jigg from node.js.
4 | *
5 | * @module JIGG
6 | */
7 |
8 | const Agent = require("./jiggClient.js");
9 |
10 | module.exports = Agent;
11 |
--------------------------------------------------------------------------------
/circuits/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "circuits"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | boolify = { git = "https://github.com/voltrevo/boolify", rev = "e9707c0" }
8 | bristol-circuit = { git = "https://github.com/voltrevo/bristol-circuit", rev = "2a8b001" }
9 |
--------------------------------------------------------------------------------
/app/laconic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laconic-ot",
3 | "version": "0.1.0",
4 | "license": "MIT",
5 | "files": [
6 | "laconic_ot_bg.wasm",
7 | "laconic_ot.js",
8 | "laconic_ot.d.ts"
9 | ],
10 | "module": "laconic_ot.js",
11 | "types": "laconic_ot.d.ts",
12 | "sideEffects": false
13 | }
--------------------------------------------------------------------------------
/app/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | body {
11 | color: var(--foreground);
12 | background: var(--background);
13 | font-family: Arial, Helvetica, sans-serif;
14 | }
15 |
--------------------------------------------------------------------------------
/app/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/laconic/pkg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laconic-ot",
3 | "version": "0.1.0",
4 | "license": "MIT",
5 | "files": [
6 | "laconic_ot_bg.wasm",
7 | "laconic_ot.js",
8 | "laconic_ot.d.ts",
9 | "LICENSE.md"
10 | ],
11 | "module": "laconic_ot.js",
12 | "types": "laconic_ot.d.ts",
13 | "sideEffects": false
14 | }
--------------------------------------------------------------------------------
/jigg/src/parse/label.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Label = require('../modules/label.js');
4 |
5 | const labelParser = function (labelStr) {
6 | const bytes = new Uint8Array(labelStr.length);
7 | for (let i = 0; i < labelStr.length; i++) {
8 | bytes[i] = labelStr.charCodeAt(i);
9 | }
10 |
11 | return new Label(bytes);
12 | };
13 |
14 | module.exports = labelParser;
--------------------------------------------------------------------------------
/app/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 |
4 | type Data = {
5 | name: string;
6 | };
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse,
11 | ) {
12 | res.status(200).json({ name: "John Doe" });
13 | }
14 |
--------------------------------------------------------------------------------
/laconic/src/lib.rs:
--------------------------------------------------------------------------------
1 | mod kzg;
2 | mod kzg_fk_open;
3 | mod kzg_types;
4 | mod kzg_utils;
5 | mod laconic_ot;
6 | mod wasm_bindings;
7 |
8 | pub use kzg_types::CommitmentKey;
9 | pub use laconic_ot::*;
10 | pub use wasm_bindings::*;
11 |
12 | // Initialize panic hook for better error messages in WASM
13 | #[wasm_bindgen::prelude::wasm_bindgen(start)]
14 | pub fn start() {
15 | std::panic::set_hook(Box::new(console_error_panic_hook::hook));
16 | }
17 |
--------------------------------------------------------------------------------
/app/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "paths": {
17 | "@/*": ["./*"]
18 | }
19 | },
20 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
21 | "exclude": ["node_modules"]
22 | }
23 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/jigg/demo/client/worker.js:
--------------------------------------------------------------------------------
1 | /* global JIGG importScripts */
2 | importScripts('/dist/jigg.js');
3 |
4 | onmessage = function (e) {
5 | const role = e.data.role;
6 | const circuit = e.data.circuit;
7 | const input = e.data.input;
8 | const base = e.data.base;
9 |
10 | const agent = new JIGG(role);
11 | agent.loadCircuit(circuit);
12 | agent.setInput(input, base);
13 |
14 | agent.addProgressListener(function (status, start, total, error) {
15 | postMessage({type: 'progress', args: [status, start, total, error]});
16 | });
17 |
18 | agent.getOutput(base).then(function (output) {
19 | postMessage({type: 'output', args: output});
20 | });
21 |
22 | agent.start();
23 | };
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "libsodium-wrappers-sumo": "^0.7.15",
13 | "next": "15.0.3",
14 | "react": "18",
15 | "react-dom": "18",
16 | "react-hook-form": "^7.53.2"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^18",
21 | "@types/react-dom": "^18",
22 | "eslint": "^8",
23 | "eslint-config-next": "15.0.3",
24 | "postcss": "^8",
25 | "tailwindcss": "^3.4.1",
26 | "typescript": "^5"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/circuits/src/demo.rs:
--------------------------------------------------------------------------------
1 | use boolify::{generate_bristol, BoolWire, CircuitOutput, IdGenerator, ValueWire};
2 | use bristol_circuit::BristolCircuit;
3 |
4 | pub fn demo() {
5 | let id_gen = IdGenerator::new_rc_refcell();
6 |
7 | let a = ValueWire::new_input("a", 8, &id_gen);
8 | let b = ValueWire::new_input("b", 8, &id_gen);
9 | let c = ValueWire::mul(&a, &b);
10 | let d = ValueWire::less_than(&c, &ValueWire::new_const(123, &id_gen));
11 | let outputs = vec![CircuitOutput::new("d", BoolWire::as_value(&d))];
12 |
13 | let bristol_circuit = generate_bristol(&outputs);
14 | println!("gates: {}", bristol_circuit.gates.len());
15 | let output = BristolCircuit::get_bristol_string(&bristol_circuit).unwrap();
16 | std::fs::write("demo.txt", output).unwrap();
17 | }
18 |
--------------------------------------------------------------------------------
/jigg/src/modules/circuit.js:
--------------------------------------------------------------------------------
1 | // Describes both garbled and plain circuits
2 |
3 | 'use strict';
4 |
5 | function Circuit(wiresCount, garblerInputSize, evaluatorInputSize, outputSize, labelSize) {
6 | this.wiresCount = wiresCount;
7 | this.garblerInputSize = garblerInputSize;
8 | this.evaluatorInputSize = evaluatorInputSize;
9 | this.outputSize = outputSize;
10 | this.labelSize = labelSize;
11 |
12 | this.gates = [];
13 | }
14 |
15 | Circuit.prototype.serialize = function () {
16 | const meta = JSON.stringify([
17 | this.wiresCount,
18 | this.garblerInputSize,
19 | this.evaluatorInputSize,
20 | this.outputSize,
21 | this.labelSize,
22 | this.gates.length
23 | ]);
24 |
25 | const gates = this.gates.map(function (gate) {
26 | return gate.serialize();
27 | });
28 | gates.unshift(meta);
29 |
30 | return gates.join('');
31 | };
32 |
33 | module.exports = Circuit;
--------------------------------------------------------------------------------
/laconic/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "laconic-ot"
3 | version = "0.1.0"
4 | edition = "2021"
5 | license = "MIT"
6 |
7 | [dependencies]
8 | ark-bls12-381 = "0.4.0"
9 | ark-ec = "0.4.2"
10 | ark-ff = "0.4.2"
11 | ark-poly = "0.4.2"
12 | ark-poly-commit = "0.4.0"
13 | ark-serialize = "0.4.2"
14 | ark-std = "0.4.0"
15 | rand = { version = "0.8.5", features = ["getrandom"] }
16 | getrandom = { version = "0.2", features = ["js"] }
17 | blake3 = "1.5"
18 | wasm-bindgen = "0.2"
19 | console_error_panic_hook = "0.1.7"
20 | serde = { version = "1.0", features = ["derive"] }
21 | serde_json = "1.0"
22 |
23 | [dev-dependencies]
24 | criterion = "0.5.1"
25 |
26 | [lib]
27 | crate-type = ["cdylib", "rlib"]
28 |
29 | [[bench]]
30 | name = "laconic_ot"
31 | harness = false
32 |
33 | [features]
34 | asm = ["ark-ff/asm"]
35 | parallel = ["ark-std/parallel", "ark-ff/parallel", "ark-poly/parallel"]
36 | print-trace = ["ark-std/print-trace"]
37 |
--------------------------------------------------------------------------------
/jigg/src/util/hexutils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const table16 = {
4 | 0: '0000', 1: '0001', 2: '0010', 3: '0011',
5 | 4: '0100', 5: '0101', 6: '0110', 7: '0111',
6 | 8: '1000', 9: '1001', A: '1010', B: '1011',
7 | C: '1100', D: '1101', E: '1110', F: '1111'
8 | };
9 | function hex2bin(hex) {
10 | let bin = '';
11 | for (let i = 0; i < hex.length; i++) {
12 | bin += table16[hex[i].toUpperCase()];
13 | }
14 | return bin;
15 | }
16 |
17 | const table2 = {
18 | '0000': '0', '0001': '1', '0010': '2', '0011': '3',
19 | '0100': '4', '0101': '5', '0110': '6', '0111': '7',
20 | '1000': '8', '1001': '9', '1010': 'A', '1011': 'B',
21 | '1100': 'C', '1101': 'D', '1110': 'E', '1111': 'F'
22 | };
23 | function bin2hex(bin) {
24 | let hex = '';
25 | bin = (new Array((4-(bin.length%4))%4)).fill('0').join('') + bin;
26 | for (let i = 0; i < bin.length; i+=4) {
27 | hex += table2[bin.substr(i, 4)];
28 | }
29 | return hex;
30 | }
31 |
32 | module.exports = {
33 | bin2hex: bin2hex,
34 | hex2bin: hex2bin
35 | };
36 |
--------------------------------------------------------------------------------
/jigg/jsdoc.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["plugins/markdown"],
3 | "recurseDepth": 10,
4 | "source": {
5 | "includePattern": ".+\\.js(doc|x)?$",
6 | "excludePattern": "(^|\\/|\\\\)_",
7 | "include": [
8 | "./README.md"
9 | ]
10 | },
11 | "sourceType": "module",
12 | "tags": {
13 | "allowUnknownTags": true,
14 | "dictionaries": ["jsdoc","closure"]
15 | },
16 | "templates": {
17 | "cleverLinks": false,
18 | "useShortNamesInLinks": true,
19 | "monospaceLinks": false
20 | },
21 | "opts": {
22 | "destination": "./docs/",
23 | "template": "node_modules/docdash"
24 | },
25 | "docdash": {
26 | "collalpse": true,
27 | "search": true,
28 | "menu":{
29 | "Github Repo":{
30 | "href":"https://github.com/multiparty/jigg",
31 | "target":"_blank",
32 | "class":"menu-item",
33 | "id":"github_link"
34 | },
35 | "Tutorial":{
36 | "href":"tutorial.html",
37 | "class":"menu-item"
38 | }
39 | },
40 | "meta": {
41 | "title": "JIGG Library Documentation"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © 2024
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the “Software”), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/jigg/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Wyatt Howe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/laconic/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © 2024
4 |
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the “Software”), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Trinity
2 |
3 | Trinity is a new 2PC scheme, developed by Cursive. It combines ideas from a trio of cryptographic primitives: Garbled Circuits, KZG Witness Encryption, and PLONK.
4 |
5 | This is a v0 version that has the first end-to-end implementation of the core scheme involving Garbled Circuits & KZG WE. PLONK will be added in a future version; it requires significant refactoring and isn't necessary to build initial applications.
6 |
7 | The specific example used is a private hiring matcher. This is the same functionality that we previously built with Multi-Party FHE using [phantom-zone](https://github.com/gausslabs/phantom-zone), which you can find [here](https://github.com/RiverRuby/pz-hiring).
8 |
9 | ## Acknowledgements
10 |
11 | This repo uses code from a number of different projects:
12 |
13 | - `/circuits`: [Boolify](https://github.com/voltrevo/boolify) to write a hiring 2PC circuit in Bristol format
14 | - `/jigg`: [JIGG](https://github.com/multiparty/jigg) for JavaScript native Garbled Circuit generation & evaluation
15 | - `/laconic`: [research-we-kzg](https://github.com/rot256/research-we-kzg), the original paper implementation of KZG Witness Encryption
16 |
--------------------------------------------------------------------------------
/jigg/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": "eslint:recommended",
4 | "rules": {
5 | "indent": [2, 2, { "SwitchCase": 1 }],
6 | "keyword-spacing": [2, { "before": true, "after": true }],
7 | "object-shorthand": [2, "consistent"],
8 | "no-unused-vars": [1, { "vars": "local", "args": "none" }],
9 | "quote-props": [1, "consistent-as-needed"],
10 | "eqeqeq": [2, "smart"],
11 | "brace-style": 2,
12 | "curly": 2,
13 | "no-trailing-spaces": 2,
14 | "space-before-function-paren": [
15 | 2,
16 | {
17 | "anonymous": "always",
18 | "named": "never",
19 | "asyncArrow": "always"
20 | }
21 | ],
22 | "no-console": 0,
23 | "space-before-blocks": [2, "always"],
24 | "requirejs/no-invalid-define": 2,
25 | "requirejs/no-multiple-define": 2,
26 | "requirejs/no-named-define": 2,
27 | "requirejs/no-commonjs-wrapper": 2,
28 | "requirejs/no-object-define": 1
29 | },
30 | "env": {
31 | "browser": true,
32 | "jquery": true,
33 | "node": true,
34 | "amd": true,
35 | "es6": true
36 | },
37 |
38 | "parserOptions": {
39 | "ecmaVersion": 6
40 | },
41 | "plugins": ["requirejs", "mocha"]
42 | }
43 |
--------------------------------------------------------------------------------
/jigg/src/modules/gate.js:
--------------------------------------------------------------------------------
1 | // Describes both garbled and plain gates
2 |
3 | "use strict";
4 |
5 | function Gate(id, operation, inputWires, outputWire, truthTable) {
6 | this.id = id;
7 | this.operation = operation;
8 | this.inputWires = inputWires;
9 | this.outputWire = outputWire;
10 | this.truthTable = truthTable;
11 |
12 | // INV is an alias for NOT
13 | if (this.operation === "INV") {
14 | this.operation = "NOT";
15 | }
16 | }
17 |
18 | Gate.prototype.serialize = function () {
19 | let gateStr = [];
20 |
21 | if (this.operation === "AND") {
22 | gateStr.push("&");
23 | } else if (this.operation === "LOR") {
24 | gateStr.push("|");
25 | } else if (this.operation === "XOR") {
26 | gateStr.push("^");
27 | } else if (this.operation === "NOT") {
28 | gateStr.push("!");
29 | }
30 |
31 | gateStr.push(this.id);
32 | gateStr.push(JSON.stringify(this.inputWires));
33 | gateStr.push(this.outputWire);
34 |
35 | if (this.operation === "AND" || this.operation === "LOR") {
36 | gateStr.push("-");
37 | for (let i = 0; i < this.truthTable.length; i++) {
38 | gateStr.push(this.truthTable[i].serialize());
39 | }
40 | }
41 |
42 | return gateStr.join("");
43 | };
44 |
45 | module.exports = Gate;
46 |
--------------------------------------------------------------------------------
/jigg/jsdoc.layout.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JSDoc:
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | JIGG','') : content ?>
23 |
24 |
25 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/jigg/demo/party.js:
--------------------------------------------------------------------------------
1 | const JIGG = require('../src/jigg.js');
2 | const fs = require('fs');
3 |
4 | // Handle command line arguments.
5 | const port = process.argv[2];
6 | const role = process.argv[3];
7 | let input = process.argv[4];
8 | let encoding = process.argv[5];
9 | let circuitName = process.argv[6];
10 | const debug = process.argv[7] !== 'false';
11 |
12 | // default arguments.
13 | if (encoding == null) {
14 | encoding = 'number';
15 | }
16 | if (circuitName == null) {
17 | circuitName = 'arith-add-32-bit-old.txt';
18 | }
19 |
20 | // encoding
21 | if (encoding === 'number') {
22 | input = parseInt(input);
23 | }
24 | if (encoding === 'bits') {
25 | input = input.split('').map(Number);
26 | }
27 |
28 | // Read circuit
29 | const circuitPath = __dirname + '/../circuits/bristol/' + circuitName;
30 | const circuit = fs.readFileSync(circuitPath, 'utf8');
31 |
32 | // Application code.
33 | const agent = new JIGG.Client(role, 'http://localhost:' + port, {debug: debug});
34 | agent.loadCircuit(circuit);
35 | agent.setInput(input, encoding);
36 |
37 | agent.addProgressListener(function (status) {
38 | if (status === 'connected') {
39 | console.time('time');
40 | }
41 | });
42 |
43 | agent.getOutput(encoding).then(function (output) {
44 | if (debug) {
45 | console.timeEnd('time');
46 | }
47 |
48 | console.log(output);
49 | agent.disconnect();
50 | });
51 |
52 | agent.start();
--------------------------------------------------------------------------------
/jigg/src/util/crypto.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const sodium = require('libsodium-wrappers-sumo');
4 | const bytes = 16;
5 |
6 | function encrypt(a, b, t, m) {
7 | const a2 = a.double();
8 | const b4 = b.quadruple();
9 | const k = a2.xor(b4);
10 | return m.xor(k).xorBytes(randomOracle(k.bytes, t));
11 | }
12 |
13 | function longToByteArray(long) {
14 | // we want to represent the input as a 24-bytes array
15 | let byteArray = new Uint8Array(sodium.crypto_secretbox_NONCEBYTES);
16 |
17 | for (let index = 0; index < byteArray.length; index++) {
18 | let byte = long & 0xff;
19 | byteArray [ index ] = byte;
20 | long = (long - byte) / 256 ;
21 | }
22 |
23 | return byteArray;
24 | }
25 |
26 | function randomOracle(m, t) {
27 | return sodium.crypto_secretbox_easy(
28 | m,
29 | longToByteArray(t), // Nonce 24 bytes because this sodium uses 192 bit blocks.
30 | sodium.from_hex('da5698be17b9b46962335799779fbeca8ce5d491c0d26243bafef9ea1837a9d8') // SHA(0).
31 | ).subarray(0, bytes+1); // Prune back to the correct number of bytes.
32 | }
33 |
34 | function encrypt_generic(plaintext, key, nonce) {
35 | return plaintext.xorBytes(key).xorBytes(randomOracle(key, nonce));
36 | }
37 |
38 | module.exports = {
39 | encrypt: encrypt, // label encryption
40 | decrypt: encrypt,
41 | encrypt_generic: encrypt_generic, // OT
42 | decrypt_generic: encrypt_generic
43 | };
44 |
--------------------------------------------------------------------------------
/app/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import JobMatching, { GarbledData } from "../components/JobMatching";
3 | import init, { WasmCommitmentKey } from "../laconic/laconic_ot.js";
4 |
5 | export default function JobMatchingPage() {
6 | const [garblerText, setGarblerText] = useState();
7 | const [commitment, setCommitment] = useState();
8 | const [commitmentKey, setCommitmentKey] = useState();
9 |
10 | useEffect(() => {
11 | init().then(() => {
12 | setCommitmentKey(WasmCommitmentKey.setup(30));
13 | });
14 | }, []);
15 |
16 | return (
17 |
18 |
19 | {commitmentKey ? (
20 | <>
21 |
27 |
33 | >
34 | ) : (
35 |
Loading...
36 | )}
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/app/laconic/laconic_ot_bg.wasm.d.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | export const memory: WebAssembly.Memory;
4 | export function __wbg_wasmcommitmentkey_free(a: number, b: number): void;
5 | export function __wbg_wasmreceiver_free(a: number, b: number): void;
6 | export function __wbg_wasmsender_free(a: number, b: number): void;
7 | export function __wbg_wasmmessage_free(a: number, b: number): void;
8 | export function wasmcommitmentkey_setup(a: number, b: number): void;
9 | export function wasmcommitmentkey_serialize(a: number, b: number): void;
10 | export function wasmcommitmentkey_deserialize(a: number, b: number, c: number): void;
11 | export function wasmreceiver_new(a: number, b: number, c: number): number;
12 | export function wasmreceiver_recv(a: number, b: number, c: number, d: number): void;
13 | export function wasmreceiver_commitment(a: number, b: number): void;
14 | export function wasmreceiver_deserialize(a: number, b: number, c: number): number;
15 | export function wasmreceiver_serialize(a: number, b: number): void;
16 | export function wasmsender_new(a: number, b: number, c: number, d: number): void;
17 | export function wasmsender_send(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
18 | export function start(): void;
19 | export function __wbindgen_add_to_stack_pointer(a: number): number;
20 | export function __wbindgen_free(a: number, b: number, c: number): void;
21 | export function __wbindgen_malloc(a: number, b: number): number;
22 | export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
23 | export function __wbindgen_exn_store(a: number): void;
24 | export function __wbindgen_start(): void;
25 |
--------------------------------------------------------------------------------
/laconic/pkg/laconic_ot_bg.wasm.d.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | export const memory: WebAssembly.Memory;
4 | export function __wbg_wasmcommitmentkey_free(a: number, b: number): void;
5 | export function __wbg_wasmreceiver_free(a: number, b: number): void;
6 | export function __wbg_wasmsender_free(a: number, b: number): void;
7 | export function __wbg_wasmmessage_free(a: number, b: number): void;
8 | export function wasmcommitmentkey_setup(a: number, b: number): void;
9 | export function wasmcommitmentkey_serialize(a: number, b: number): void;
10 | export function wasmcommitmentkey_deserialize(a: number, b: number, c: number): void;
11 | export function wasmreceiver_new(a: number, b: number, c: number): number;
12 | export function wasmreceiver_recv(a: number, b: number, c: number, d: number): void;
13 | export function wasmreceiver_commitment(a: number, b: number): void;
14 | export function wasmreceiver_deserialize(a: number, b: number, c: number): number;
15 | export function wasmreceiver_serialize(a: number, b: number): void;
16 | export function wasmsender_new(a: number, b: number, c: number, d: number): void;
17 | export function wasmsender_send(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
18 | export function start(): void;
19 | export function __wbindgen_add_to_stack_pointer(a: number): number;
20 | export function __wbindgen_free(a: number, b: number, c: number): void;
21 | export function __wbindgen_malloc(a: number, b: number): number;
22 | export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
23 | export function __wbindgen_exn_store(a: number): void;
24 | export function __wbindgen_start(): void;
25 |
--------------------------------------------------------------------------------
/jigg/src/comm/clientSocket.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const socketio = require('socket.io-client');
4 |
5 | function ClientSocket(hostname, agent) {
6 | const self = this;
7 |
8 | this._nextId = 0;
9 | this.socket = socketio(hostname, {forceNew: true});
10 |
11 | // mailbox and listeners
12 | this.listeners = {};
13 | this.mailbox = {};
14 | this.socket.on('receive', function (tag, msg) {
15 | if (self.listeners[tag] != null) {
16 | self.listeners[tag](msg);
17 | delete self.listeners[tag];
18 | } else {
19 | self.mailbox[tag] = msg;
20 | }
21 | });
22 |
23 | // handle error
24 | this.socket.on('error', function (error) {
25 | agent.progress('error', null, null, error);
26 | throw error;
27 | });
28 | }
29 |
30 | ClientSocket.prototype.join = function (role) {
31 | this.socket.emit('join', role);
32 | };
33 |
34 | ClientSocket.prototype.hear = function (tag, _id) {
35 | if (_id == null) {
36 | _id = this._nextId++;
37 | }
38 |
39 | const self = this;
40 | return new Promise(function (resolve) {
41 | self.on(tag + _id, resolve);
42 | });
43 | };
44 |
45 | ClientSocket.prototype.on = function (tag, callback) {
46 | if (this.mailbox[tag] != null) {
47 | callback(this.mailbox[tag]);
48 | delete this.mailbox[tag];
49 | } else {
50 | this.listeners[tag] = callback;
51 | }
52 | };
53 |
54 | ClientSocket.prototype.send = function (tag, msg, _id) {
55 | if (_id == null) {
56 | _id = this._nextId++;
57 | }
58 |
59 | this.socket.emit('send', tag + _id, msg);
60 | };
61 |
62 | ClientSocket.prototype.nextId = function () {
63 | return this._nextId++;
64 | };
65 |
66 | ClientSocket.prototype.disconnect = function () {
67 | this.socket.disconnect();
68 | };
69 |
70 | module.exports = ClientSocket;
71 |
--------------------------------------------------------------------------------
/laconic/table.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import tabulate
4 |
5 | from fractions import Fraction
6 |
7 | PATH = "target/criterion"
8 | SIZES = list(range(3, 10))
9 | SUB_DIRS = [
10 | ("Hash (Time)", "laconic_ot_commit"),
11 | ("Send (Time)", "laconic_ot_send"),
12 | ("Recv (Time)", "laconic_ot_recv"),
13 | ]
14 |
15 | def fmt_time(ns):
16 | if ns < 1_000:
17 | return f"{ns:.2f} ns"
18 |
19 | if ns < 1_000_000:
20 | return f"{ns / 1_000:.2f} µs"
21 |
22 | ms = ns / 1_000_000
23 |
24 | if ms < 1_000:
25 | return f"{ms:.2f} ms"
26 |
27 | s = ms / 1_000
28 |
29 | if s < 60:
30 | return f"{s:.2f} s"
31 |
32 | m = s / 60
33 | s = s % 60
34 |
35 | return f"{m:.0f}:{round(s)} m"
36 |
37 | table = []
38 |
39 | header = ["Database Size"]
40 | for (name, _) in SUB_DIRS:
41 | header.append(name)
42 |
43 | for size in SIZES:
44 | row = []
45 | row.append(f"$2^{{{size}}}$")
46 | for (name, sub_dir) in SUB_DIRS:
47 | path = os.path.join(PATH, sub_dir, f"{size}", "new", "sample.json")
48 | data = json.loads(open(path, "r").read())
49 |
50 | iters = data["iters"]
51 | times = data["times"]
52 |
53 | assert len(iters) == len(times)
54 |
55 | # compute average time
56 | total = 0
57 | for (iter, time) in zip(iters, times):
58 | total += Fraction(time) / iter
59 |
60 | # format time
61 | average_ns = total / len(iters)
62 | time = fmt_time(average_ns)
63 | print(f"{sub_dir} {size} {time}")
64 | row.append(time)
65 | table.append(row)
66 |
67 | print(tabulate.tabulate(
68 | table,
69 | headers=header,
70 | tablefmt="github",
71 | colalign=("center", "center", "center", "center"),
72 | stralign="center",
73 | ))
74 |
--------------------------------------------------------------------------------
/app/utils/jobMatchingUtils.ts:
--------------------------------------------------------------------------------
1 | export const convertToBinary = (num: number, bits: number): string => {
2 | let binary = "";
3 | for (let i = bits - 1; i >= 0; i--) {
4 | binary += num & (1 << i) ? "1" : "0";
5 | }
6 | return binary;
7 | };
8 |
9 | export const convertToVector = (
10 | num: number,
11 | bits: number,
12 | isRecruiter: boolean
13 | ): string => {
14 | if (num > bits) {
15 | throw new Error("Number of ones cannot be greater than the number of bits");
16 | }
17 |
18 | console.log(num, bits, isRecruiter);
19 |
20 | const vector = Array(bits).fill("0");
21 | if (!isRecruiter) {
22 | for (let i = 0; i < num; i++) {
23 | vector[i] = "1";
24 | }
25 | } else {
26 | for (let i = 0; i < bits - num + 1; i++) {
27 | vector[bits - 1 - i] = "1";
28 | }
29 | }
30 |
31 | return vector.join("");
32 | };
33 |
34 | export const generateBinaryInput = (data: {
35 | isRecruiter: boolean;
36 | commitment: boolean;
37 | education: number;
38 | experience: number;
39 | interests: boolean[];
40 | companyStage: boolean[];
41 | salary: number;
42 | }): string => {
43 | console.log(data);
44 | const parts = [
45 | data.isRecruiter ? "1" : "0", // Position bit (0)
46 | data.commitment ? "1" : "0", // Commitment bit (1)
47 | convertToVector(data.education, 4, data.isRecruiter), // Education bits (2-5)
48 | convertToVector(data.experience, 8, data.isRecruiter), // Experience bits (6-13)
49 | data.interests.map((i) => (i ? "1" : "0")).join(""), // Interest bits (14-17)
50 | data.companyStage.map((s) => (s ? "1" : "0")).join(""), // Company stage bits (18-21)
51 | convertToBinary(Math.min(data.salary, 255), 8), // Salary bits (22-29)
52 | ];
53 |
54 | console.log(parts);
55 | console.log(parts.join("").length);
56 |
57 | return parts.join("");
58 | };
59 |
--------------------------------------------------------------------------------
/jigg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jigg",
3 | "version": "1.0.1",
4 | "description": "JavaScript Implementation of Garbled Gates (two-party computation library for boolean circuits)",
5 | "main": "src/jigg.js",
6 | "dependencies": {
7 | "casm": "^0.2.2",
8 | "libsodium-wrappers-sumo": "^0.7.6",
9 | "socket.io": "^4.8.1"
10 | },
11 | "devDependencies": {
12 | "browserify": "^16.5.0",
13 | "child_process": "^1.0.2",
14 | "docdash": "^1.1.1",
15 | "eslint": "^5.16.0",
16 | "eslint-plugin-mocha": "^6.2.2",
17 | "eslint-plugin-requirejs": "^4.0.0",
18 | "express": "^4.17.1",
19 | "jsdoc": "^4.0.4",
20 | "mocha": "^10.8.2",
21 | "neptune-notebook": "^1.3.1"
22 | },
23 | "scripts": {
24 | "test": "mocha test/test.js --timeout 120000",
25 | "build": "browserify src/jiggClient.js --debug --s JIGG -o dist/jigg.js",
26 | "docs": "jsdoc -r src -c jsdoc.conf.json",
27 | "casm": "node node_modules/casm/casm.js",
28 | "lint": "eslint ./src/ ./demo/",
29 | "tutorial": "node tutorial/index.js"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/multiparty/jigg.git"
34 | },
35 | "keywords": [
36 | "Garbled-Circuit",
37 | "2PC"
38 | ],
39 | "author": "Wyatt Howe",
40 | "contributors": [
41 | {
42 | "name": "Kinan Dak Albab",
43 | "email": "babman@bu.edu",
44 | "url": "http://cs-people.bu.edu/babman/"
45 | },
46 | {
47 | "name": "Andrei Lapets",
48 | "email": "a@lapets.io",
49 | "url": "https://lapets.io"
50 | }
51 | ],
52 | "license": "MIT",
53 | "bugs": {
54 | "url": "https://github.com/multiparty/jigg/issues"
55 | },
56 | "legacy_homepage": "https://github.com/wyatt-howe/jigg#readme",
57 | "homepage": "https://github.com/multiparty/jigg#readme"
58 | }
59 |
--------------------------------------------------------------------------------
/jigg/src/comm/serverSocket.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function ServerSocket(jiggServer, agent) {
4 | this.id = '__SERVER__';
5 | this._nextId = 0;
6 |
7 | // instances
8 | this.jiggServer = jiggServer;
9 | this.agent = agent;
10 |
11 | // mailbox and listeners
12 | this.listeners = {};
13 | this.mailbox = {};
14 | }
15 |
16 | ServerSocket.prototype.join = function (role) {
17 | this.jiggServer.join(this, role);
18 | };
19 |
20 | ServerSocket.prototype.hear = function (tag, _id) {
21 | if (_id == null) {
22 | _id = this._nextId++;
23 | }
24 |
25 | const self = this;
26 | return new Promise(function (resolve) {
27 | self.on(tag + _id, resolve);
28 | });
29 | };
30 |
31 | ServerSocket.prototype.on = function (tag, callback) {
32 | if (this.mailbox[tag] != null) {
33 | callback(this.mailbox[tag]);
34 | delete this.mailbox[tag];
35 | } else {
36 | this.listeners[tag] = callback;
37 | }
38 | };
39 |
40 | ServerSocket.prototype.send = function (tag, msg, _id) {
41 | if (_id == null) {
42 | _id = this._nextId++;
43 | }
44 |
45 | this.jiggServer.send(this, tag + _id, msg);
46 | };
47 |
48 | ServerSocket.prototype.nextId = function () {
49 | return this._nextId++;
50 | };
51 |
52 | ServerSocket.prototype.disconnect = function () {};
53 |
54 | // Specific to a Server Socket, to emulate the server side of the socket
55 | ServerSocket.prototype.emit = function (label, tag, msg) {
56 | if (label === 'error') {
57 | this.agent.progress('error', null, null, tag);
58 | return;
59 | }
60 |
61 | // label must be 'receive'
62 | if (this.listeners[tag] != null) {
63 | this.listeners[tag](msg);
64 | delete this.listeners[tag];
65 | } else {
66 | this.mailbox[tag] = msg;
67 | }
68 | };
69 |
70 | ServerSocket.prototype.disconnect = function () {
71 | this.listeners['disconnect']();
72 | };
73 |
74 | module.exports = ServerSocket;
75 |
--------------------------------------------------------------------------------
/jigg/demo/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const express = require('express');
5 | const http = require('http');
6 |
7 | const app = express();
8 | const httpServer = http.createServer(app);
9 |
10 | // Static routes
11 | app.get('/', (request, response) => response.sendFile(__dirname + '/client.html'));
12 | app.use('/client', express.static(__dirname + '/client/'));
13 | app.use('/dist', express.static(__dirname + '/../dist/'));
14 | app.use('/circuits', express.static(__dirname + '/../circuits/'));
15 |
16 | // Create new JIGG Server and run it (by running http)
17 | const JIGG = require('../src/jigg.js');
18 | const server = new JIGG.Server(httpServer);
19 |
20 | const port = parseInt(process.argv[2]);
21 | httpServer.listen(port, function () {
22 | console.log('listening on *:', port);
23 | });
24 |
25 | // Optional: in case the server is also a garbler or evaluator
26 | const role = process.argv[3];
27 | if (role != null) {
28 | let input = process.argv[4];
29 | let encoding = process.argv[5];
30 | let circuitName = process.argv[6];
31 | let debug = process.argv[7] !== 'false';
32 |
33 | // default arguments.
34 | if (encoding == null) {
35 | encoding = 'number';
36 | }
37 | if (circuitName == null) {
38 | circuitName = 'arith-add-32-bit-old.txt';
39 | }
40 |
41 | // encoding
42 | if (encoding === 'number') {
43 | input = parseInt(input);
44 | }
45 | if (encoding === 'bits') {
46 | input = input.split('').map(Number);
47 | }
48 |
49 | // Read circuit
50 | const circuitPath = __dirname + '/../circuits/bristol/' + circuitName;
51 | const circuit = fs.readFileSync(circuitPath, 'utf8');
52 |
53 | const agent = server.makeAgent(role, {debug: debug});
54 | agent.loadCircuit(circuit);
55 | agent.setInput(input, encoding);
56 | agent.start();
57 |
58 | agent.getOutput(encoding).then(function (output) {
59 | console.log(output);
60 | agent.socket.disconnect();
61 | });
62 | }
--------------------------------------------------------------------------------
/jigg/src/comm/ot.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const sodium = require('libsodium-wrappers-sumo');
4 |
5 | const crypto = require('../util/crypto.js');
6 | const labelParser = require('../parse/label.js');
7 |
8 | function OT(socket) {
9 | this.socket = socket;
10 | }
11 |
12 | OT.prototype.send = function (tag, m0, m1) {
13 | const self = this;
14 | const _id = this.socket.nextId();
15 |
16 | const a = sodium.crypto_core_ristretto255_scalar_random();
17 | const A = sodium.crypto_scalarmult_ristretto255_base(a);
18 |
19 | this.socket.send('A', Array.from(A), _id);
20 | this.socket.hear('B', _id).then(function (B) {
21 | B = Uint8Array.from(B);
22 | let k0 = sodium.crypto_scalarmult_ristretto255(a, B);
23 | let k1 = sodium.crypto_scalarmult_ristretto255(a, sodium.crypto_core_ristretto255_sub(B, A));
24 |
25 | k0 = sodium.crypto_generichash(m0.bytes.length, k0);
26 | k1 = sodium.crypto_generichash(m1.bytes.length, k1);
27 |
28 | const e0 = crypto.encrypt_generic(m0, k0, 0);
29 | const e1 = crypto.encrypt_generic(m1, k1, 0);
30 |
31 | self.socket.send('e', [e0.serialize(), e1.serialize()], _id);
32 | });
33 | };
34 |
35 | OT.prototype.receive = function (tag, c) {
36 | const self = this;
37 | const _id = this.socket.nextId();
38 |
39 | const b = sodium.crypto_core_ristretto255_scalar_random();
40 | let B = sodium.crypto_scalarmult_ristretto255_base(b);
41 |
42 | return new Promise(function (resolve) {
43 | self.socket.hear('A', _id).then(function (A) {
44 | A = Uint8Array.from(A);
45 | if (c === 1) {
46 | B = sodium.crypto_core_ristretto255_add(A, B);
47 | }
48 |
49 | self.socket.send('B', Array.from(B), _id);
50 | self.socket.hear('e', _id).then(function (e) {
51 | e = labelParser(e[c]);
52 |
53 | let k = sodium.crypto_scalarmult_ristretto255(b, A);
54 | k = sodium.crypto_generichash(e.bytes.length, k);
55 |
56 | resolve(crypto.decrypt_generic(e, k, 0));
57 | });
58 | });
59 | });
60 | };
61 |
62 | module.exports = OT;
63 |
--------------------------------------------------------------------------------
/app/public/circuits/job_matching.txt:
--------------------------------------------------------------------------------
1 | 95 155
2 | 2 30 30
3 | 1 1
4 | 2 1 0 30 60 XOR
5 | 2 1 60 60 61 AND
6 | 2 1 61 0 62 AND
7 | 2 1 2 32 63 AND
8 | 2 1 3 33 64 AND
9 | 2 1 63 64 65 LOR
10 | 2 1 4 34 66 AND
11 | 2 1 65 66 67 LOR
12 | 2 1 5 35 68 AND
13 | 2 1 67 68 69 LOR
14 | 2 1 62 69 70 AND
15 | 2 1 6 36 71 AND
16 | 2 1 7 37 72 AND
17 | 2 1 71 72 73 LOR
18 | 2 1 8 38 74 AND
19 | 2 1 73 74 75 LOR
20 | 2 1 9 39 76 AND
21 | 2 1 75 76 77 LOR
22 | 2 1 10 40 78 AND
23 | 2 1 77 78 79 LOR
24 | 2 1 11 41 80 AND
25 | 2 1 79 80 81 LOR
26 | 2 1 12 42 82 AND
27 | 2 1 81 82 83 LOR
28 | 2 1 13 43 84 AND
29 | 2 1 83 84 85 LOR
30 | 2 1 70 85 86 AND
31 | 1 1 52 87 NOT
32 | 2 1 87 22 88 AND
33 | 2 1 52 22 89 XOR
34 | 1 1 89 90 NOT
35 | 1 1 53 91 NOT
36 | 2 1 91 23 92 AND
37 | 2 1 90 92 93 AND
38 | 2 1 88 93 94 LOR
39 | 2 1 53 23 95 XOR
40 | 1 1 95 96 NOT
41 | 2 1 96 90 97 AND
42 | 1 1 54 98 NOT
43 | 2 1 98 24 99 AND
44 | 2 1 54 24 100 XOR
45 | 1 1 100 101 NOT
46 | 1 1 55 102 NOT
47 | 2 1 102 25 103 AND
48 | 2 1 101 103 104 AND
49 | 2 1 99 104 105 LOR
50 | 2 1 97 105 106 AND
51 | 2 1 94 106 107 LOR
52 | 2 1 55 25 108 XOR
53 | 1 1 108 109 NOT
54 | 2 1 109 101 110 AND
55 | 2 1 110 97 111 AND
56 | 1 1 56 112 NOT
57 | 2 1 112 26 113 AND
58 | 2 1 56 26 114 XOR
59 | 1 1 114 115 NOT
60 | 1 1 57 116 NOT
61 | 2 1 116 27 117 AND
62 | 2 1 115 117 118 AND
63 | 2 1 113 118 119 LOR
64 | 2 1 57 27 120 XOR
65 | 1 1 120 121 NOT
66 | 2 1 121 115 122 AND
67 | 1 1 58 123 NOT
68 | 2 1 123 28 124 AND
69 | 2 1 58 28 125 XOR
70 | 1 1 125 126 NOT
71 | 1 1 59 127 NOT
72 | 2 1 127 29 128 AND
73 | 2 1 126 128 129 AND
74 | 2 1 124 129 130 LOR
75 | 2 1 122 130 131 AND
76 | 2 1 119 131 132 LOR
77 | 2 1 111 132 133 AND
78 | 2 1 107 133 134 LOR
79 | 2 1 86 134 135 AND
80 | 2 1 14 44 136 AND
81 | 2 1 15 45 137 AND
82 | 2 1 136 137 138 LOR
83 | 2 1 16 46 139 AND
84 | 2 1 138 139 140 LOR
85 | 2 1 17 47 141 AND
86 | 2 1 140 141 142 LOR
87 | 2 1 135 142 143 AND
88 | 2 1 18 48 144 AND
89 | 2 1 19 49 145 AND
90 | 2 1 144 145 146 LOR
91 | 2 1 20 50 147 AND
92 | 2 1 146 147 148 LOR
93 | 2 1 21 51 149 AND
94 | 2 1 148 149 150 LOR
95 | 2 1 143 150 151 AND
96 | 1 1 1 152 NOT
97 | 2 1 152 31 153 LOR
98 | 2 1 151 153 154 AND
99 |
--------------------------------------------------------------------------------
/circuits/job_matching_two_input.txt:
--------------------------------------------------------------------------------
1 | 95 155
2 | 2 30 30
3 | 1 1
4 |
5 | 2 1 29 59 60 XOR
6 | 2 1 60 60 61 AND
7 | 2 1 61 29 62 AND
8 | 2 1 27 57 63 AND
9 | 2 1 26 56 64 AND
10 | 2 1 63 64 65 OR
11 | 2 1 25 55 66 AND
12 | 2 1 65 66 67 OR
13 | 2 1 24 54 68 AND
14 | 2 1 67 68 69 OR
15 | 2 1 62 69 70 AND
16 | 2 1 23 53 71 AND
17 | 2 1 22 52 72 AND
18 | 2 1 71 72 73 OR
19 | 2 1 21 51 74 AND
20 | 2 1 73 74 75 OR
21 | 2 1 20 50 76 AND
22 | 2 1 75 76 77 OR
23 | 2 1 19 49 78 AND
24 | 2 1 77 78 79 OR
25 | 2 1 18 48 80 AND
26 | 2 1 79 80 81 OR
27 | 2 1 17 47 82 AND
28 | 2 1 81 82 83 OR
29 | 2 1 16 46 84 AND
30 | 2 1 83 84 85 OR
31 | 2 1 70 85 86 AND
32 | 1 1 30 87 NOT
33 | 2 1 87 0 88 AND
34 | 2 1 30 0 89 XOR
35 | 1 1 89 90 NOT
36 | 1 1 31 91 NOT
37 | 2 1 91 1 92 AND
38 | 2 1 90 92 93 AND
39 | 2 1 88 93 94 OR
40 | 2 1 31 1 95 XOR
41 | 1 1 95 96 NOT
42 | 2 1 96 90 97 AND
43 | 1 1 32 98 NOT
44 | 2 1 98 2 99 AND
45 | 2 1 32 2 100 XOR
46 | 1 1 100 101 NOT
47 | 1 1 33 102 NOT
48 | 2 1 102 3 103 AND
49 | 2 1 101 103 104 AND
50 | 2 1 99 104 105 OR
51 | 2 1 97 105 106 AND
52 | 2 1 94 106 107 OR
53 | 2 1 33 3 108 XOR
54 | 1 1 108 109 NOT
55 | 2 1 109 101 110 AND
56 | 2 1 110 97 111 AND
57 | 1 1 34 112 NOT
58 | 2 1 112 4 113 AND
59 | 2 1 34 4 114 XOR
60 | 1 1 114 115 NOT
61 | 1 1 35 116 NOT
62 | 2 1 116 5 117 AND
63 | 2 1 115 117 118 AND
64 | 2 1 113 118 119 OR
65 | 2 1 35 5 120 XOR
66 | 1 1 120 121 NOT
67 | 2 1 121 115 122 AND
68 | 1 1 36 123 NOT
69 | 2 1 123 6 124 AND
70 | 2 1 36 6 125 XOR
71 | 1 1 125 126 NOT
72 | 1 1 37 127 NOT
73 | 2 1 127 7 128 AND
74 | 2 1 126 128 129 AND
75 | 2 1 124 129 130 OR
76 | 2 1 122 130 131 AND
77 | 2 1 119 131 132 OR
78 | 2 1 111 132 133 AND
79 | 2 1 107 133 134 OR
80 | 2 1 86 134 135 AND
81 | 2 1 15 45 136 AND
82 | 2 1 14 44 137 AND
83 | 2 1 136 137 138 OR
84 | 2 1 13 43 139 AND
85 | 2 1 138 139 140 OR
86 | 2 1 12 42 141 AND
87 | 2 1 140 141 142 OR
88 | 2 1 135 142 143 AND
89 | 2 1 11 41 144 AND
90 | 2 1 10 40 145 AND
91 | 2 1 144 145 146 OR
92 | 2 1 9 39 147 AND
93 | 2 1 146 147 148 OR
94 | 2 1 8 38 149 AND
95 | 2 1 148 149 150 OR
96 | 2 1 143 150 151 AND
97 | 1 1 28 152 NOT
98 | 2 1 152 58 153 OR
99 | 2 1 151 153 154 AND
100 |
--------------------------------------------------------------------------------
/jigg/src/modules/label.js:
--------------------------------------------------------------------------------
1 | // Wrapper around Uint8Arrays
2 |
3 | "use strict";
4 |
5 | function Label(bytes) {
6 | this.bytes = bytes;
7 | }
8 |
9 | Label.prototype.xor = function (label2) {
10 | return this.xorBytes(label2.bytes);
11 | };
12 |
13 | Label.prototype.xorBytes = function (bytes2) {
14 | const bytes = this.bytes.slice();
15 |
16 | for (let i = 0; i < bytes.length; i++) {
17 | bytes[i] = bytes[i] ^ bytes2[i];
18 | }
19 |
20 | return new Label(bytes);
21 | };
22 |
23 | Label.prototype.double = function () {
24 | const bytes = this.bytes.slice();
25 | const leastbyte = bytes[0];
26 | bytes.copyWithin(0, 1, 15); // Logical left shift by 1 byte
27 | bytes[14] = leastbyte; // Restore old least byte as new greatest (non-pointer) byte
28 | return new Label(bytes);
29 | };
30 |
31 | Label.prototype.quadruple = function () {
32 | const bytes = this.bytes.slice();
33 | const leastbytes = [bytes[0], bytes[1]];
34 | bytes.copyWithin(0, 2, 15); // Logical left shift by 2 byte
35 | [bytes[13], bytes[14]] = leastbytes; // Restore old least two bytes as new greatest bytes
36 | return new Label(bytes);
37 | };
38 |
39 | Label.prototype.getPoint = function () {
40 | return this.bytes[15] & 0x01;
41 | };
42 |
43 | Label.prototype.setPoint = function (point) {
44 | if (point === 0) {
45 | this.bytes[15] = this.bytes[15] & 0xfe;
46 | } else {
47 | this.bytes[15] = this.bytes[15] | 0x01;
48 | }
49 | };
50 |
51 | Label.prototype.equals = function (label2) {
52 | if (this.bytes.length !== label2.bytes.length) {
53 | return false;
54 | }
55 |
56 | for (let i = 0; i < this.bytes.length; i++) {
57 | if (this.bytes[i] !== label2.bytes[i]) {
58 | return false;
59 | }
60 | }
61 |
62 | return true;
63 | };
64 |
65 | Label.prototype.isZero = function () {
66 | for (let i = 0; i < this.bytes.length; i++) {
67 | if (this.bytes[i] !== 0) {
68 | return false;
69 | }
70 | }
71 |
72 | return true;
73 | };
74 |
75 | Label.prototype.serialize = function () {
76 | const arr = [];
77 | for (let i = 0; i < this.bytes.length; i++) {
78 | arr[i] = String.fromCharCode(this.bytes[i]);
79 | }
80 |
81 | return arr.join("");
82 | };
83 |
84 | module.exports = Label;
85 |
--------------------------------------------------------------------------------
/circuits/job_matching.txt:
--------------------------------------------------------------------------------
1 | 95 155
2 | 46 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 8
3 | 1 1
4 |
5 | 2 1 0 30 60 XOR
6 | 2 1 60 60 61 AND
7 | 2 1 61 0 62 AND
8 | 2 1 2 32 63 AND
9 | 2 1 3 33 64 AND
10 | 2 1 63 64 65 OR
11 | 2 1 4 34 66 AND
12 | 2 1 65 66 67 OR
13 | 2 1 5 35 68 AND
14 | 2 1 67 68 69 OR
15 | 2 1 62 69 70 AND
16 | 2 1 6 36 71 AND
17 | 2 1 7 37 72 AND
18 | 2 1 71 72 73 OR
19 | 2 1 8 38 74 AND
20 | 2 1 73 74 75 OR
21 | 2 1 9 39 76 AND
22 | 2 1 75 76 77 OR
23 | 2 1 10 40 78 AND
24 | 2 1 77 78 79 OR
25 | 2 1 11 41 80 AND
26 | 2 1 79 80 81 OR
27 | 2 1 12 42 82 AND
28 | 2 1 81 82 83 OR
29 | 2 1 13 43 84 AND
30 | 2 1 83 84 85 OR
31 | 2 1 70 85 86 AND
32 | 1 1 52 87 NOT
33 | 2 1 87 22 88 AND
34 | 2 1 52 22 89 XOR
35 | 1 1 89 90 NOT
36 | 1 1 53 91 NOT
37 | 2 1 91 23 92 AND
38 | 2 1 90 92 93 AND
39 | 2 1 88 93 94 OR
40 | 2 1 53 23 95 XOR
41 | 1 1 95 96 NOT
42 | 2 1 96 90 97 AND
43 | 1 1 54 98 NOT
44 | 2 1 98 24 99 AND
45 | 2 1 54 24 100 XOR
46 | 1 1 100 101 NOT
47 | 1 1 55 102 NOT
48 | 2 1 102 25 103 AND
49 | 2 1 101 103 104 AND
50 | 2 1 99 104 105 OR
51 | 2 1 97 105 106 AND
52 | 2 1 94 106 107 OR
53 | 2 1 55 25 108 XOR
54 | 1 1 108 109 NOT
55 | 2 1 109 101 110 AND
56 | 2 1 110 97 111 AND
57 | 1 1 56 112 NOT
58 | 2 1 112 26 113 AND
59 | 2 1 56 26 114 XOR
60 | 1 1 114 115 NOT
61 | 1 1 57 116 NOT
62 | 2 1 116 27 117 AND
63 | 2 1 115 117 118 AND
64 | 2 1 113 118 119 OR
65 | 2 1 57 27 120 XOR
66 | 1 1 120 121 NOT
67 | 2 1 121 115 122 AND
68 | 1 1 58 123 NOT
69 | 2 1 123 28 124 AND
70 | 2 1 58 28 125 XOR
71 | 1 1 125 126 NOT
72 | 1 1 59 127 NOT
73 | 2 1 127 29 128 AND
74 | 2 1 126 128 129 AND
75 | 2 1 124 129 130 OR
76 | 2 1 122 130 131 AND
77 | 2 1 119 131 132 OR
78 | 2 1 111 132 133 AND
79 | 2 1 107 133 134 OR
80 | 2 1 86 134 135 AND
81 | 2 1 14 44 136 AND
82 | 2 1 15 45 137 AND
83 | 2 1 136 137 138 OR
84 | 2 1 16 46 139 AND
85 | 2 1 138 139 140 OR
86 | 2 1 17 47 141 AND
87 | 2 1 140 141 142 OR
88 | 2 1 135 142 143 AND
89 | 2 1 18 48 144 AND
90 | 2 1 19 49 145 AND
91 | 2 1 144 145 146 OR
92 | 2 1 20 50 147 AND
93 | 2 1 146 147 148 OR
94 | 2 1 21 51 149 AND
95 | 2 1 148 149 150 OR
96 | 2 1 143 150 151 AND
97 | 1 1 1 152 NOT
98 | 2 1 152 31 153 OR
99 | 2 1 151 153 154 AND
100 |
--------------------------------------------------------------------------------
/jigg/demo/client/client.js:
--------------------------------------------------------------------------------
1 | /* global JIGG */
2 | let timeStart;
3 |
4 | const getCircuit = function () {
5 | return $.ajax('/circuits/bristol/' + $('#circuit').val());
6 | };
7 | const countbits = function () {
8 | const input = $('#input').val();
9 | const base = $('#base').val();
10 |
11 | let count = input.length;
12 | if (base === 'hex') {
13 | count *= 4;
14 | }
15 | if (base === 'number') {
16 | count = Number(input).toString(2).length;
17 | }
18 |
19 | $('#bitsCount').text('Entered ' + count + ' bits');
20 | };
21 |
22 | const progress = function (status, start, total, error) {
23 | if (status === 'connected') {
24 | timeStart = new Date().getTime();
25 | }
26 | if (status === 'error') {
27 | document.getElementById('results').innerHTML += '' + error.toString() + '
';
28 | console.log(error);
29 | return;
30 | }
31 | if (status === 'garbling' || status === 'evaluating') {
32 | document.getElementById('results').innerHTML += '' + status + ': ' + start + '/' + total + '
';
33 | return;
34 | }
35 | document.getElementById('results').innerHTML += '' + status + '
';
36 | };
37 | const displayOutput = function (output) {
38 | const timeEnd = new Date().getTime();
39 | const time = (timeEnd - timeStart) / 1000;
40 |
41 | const base = $('#base').val();
42 | if (base === 'bits') {
43 | output = output.reverse().join('');
44 | }
45 | document.getElementById('results').innerHTML += 'Results: ' + output + ' Took: ' + time + 'seconds
';
46 | };
47 |
48 | const start = function () {
49 | getCircuit().then(function (circuit) {
50 | const role = $('#partytype').val();
51 | const base = $('#base').val();
52 |
53 | let input = $('#input').val();
54 | if (base === 'bits') {
55 | input = input.split('').map(Number).reverse();
56 | } else if (base === 'number') {
57 | input = Number(input);
58 | }
59 |
60 | if (window.Worker) {
61 | const worker = new Worker('client/worker.js');
62 | worker.postMessage({role: role, circuit: circuit, input: input, base: base});
63 | worker.onmessage = function (e) {
64 | if (e.data.type === 'progress') {
65 | progress.apply(window, e.data.args);
66 | } else {
67 | displayOutput(e.data.args);
68 | }
69 | };
70 | } else {
71 | const agent = new JIGG(role);
72 | agent.addProgressListener(progress);
73 | agent.loadCircuit(circuit);
74 | agent.setInput(input, base);
75 | agent.getOutput(base).then(displayOutput);
76 | agent.start();
77 | }
78 | });
79 | };
--------------------------------------------------------------------------------
/jigg/src/jiggServer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const io = require('socket.io');
4 |
5 | const JIGGClient = require('./jiggClient.js');
6 | const ServerSocket = require('./comm/serverSocket.js');
7 |
8 | /**
9 | * Create a new JIGG server.
10 | * @param {http} http - An HTTP server to be used to initialize a socket.io server.
11 | * @param {object} [options] - additional optional options including:
12 | * debug, which defaults to false.
13 | * @constructor
14 | * @alias Server
15 | */
16 | function Server(http, options) {
17 | const self = this;
18 |
19 | if (options == null) {
20 | options = {};
21 | }
22 |
23 | this.http = http;
24 | this.io = io(http);
25 |
26 | this.parties = {
27 | Garbler: null,
28 | Evaluator: null
29 | };
30 |
31 | this.io.on('connection', function (socket) {
32 | socket.on('join', self.join.bind(self, socket));
33 | socket.on('send', self.send.bind(self, socket));
34 | });
35 |
36 | this.log = function () {};
37 | if (options.debug) {
38 | this.log = console.log.bind(console);
39 | }
40 | }
41 |
42 | Server.prototype.join = function (socket, role) {
43 | const self = this;
44 |
45 | this.log('join', role);
46 | if (role !== 'Garbler' && role !== 'Evaluator') {
47 | socket.emit('error', 'Invalid role!');
48 | return;
49 | }
50 |
51 | if (this.parties[role] != null) {
52 | socket.emit('error', 'Role already taken!');
53 | return;
54 | }
55 |
56 | this.parties[role] = socket;
57 | socket.on('disconnect', function () {
58 | self.parties[role] = null;
59 | });
60 |
61 | if (this.parties['Garbler'] != null && this.parties['Evaluator'] != null) {
62 | this.log('Both parties connected');
63 | this.parties['Garbler'].emit('receive', 'go0');
64 | this.parties['Evaluator'].emit('receive', 'go0');
65 | }
66 | };
67 |
68 | Server.prototype.send = function (socket, tag, msg) {
69 | this.log('send', tag);
70 | let target = this.parties['Evaluator'];
71 | if (socket === this.parties['Evaluator']) {
72 | target = this.parties['Garbler'];
73 | }
74 |
75 | target.emit('receive', tag, msg);
76 | };
77 |
78 | /**
79 | * Creates and returns a new JIGG Client Agent
80 | * This agent is wired into the server, to avoid unnecessary overheads.
81 | * @param {string} role - either 'Garbler' or 'Evaluator'.
82 | * @param {object} [options] - same format as the Client Agent constructor.
83 | * @return {Agent} the client Agent ready to use normally.
84 | */
85 | Server.prototype.makeAgent = function (role, options) {
86 | options = Object.assign({}, options, {__Socket: ServerSocket});
87 | return new JIGGClient(role, this, options);
88 | };
89 |
90 | module.exports = Server;
--------------------------------------------------------------------------------
/laconic/README.md:
--------------------------------------------------------------------------------
1 | # KZG Witness Encryption
2 |
3 | This is the implementation artifacts for the paper:
4 |
5 | [Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT](https://eprint.iacr.org/2024/264)
6 |
7 | **Abstract:**
8 | We present a concretely efficient and simple extractable witness encryption scheme for KZG polynomial commitments.
9 | It allows to encrypt a message towards a triple $(\mathsf{com}, \alpha, \beta)$, where $\mathsf{com}$ is a KZG commitment for some polynomial $f(X)$.
10 | Anyone with an opening for the commitment attesting $f(\alpha) = \beta$ can decrypt, but without knowledge of a valid opening the message is computationally hidden.
11 |
12 | Our construction is simple and highly efficient. The ciphertext is only a single group element.
13 | Encryption and decryption both require a single pairing evaluation and a constant number of group operations.
14 |
15 | ## Laconic OT Performance
16 |
17 | Using our witness encryption scheme for KZG we construct a simple and highly efficient laconic OT protocol
18 | which significantly outperforms the state of the art in most important metrics.
19 |
20 | At 128-bits of security, the digest is a constant 48 bytes and the communication is just 256 bytes.
21 |
22 | Below are the running times on an Macbook Pro M3 Max for different operations and different database sizes:
23 |
24 | | Database Size | Hash (Time) | Send (Time) | Recv (Time) |
25 | |-----------------|---------------|---------------|---------------|
26 | | $2^{3}$ | 12.39 ms | 2.59 ms | 595.52 µs |
27 | | $2^{4}$ | 24.58 ms | 2.62 ms | 594.31 µs |
28 | | $2^{5}$ | 49.27 ms | 2.63 ms | 595.35 µs |
29 | | $2^{6}$ | 98.93 ms | 2.64 ms | 590.88 µs |
30 | | $2^{7}$ | 199.65 ms | 2.64 ms | 597.03 µs |
31 | | $2^{8}$ | 405.10 ms | 2.64 ms | 597.60 µs |
32 | | $2^{9}$ | 819.49 ms | 2.64 ms | 595.67 µs |
33 | | $2^{10}$ | 1.65 s | 2.65 ms | 596.54 µs |
34 | | $2^{11}$ | 2.90 s | 2.64 ms | 592.32 µs |
35 | | $2^{12}$ | 5.19 s | 2.65 ms | 592.85 µs |
36 | | $2^{13}$ | 10.08 s | 2.65 ms | 597.57 µs |
37 | | $2^{14}$ | 20.01 s | 2.65 ms | 592.74 µs |
38 | | $2^{15}$ | 40.76 s | 2.65 ms | 591.76 µs |
39 | | $2^{16}$ | 1:22 m | 2.65 ms | 592.44 µs |
40 | | $2^{17}$ | 3:48 m | 2.65 ms | 593.00 µs |
41 | | $2^{18}$ | 6:39 m | 2.64 ms | 592.98 µs |
42 |
43 | Where:
44 |
45 | - Hash: compute the digest of the database (containing the OT choice bits)
46 | - Send: OT send.
47 | - Recv: OT receive.
48 |
49 | ## Reproduction of The Results
50 |
51 | Benchmarks can be reproduced by simply running `cargo bench`.
52 |
--------------------------------------------------------------------------------
/laconic/pkg/README.md:
--------------------------------------------------------------------------------
1 | # KZG Witness Encryption
2 |
3 | This is the implementation artifacts for the paper:
4 |
5 | [Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT](https://eprint.iacr.org/2024/264)
6 |
7 | **Abstract:**
8 | We present a concretely efficient and simple extractable witness encryption scheme for KZG polynomial commitments.
9 | It allows to encrypt a message towards a triple $(\mathsf{com}, \alpha, \beta)$, where $\mathsf{com}$ is a KZG commitment for some polynomial $f(X)$.
10 | Anyone with an opening for the commitment attesting $f(\alpha) = \beta$ can decrypt, but without knowledge of a valid opening the message is computationally hidden.
11 |
12 | Our construction is simple and highly efficient. The ciphertext is only a single group element.
13 | Encryption and decryption both require a single pairing evaluation and a constant number of group operations.
14 |
15 | ## Laconic OT Performance
16 |
17 | Using our witness encryption scheme for KZG we construct a simple and highly efficient laconic OT protocol
18 | which significantly outperforms the state of the art in most important metrics.
19 |
20 | At 128-bits of security, the digest is a constant 48 bytes and the communication is just 256 bytes.
21 |
22 | Below are the running times on an Macbook Pro M3 Max for different operations and different database sizes:
23 |
24 | | Database Size | Hash (Time) | Send (Time) | Recv (Time) |
25 | |-----------------|---------------|---------------|---------------|
26 | | $2^{3}$ | 12.39 ms | 2.59 ms | 595.52 µs |
27 | | $2^{4}$ | 24.58 ms | 2.62 ms | 594.31 µs |
28 | | $2^{5}$ | 49.27 ms | 2.63 ms | 595.35 µs |
29 | | $2^{6}$ | 98.93 ms | 2.64 ms | 590.88 µs |
30 | | $2^{7}$ | 199.65 ms | 2.64 ms | 597.03 µs |
31 | | $2^{8}$ | 405.10 ms | 2.64 ms | 597.60 µs |
32 | | $2^{9}$ | 819.49 ms | 2.64 ms | 595.67 µs |
33 | | $2^{10}$ | 1.65 s | 2.65 ms | 596.54 µs |
34 | | $2^{11}$ | 2.90 s | 2.64 ms | 592.32 µs |
35 | | $2^{12}$ | 5.19 s | 2.65 ms | 592.85 µs |
36 | | $2^{13}$ | 10.08 s | 2.65 ms | 597.57 µs |
37 | | $2^{14}$ | 20.01 s | 2.65 ms | 592.74 µs |
38 | | $2^{15}$ | 40.76 s | 2.65 ms | 591.76 µs |
39 | | $2^{16}$ | 1:22 m | 2.65 ms | 592.44 µs |
40 | | $2^{17}$ | 3:48 m | 2.65 ms | 593.00 µs |
41 | | $2^{18}$ | 6:39 m | 2.64 ms | 592.98 µs |
42 |
43 | Where:
44 |
45 | - Hash: compute the digest of the database (containing the OT choice bits)
46 | - Send: OT send.
47 | - Recv: OT receive.
48 |
49 | ## Reproduction of The Results
50 |
51 | Benchmarks can be reproduced by simply running `cargo bench`.
52 |
--------------------------------------------------------------------------------
/laconic/src/kzg.rs:
--------------------------------------------------------------------------------
1 | use ark_ec::pairing::Pairing;
2 | use ark_ec::CurveGroup;
3 | use ark_poly::EvaluationDomain;
4 | use ark_std::UniformRand;
5 | use ark_std::Zero;
6 |
7 | use std::ops::Mul;
8 |
9 | use crate::kzg_types::Commitment;
10 | use crate::kzg_types::Opening;
11 | use crate::kzg_types::State;
12 | use crate::kzg_utils::plain_kzg_com;
13 | use crate::kzg_utils::witness_evals_inside;
14 | use crate::{
15 | kzg_fk_open::precompute_y,
16 | kzg_types::{CommitmentKey, VcKZG},
17 | };
18 |
19 | impl> VcKZG {
20 | pub fn setup(
21 | rng: &mut R,
22 | message_length: usize,
23 | ) -> Result, ()> {
24 | CommitmentKey::setup(rng, message_length)
25 | }
26 |
27 | pub fn commit(
28 | rng: &mut R,
29 | ck: &CommitmentKey,
30 | m: &Vec,
31 | ) -> (Commitment, State) {
32 | // evals[0..domain.size] will store evaluations of our polynomial
33 | // over our evaluation domain, namely
34 | // evals[i] = m[i] if m[i] is defined,
35 | // evals[i] = random if not
36 | // evals[domain.size()..2*domain.size()] will store evaluations of
37 | // the random masking polynomial used for hiding
38 | // we keep both evaluations in the same vector so that
39 | // we can easily do a single MSM later
40 | let dsize = ck.domain.size();
41 | let mut evals = Vec::with_capacity(2 * dsize);
42 | for i in 0..m.len() {
43 | evals.push(m[i]);
44 | }
45 | for _ in m.len()..2 * ck.domain.size() {
46 | evals.push(E::ScalarField::rand(rng));
47 | }
48 |
49 | // from our evaluations, we compute a standard KZG commitment
50 | let com_kzg = plain_kzg_com(ck, &evals);
51 |
52 | let state = State {
53 | evals,
54 | precomputed_v: None,
55 | };
56 | let com = Commitment { com_kzg };
57 | (com, state)
58 | }
59 |
60 | pub fn open(ck: &CommitmentKey, st: &State, i: u32) -> Result, ()> {
61 | if i as usize >= ck.message_length {
62 | return Err(());
63 | }
64 |
65 | // compute v: the KZG opening, which is a KZG commitment
66 | // to the witness polynomial. Either we already have it
67 | // precomputed, or we compute it in evaluation form
68 | let v = if let Some(vs) = &st.precomputed_v {
69 | vs[i as usize].into_affine()
70 | } else {
71 | let deg = ck.domain.size();
72 | let mut witn_evals = Vec::new();
73 | witness_evals_inside::(&ck.domain, &st.evals, i as usize, &mut witn_evals);
74 | witness_evals_inside::(
75 | &ck.domain,
76 | &st.evals[deg..2 * deg],
77 | i as usize,
78 | &mut witn_evals,
79 | );
80 | plain_kzg_com(&ck, &witn_evals)
81 | };
82 |
83 | Ok(Opening { v })
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/jigg/src/parse/circuit.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Circuit = require("../modules/circuit.js");
4 | const Gate = require("../modules/gate.js");
5 | const Label = require("../modules/label.js");
6 |
7 | const circuitParser = function (text) {
8 | const metaEnd = text.indexOf("]");
9 | const meta = JSON.parse(text.substring(0, metaEnd + 1));
10 |
11 | const circuit = new Circuit(meta[0], meta[1], meta[2], meta[3], meta[4]);
12 | let gatesCount = meta[5];
13 |
14 | let i = metaEnd + 1;
15 | // parse one gate at a time
16 | while (i < text.length) {
17 | // console.log("gate", gatesCount); // DEBUGGING
18 |
19 | let operation, inputWires, outputWire, gateId, truthTable;
20 |
21 | // parse operation
22 | operation = text.charAt(i);
23 | if (operation === "&") {
24 | operation = "AND";
25 | } else if (operation === "^") {
26 | operation = "XOR";
27 | } else if (operation === "!") {
28 | operation = "NOT";
29 | } else if (operation === "|") {
30 | operation = "LOR";
31 | }
32 |
33 | // console.log("operation", operation); // DEBUGGING
34 |
35 | // parse gate Id
36 | i++;
37 | gateId = "";
38 | while (text.charAt(i) !== "[") {
39 | // console.log("looking for [", i); // DEBUGGING
40 |
41 | gateId += text.charAt(i);
42 | i++;
43 | }
44 | gateId = parseInt(gateId);
45 |
46 | // parse input wires
47 | inputWires = "";
48 | while (text.charAt(i) !== "]") {
49 | // console.log("looking for ]", i); // DEBUGGING
50 |
51 | inputWires += text.charAt(i);
52 | i++;
53 | }
54 | inputWires = JSON.parse(inputWires + "]");
55 |
56 | // parse output wires
57 | i++;
58 | outputWire = "";
59 | while (
60 | text.charAt(i) !== "-" &&
61 | text.charAt(i) !== "&" &&
62 | text.charAt(i) !== "^" &&
63 | text.charAt(i) !== "!" &&
64 | text.charAt(i) !== "|" &&
65 | i < text.length
66 | ) {
67 | // console.log("looking for op", i); // DEBUGGING
68 | outputWire += text.charAt(i);
69 | i++;
70 | }
71 | outputWire = parseInt(outputWire);
72 |
73 | // parse truth table if exists
74 | if (operation === "AND" || operation === "LOR") {
75 | i++;
76 | truthTable = [];
77 | // parse one label at a time
78 | for (let k = 0; k < 4; k++) {
79 | truthTable[k] = new Uint8Array(circuit.labelSize);
80 | for (let l = 0; l < circuit.labelSize; l++) {
81 | truthTable[k][l] = text.charCodeAt(i);
82 | i++;
83 | }
84 | truthTable[k] = new Label(truthTable[k]);
85 | }
86 | }
87 |
88 | // build gate and put it in circuit
89 | circuit.gates.push(
90 | new Gate(gateId, operation, inputWires, outputWire, truthTable)
91 | );
92 | gatesCount--;
93 | }
94 |
95 | if (gatesCount !== 0) {
96 | console.log("problem parsing circuit!");
97 | throw new Error("problem parsing circuit!");
98 | }
99 |
100 | return circuit;
101 | };
102 |
103 | module.exports = circuitParser;
104 |
--------------------------------------------------------------------------------
/jigg/src/parse/parse.js:
--------------------------------------------------------------------------------
1 | // Parses bristol fashion text circuits into Circuit objects
2 |
3 | "use strict";
4 |
5 | /*
6 | * Bristol fashion has the following format:
7 | *
8 | * ...
9 | *