/
signals.ts
133 lines (121 loc) · 3.69 KB
/
signals.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { CURVE, getPublicKey, Point, utils } from "@noble/secp256k1";
import {
concatUint8Arrays,
hexToBigInt,
hexToUint8Array,
messageToUint8Array,
uint8ArrayToBigInt,
} from "./utils/encoding";
import hashToCurve from "./utils/hashToCurve";
import { HashedPoint, multiplyPoint } from "./utils/curve";
import { sha256 } from "js-sha256";
// PLUME version
export enum PlumeVersion {
V1 = 1,
V2 = 2,
}
export function computeHashToCurve(
message: Uint8Array,
pk: Uint8Array,
): HashedPoint {
// Concatenate message and publicKey
const preimage = new Uint8Array(message.length + pk.length);
preimage.set(message);
preimage.set(pk, message.length);
return hashToCurve(Array.from(preimage));
}
export function computeC_V2(
nullifier: Point,
rPoint: Point,
hashedToCurveR: Point,
) {
const nullifierBytes = nullifier.toRawBytes(true);
const preimage = concatUint8Arrays([
nullifierBytes,
rPoint.toRawBytes(true),
hashedToCurveR.toRawBytes(true),
]);
return sha256.create().update(preimage).hex();
}
export function computeC_V1(
pkBytes: Uint8Array,
hashedToCurve: HashedPoint,
nullifier: Point,
rPoint: Point,
hashedToCurveR: Point,
) {
const nullifierBytes = nullifier.toRawBytes(true);
const preimage = concatUint8Arrays([
Point.BASE.toRawBytes(true),
pkBytes,
new Point(
hexToBigInt(hashedToCurve.x.toString()),
hexToBigInt(hashedToCurve.y.toString()),
).toRawBytes(true),
nullifierBytes,
rPoint.toRawBytes(true),
hashedToCurveR.toRawBytes(true),
]);
return sha256.create().update(preimage).hex();
}
export function computeNullifer(hashedToCurve: HashedPoint, sk: Uint8Array) {
return multiplyPoint(hashedToCurve, sk);
}
export function computeRPoint(rScalar: Uint8Array) {
return Point.fromPrivateKey(rScalar);
}
export function computeHashToCurveR(
hashedToCurve: HashedPoint,
rScalar: Uint8Array,
) {
return multiplyPoint(hashedToCurve, rScalar);
}
export function computeS(rScalar: Uint8Array, sk: Uint8Array, c: string) {
return (
(((uint8ArrayToBigInt(sk) * hexToBigInt(c)) % CURVE.n) +
uint8ArrayToBigInt(rScalar)) %
CURVE.n
).toString(16);
}
/**
* Computes and returns the Plume and other signals for the prover.
* @param {string | Uint8Array} message - Message to sign, in either string or UTF-8 array format.
* @param {string | Uint8Array} sk - ECDSA secret key to sign with.
* @param {string| Uint8Array} rScalar - Optional seed for randomness.
* @returns Object containing Plume and other signals - public key, s, c, gPowR, and hashMPKPowR.
*/
export function computeAllInputs(
message: string | Uint8Array,
sk: string | Uint8Array,
rScalar?: string | Uint8Array,
version: PlumeVersion = PlumeVersion.V2,
) {
const skBytes = typeof sk === "string" ? hexToUint8Array(sk) : sk;
const messageBytes =
typeof message === "string" ? messageToUint8Array(message) : message;
const pkBytes = getPublicKey(skBytes, true);
let rScalarBytes: Uint8Array;
if (rScalar) {
rScalarBytes =
typeof rScalar === "string" ? hexToUint8Array(rScalar) : rScalar;
} else {
rScalarBytes = utils.randomPrivateKey();
}
const hashedToCurve = computeHashToCurve(messageBytes, pkBytes);
const nullifier = computeNullifer(hashedToCurve, skBytes);
const hashedToCurveR = computeHashToCurveR(hashedToCurve, rScalarBytes);
const rPoint = computeRPoint(rScalarBytes);
const c =
version == PlumeVersion.V1
? computeC_V1(pkBytes, hashedToCurve, nullifier, rPoint, hashedToCurveR)
: computeC_V2(nullifier, rPoint, hashedToCurveR);
const s = computeS(rScalarBytes, skBytes, c);
return {
plume: nullifier,
s,
pk: pkBytes,
c,
rPoint,
hashedToCurveR,
};
}