@silvana-one/mina-curves 0.3.0
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 +3 -0
- package/dist/node/curve/constants.d.ts +34 -0
- package/dist/node/curve/constants.js +323 -0
- package/dist/node/curve/constants.js.map +1 -0
- package/dist/node/curve/curve.d.ts +50 -0
- package/dist/node/curve/curve.js +307 -0
- package/dist/node/curve/curve.js.map +1 -0
- package/dist/node/curve/hash.d.ts +6 -0
- package/dist/node/curve/hash.js +89 -0
- package/dist/node/curve/hash.js.map +1 -0
- package/dist/node/curve/index.d.ts +4 -0
- package/dist/node/curve/index.js +5 -0
- package/dist/node/curve/index.js.map +1 -0
- package/dist/node/curve/verify.d.ts +4 -0
- package/dist/node/curve/verify.js +19 -0
- package/dist/node/curve/verify.js.map +1 -0
- package/dist/node/index.cjs +711 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +3 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/poseidon.d.ts +1 -0
- package/dist/node/poseidon.js +5 -0
- package/dist/node/poseidon.js.map +1 -0
- package/dist/node/signature.d.ts +6 -0
- package/dist/node/signature.js +6 -0
- package/dist/node/signature.js.map +1 -0
- package/dist/tsconfig.node.tsbuildinfo +1 -0
- package/dist/tsconfig.web.tsbuildinfo +1 -0
- package/dist/web/curve/constants.d.ts +34 -0
- package/dist/web/curve/constants.js +323 -0
- package/dist/web/curve/constants.js.map +1 -0
- package/dist/web/curve/curve.d.ts +50 -0
- package/dist/web/curve/curve.js +307 -0
- package/dist/web/curve/curve.js.map +1 -0
- package/dist/web/curve/hash.d.ts +6 -0
- package/dist/web/curve/hash.js +89 -0
- package/dist/web/curve/hash.js.map +1 -0
- package/dist/web/curve/index.d.ts +4 -0
- package/dist/web/curve/index.js +5 -0
- package/dist/web/curve/index.js.map +1 -0
- package/dist/web/curve/verify.d.ts +4 -0
- package/dist/web/curve/verify.js +19 -0
- package/dist/web/curve/verify.js.map +1 -0
- package/dist/web/index.d.ts +2 -0
- package/dist/web/index.js +3 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/poseidon.d.ts +1 -0
- package/dist/web/poseidon.js +5 -0
- package/dist/web/poseidon.js.map +1 -0
- package/dist/web/signature.d.ts +6 -0
- package/dist/web/signature.js +6 -0
- package/dist/web/signature.js.map +1 -0
- package/package.json +59 -0
- package/src/curve/constants.ts +328 -0
- package/src/curve/curve.ts +370 -0
- package/src/curve/hash.ts +108 -0
- package/src/curve/index.ts +4 -0
- package/src/curve/verify.ts +31 -0
- package/src/index.ts +2 -0
- package/src/poseidon.ts +5 -0
- package/src/signature.ts +11 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { PallasConstants } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
Field,
|
|
5
|
+
Bool,
|
|
6
|
+
Scalar,
|
|
7
|
+
PublicKey,
|
|
8
|
+
Signature,
|
|
9
|
+
Group,
|
|
10
|
+
publicKeyToGroup,
|
|
11
|
+
scale,
|
|
12
|
+
sub,
|
|
13
|
+
isEven,
|
|
14
|
+
equal,
|
|
15
|
+
power,
|
|
16
|
+
add,
|
|
17
|
+
mul,
|
|
18
|
+
sqrt,
|
|
19
|
+
dot,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Field = bigint;
|
|
23
|
+
type Bool = boolean;
|
|
24
|
+
type Group = { x: Field; y: Field };
|
|
25
|
+
type PublicKey = { x: Field; isOdd: Bool };
|
|
26
|
+
type Scalar = bigint;
|
|
27
|
+
type Signature = { r: Field; s: Scalar };
|
|
28
|
+
const projectiveZero = { x: 1n, y: 1n, z: 0n };
|
|
29
|
+
|
|
30
|
+
type GroupProjective = { x: bigint; y: bigint; z: bigint };
|
|
31
|
+
type PointAtInfinity = { x: bigint; y: bigint; infinity: true };
|
|
32
|
+
type FinitePoint = { x: bigint; y: bigint; infinity: false };
|
|
33
|
+
type GroupAffine = PointAtInfinity | FinitePoint;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A non-zero point on the Pallas curve in affine form { x, y }
|
|
37
|
+
*/
|
|
38
|
+
const Group = {
|
|
39
|
+
toProjective({ x, y }: Group): GroupProjective {
|
|
40
|
+
return projectiveFromAffine({ x, y, infinity: false });
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Convert a projective point to a non-zero affine point.
|
|
44
|
+
* Throws an error if the point is zero / infinity, i.e. if z === 0
|
|
45
|
+
*/
|
|
46
|
+
fromProjective(point: GroupProjective): Group {
|
|
47
|
+
let { x, y, infinity } = projectiveToAffine(point);
|
|
48
|
+
if (infinity) throw Error("Group.fromProjective: point is infinity");
|
|
49
|
+
return { x, y };
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const { p, a, b, twoadicRoot, twoadicity, oddFactor } = PallasConstants;
|
|
54
|
+
|
|
55
|
+
function mod(x: bigint, p: bigint) {
|
|
56
|
+
x = x % p;
|
|
57
|
+
if (x < 0) return x + p;
|
|
58
|
+
return x;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function projectiveDoubleA0(g: GroupProjective, p: bigint) {
|
|
62
|
+
if (g.z === 0n) return g;
|
|
63
|
+
let X1 = g.x,
|
|
64
|
+
Y1 = g.y,
|
|
65
|
+
Z1 = g.z;
|
|
66
|
+
if (Y1 === 0n) throw Error("projectiveDouble: unhandled case");
|
|
67
|
+
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
|
68
|
+
// A = X1^2
|
|
69
|
+
let A = mod(X1 * X1, p);
|
|
70
|
+
// B = Y1^2
|
|
71
|
+
let B = mod(Y1 * Y1, p);
|
|
72
|
+
// C = B^2
|
|
73
|
+
let C = mod(B * B, p);
|
|
74
|
+
// D = 2*((X1+B)^2-A-C)
|
|
75
|
+
let D = mod(2n * ((X1 + B) * (X1 + B) - A - C), p);
|
|
76
|
+
// E = 3*A
|
|
77
|
+
let E = 3n * A;
|
|
78
|
+
// F = E^2
|
|
79
|
+
let F = mod(E * E, p);
|
|
80
|
+
// X3 = F-2*D
|
|
81
|
+
let X3 = mod(F - 2n * D, p);
|
|
82
|
+
// Y3 = E*(D-X3)-8*C
|
|
83
|
+
let Y3 = mod(E * (D - X3) - 8n * C, p);
|
|
84
|
+
// Z3 = 2*Y1*Z1
|
|
85
|
+
let Z3 = mod(2n * Y1 * Z1, p);
|
|
86
|
+
return { x: X3, y: Y3, z: Z3 };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function projectiveDoubleAminus3(g: GroupProjective, p: bigint) {
|
|
90
|
+
if (g.z === 0n) return g;
|
|
91
|
+
let X1 = g.x,
|
|
92
|
+
Y1 = g.y,
|
|
93
|
+
Z1 = g.z;
|
|
94
|
+
if (Y1 === 0n) throw Error("projectiveDouble: unhandled case");
|
|
95
|
+
|
|
96
|
+
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
|
|
97
|
+
// delta = Z1^2
|
|
98
|
+
let delta = mod(Z1 * Z1, p);
|
|
99
|
+
// gamma = Y1^2
|
|
100
|
+
let gamma = mod(Y1 * Y1, p);
|
|
101
|
+
// beta = X1*gamma
|
|
102
|
+
let beta = mod(X1 * gamma, p);
|
|
103
|
+
// alpha = 3*(X1-delta)*(X1+delta)
|
|
104
|
+
let alpha = mod((X1 - delta) * (X1 + delta), p);
|
|
105
|
+
alpha = alpha + alpha + alpha;
|
|
106
|
+
// X3 = alpha^2-8*beta
|
|
107
|
+
let X3 = mod(alpha * alpha - 8n * beta, p);
|
|
108
|
+
// Z3 = (Y1+Z1)^2-gamma-delta
|
|
109
|
+
let Z3 = mod((Y1 + Z1) * (Y1 + Z1) - gamma - delta, p);
|
|
110
|
+
// Y3 = alpha*(4*beta-X3)-8*gamma^2
|
|
111
|
+
let Y3 = mod(alpha * (4n * beta - X3) - 8n * gamma * gamma, p);
|
|
112
|
+
return { x: X3, y: Y3, z: Z3 };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function projectiveDouble(g: GroupProjective, p: bigint, a: bigint) {
|
|
116
|
+
if (a === 0n) return projectiveDoubleA0(g, p);
|
|
117
|
+
if (a + 3n === p) return projectiveDoubleAminus3(g, p);
|
|
118
|
+
throw Error(
|
|
119
|
+
"Projective doubling is not implemented for general curve parameter a, only a = 0 and a = -3"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function projectiveNeg({ x, y, z }: GroupProjective, p: bigint) {
|
|
124
|
+
return { x, y: y === 0n ? 0n : p - y, z };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function projectiveAdd(
|
|
128
|
+
g: GroupProjective,
|
|
129
|
+
h: GroupProjective,
|
|
130
|
+
p: bigint,
|
|
131
|
+
a: bigint
|
|
132
|
+
) {
|
|
133
|
+
if (g.z === 0n) return h;
|
|
134
|
+
if (h.z === 0n) return g;
|
|
135
|
+
let X1 = g.x,
|
|
136
|
+
Y1 = g.y,
|
|
137
|
+
Z1 = g.z,
|
|
138
|
+
X2 = h.x,
|
|
139
|
+
Y2 = h.y,
|
|
140
|
+
Z2 = h.z;
|
|
141
|
+
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
|
142
|
+
// Z1Z1 = Z1^2
|
|
143
|
+
let Z1Z1 = mod(Z1 * Z1, p);
|
|
144
|
+
// Z2Z2 = Z2^2
|
|
145
|
+
let Z2Z2 = mod(Z2 * Z2, p);
|
|
146
|
+
// U1 = X1*Z2Z2
|
|
147
|
+
let U1 = mod(X1 * Z2Z2, p);
|
|
148
|
+
// U2 = X2*Z1Z1
|
|
149
|
+
let U2 = mod(X2 * Z1Z1, p);
|
|
150
|
+
// S1 = Y1*Z2*Z2Z2
|
|
151
|
+
let S1 = mod(Y1 * Z2 * Z2Z2, p);
|
|
152
|
+
// S2 = Y2*Z1*Z1Z1
|
|
153
|
+
let S2 = mod(Y2 * Z1 * Z1Z1, p);
|
|
154
|
+
// H = U2-U1
|
|
155
|
+
let H = mod(U2 - U1, p);
|
|
156
|
+
// H = 0 <==> x1 = X1/Z1^2 = X2/Z2^2 = x2 <==> degenerate case (Z3 would become 0)
|
|
157
|
+
if (H === 0n) {
|
|
158
|
+
// if S1 = S2 <==> y1 = y2, the points are equal, so we double instead
|
|
159
|
+
if (S1 === S2) return projectiveDouble(g, p, a);
|
|
160
|
+
// if S1 = -S2, the points are inverse, so return zero
|
|
161
|
+
if (mod(S1 + S2, p) === 0n) return projectiveZero;
|
|
162
|
+
throw Error("projectiveAdd: invalid point");
|
|
163
|
+
}
|
|
164
|
+
// I = (2*H)^2
|
|
165
|
+
let I = mod((H * H) << 2n, p);
|
|
166
|
+
// J = H*I
|
|
167
|
+
let J = mod(H * I, p);
|
|
168
|
+
// r = 2*(S2-S1)
|
|
169
|
+
let r = 2n * (S2 - S1);
|
|
170
|
+
// V = U1*I
|
|
171
|
+
let V = mod(U1 * I, p);
|
|
172
|
+
// X3 = r^2-J-2*V
|
|
173
|
+
let X3 = mod(r * r - J - 2n * V, p);
|
|
174
|
+
// Y3 = r*(V-X3)-2*S1*J
|
|
175
|
+
let Y3 = mod(r * (V - X3) - 2n * S1 * J, p);
|
|
176
|
+
// Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H
|
|
177
|
+
let Z3 = mod(((Z1 + Z2) * (Z1 + Z2) - Z1Z1 - Z2Z2) * H, p);
|
|
178
|
+
return { x: X3, y: Y3, z: Z3 };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function projectiveSub(
|
|
182
|
+
g: GroupProjective,
|
|
183
|
+
h: GroupProjective,
|
|
184
|
+
p: bigint,
|
|
185
|
+
a: bigint
|
|
186
|
+
) {
|
|
187
|
+
return projectiveAdd(g, projectiveNeg(h, p), p, a);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getProjectiveDouble(p: bigint, a: bigint) {
|
|
191
|
+
if (a === 0n) return projectiveDoubleA0;
|
|
192
|
+
if (a + 3n === p) return projectiveDoubleAminus3;
|
|
193
|
+
throw Error(
|
|
194
|
+
"Projective doubling is not implemented for general curve parameter a, only a = 0 and a = -3"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function bigIntToBits(x: bigint) {
|
|
199
|
+
if (x < 0n) {
|
|
200
|
+
throw Error(`bigIntToBits: negative numbers are not supported, got ${x}`);
|
|
201
|
+
}
|
|
202
|
+
let bits: boolean[] = [];
|
|
203
|
+
for (; x > 0n; x >>= 1n) {
|
|
204
|
+
let bit = !!(x & 1n);
|
|
205
|
+
bits.push(bit);
|
|
206
|
+
}
|
|
207
|
+
return bits;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function projectiveScale(
|
|
211
|
+
g: GroupProjective,
|
|
212
|
+
x: bigint | boolean[],
|
|
213
|
+
p: bigint,
|
|
214
|
+
a: bigint
|
|
215
|
+
) {
|
|
216
|
+
let double = getProjectiveDouble(p, a);
|
|
217
|
+
let bits = typeof x === "bigint" ? bigIntToBits(x) : x;
|
|
218
|
+
let h = projectiveZero;
|
|
219
|
+
for (let bit of bits) {
|
|
220
|
+
if (bit) h = projectiveAdd(h, g, p, a);
|
|
221
|
+
g = double(g, p);
|
|
222
|
+
}
|
|
223
|
+
return h;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function sub(g: GroupProjective, h: GroupProjective) {
|
|
227
|
+
return projectiveSub(g, h, p, PallasConstants.a);
|
|
228
|
+
}
|
|
229
|
+
function scale(g: GroupProjective, s: bigint) {
|
|
230
|
+
return projectiveScale(g, s, p, PallasConstants.a);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function projectiveFromAffine({
|
|
234
|
+
x,
|
|
235
|
+
y,
|
|
236
|
+
infinity,
|
|
237
|
+
}: GroupAffine): GroupProjective {
|
|
238
|
+
if (infinity) return projectiveZero;
|
|
239
|
+
return { x, y, z: 1n };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function projectiveToAffine(g: GroupProjective): GroupAffine {
|
|
243
|
+
let z = g.z;
|
|
244
|
+
if (z === 0n) {
|
|
245
|
+
// infinity
|
|
246
|
+
return { x: 0n, y: 0n, infinity: true };
|
|
247
|
+
} else if (z === 1n) {
|
|
248
|
+
// already normalized affine form
|
|
249
|
+
return { x: g.x, y: g.y, infinity: false };
|
|
250
|
+
} else {
|
|
251
|
+
let zinv = inverse(z, p)!; // we checked for z === 0, so inverse exists
|
|
252
|
+
let zinv_squared = mod(zinv * zinv, p);
|
|
253
|
+
// x/z^2
|
|
254
|
+
let x = mod(g.x * zinv_squared, p);
|
|
255
|
+
// y/z^3
|
|
256
|
+
let y = mod(g.y * zinv * zinv_squared, p);
|
|
257
|
+
return { x: x, y: y, infinity: false };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// inverting with EGCD, 1/a in Z_p
|
|
262
|
+
function inverse(a: bigint, p: bigint) {
|
|
263
|
+
a = mod(a, p);
|
|
264
|
+
if (a === 0n) return undefined;
|
|
265
|
+
let b = p;
|
|
266
|
+
let x = 0n;
|
|
267
|
+
let y = 1n;
|
|
268
|
+
let u = 1n;
|
|
269
|
+
let v = 0n;
|
|
270
|
+
while (a !== 0n) {
|
|
271
|
+
let q = b / a;
|
|
272
|
+
let r = mod(b, a);
|
|
273
|
+
let m = x - u * q;
|
|
274
|
+
let n = y - v * q;
|
|
275
|
+
b = a;
|
|
276
|
+
a = r;
|
|
277
|
+
x = u;
|
|
278
|
+
y = v;
|
|
279
|
+
u = m;
|
|
280
|
+
v = n;
|
|
281
|
+
}
|
|
282
|
+
if (b !== 1n) return undefined;
|
|
283
|
+
return mod(x, p);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function isEven(x: bigint) {
|
|
287
|
+
return !(mod(x, p) & 1n);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function equal(x: bigint, y: bigint) {
|
|
291
|
+
// We check if x and y are both in the range [0, p). If they are, can do a simple comparison. Otherwise, we need to reduce them to the proper canonical field range.
|
|
292
|
+
let x_ = x >= 0n && x < p ? x : mod(x, p);
|
|
293
|
+
let y_ = y >= 0n && y < p ? y : mod(y, p);
|
|
294
|
+
return x_ === y_;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// modular exponentiation, a^n % p
|
|
298
|
+
function power(a: bigint, n: bigint) {
|
|
299
|
+
a = mod(a, p);
|
|
300
|
+
let x = 1n;
|
|
301
|
+
for (; n > 0n; n >>= 1n) {
|
|
302
|
+
if (n & 1n) x = mod(x * a, p);
|
|
303
|
+
a = mod(a * a, p);
|
|
304
|
+
}
|
|
305
|
+
return x;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function add(x: bigint, y: bigint) {
|
|
309
|
+
return mod(x + y, p);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function mul(x: bigint, y: bigint) {
|
|
313
|
+
return mod(x * y, p);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function dot(x: bigint[], y: bigint[]) {
|
|
317
|
+
let z = 0n;
|
|
318
|
+
let n = x.length;
|
|
319
|
+
for (let i = 0; i < n; i++) {
|
|
320
|
+
z += x[i] * y[i];
|
|
321
|
+
}
|
|
322
|
+
return mod(z, p);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function sqrt(n_: bigint, p: bigint, Q: bigint, c: bigint, M: bigint) {
|
|
326
|
+
// https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm#The_algorithm
|
|
327
|
+
// variable naming is the same as in that link ^
|
|
328
|
+
// Q is what we call `t` elsewhere - the odd factor in p - 1
|
|
329
|
+
// c is a known primitive root of unity
|
|
330
|
+
// M is the twoadicity = exponent of 2 in factorization of p - 1
|
|
331
|
+
const n = mod(n_, p);
|
|
332
|
+
if (n === 0n) return 0n;
|
|
333
|
+
let t = power(n, (Q - 1n) >> 1n); // n^(Q - 1)/2
|
|
334
|
+
let R = mod(t * n, p); // n^((Q - 1)/2 + 1) = n^((Q + 1)/2)
|
|
335
|
+
t = mod(t * R, p); // n^((Q - 1)/2 + (Q + 1)/2) = n^Q
|
|
336
|
+
while (true) {
|
|
337
|
+
if (t === 1n) return R;
|
|
338
|
+
// use repeated squaring to find the least i, 0 < i < M, such that t^(2^i) = 1
|
|
339
|
+
let i = 0n;
|
|
340
|
+
let s = t;
|
|
341
|
+
while (s !== 1n) {
|
|
342
|
+
s = mod(s * s, p);
|
|
343
|
+
i = i + 1n;
|
|
344
|
+
}
|
|
345
|
+
if (i === M) return undefined; // no solution
|
|
346
|
+
let b = power(c, 1n << (M - i - 1n)); // c^(2^(M-i-1))
|
|
347
|
+
M = i;
|
|
348
|
+
c = mod(b * b, p);
|
|
349
|
+
t = mod(t * c, p);
|
|
350
|
+
R = mod(R * b, p);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function sqrt_internal(x: bigint) {
|
|
355
|
+
return sqrt(x, p, oddFactor, twoadicRoot, twoadicity);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function negate(x: bigint) {
|
|
359
|
+
return x === 0n ? 0n : mod(-x, p);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function publicKeyToGroup({ x, isOdd }: PublicKey): Group {
|
|
363
|
+
const ySquared = add(mul(x, mul(x, x)), b);
|
|
364
|
+
let y = sqrt_internal(ySquared);
|
|
365
|
+
if (y === undefined) {
|
|
366
|
+
throw Error("PublicKey.toGroup: not a valid group element");
|
|
367
|
+
}
|
|
368
|
+
if (isOdd !== !!(y & 1n)) y = negate(y);
|
|
369
|
+
return { x, y };
|
|
370
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Field, Scalar, Group, add, power, dot } from "./curve.js";
|
|
2
|
+
import { PoseidonConstants } from "./constants.js";
|
|
3
|
+
|
|
4
|
+
export function poseidonInitialState(): bigint[] {
|
|
5
|
+
return Array(PoseidonConstants.stateSize).fill(0n);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function poseidonUpdate([...state]: bigint[], input: bigint[]) {
|
|
9
|
+
// special case for empty input
|
|
10
|
+
const { rate } = PoseidonConstants;
|
|
11
|
+
if (input.length === 0) {
|
|
12
|
+
permutation(state);
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
// pad input with zeros so its length is a multiple of the rate
|
|
16
|
+
const n = Math.ceil(input.length / rate) * rate;
|
|
17
|
+
input = input.concat(Array(n - input.length).fill(0n));
|
|
18
|
+
// for every block of length `rate`, add block to the first `rate` elements of the state, and apply the permutation
|
|
19
|
+
for (let blockIndex = 0; blockIndex < n; blockIndex += rate) {
|
|
20
|
+
for (let i = 0; i < rate; i++) {
|
|
21
|
+
state[i] = add(state[i], input[blockIndex + i]);
|
|
22
|
+
}
|
|
23
|
+
permutation(state);
|
|
24
|
+
}
|
|
25
|
+
return state;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function hashMessage(
|
|
29
|
+
message: { fields: bigint[] },
|
|
30
|
+
publicKey: Group,
|
|
31
|
+
r: Field
|
|
32
|
+
): Scalar {
|
|
33
|
+
let { x, y } = publicKey;
|
|
34
|
+
|
|
35
|
+
let input = append(message, { fields: [x, y, r] });
|
|
36
|
+
return hashWithPrefix(packToFields(input));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type GenericHashInput<Field> = { fields?: Field[]; packed?: [Field, number][] };
|
|
40
|
+
type HashInput = GenericHashInput<Field>;
|
|
41
|
+
function append(input1: HashInput, input2: HashInput): HashInput {
|
|
42
|
+
return {
|
|
43
|
+
fields: (input1.fields ?? []).concat(input2.fields ?? []),
|
|
44
|
+
packed: (input1.packed ?? []).concat(input2.packed ?? []),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function packToFields({ fields = [], packed = [] }: HashInput) {
|
|
49
|
+
if (packed.length === 0) return fields;
|
|
50
|
+
const packedBits = [];
|
|
51
|
+
let currentPackedField = 0n;
|
|
52
|
+
let currentSize = 0;
|
|
53
|
+
for (let [field, size] of packed) {
|
|
54
|
+
currentSize += size;
|
|
55
|
+
if (currentSize < 255) {
|
|
56
|
+
currentPackedField = currentPackedField * (1n << BigInt(size)) + field;
|
|
57
|
+
} else {
|
|
58
|
+
packedBits.push(currentPackedField);
|
|
59
|
+
currentSize = size;
|
|
60
|
+
currentPackedField = field;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
packedBits.push(currentPackedField);
|
|
64
|
+
return fields.concat(packedBits);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//const signaturePrefix = "CodaSignature*******";
|
|
68
|
+
const prefix = 240717916736854602989207148466022993262069182275n;
|
|
69
|
+
function salt() {
|
|
70
|
+
return poseidonUpdate(poseidonInitialState(), [prefix]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function hashWithPrefix(input: Field[]) {
|
|
74
|
+
let init = salt();
|
|
75
|
+
return poseidonUpdate(init, input)[0];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function permutation(state: bigint[]) {
|
|
79
|
+
// special case: initial round constant
|
|
80
|
+
const {
|
|
81
|
+
hasInitialRoundConstant,
|
|
82
|
+
stateSize,
|
|
83
|
+
roundConstants,
|
|
84
|
+
fullRounds,
|
|
85
|
+
power: power_,
|
|
86
|
+
mds,
|
|
87
|
+
} = PoseidonConstants;
|
|
88
|
+
let offset = 0;
|
|
89
|
+
if (hasInitialRoundConstant) {
|
|
90
|
+
for (let i = 0; i < stateSize; i++) {
|
|
91
|
+
state[i] = add(state[i], roundConstants[0][i]);
|
|
92
|
+
}
|
|
93
|
+
offset = 1;
|
|
94
|
+
}
|
|
95
|
+
for (let round = 0; round < fullRounds; round++) {
|
|
96
|
+
// raise to a power
|
|
97
|
+
for (let i = 0; i < stateSize; i++) {
|
|
98
|
+
state[i] = power(state[i], power_);
|
|
99
|
+
}
|
|
100
|
+
let oldState = [...state];
|
|
101
|
+
for (let i = 0; i < stateSize; i++) {
|
|
102
|
+
// multiply by mds matrix
|
|
103
|
+
state[i] = dot(mds[i], oldState);
|
|
104
|
+
// add round constants
|
|
105
|
+
state[i] = add(state[i], roundConstants[round + offset][i]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Signature,
|
|
3
|
+
PublicKey,
|
|
4
|
+
Group,
|
|
5
|
+
publicKeyToGroup,
|
|
6
|
+
sub,
|
|
7
|
+
scale,
|
|
8
|
+
isEven,
|
|
9
|
+
equal,
|
|
10
|
+
} from "./curve.js";
|
|
11
|
+
import { hashMessage } from "./hash.js";
|
|
12
|
+
import { PallasConstants } from "./constants.js";
|
|
13
|
+
|
|
14
|
+
export function verify(
|
|
15
|
+
signature: Signature,
|
|
16
|
+
message: { fields: bigint[] },
|
|
17
|
+
publicKey: PublicKey
|
|
18
|
+
) {
|
|
19
|
+
const { r, s } = signature;
|
|
20
|
+
const pk = publicKeyToGroup(publicKey);
|
|
21
|
+
const e = hashMessage(message, pk, r);
|
|
22
|
+
const { one } = PallasConstants;
|
|
23
|
+
const R = sub(scale(one, s), scale(Group.toProjective(pk), e));
|
|
24
|
+
try {
|
|
25
|
+
// if `R` is infinity, Group.fromProjective throws an error, so `verify` returns false
|
|
26
|
+
const { x: rx, y: ry } = Group.fromProjective(R);
|
|
27
|
+
return isEven(ry) && equal(rx, r);
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
package/src/poseidon.ts
ADDED
package/src/signature.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Signature, PublicKey } from "./curve/index.js";
|
|
2
|
+
import { verify } from "./curve/verify.js";
|
|
3
|
+
|
|
4
|
+
export function verifySignature(params: {
|
|
5
|
+
data: bigint[];
|
|
6
|
+
signature: Signature;
|
|
7
|
+
publicKey: PublicKey;
|
|
8
|
+
}): boolean {
|
|
9
|
+
const { data, signature, publicKey } = params;
|
|
10
|
+
return verify(signature, { fields: data }, publicKey);
|
|
11
|
+
}
|