@fairblock/stabletrust 2.0.7 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -53
- package/examples/anonymous-flow.js +13 -7
- package/package.json +10 -3
- package/src/anonymous-client.js +531 -76
- package/src/backend.js +19 -0
- package/src/confidential-client.js +498 -82
- package/src/constants.js +36 -9
- package/src/ibe.js +59 -0
- package/src/index.d.ts +85 -18
- package/src/index.js +6 -0
- package/src/utils.js +169 -62
- package/src/vendor/ts-ibe/age/age-encrypt-decrypt.js +127 -0
- package/src/vendor/ts-ibe/age/age-reader-writer.js +94 -0
- package/src/vendor/ts-ibe/age/armor.js +52 -0
- package/src/vendor/ts-ibe/age/no-op-encdec.js +72 -0
- package/src/vendor/ts-ibe/age/stream-cipher.js +99 -0
- package/src/vendor/ts-ibe/age/utils-crypto.js +60 -0
- package/src/vendor/ts-ibe/age/utils.js +59 -0
- package/src/vendor/ts-ibe/crypto/ibe.js +198 -0
- package/src/vendor/ts-ibe/crypto/utils.js +50 -0
- package/src/vendor/ts-ibe/index.js +87 -0
- package/src/vendor/ts-ibe/types/index.js +2 -0
- package/src/vendor/ts-ibe/version.js +2 -0
- package/src/wasm-loader.js +3 -3
- package/pkg/README.md +0 -310
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import * as bls from "@noble/bls12-381";
|
|
38
|
+
import { utils } from "@noble/bls12-381";
|
|
39
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
40
|
+
import { bytesToNumberBE, fp12ToBytes, xor } from "./utils.js";
|
|
41
|
+
import { Buffer } from "buffer";
|
|
42
|
+
export function encryptOnG1(master, ID, msg) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
44
|
+
var Qid, Gid, sigma, r, U, rGid, hrGid, V, hsigma, W;
|
|
45
|
+
return __generator(this, function (_a) {
|
|
46
|
+
switch (_a.label) {
|
|
47
|
+
case 0:
|
|
48
|
+
if (msg.length >> 8 > 1) {
|
|
49
|
+
throw new Error("cannot encrypt messages larger than our hash output: 256 bits.");
|
|
50
|
+
}
|
|
51
|
+
return [4 /*yield*/, bls.PointG2.hashToCurve(ID)];
|
|
52
|
+
case 1:
|
|
53
|
+
Qid = _a.sent();
|
|
54
|
+
Gid = bls.pairing(master, Qid);
|
|
55
|
+
sigma = utils.randomBytes(msg.length);
|
|
56
|
+
r = h3(sigma, msg);
|
|
57
|
+
U = bls.PointG1.BASE.multiply(r);
|
|
58
|
+
rGid = Gid.pow(r);
|
|
59
|
+
return [4 /*yield*/, gtToHash(rGid, msg.length)];
|
|
60
|
+
case 2:
|
|
61
|
+
hrGid = _a.sent();
|
|
62
|
+
V = xor(sigma, hrGid);
|
|
63
|
+
hsigma = h4(sigma, msg.length);
|
|
64
|
+
W = xor(msg, hsigma);
|
|
65
|
+
return [2 /*return*/, {
|
|
66
|
+
U: U,
|
|
67
|
+
V: V,
|
|
68
|
+
W: W,
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function encryptOnG2(master, ID, msg) {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
76
|
+
var Qid, Gid, sigma, r, U, rGid, hrGid, V, hsigma, W;
|
|
77
|
+
return __generator(this, function (_a) {
|
|
78
|
+
switch (_a.label) {
|
|
79
|
+
case 0:
|
|
80
|
+
if (msg.length >> 8 > 1) {
|
|
81
|
+
throw new Error("cannot encrypt messages larger than our hash output: 256 bits.");
|
|
82
|
+
}
|
|
83
|
+
return [4 /*yield*/, bls.PointG1.hashToCurve(ID)];
|
|
84
|
+
case 1:
|
|
85
|
+
Qid = _a.sent();
|
|
86
|
+
Gid = bls.pairing(Qid, master);
|
|
87
|
+
sigma = utils.randomBytes(msg.length);
|
|
88
|
+
r = h3(sigma, msg);
|
|
89
|
+
U = bls.PointG2.BASE.multiply(r);
|
|
90
|
+
rGid = Gid.pow(r);
|
|
91
|
+
return [4 /*yield*/, gtToHash(rGid, msg.length)];
|
|
92
|
+
case 2:
|
|
93
|
+
hrGid = _a.sent();
|
|
94
|
+
V = xor(sigma, hrGid);
|
|
95
|
+
hsigma = h4(sigma, msg.length);
|
|
96
|
+
W = xor(msg, hsigma);
|
|
97
|
+
return [2 /*return*/, {
|
|
98
|
+
U: U,
|
|
99
|
+
V: V,
|
|
100
|
+
W: W,
|
|
101
|
+
}];
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export function decryptOnG1(p, c) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
108
|
+
var gidt, hgidt, sigma, hsigma, msg, r, rP;
|
|
109
|
+
return __generator(this, function (_a) {
|
|
110
|
+
gidt = bls.pairing(c.U, p);
|
|
111
|
+
hgidt = gtToHash(gidt, c.W.length);
|
|
112
|
+
if (hgidt.length != c.V.length) {
|
|
113
|
+
throw new Error("XorSigma is of invalid length");
|
|
114
|
+
}
|
|
115
|
+
sigma = xor(hgidt, c.V);
|
|
116
|
+
hsigma = h4(sigma, c.W.length);
|
|
117
|
+
msg = xor(hsigma, c.W);
|
|
118
|
+
r = h3(sigma, msg);
|
|
119
|
+
rP = bls.PointG1.BASE.multiply(r);
|
|
120
|
+
if (!rP.equals(c.U)) {
|
|
121
|
+
throw new Error("invalid proof: rP check failed");
|
|
122
|
+
}
|
|
123
|
+
return [2 /*return*/, msg];
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
export function decryptOnG2(p, c) {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
129
|
+
var gidt, hgidt, sigma, hsigma, msg, r, rP;
|
|
130
|
+
return __generator(this, function (_a) {
|
|
131
|
+
gidt = bls.pairing(p, c.U);
|
|
132
|
+
hgidt = gtToHash(gidt, c.W.length);
|
|
133
|
+
if (hgidt.length != c.V.length) {
|
|
134
|
+
throw new Error("XorSigma is of invalid length");
|
|
135
|
+
}
|
|
136
|
+
sigma = xor(hgidt, c.V);
|
|
137
|
+
hsigma = h4(sigma, c.W.length);
|
|
138
|
+
msg = xor(hsigma, c.W);
|
|
139
|
+
r = h3(sigma, msg);
|
|
140
|
+
rP = bls.PointG2.BASE.multiply(r);
|
|
141
|
+
if (!rP.equals(c.U)) {
|
|
142
|
+
throw new Error("invalid proof: rP check failed");
|
|
143
|
+
}
|
|
144
|
+
return [2 /*return*/, msg];
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function gtToHash(gt, len) {
|
|
149
|
+
return sha256
|
|
150
|
+
.create()
|
|
151
|
+
.update("IBE-H2")
|
|
152
|
+
.update(fp12ToBytes(gt))
|
|
153
|
+
.digest()
|
|
154
|
+
.slice(0, len);
|
|
155
|
+
}
|
|
156
|
+
// Our IBE hashes
|
|
157
|
+
var BitsToMaskForBLS12381 = 1;
|
|
158
|
+
function h3(sigma, msg) {
|
|
159
|
+
var h3ret = sha256
|
|
160
|
+
.create()
|
|
161
|
+
.update("IBE-H3")
|
|
162
|
+
.update(sigma)
|
|
163
|
+
.update(msg)
|
|
164
|
+
.digest();
|
|
165
|
+
// We will hash iteratively: H(i || H("IBE-H3" || sigma || msg)) until we get a
|
|
166
|
+
// value that is suitable as a scalar.
|
|
167
|
+
for (var i = 1; i < 65535; i++) {
|
|
168
|
+
var data = h3ret;
|
|
169
|
+
data = sha256
|
|
170
|
+
.create()
|
|
171
|
+
.update(create16BitUintBuffer(i))
|
|
172
|
+
.update(data)
|
|
173
|
+
.digest();
|
|
174
|
+
// assuming Big Endianness
|
|
175
|
+
data[0] = data[0] >> BitsToMaskForBLS12381;
|
|
176
|
+
var n = bytesToNumberBE(data);
|
|
177
|
+
if (n < bls.CURVE.r) {
|
|
178
|
+
return n;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
throw new Error("invalid proof: rP check failed");
|
|
182
|
+
}
|
|
183
|
+
function h4(sigma, len) {
|
|
184
|
+
var h4sigma = sha256.create().update("IBE-H4").update(sigma).digest();
|
|
185
|
+
return h4sigma.slice(0, len);
|
|
186
|
+
}
|
|
187
|
+
function create16BitUintBuffer(input) {
|
|
188
|
+
if (input < 0) {
|
|
189
|
+
throw Error("cannot write a negative value as uint!");
|
|
190
|
+
}
|
|
191
|
+
if (input > Math.pow(2, 16)) {
|
|
192
|
+
throw Error("input value too large to fit in a uint16!");
|
|
193
|
+
}
|
|
194
|
+
var buf = Buffer.alloc(2);
|
|
195
|
+
buf.writeUint16LE(input);
|
|
196
|
+
return buf;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=ibe.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Buffer } from "buffer";
|
|
2
|
+
// returns a new array with the xor of a ^ b
|
|
3
|
+
export function xor(a, b) {
|
|
4
|
+
if (a.length != b.length) {
|
|
5
|
+
throw new Error("Error: incompatible sizes");
|
|
6
|
+
}
|
|
7
|
+
var ret = new Uint8Array(a.length);
|
|
8
|
+
for (var i = 0; i < a.length; i++) {
|
|
9
|
+
ret[i] = a[i] ^ b[i];
|
|
10
|
+
}
|
|
11
|
+
return ret;
|
|
12
|
+
}
|
|
13
|
+
// code from Noble:
|
|
14
|
+
// https://github.com/paulmillr/noble-bls12-381/blob/6380415f1b7e5078c8883a5d8d687f2dd3bff6c2/index.ts#L132-L145
|
|
15
|
+
export function bytesToNumberBE(uint8a) {
|
|
16
|
+
return BigInt("0x" + bytesToHex(Uint8Array.from(uint8a)));
|
|
17
|
+
}
|
|
18
|
+
var hexes = Array.from({ length: 256 }, function (v, i) {
|
|
19
|
+
return i.toString(16).padStart(2, "0");
|
|
20
|
+
});
|
|
21
|
+
export function bytesToHex(uint8a) {
|
|
22
|
+
// pre-caching chars could speed this up 6x.
|
|
23
|
+
var hex = "";
|
|
24
|
+
for (var i = 0; i < uint8a.length; i++) {
|
|
25
|
+
hex += hexes[uint8a[i]];
|
|
26
|
+
}
|
|
27
|
+
return hex;
|
|
28
|
+
}
|
|
29
|
+
////// end of code from Noble.
|
|
30
|
+
// Function to convert Noble's FPs to byte arrays compatible with Kilic library.
|
|
31
|
+
// weirdly all the child FPs have to be reversed when serialising to bytes
|
|
32
|
+
export function fpToBytes(fp) {
|
|
33
|
+
// 48 bytes = 96 hex bytes
|
|
34
|
+
var hex = BigInt(fp.value).toString(16).padStart(96, "0");
|
|
35
|
+
var buf = Buffer.alloc(hex.length / 2);
|
|
36
|
+
buf.write(hex, "hex");
|
|
37
|
+
return buf;
|
|
38
|
+
}
|
|
39
|
+
export function fp2ToBytes(fp2) {
|
|
40
|
+
return Buffer.concat([fp2.c1, fp2.c0].map(fpToBytes));
|
|
41
|
+
}
|
|
42
|
+
// fp6 isn't exported by noble... let's take off the guard rails
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
export function fp6ToBytes(fp6) {
|
|
45
|
+
return Buffer.concat([fp6.c2, fp6.c1, fp6.c0].map(fp2ToBytes));
|
|
46
|
+
}
|
|
47
|
+
export function fp12ToBytes(fp12) {
|
|
48
|
+
return Buffer.concat([fp12.c1, fp12.c0].map(fp6ToBytes));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { PointG1 } from "@noble/bls12-381";
|
|
38
|
+
import * as ibe from "./crypto/ibe.js";
|
|
39
|
+
import { encryptAge } from "./age/age-encrypt-decrypt.js";
|
|
40
|
+
import { Buffer } from "buffer";
|
|
41
|
+
function createTimelockEncrypter(id, pubKey) {
|
|
42
|
+
var _this = this;
|
|
43
|
+
return function (fileKey) { return __awaiter(_this, void 0, void 0, function () {
|
|
44
|
+
var idByte, point, ciphertext;
|
|
45
|
+
return __generator(this, function (_a) {
|
|
46
|
+
switch (_a.label) {
|
|
47
|
+
case 0:
|
|
48
|
+
idByte = new TextEncoder().encode(id);
|
|
49
|
+
point = PointG1.fromHex(pubKey);
|
|
50
|
+
return [4 /*yield*/, ibe.encryptOnG1(point, idByte, fileKey)];
|
|
51
|
+
case 1:
|
|
52
|
+
ciphertext = _a.sent();
|
|
53
|
+
return [2 /*return*/, [
|
|
54
|
+
{
|
|
55
|
+
type: "distIBE",
|
|
56
|
+
args: ["".concat(id)],
|
|
57
|
+
body: serialisedCiphertext(ciphertext),
|
|
58
|
+
},
|
|
59
|
+
]];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}); };
|
|
63
|
+
}
|
|
64
|
+
function serialisedCiphertext(ciphertext) {
|
|
65
|
+
return Buffer.concat([
|
|
66
|
+
ciphertext.U.toRawBytes(true),
|
|
67
|
+
ciphertext.V,
|
|
68
|
+
ciphertext.W,
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
export function timelockEncrypt(identity, pubKey, payload) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
73
|
+
var timelockEncrypter, agePayload, hexPayload;
|
|
74
|
+
return __generator(this, function (_a) {
|
|
75
|
+
switch (_a.label) {
|
|
76
|
+
case 0:
|
|
77
|
+
timelockEncrypter = createTimelockEncrypter(identity, pubKey);
|
|
78
|
+
return [4 /*yield*/, encryptAge(payload, timelockEncrypter)];
|
|
79
|
+
case 1:
|
|
80
|
+
agePayload = _a.sent();
|
|
81
|
+
hexPayload = Buffer.from(agePayload, "binary").toString("hex");
|
|
82
|
+
return [2 /*return*/, hexPayload];
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
package/src/wasm-loader.js
CHANGED
|
@@ -50,7 +50,7 @@ export async function initializeWasm(input) {
|
|
|
50
50
|
|
|
51
51
|
if (input) {
|
|
52
52
|
// User provided custom input
|
|
53
|
-
await init(input);
|
|
53
|
+
await init({ module_or_path: input });
|
|
54
54
|
} else if (isNode) {
|
|
55
55
|
// Node.js: Load from filesystem
|
|
56
56
|
const fs = await import("fs");
|
|
@@ -65,11 +65,11 @@ export async function initializeWasm(input) {
|
|
|
65
65
|
);
|
|
66
66
|
const wasmBuffer = fs.readFileSync(wasmPath);
|
|
67
67
|
|
|
68
|
-
await init(wasmBuffer);
|
|
68
|
+
await init({ module_or_path: wasmBuffer });
|
|
69
69
|
} else {
|
|
70
70
|
// Browser: Let the generated code handle it automatically
|
|
71
71
|
// It will use new URL(..., import.meta.url) which bundlers handle correctly
|
|
72
|
-
await init();
|
|
72
|
+
await init({ module_or_path: undefined });
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
isInitialized = true;
|
package/pkg/README.md
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
# Confidential Tokens - Privacy-First Digital Assets
|
|
2
|
-
|
|
3
|
-
A complete implementation of confidential token operations using ElGamal encryption and zero-knowledge proofs. This project includes both a Solidity smart contract (GatewayContract) and a professional web frontend for confidential token management.
|
|
4
|
-
|
|
5
|
-
## 🚀 Features
|
|
6
|
-
|
|
7
|
-
### Smart Contract (GatewayContract.sol)
|
|
8
|
-
- **Account Management**: Create and manage confidential accounts with ElGamal keypairs
|
|
9
|
-
- **Token Conversion**: Convert ERC20 tokens to confidential tokens
|
|
10
|
-
- **Private Transfers**: Transfer confidential tokens with zero-knowledge proofs
|
|
11
|
-
- **Secure Withdrawals**: Convert confidential tokens back to ERC20 tokens
|
|
12
|
-
- **Access Control**: Role-based permissions for admin and relayer operations
|
|
13
|
-
- **Event Logging**: Comprehensive event system for tracking operations
|
|
14
|
-
|
|
15
|
-
### Frontend Application
|
|
16
|
-
- **Professional UI**: Modern, responsive design optimized for real-world usage
|
|
17
|
-
- **Wallet Integration**: MetaMask connection for Ethereum blockchain access
|
|
18
|
-
- **Account Dashboard**: View encrypted balances and account status
|
|
19
|
-
- **Token Operations**: Convert, transfer, and withdraw confidential tokens
|
|
20
|
-
- **Client-Side Security**: All cryptographic operations performed in WebAssembly
|
|
21
|
-
- **Real-Time Updates**: Live status updates and transaction tracking
|
|
22
|
-
|
|
23
|
-
## 🏗️ Architecture
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
27
|
-
│ Frontend │ │ GatewayContract │ │ Relayer │
|
|
28
|
-
│ (Web App) │◄──►│ (Solidity) │◄──►│ (Off-chain) │
|
|
29
|
-
│ │ │ │ │ │
|
|
30
|
-
│ • MetaMask │ │ • Account Mgmt │ │ • Proof Verify │
|
|
31
|
-
│ • WebAssembly │ │ • Token Escrow │ │ • State Update │
|
|
32
|
-
│ • Proof Gen │ │ • Event Logging │ │ • Balance Mgmt │
|
|
33
|
-
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 📁 Project Structure
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
elgamal-demo/
|
|
40
|
-
├── GatewayContract.sol # Main smart contract
|
|
41
|
-
├── src/ # Rust cryptographic library
|
|
42
|
-
│ ├── components/ # Core cryptographic components
|
|
43
|
-
│ │ ├── elgamal.rs # ElGamal encryption implementation
|
|
44
|
-
│ │ ├── proofs.rs # Zero-knowledge proofs
|
|
45
|
-
│ │ └── ...
|
|
46
|
-
│ ├── transfer.rs # Transfer proof generation
|
|
47
|
-
│ ├── withdraw.rs # Withdraw proof generation
|
|
48
|
-
│ └── lib.rs # Library exports
|
|
49
|
-
├── frontend/ # Web application
|
|
50
|
-
│ ├── index.html # Main application page
|
|
51
|
-
│ ├── styles.css # Professional styling
|
|
52
|
-
│ ├── app.js # Main application logic
|
|
53
|
-
│ ├── deployment-config.js # Configuration file
|
|
54
|
-
│ ├── src/lib.rs # WebAssembly bindings
|
|
55
|
-
│ └── pkg/ # Built WebAssembly files
|
|
56
|
-
└── scripts/ # Deployment scripts
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## 🛠️ Installation & Setup
|
|
60
|
-
|
|
61
|
-
### Prerequisites
|
|
62
|
-
- **Rust**: Latest stable version
|
|
63
|
-
- **Node.js**: 16+ with npm
|
|
64
|
-
- **Solidity**: For smart contract development
|
|
65
|
-
- **MetaMask**: Browser extension for wallet connection
|
|
66
|
-
|
|
67
|
-
### 1. Clone and Setup
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
git clone <repository-url>
|
|
71
|
-
cd elgamal-demo
|
|
72
|
-
|
|
73
|
-
# Install Rust dependencies
|
|
74
|
-
cargo build
|
|
75
|
-
|
|
76
|
-
# Setup frontend
|
|
77
|
-
cd frontend
|
|
78
|
-
npm install
|
|
79
|
-
npm run build:wasm
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### 2. Smart Contract Deployment
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
# Deploy GatewayContract (example using Hardhat)
|
|
86
|
-
npx hardhat compile
|
|
87
|
-
npx hardhat run scripts/deploy.js --network <network>
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### 3. Configuration
|
|
91
|
-
|
|
92
|
-
Update `frontend/deployment-config.js` with your deployed contract address and network settings:
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
export const CONFIG = {
|
|
96
|
-
GATEWAY_CONTRACT_ADDRESS: '0xYourDeployedContractAddress',
|
|
97
|
-
NETWORK: {
|
|
98
|
-
chainId: 1, // Mainnet or testnet
|
|
99
|
-
name: 'Ethereum Mainnet',
|
|
100
|
-
// ... other settings
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### 4. Start Development Server
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
cd frontend
|
|
109
|
-
npm run dev
|
|
110
|
-
# Open http://localhost:8080
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## 🔧 Usage
|
|
114
|
-
|
|
115
|
-
### 1. Connect Wallet
|
|
116
|
-
- Click "Connect Wallet" in the header
|
|
117
|
-
- Approve MetaMask connection
|
|
118
|
-
- Verify your address is displayed
|
|
119
|
-
|
|
120
|
-
### 2. Create Confidential Account
|
|
121
|
-
- Click "Create Account" card
|
|
122
|
-
- Generate ElGamal keypair (stored locally)
|
|
123
|
-
- Submit account creation to contract
|
|
124
|
-
|
|
125
|
-
### 3. Convert Tokens
|
|
126
|
-
- Select token (USDC, USDT, DAI)
|
|
127
|
-
- Enter amount to convert
|
|
128
|
-
- Approve token transfer and confirm conversion
|
|
129
|
-
|
|
130
|
-
### 4. Private Transfers
|
|
131
|
-
- Enter recipient address
|
|
132
|
-
- Select token and amount
|
|
133
|
-
- Generate zero-knowledge proof
|
|
134
|
-
- Submit confidential transfer
|
|
135
|
-
|
|
136
|
-
### 5. Withdraw Tokens
|
|
137
|
-
- Select token and amount
|
|
138
|
-
- Generate withdrawal proof
|
|
139
|
-
- Convert back to ERC20 tokens
|
|
140
|
-
|
|
141
|
-
## 🔐 Security Features
|
|
142
|
-
|
|
143
|
-
### Cryptographic Security
|
|
144
|
-
- **ElGamal Encryption**: All balances encrypted with user-specific keys
|
|
145
|
-
- **Zero-Knowledge Proofs**: Transfer amounts and balances remain private
|
|
146
|
-
- **Client-Side Generation**: All proofs generated locally in WebAssembly
|
|
147
|
-
- **No Server Dependency**: Cryptographic operations don't require server trust
|
|
148
|
-
|
|
149
|
-
### Smart Contract Security
|
|
150
|
-
- **Access Control**: Role-based permissions for sensitive operations
|
|
151
|
-
- **Reentrancy Protection**: Guards against reentrancy attacks
|
|
152
|
-
- **Pausable Operations**: Admin can pause contract in emergencies
|
|
153
|
-
- **Event Logging**: Comprehensive audit trail for all operations
|
|
154
|
-
|
|
155
|
-
### Frontend Security
|
|
156
|
-
- **Local Key Storage**: Private keys stored in browser (use secure storage in production)
|
|
157
|
-
- **Input Validation**: All user inputs validated before processing
|
|
158
|
-
- **Error Handling**: Comprehensive error handling and user feedback
|
|
159
|
-
- **HTTPS Required**: All operations require secure connections
|
|
160
|
-
|
|
161
|
-
## 🌐 Network Support
|
|
162
|
-
|
|
163
|
-
### Mainnet
|
|
164
|
-
- Ethereum Mainnet
|
|
165
|
-
- Token addresses for USDC, USDT, DAI
|
|
166
|
-
|
|
167
|
-
### Testnet
|
|
168
|
-
- Sepolia Testnet
|
|
169
|
-
- Test token addresses
|
|
170
|
-
- Faucet integration for testing
|
|
171
|
-
|
|
172
|
-
## 📊 API Reference
|
|
173
|
-
|
|
174
|
-
### WebAssembly Functions
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
// Generate ElGamal keypair
|
|
178
|
-
const keypair = await generate_keypair();
|
|
179
|
-
|
|
180
|
-
// Encrypt amount with public key
|
|
181
|
-
const encrypted = await encrypt_amount(amount, pubkey);
|
|
182
|
-
|
|
183
|
-
// Generate transfer proof
|
|
184
|
-
const proof = await generate_transfer_proof({
|
|
185
|
-
current_balance_ciphertext,
|
|
186
|
-
current_balance,
|
|
187
|
-
transfer_amount,
|
|
188
|
-
source_keypair,
|
|
189
|
-
destination_pubkey
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// Generate withdraw proof
|
|
193
|
-
const proof = await generate_withdraw_proof({
|
|
194
|
-
current_balance_ciphertext,
|
|
195
|
-
current_balance,
|
|
196
|
-
withdraw_amount,
|
|
197
|
-
keypair
|
|
198
|
-
});
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Smart Contract Functions
|
|
202
|
-
|
|
203
|
-
```solidity
|
|
204
|
-
// Create confidential account
|
|
205
|
-
function createConfidentialAccount(bytes calldata elgamalPubkey)
|
|
206
|
-
external returns (bytes32);
|
|
207
|
-
|
|
208
|
-
// Convert tokens to confidential
|
|
209
|
-
function deposit(DepositItem[] calldata items)
|
|
210
|
-
external returns (bytes32);
|
|
211
|
-
|
|
212
|
-
// Private transfer
|
|
213
|
-
function transferConfidential(address recipient, string calldata denom, bytes calldata transferProofData)
|
|
214
|
-
external returns (bytes32);
|
|
215
|
-
|
|
216
|
-
// Withdraw tokens
|
|
217
|
-
function withdraw(string calldata denom, uint256 amount, bytes calldata withdrawProofData)
|
|
218
|
-
external returns (bytes32);
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## 🚀 Deployment
|
|
222
|
-
|
|
223
|
-
### Frontend Deployment (Vercel)
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
# Install Vercel CLI
|
|
227
|
-
npm i -g vercel
|
|
228
|
-
|
|
229
|
-
# Deploy
|
|
230
|
-
cd frontend
|
|
231
|
-
vercel --prod
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Smart Contract Deployment
|
|
235
|
-
|
|
236
|
-
```bash
|
|
237
|
-
# Using Hardhat
|
|
238
|
-
npx hardhat run scripts/deploy.js --network mainnet
|
|
239
|
-
|
|
240
|
-
# Using Foundry
|
|
241
|
-
forge create --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> GatewayContract
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## 🔍 Testing
|
|
245
|
-
|
|
246
|
-
### Frontend Testing
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
cd frontend
|
|
250
|
-
npm test
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Smart Contract Testing
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
npx hardhat test
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### WebAssembly Testing
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
cd frontend
|
|
263
|
-
npm run test:wasm
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## 📝 Development
|
|
267
|
-
|
|
268
|
-
### Adding New Features
|
|
269
|
-
|
|
270
|
-
1. **Smart Contract**: Add functions to `GatewayContract.sol`
|
|
271
|
-
2. **Cryptography**: Implement in Rust library (`src/`)
|
|
272
|
-
3. **WebAssembly**: Expose functions in `frontend/src/lib.rs`
|
|
273
|
-
4. **Frontend**: Add UI components in `app.js`
|
|
274
|
-
|
|
275
|
-
### Code Style
|
|
276
|
-
|
|
277
|
-
- **Rust**: Follow standard Rust conventions
|
|
278
|
-
- **JavaScript**: Use ES6+ features, async/await
|
|
279
|
-
- **Solidity**: Follow OpenZeppelin patterns
|
|
280
|
-
- **CSS**: Use CSS custom properties for theming
|
|
281
|
-
|
|
282
|
-
## 🤝 Contributing
|
|
283
|
-
|
|
284
|
-
1. Fork the repository
|
|
285
|
-
2. Create feature branch (`git checkout -b feature/amazing-feature`)
|
|
286
|
-
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
287
|
-
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
288
|
-
5. Open Pull Request
|
|
289
|
-
|
|
290
|
-
## 📄 License
|
|
291
|
-
|
|
292
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
293
|
-
|
|
294
|
-
## 🆘 Support
|
|
295
|
-
|
|
296
|
-
- **Issues**: Report bugs and feature requests on GitHub Issues
|
|
297
|
-
- **Discussions**: Join community discussions on GitHub Discussions
|
|
298
|
-
- **Documentation**: Check the [USAGE.md](frontend/USAGE.md) for detailed usage guide
|
|
299
|
-
|
|
300
|
-
## 🔮 Roadmap
|
|
301
|
-
|
|
302
|
-
- [ ] Multi-asset support
|
|
303
|
-
- [ ] Batch operations
|
|
304
|
-
- [ ] Mobile app
|
|
305
|
-
- [ ] Advanced privacy features
|
|
306
|
-
- [ ] Integration with other DeFi protocols
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
**Built with ❤️ for privacy and decentralization**
|