@tezos-x/octez.js-timelock 0.9.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 ADDED
@@ -0,0 +1,69 @@
1
+ # octez.js Timelock package (BETA)
2
+
3
+ **Maintained by [Nomadic Labs](https://www.nomadic-labs.com/).**
4
+
5
+ :::info
6
+ This feature is a work in progress, and might be refined in the near future. We encourage octez.js users to try this feature and reach out to us if you have any issues or concerns.
7
+ :::
8
+
9
+ Timelock is a cryptographic primitive that can be used as a part of a commit & reveal scheme, it provides a guarantee that the information associated to the commit phase is eventually revealed.
10
+
11
+ ## Commit & Reveal
12
+ A classic commit & reveal scheme consists of these 2 stepsL
13
+ - Before the deadline, each participant makes a decision and publishes a commitment, which is proof that they have made a decision that they are unable to change. The proof is the hash of the data they have decided on.
14
+ - After the deadline, each participant reveals the data corresponding to their commitment. Other participants will be able to check that the hash of this data is indeed the same as the previous commitment
15
+
16
+ This scheme makes it possible to prove a certain decision was made before the information is revealed. This information might be the decision of other participants, or some external independent information.
17
+
18
+ above excerpt, taken from [here](https://docs.tezos.com/smart-contracts/data-types/crypto-data-types#classical-commit--reveal-scheme)
19
+
20
+ ## octez.js Implementation
21
+
22
+ ### Creating a chest
23
+ ```
24
+ import { Chest } from '@tezos-x/octez.js-timelock'
25
+
26
+ const time = 10000;
27
+ const payload = new TextEncoder().encode('message');
28
+ const { chest, key } = Chest.newChestAndKey(payload, time);
29
+
30
+ const chestBytes = chest.encode();
31
+ const keyBytes = key.encode();
32
+ ```
33
+
34
+ ### Create a chest from an existing Timelock
35
+ ```
36
+ import { Chest, Timelock } from '@tezos-x/octez.js-timelock';
37
+
38
+ // ...
39
+ const time = 10000;
40
+ const precomputedTimelock = Timelock.precompute(time); // can be cached
41
+ const { chest, key } = Chest.fromTimelock(payload, time, precomputedTimelock);
42
+
43
+ const chestBytes = chest.encode();
44
+ const keyBytes = key.encode();
45
+ ```
46
+
47
+ ### Opening a chest
48
+ ```
49
+ import { Chest, ChestKey} from '@tezos-x/octez.js-timelock';
50
+
51
+ //...
52
+ const time = 10000;
53
+ const [chest] = Chest.fromArray(chestBytes);
54
+ const [chestKey] = ChestKey.fromArray(chestKeyBytes);
55
+ const data = chest.open(chestKey, time);
56
+
57
+ ```
58
+
59
+ **Important Notes**:
60
+ - `time` param being passed should not be mistaken with the 'time' it takes for a chest to open in Timelocks. The `time` param here relates to a complexity relating to the number of power by modulo operations required to compute the key. Without getting too much into the weeds, we recommend using a value of `10000` and adjust accordingly.
61
+ - `payload` relates to the message payload you would like to lock in a chest
62
+
63
+ ## Additional info
64
+
65
+ See the top-level [https://github.com/trilitech/octez.js](https://github.com/trilitech/octez.js) file for details on reporting issues, contributing and versioning.
66
+
67
+ ## Disclaimer
68
+
69
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Chest = exports.ChestKey = void 0;
4
+ exports.encrypt = encrypt;
5
+ exports.decrypt = decrypt;
6
+ const nacl_1 = require("@stablelib/nacl");
7
+ const timelock_util_1 = require("./timelock-util");
8
+ // globalThis.crypto is available in browsers and Node.js 20+
9
+ const defaultRNG = globalThis.crypto;
10
+ class ChestKey extends timelock_util_1.TimelockProof {
11
+ }
12
+ exports.ChestKey = ChestKey;
13
+ function encrypt(key, text, rng = defaultRNG) {
14
+ const nonce = new Uint8Array(24);
15
+ rng.getRandomValues(nonce);
16
+ const payload = (0, nacl_1.secretBox)(key, nonce, text);
17
+ return {
18
+ payload,
19
+ nonce,
20
+ };
21
+ }
22
+ function decrypt(key, c) {
23
+ return (0, nacl_1.openSecretBox)(key, c.nonce, c.payload);
24
+ }
25
+ class Chest {
26
+ constructor({ lockedValue, cipherText }) {
27
+ this.lockedValue = lockedValue;
28
+ this.cipherText = cipherText;
29
+ }
30
+ static newChestAndKey(payload, time, mod, rng) {
31
+ if (time <= 0) {
32
+ throw new Error('Invalid argument');
33
+ }
34
+ const vdfTuple = timelock_util_1.Timelock.precompute(time, mod, rng);
35
+ return Chest.fromTimelock(payload, time, vdfTuple, rng);
36
+ }
37
+ static fromTimelock(payload, time, timelock, rng) {
38
+ if (time <= 0) {
39
+ throw new Error('Invalid argument');
40
+ }
41
+ const { lockedValue, proof } = timelock.getProof(time, rng);
42
+ const symKey = proof.symmetricKey();
43
+ const cipherText = encrypt(symKey, payload, rng);
44
+ return { chest: new Chest({ lockedValue, cipherText }), key: proof };
45
+ }
46
+ newKey(time, mod) {
47
+ if (time <= 0) {
48
+ throw new Error('Invalid argument');
49
+ }
50
+ return (0, timelock_util_1.unlockAndProve)(time, this.lockedValue, mod);
51
+ }
52
+ open(key, time) {
53
+ if (time <= 0) {
54
+ throw new Error('Invalid argument');
55
+ }
56
+ if (!(0, timelock_util_1.verify)(this.lockedValue, key, time)) {
57
+ return null;
58
+ }
59
+ const symKey = key.symmetricKey();
60
+ return decrypt(symKey, this.cipherText);
61
+ }
62
+ encode() {
63
+ const locked = (0, timelock_util_1.encodeBigInt)(this.lockedValue);
64
+ const res = new Uint8Array(locked.length + 24 + 4 + this.cipherText.payload.length);
65
+ res.set(locked);
66
+ let off = locked.length;
67
+ res.set(this.cipherText.nonce, off);
68
+ off += 24;
69
+ new DataView(res.buffer, res.byteOffset, res.byteLength).setUint32(off, this.cipherText.payload.length);
70
+ off += 4;
71
+ res.set(this.cipherText.payload, off);
72
+ return res;
73
+ }
74
+ static fromArray(buf) {
75
+ let i = 0;
76
+ const [lockedValue, n] = (0, timelock_util_1.decodeBigInt)(buf);
77
+ i += n;
78
+ if (buf.length - i < 24 + 4) {
79
+ throw new Error('Buffer is too short');
80
+ }
81
+ const nonce = new Uint8Array(buf.slice(i, i + 24));
82
+ i += 24;
83
+ const len = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(i);
84
+ i += 4;
85
+ if (buf.length - i < len) {
86
+ throw new Error('Buffer is too short');
87
+ }
88
+ const payload = new Uint8Array(buf.slice(i, i + len));
89
+ i += len;
90
+ return [new Chest({ lockedValue, cipherText: { nonce, payload } }), i];
91
+ }
92
+ }
93
+ exports.Chest = Chest;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.TimelockProof = exports.Timelock = void 0;
18
+ __exportStar(require("./chest"), exports);
19
+ __exportStar(require("./interface"), exports);
20
+ var timelock_util_1 = require("./timelock-util");
21
+ Object.defineProperty(exports, "Timelock", { enumerable: true, get: function () { return timelock_util_1.Timelock; } });
22
+ Object.defineProperty(exports, "TimelockProof", { enumerable: true, get: function () { return timelock_util_1.TimelockProof; } });
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelockProof = exports.Timelock = exports.RSA_MODULUS = void 0;
4
+ exports.prove = prove;
5
+ exports.unlockAndProve = unlockAndProve;
6
+ exports.verify = verify;
7
+ exports.encodeBigInt = encodeBigInt;
8
+ exports.decodeBigInt = decodeBigInt;
9
+ const big_integer_1 = require("big-integer");
10
+ const blake2b_1 = require("@stablelib/blake2b");
11
+ // globalThis.crypto is available in browsers and Node.js 20+
12
+ const defaultRNG = globalThis.crypto;
13
+ exports.RSA_MODULUS = (0, big_integer_1.default)('25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784406918290641249515082189298559149176184502808489120072844992687392807287776735971418347270261896375014971824691165077613379859095700097330459748808428401797429100642458691817195118746121515172654632282216869987549182422433637259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133844143603833904414952634432190114657544454178424020924616515723350778707749817125772467962926386356373289912154831438167899885040445364023527381951378636564391212010397122822120720357');
14
+ function rand(rng = defaultRNG) {
15
+ const bytes = new Uint8Array(4);
16
+ rng.getRandomValues(bytes);
17
+ const x = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;
18
+ return x / Math.pow(2, 32);
19
+ }
20
+ function randomInteger(rng, bits) {
21
+ const bytes = new Uint8Array(bits / 8);
22
+ rng.getRandomValues(bytes);
23
+ return big_integer_1.default.fromArray(Array.from(bytes), 256);
24
+ }
25
+ function nextPrime(n) {
26
+ if (n.compare(2) < 0) {
27
+ return (0, big_integer_1.default)(2);
28
+ }
29
+ const limit = n.multiply(2);
30
+ for (let p = n.next(); p.compare(limit) < 0; p = p.next()) {
31
+ // use 25 bases like in GMP mpz_nextprime and thus in Tezos
32
+ if (p.isProbablePrime(25, rand)) {
33
+ return p;
34
+ }
35
+ }
36
+ throw new Error('!!!nextPrime!!!'); // shouldn't happen
37
+ }
38
+ function generate(rng, mod) {
39
+ const m = mod.subtract((0, big_integer_1.default)(2));
40
+ return randomInteger(rng, mod.bitLength().toJSNumber()).mod(m).add((0, big_integer_1.default)(2));
41
+ }
42
+ function toLE(x) {
43
+ return x.toArray(256).value.reverse();
44
+ }
45
+ function fromLE(x) {
46
+ return big_integer_1.default.fromArray(x.reverse(), 256);
47
+ }
48
+ const HASH_SEPARATOR = [0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00];
49
+ function hashToPrime(time, value, key, mod) {
50
+ const b = [
51
+ ...new TextEncoder().encode(String(time)),
52
+ ...HASH_SEPARATOR,
53
+ ...toLE(mod),
54
+ ...HASH_SEPARATOR,
55
+ ...toLE(value),
56
+ ...HASH_SEPARATOR,
57
+ ...toLE(key),
58
+ ];
59
+ const sum = (0, blake2b_1.hash)(new Uint8Array(b), 32, { key: new Uint8Array([32]) });
60
+ const val = fromLE(Array.from(sum));
61
+ return nextPrime(val);
62
+ }
63
+ function proveWesolowski(time, locked, unlocked, mod) {
64
+ const l = hashToPrime(time, locked, unlocked, mod);
65
+ let pi = (0, big_integer_1.default)(1);
66
+ let r = (0, big_integer_1.default)(1);
67
+ for (; time > 0; time -= 1) {
68
+ const rr = r.multiply(2);
69
+ r = rr.mod(l);
70
+ const pi2 = pi.multiply(pi).mod(mod);
71
+ if (rr.compare(l) >= 0) {
72
+ pi = pi2.multiply(locked);
73
+ }
74
+ else {
75
+ pi = pi2;
76
+ }
77
+ }
78
+ return pi.mod(mod);
79
+ }
80
+ function prove(time, locked, unlocked, mod = exports.RSA_MODULUS) {
81
+ return new TimelockProof({
82
+ vdfTuple: new Timelock({
83
+ lockedValue: locked,
84
+ unlockedValue: unlocked,
85
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
86
+ modulus: mod,
87
+ }),
88
+ nonce: (0, big_integer_1.default)(1),
89
+ });
90
+ }
91
+ function unlockTimelock(time, locked, mod) {
92
+ if (locked.compare((0, big_integer_1.default)(1)) <= 0) {
93
+ return locked;
94
+ }
95
+ let x = locked;
96
+ for (; time > 0; time -= 1) {
97
+ x = x.multiply(x).mod(mod);
98
+ }
99
+ return x;
100
+ }
101
+ function unlockAndProve(time, locked, mod = exports.RSA_MODULUS) {
102
+ const unlocked = unlockTimelock(time, locked, mod);
103
+ return prove(time, locked, unlocked, mod);
104
+ }
105
+ function verifyWesolowski(vdfTuple, time) {
106
+ const l = hashToPrime(time, vdfTuple.lockedValue, vdfTuple.unlockedValue, vdfTuple.modulus);
107
+ const ll = vdfTuple.vdfProof.modPow(l, vdfTuple.modulus);
108
+ const r = (0, big_integer_1.default)(2).modPow(time, l);
109
+ const rr = vdfTuple.lockedValue.modPow(r, vdfTuple.modulus);
110
+ const unlocked = ll.multiply(rr).mod(vdfTuple.modulus);
111
+ return unlocked.compare(vdfTuple.unlockedValue) === 0;
112
+ }
113
+ function verify(locked, proof, time) {
114
+ const randomizedChallenge = proof.vdfTuple.lockedValue.modPow(proof.nonce, proof.vdfTuple.modulus);
115
+ return randomizedChallenge.compare(locked) === 0 && verifyWesolowski(proof.vdfTuple, time);
116
+ }
117
+ class Timelock {
118
+ constructor({ lockedValue, unlockedValue, vdfProof, modulus }) {
119
+ this.lockedValue = lockedValue;
120
+ this.unlockedValue = unlockedValue;
121
+ this.vdfProof = vdfProof;
122
+ this.modulus = modulus !== null && modulus !== void 0 ? modulus : exports.RSA_MODULUS;
123
+ }
124
+ static precompute(time, mod = exports.RSA_MODULUS, rng = defaultRNG) {
125
+ const locked = generate(rng, mod);
126
+ const unlocked = unlockTimelock(time, locked, mod);
127
+ return new Timelock({
128
+ lockedValue: locked,
129
+ unlockedValue: unlocked,
130
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
131
+ modulus: mod,
132
+ });
133
+ }
134
+ getProof(time, rng = defaultRNG) {
135
+ if (this.lockedValue.compare((0, big_integer_1.default)(1)) < 1 ||
136
+ this.unlockedValue.compare((0, big_integer_1.default)(0)) < 1 ||
137
+ this.vdfProof.compare((0, big_integer_1.default)(0)) < 1 ||
138
+ this.lockedValue.compare(this.modulus) > 0 ||
139
+ this.unlockedValue.compare(this.modulus) > 0 ||
140
+ this.vdfProof.compare(this.modulus) >= 0) {
141
+ throw new Error('Invalid argument');
142
+ }
143
+ if (!verifyWesolowski(this, time)) {
144
+ throw new Error('Verification error');
145
+ }
146
+ const nonce = randomInteger(rng, 16 * 8);
147
+ const lockedValue = this.lockedValue.modPow(nonce, this.modulus);
148
+ return {
149
+ lockedValue,
150
+ proof: new TimelockProof({
151
+ vdfTuple: this,
152
+ nonce,
153
+ }),
154
+ };
155
+ }
156
+ encode() {
157
+ return new Uint8Array([
158
+ ...encodeBigInt(this.lockedValue),
159
+ ...encodeBigInt(this.unlockedValue),
160
+ ...encodeBigInt(this.vdfProof),
161
+ ]);
162
+ }
163
+ static fromArray(buf, mod = exports.RSA_MODULUS) {
164
+ let i = 0;
165
+ const [lockedValue, n1] = decodeBigInt(buf);
166
+ i += n1;
167
+ const [unlockedValue, n2] = decodeBigInt(buf.slice(i));
168
+ i += n2;
169
+ const [vdfProof, n3] = decodeBigInt(buf.slice(i));
170
+ i += n3;
171
+ return [
172
+ new Timelock({
173
+ lockedValue,
174
+ unlockedValue,
175
+ vdfProof,
176
+ modulus: mod,
177
+ }),
178
+ i,
179
+ ];
180
+ }
181
+ }
182
+ exports.Timelock = Timelock;
183
+ const KDF_KEY = new TextEncoder().encode('Tezoskdftimelockv1');
184
+ class TimelockProof {
185
+ constructor({ vdfTuple, nonce }) {
186
+ this.vdfTuple = vdfTuple;
187
+ this.nonce = nonce;
188
+ }
189
+ symmetricKey() {
190
+ const updated = this.vdfTuple.unlockedValue.modPow(this.nonce, this.vdfTuple.modulus);
191
+ return (0, blake2b_1.hash)(new TextEncoder().encode(String(updated)), 32, { key: KDF_KEY });
192
+ }
193
+ encode() {
194
+ return new Uint8Array([...this.vdfTuple.encode(), ...encodeBigInt(this.nonce)]);
195
+ }
196
+ static fromArray(buf, mod = exports.RSA_MODULUS) {
197
+ let i = 0;
198
+ const [vdfTuple, n1] = Timelock.fromArray(buf, mod);
199
+ i += n1;
200
+ const [nonce, n2] = decodeBigInt(buf.slice(i));
201
+ i += n2;
202
+ return [new TimelockProof({ vdfTuple, nonce }), i];
203
+ }
204
+ }
205
+ exports.TimelockProof = TimelockProof;
206
+ function encodeBigInt(v) {
207
+ if (v.isNegative()) {
208
+ throw new Error('Negative value');
209
+ }
210
+ const res = [];
211
+ for (let i = 0;; i++) {
212
+ const x = v.and((0, big_integer_1.default)(0x7f)).toJSNumber();
213
+ v = v.shiftRight(7);
214
+ if (!v.isZero()) {
215
+ res.push(x | 0x80);
216
+ }
217
+ else {
218
+ res.push(x);
219
+ break;
220
+ }
221
+ }
222
+ return res;
223
+ }
224
+ function decodeBigInt(buf) {
225
+ let shift = 0;
226
+ let i = 0;
227
+ let res = (0, big_integer_1.default)(0);
228
+ while (i < buf.length) {
229
+ const x = buf[i];
230
+ res = res.add((0, big_integer_1.default)(x & 0x7f).shiftLeft(shift));
231
+ shift += 7;
232
+ i++;
233
+ if ((x & 0x80) === 0)
234
+ break;
235
+ }
236
+ return [res, i];
237
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ // IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT!
5
+ exports.VERSION = {
6
+ "commitHash": "f798e97ba998acc2cc5310278d812448591bd312",
7
+ "version": "24.0.2"
8
+ };
@@ -0,0 +1,317 @@
1
+ import { openSecretBox, secretBox } from '@stablelib/nacl';
2
+ import bigInt from 'big-integer';
3
+ import { hash } from '@stablelib/blake2b';
4
+
5
+ // globalThis.crypto is available in browsers and Node.js 20+
6
+ const defaultRNG$1 = globalThis.crypto;
7
+ const RSA_MODULUS = bigInt('25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784406918290641249515082189298559149176184502808489120072844992687392807287776735971418347270261896375014971824691165077613379859095700097330459748808428401797429100642458691817195118746121515172654632282216869987549182422433637259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133844143603833904414952634432190114657544454178424020924616515723350778707749817125772467962926386356373289912154831438167899885040445364023527381951378636564391212010397122822120720357');
8
+ function rand(rng = defaultRNG$1) {
9
+ const bytes = new Uint8Array(4);
10
+ rng.getRandomValues(bytes);
11
+ const x = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;
12
+ return x / Math.pow(2, 32);
13
+ }
14
+ function randomInteger(rng, bits) {
15
+ const bytes = new Uint8Array(bits / 8);
16
+ rng.getRandomValues(bytes);
17
+ return bigInt.fromArray(Array.from(bytes), 256);
18
+ }
19
+ function nextPrime(n) {
20
+ if (n.compare(2) < 0) {
21
+ return bigInt(2);
22
+ }
23
+ const limit = n.multiply(2);
24
+ for (let p = n.next(); p.compare(limit) < 0; p = p.next()) {
25
+ // use 25 bases like in GMP mpz_nextprime and thus in Tezos
26
+ if (p.isProbablePrime(25, rand)) {
27
+ return p;
28
+ }
29
+ }
30
+ throw new Error('!!!nextPrime!!!'); // shouldn't happen
31
+ }
32
+ function generate(rng, mod) {
33
+ const m = mod.subtract(bigInt(2));
34
+ return randomInteger(rng, mod.bitLength().toJSNumber()).mod(m).add(bigInt(2));
35
+ }
36
+ function toLE(x) {
37
+ return x.toArray(256).value.reverse();
38
+ }
39
+ function fromLE(x) {
40
+ return bigInt.fromArray(x.reverse(), 256);
41
+ }
42
+ const HASH_SEPARATOR = [0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00];
43
+ function hashToPrime(time, value, key, mod) {
44
+ const b = [
45
+ ...new TextEncoder().encode(String(time)),
46
+ ...HASH_SEPARATOR,
47
+ ...toLE(mod),
48
+ ...HASH_SEPARATOR,
49
+ ...toLE(value),
50
+ ...HASH_SEPARATOR,
51
+ ...toLE(key),
52
+ ];
53
+ const sum = hash(new Uint8Array(b), 32, { key: new Uint8Array([32]) });
54
+ const val = fromLE(Array.from(sum));
55
+ return nextPrime(val);
56
+ }
57
+ function proveWesolowski(time, locked, unlocked, mod) {
58
+ const l = hashToPrime(time, locked, unlocked, mod);
59
+ let pi = bigInt(1);
60
+ let r = bigInt(1);
61
+ for (; time > 0; time -= 1) {
62
+ const rr = r.multiply(2);
63
+ r = rr.mod(l);
64
+ const pi2 = pi.multiply(pi).mod(mod);
65
+ if (rr.compare(l) >= 0) {
66
+ pi = pi2.multiply(locked);
67
+ }
68
+ else {
69
+ pi = pi2;
70
+ }
71
+ }
72
+ return pi.mod(mod);
73
+ }
74
+ function prove(time, locked, unlocked, mod = RSA_MODULUS) {
75
+ return new TimelockProof({
76
+ vdfTuple: new Timelock({
77
+ lockedValue: locked,
78
+ unlockedValue: unlocked,
79
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
80
+ modulus: mod,
81
+ }),
82
+ nonce: bigInt(1),
83
+ });
84
+ }
85
+ function unlockTimelock(time, locked, mod) {
86
+ if (locked.compare(bigInt(1)) <= 0) {
87
+ return locked;
88
+ }
89
+ let x = locked;
90
+ for (; time > 0; time -= 1) {
91
+ x = x.multiply(x).mod(mod);
92
+ }
93
+ return x;
94
+ }
95
+ function unlockAndProve(time, locked, mod = RSA_MODULUS) {
96
+ const unlocked = unlockTimelock(time, locked, mod);
97
+ return prove(time, locked, unlocked, mod);
98
+ }
99
+ function verifyWesolowski(vdfTuple, time) {
100
+ const l = hashToPrime(time, vdfTuple.lockedValue, vdfTuple.unlockedValue, vdfTuple.modulus);
101
+ const ll = vdfTuple.vdfProof.modPow(l, vdfTuple.modulus);
102
+ const r = bigInt(2).modPow(time, l);
103
+ const rr = vdfTuple.lockedValue.modPow(r, vdfTuple.modulus);
104
+ const unlocked = ll.multiply(rr).mod(vdfTuple.modulus);
105
+ return unlocked.compare(vdfTuple.unlockedValue) === 0;
106
+ }
107
+ function verify(locked, proof, time) {
108
+ const randomizedChallenge = proof.vdfTuple.lockedValue.modPow(proof.nonce, proof.vdfTuple.modulus);
109
+ return randomizedChallenge.compare(locked) === 0 && verifyWesolowski(proof.vdfTuple, time);
110
+ }
111
+ class Timelock {
112
+ constructor({ lockedValue, unlockedValue, vdfProof, modulus }) {
113
+ this.lockedValue = lockedValue;
114
+ this.unlockedValue = unlockedValue;
115
+ this.vdfProof = vdfProof;
116
+ this.modulus = modulus !== null && modulus !== void 0 ? modulus : RSA_MODULUS;
117
+ }
118
+ static precompute(time, mod = RSA_MODULUS, rng = defaultRNG$1) {
119
+ const locked = generate(rng, mod);
120
+ const unlocked = unlockTimelock(time, locked, mod);
121
+ return new Timelock({
122
+ lockedValue: locked,
123
+ unlockedValue: unlocked,
124
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
125
+ modulus: mod,
126
+ });
127
+ }
128
+ getProof(time, rng = defaultRNG$1) {
129
+ if (this.lockedValue.compare(bigInt(1)) < 1 ||
130
+ this.unlockedValue.compare(bigInt(0)) < 1 ||
131
+ this.vdfProof.compare(bigInt(0)) < 1 ||
132
+ this.lockedValue.compare(this.modulus) > 0 ||
133
+ this.unlockedValue.compare(this.modulus) > 0 ||
134
+ this.vdfProof.compare(this.modulus) >= 0) {
135
+ throw new Error('Invalid argument');
136
+ }
137
+ if (!verifyWesolowski(this, time)) {
138
+ throw new Error('Verification error');
139
+ }
140
+ const nonce = randomInteger(rng, 16 * 8);
141
+ const lockedValue = this.lockedValue.modPow(nonce, this.modulus);
142
+ return {
143
+ lockedValue,
144
+ proof: new TimelockProof({
145
+ vdfTuple: this,
146
+ nonce,
147
+ }),
148
+ };
149
+ }
150
+ encode() {
151
+ return new Uint8Array([
152
+ ...encodeBigInt(this.lockedValue),
153
+ ...encodeBigInt(this.unlockedValue),
154
+ ...encodeBigInt(this.vdfProof),
155
+ ]);
156
+ }
157
+ static fromArray(buf, mod = RSA_MODULUS) {
158
+ let i = 0;
159
+ const [lockedValue, n1] = decodeBigInt(buf);
160
+ i += n1;
161
+ const [unlockedValue, n2] = decodeBigInt(buf.slice(i));
162
+ i += n2;
163
+ const [vdfProof, n3] = decodeBigInt(buf.slice(i));
164
+ i += n3;
165
+ return [
166
+ new Timelock({
167
+ lockedValue,
168
+ unlockedValue,
169
+ vdfProof,
170
+ modulus: mod,
171
+ }),
172
+ i,
173
+ ];
174
+ }
175
+ }
176
+ const KDF_KEY = new TextEncoder().encode('Tezoskdftimelockv1');
177
+ class TimelockProof {
178
+ constructor({ vdfTuple, nonce }) {
179
+ this.vdfTuple = vdfTuple;
180
+ this.nonce = nonce;
181
+ }
182
+ symmetricKey() {
183
+ const updated = this.vdfTuple.unlockedValue.modPow(this.nonce, this.vdfTuple.modulus);
184
+ return hash(new TextEncoder().encode(String(updated)), 32, { key: KDF_KEY });
185
+ }
186
+ encode() {
187
+ return new Uint8Array([...this.vdfTuple.encode(), ...encodeBigInt(this.nonce)]);
188
+ }
189
+ static fromArray(buf, mod = RSA_MODULUS) {
190
+ let i = 0;
191
+ const [vdfTuple, n1] = Timelock.fromArray(buf, mod);
192
+ i += n1;
193
+ const [nonce, n2] = decodeBigInt(buf.slice(i));
194
+ i += n2;
195
+ return [new TimelockProof({ vdfTuple, nonce }), i];
196
+ }
197
+ }
198
+ function encodeBigInt(v) {
199
+ if (v.isNegative()) {
200
+ throw new Error('Negative value');
201
+ }
202
+ const res = [];
203
+ for (let i = 0;; i++) {
204
+ const x = v.and(bigInt(0x7f)).toJSNumber();
205
+ v = v.shiftRight(7);
206
+ if (!v.isZero()) {
207
+ res.push(x | 0x80);
208
+ }
209
+ else {
210
+ res.push(x);
211
+ break;
212
+ }
213
+ }
214
+ return res;
215
+ }
216
+ function decodeBigInt(buf) {
217
+ let shift = 0;
218
+ let i = 0;
219
+ let res = bigInt(0);
220
+ while (i < buf.length) {
221
+ const x = buf[i];
222
+ res = res.add(bigInt(x & 0x7f).shiftLeft(shift));
223
+ shift += 7;
224
+ i++;
225
+ if ((x & 0x80) === 0)
226
+ break;
227
+ }
228
+ return [res, i];
229
+ }
230
+
231
+ // globalThis.crypto is available in browsers and Node.js 20+
232
+ const defaultRNG = globalThis.crypto;
233
+ class ChestKey extends TimelockProof {
234
+ }
235
+ function encrypt(key, text, rng = defaultRNG) {
236
+ const nonce = new Uint8Array(24);
237
+ rng.getRandomValues(nonce);
238
+ const payload = secretBox(key, nonce, text);
239
+ return {
240
+ payload,
241
+ nonce,
242
+ };
243
+ }
244
+ function decrypt(key, c) {
245
+ return openSecretBox(key, c.nonce, c.payload);
246
+ }
247
+ class Chest {
248
+ constructor({ lockedValue, cipherText }) {
249
+ this.lockedValue = lockedValue;
250
+ this.cipherText = cipherText;
251
+ }
252
+ static newChestAndKey(payload, time, mod, rng) {
253
+ if (time <= 0) {
254
+ throw new Error('Invalid argument');
255
+ }
256
+ const vdfTuple = Timelock.precompute(time, mod, rng);
257
+ return Chest.fromTimelock(payload, time, vdfTuple, rng);
258
+ }
259
+ static fromTimelock(payload, time, timelock, rng) {
260
+ if (time <= 0) {
261
+ throw new Error('Invalid argument');
262
+ }
263
+ const { lockedValue, proof } = timelock.getProof(time, rng);
264
+ const symKey = proof.symmetricKey();
265
+ const cipherText = encrypt(symKey, payload, rng);
266
+ return { chest: new Chest({ lockedValue, cipherText }), key: proof };
267
+ }
268
+ newKey(time, mod) {
269
+ if (time <= 0) {
270
+ throw new Error('Invalid argument');
271
+ }
272
+ return unlockAndProve(time, this.lockedValue, mod);
273
+ }
274
+ open(key, time) {
275
+ if (time <= 0) {
276
+ throw new Error('Invalid argument');
277
+ }
278
+ if (!verify(this.lockedValue, key, time)) {
279
+ return null;
280
+ }
281
+ const symKey = key.symmetricKey();
282
+ return decrypt(symKey, this.cipherText);
283
+ }
284
+ encode() {
285
+ const locked = encodeBigInt(this.lockedValue);
286
+ const res = new Uint8Array(locked.length + 24 + 4 + this.cipherText.payload.length);
287
+ res.set(locked);
288
+ let off = locked.length;
289
+ res.set(this.cipherText.nonce, off);
290
+ off += 24;
291
+ new DataView(res.buffer, res.byteOffset, res.byteLength).setUint32(off, this.cipherText.payload.length);
292
+ off += 4;
293
+ res.set(this.cipherText.payload, off);
294
+ return res;
295
+ }
296
+ static fromArray(buf) {
297
+ let i = 0;
298
+ const [lockedValue, n] = decodeBigInt(buf);
299
+ i += n;
300
+ if (buf.length - i < 24 + 4) {
301
+ throw new Error('Buffer is too short');
302
+ }
303
+ const nonce = new Uint8Array(buf.slice(i, i + 24));
304
+ i += 24;
305
+ const len = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(i);
306
+ i += 4;
307
+ if (buf.length - i < len) {
308
+ throw new Error('Buffer is too short');
309
+ }
310
+ const payload = new Uint8Array(buf.slice(i, i + len));
311
+ i += len;
312
+ return [new Chest({ lockedValue, cipherText: { nonce, payload } }), i];
313
+ }
314
+ }
315
+
316
+ export { Chest, ChestKey, Timelock, TimelockProof, decrypt, encrypt };
317
+ //# sourceMappingURL=octez.js-timelock.es6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"octez.js-timelock.es6.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,326 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@stablelib/nacl'), require('big-integer'), require('@stablelib/blake2b')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@stablelib/nacl', 'big-integer', '@stablelib/blake2b'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.octezJsTimelock = {}, global.nacl, global.bigInt, global.blake2b));
5
+ })(this, (function (exports, nacl, bigInt, blake2b) { 'use strict';
6
+
7
+ // globalThis.crypto is available in browsers and Node.js 20+
8
+ const defaultRNG$1 = globalThis.crypto;
9
+ const RSA_MODULUS = bigInt('25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784406918290641249515082189298559149176184502808489120072844992687392807287776735971418347270261896375014971824691165077613379859095700097330459748808428401797429100642458691817195118746121515172654632282216869987549182422433637259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133844143603833904414952634432190114657544454178424020924616515723350778707749817125772467962926386356373289912154831438167899885040445364023527381951378636564391212010397122822120720357');
10
+ function rand(rng = defaultRNG$1) {
11
+ const bytes = new Uint8Array(4);
12
+ rng.getRandomValues(bytes);
13
+ const x = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;
14
+ return x / Math.pow(2, 32);
15
+ }
16
+ function randomInteger(rng, bits) {
17
+ const bytes = new Uint8Array(bits / 8);
18
+ rng.getRandomValues(bytes);
19
+ return bigInt.fromArray(Array.from(bytes), 256);
20
+ }
21
+ function nextPrime(n) {
22
+ if (n.compare(2) < 0) {
23
+ return bigInt(2);
24
+ }
25
+ const limit = n.multiply(2);
26
+ for (let p = n.next(); p.compare(limit) < 0; p = p.next()) {
27
+ // use 25 bases like in GMP mpz_nextprime and thus in Tezos
28
+ if (p.isProbablePrime(25, rand)) {
29
+ return p;
30
+ }
31
+ }
32
+ throw new Error('!!!nextPrime!!!'); // shouldn't happen
33
+ }
34
+ function generate(rng, mod) {
35
+ const m = mod.subtract(bigInt(2));
36
+ return randomInteger(rng, mod.bitLength().toJSNumber()).mod(m).add(bigInt(2));
37
+ }
38
+ function toLE(x) {
39
+ return x.toArray(256).value.reverse();
40
+ }
41
+ function fromLE(x) {
42
+ return bigInt.fromArray(x.reverse(), 256);
43
+ }
44
+ const HASH_SEPARATOR = [0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00];
45
+ function hashToPrime(time, value, key, mod) {
46
+ const b = [
47
+ ...new TextEncoder().encode(String(time)),
48
+ ...HASH_SEPARATOR,
49
+ ...toLE(mod),
50
+ ...HASH_SEPARATOR,
51
+ ...toLE(value),
52
+ ...HASH_SEPARATOR,
53
+ ...toLE(key),
54
+ ];
55
+ const sum = blake2b.hash(new Uint8Array(b), 32, { key: new Uint8Array([32]) });
56
+ const val = fromLE(Array.from(sum));
57
+ return nextPrime(val);
58
+ }
59
+ function proveWesolowski(time, locked, unlocked, mod) {
60
+ const l = hashToPrime(time, locked, unlocked, mod);
61
+ let pi = bigInt(1);
62
+ let r = bigInt(1);
63
+ for (; time > 0; time -= 1) {
64
+ const rr = r.multiply(2);
65
+ r = rr.mod(l);
66
+ const pi2 = pi.multiply(pi).mod(mod);
67
+ if (rr.compare(l) >= 0) {
68
+ pi = pi2.multiply(locked);
69
+ }
70
+ else {
71
+ pi = pi2;
72
+ }
73
+ }
74
+ return pi.mod(mod);
75
+ }
76
+ function prove(time, locked, unlocked, mod = RSA_MODULUS) {
77
+ return new TimelockProof({
78
+ vdfTuple: new Timelock({
79
+ lockedValue: locked,
80
+ unlockedValue: unlocked,
81
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
82
+ modulus: mod,
83
+ }),
84
+ nonce: bigInt(1),
85
+ });
86
+ }
87
+ function unlockTimelock(time, locked, mod) {
88
+ if (locked.compare(bigInt(1)) <= 0) {
89
+ return locked;
90
+ }
91
+ let x = locked;
92
+ for (; time > 0; time -= 1) {
93
+ x = x.multiply(x).mod(mod);
94
+ }
95
+ return x;
96
+ }
97
+ function unlockAndProve(time, locked, mod = RSA_MODULUS) {
98
+ const unlocked = unlockTimelock(time, locked, mod);
99
+ return prove(time, locked, unlocked, mod);
100
+ }
101
+ function verifyWesolowski(vdfTuple, time) {
102
+ const l = hashToPrime(time, vdfTuple.lockedValue, vdfTuple.unlockedValue, vdfTuple.modulus);
103
+ const ll = vdfTuple.vdfProof.modPow(l, vdfTuple.modulus);
104
+ const r = bigInt(2).modPow(time, l);
105
+ const rr = vdfTuple.lockedValue.modPow(r, vdfTuple.modulus);
106
+ const unlocked = ll.multiply(rr).mod(vdfTuple.modulus);
107
+ return unlocked.compare(vdfTuple.unlockedValue) === 0;
108
+ }
109
+ function verify(locked, proof, time) {
110
+ const randomizedChallenge = proof.vdfTuple.lockedValue.modPow(proof.nonce, proof.vdfTuple.modulus);
111
+ return randomizedChallenge.compare(locked) === 0 && verifyWesolowski(proof.vdfTuple, time);
112
+ }
113
+ class Timelock {
114
+ constructor({ lockedValue, unlockedValue, vdfProof, modulus }) {
115
+ this.lockedValue = lockedValue;
116
+ this.unlockedValue = unlockedValue;
117
+ this.vdfProof = vdfProof;
118
+ this.modulus = modulus !== null && modulus !== void 0 ? modulus : RSA_MODULUS;
119
+ }
120
+ static precompute(time, mod = RSA_MODULUS, rng = defaultRNG$1) {
121
+ const locked = generate(rng, mod);
122
+ const unlocked = unlockTimelock(time, locked, mod);
123
+ return new Timelock({
124
+ lockedValue: locked,
125
+ unlockedValue: unlocked,
126
+ vdfProof: proveWesolowski(time, locked, unlocked, mod),
127
+ modulus: mod,
128
+ });
129
+ }
130
+ getProof(time, rng = defaultRNG$1) {
131
+ if (this.lockedValue.compare(bigInt(1)) < 1 ||
132
+ this.unlockedValue.compare(bigInt(0)) < 1 ||
133
+ this.vdfProof.compare(bigInt(0)) < 1 ||
134
+ this.lockedValue.compare(this.modulus) > 0 ||
135
+ this.unlockedValue.compare(this.modulus) > 0 ||
136
+ this.vdfProof.compare(this.modulus) >= 0) {
137
+ throw new Error('Invalid argument');
138
+ }
139
+ if (!verifyWesolowski(this, time)) {
140
+ throw new Error('Verification error');
141
+ }
142
+ const nonce = randomInteger(rng, 16 * 8);
143
+ const lockedValue = this.lockedValue.modPow(nonce, this.modulus);
144
+ return {
145
+ lockedValue,
146
+ proof: new TimelockProof({
147
+ vdfTuple: this,
148
+ nonce,
149
+ }),
150
+ };
151
+ }
152
+ encode() {
153
+ return new Uint8Array([
154
+ ...encodeBigInt(this.lockedValue),
155
+ ...encodeBigInt(this.unlockedValue),
156
+ ...encodeBigInt(this.vdfProof),
157
+ ]);
158
+ }
159
+ static fromArray(buf, mod = RSA_MODULUS) {
160
+ let i = 0;
161
+ const [lockedValue, n1] = decodeBigInt(buf);
162
+ i += n1;
163
+ const [unlockedValue, n2] = decodeBigInt(buf.slice(i));
164
+ i += n2;
165
+ const [vdfProof, n3] = decodeBigInt(buf.slice(i));
166
+ i += n3;
167
+ return [
168
+ new Timelock({
169
+ lockedValue,
170
+ unlockedValue,
171
+ vdfProof,
172
+ modulus: mod,
173
+ }),
174
+ i,
175
+ ];
176
+ }
177
+ }
178
+ const KDF_KEY = new TextEncoder().encode('Tezoskdftimelockv1');
179
+ class TimelockProof {
180
+ constructor({ vdfTuple, nonce }) {
181
+ this.vdfTuple = vdfTuple;
182
+ this.nonce = nonce;
183
+ }
184
+ symmetricKey() {
185
+ const updated = this.vdfTuple.unlockedValue.modPow(this.nonce, this.vdfTuple.modulus);
186
+ return blake2b.hash(new TextEncoder().encode(String(updated)), 32, { key: KDF_KEY });
187
+ }
188
+ encode() {
189
+ return new Uint8Array([...this.vdfTuple.encode(), ...encodeBigInt(this.nonce)]);
190
+ }
191
+ static fromArray(buf, mod = RSA_MODULUS) {
192
+ let i = 0;
193
+ const [vdfTuple, n1] = Timelock.fromArray(buf, mod);
194
+ i += n1;
195
+ const [nonce, n2] = decodeBigInt(buf.slice(i));
196
+ i += n2;
197
+ return [new TimelockProof({ vdfTuple, nonce }), i];
198
+ }
199
+ }
200
+ function encodeBigInt(v) {
201
+ if (v.isNegative()) {
202
+ throw new Error('Negative value');
203
+ }
204
+ const res = [];
205
+ for (let i = 0;; i++) {
206
+ const x = v.and(bigInt(0x7f)).toJSNumber();
207
+ v = v.shiftRight(7);
208
+ if (!v.isZero()) {
209
+ res.push(x | 0x80);
210
+ }
211
+ else {
212
+ res.push(x);
213
+ break;
214
+ }
215
+ }
216
+ return res;
217
+ }
218
+ function decodeBigInt(buf) {
219
+ let shift = 0;
220
+ let i = 0;
221
+ let res = bigInt(0);
222
+ while (i < buf.length) {
223
+ const x = buf[i];
224
+ res = res.add(bigInt(x & 0x7f).shiftLeft(shift));
225
+ shift += 7;
226
+ i++;
227
+ if ((x & 0x80) === 0)
228
+ break;
229
+ }
230
+ return [res, i];
231
+ }
232
+
233
+ // globalThis.crypto is available in browsers and Node.js 20+
234
+ const defaultRNG = globalThis.crypto;
235
+ class ChestKey extends TimelockProof {
236
+ }
237
+ function encrypt(key, text, rng = defaultRNG) {
238
+ const nonce = new Uint8Array(24);
239
+ rng.getRandomValues(nonce);
240
+ const payload = nacl.secretBox(key, nonce, text);
241
+ return {
242
+ payload,
243
+ nonce,
244
+ };
245
+ }
246
+ function decrypt(key, c) {
247
+ return nacl.openSecretBox(key, c.nonce, c.payload);
248
+ }
249
+ class Chest {
250
+ constructor({ lockedValue, cipherText }) {
251
+ this.lockedValue = lockedValue;
252
+ this.cipherText = cipherText;
253
+ }
254
+ static newChestAndKey(payload, time, mod, rng) {
255
+ if (time <= 0) {
256
+ throw new Error('Invalid argument');
257
+ }
258
+ const vdfTuple = Timelock.precompute(time, mod, rng);
259
+ return Chest.fromTimelock(payload, time, vdfTuple, rng);
260
+ }
261
+ static fromTimelock(payload, time, timelock, rng) {
262
+ if (time <= 0) {
263
+ throw new Error('Invalid argument');
264
+ }
265
+ const { lockedValue, proof } = timelock.getProof(time, rng);
266
+ const symKey = proof.symmetricKey();
267
+ const cipherText = encrypt(symKey, payload, rng);
268
+ return { chest: new Chest({ lockedValue, cipherText }), key: proof };
269
+ }
270
+ newKey(time, mod) {
271
+ if (time <= 0) {
272
+ throw new Error('Invalid argument');
273
+ }
274
+ return unlockAndProve(time, this.lockedValue, mod);
275
+ }
276
+ open(key, time) {
277
+ if (time <= 0) {
278
+ throw new Error('Invalid argument');
279
+ }
280
+ if (!verify(this.lockedValue, key, time)) {
281
+ return null;
282
+ }
283
+ const symKey = key.symmetricKey();
284
+ return decrypt(symKey, this.cipherText);
285
+ }
286
+ encode() {
287
+ const locked = encodeBigInt(this.lockedValue);
288
+ const res = new Uint8Array(locked.length + 24 + 4 + this.cipherText.payload.length);
289
+ res.set(locked);
290
+ let off = locked.length;
291
+ res.set(this.cipherText.nonce, off);
292
+ off += 24;
293
+ new DataView(res.buffer, res.byteOffset, res.byteLength).setUint32(off, this.cipherText.payload.length);
294
+ off += 4;
295
+ res.set(this.cipherText.payload, off);
296
+ return res;
297
+ }
298
+ static fromArray(buf) {
299
+ let i = 0;
300
+ const [lockedValue, n] = decodeBigInt(buf);
301
+ i += n;
302
+ if (buf.length - i < 24 + 4) {
303
+ throw new Error('Buffer is too short');
304
+ }
305
+ const nonce = new Uint8Array(buf.slice(i, i + 24));
306
+ i += 24;
307
+ const len = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(i);
308
+ i += 4;
309
+ if (buf.length - i < len) {
310
+ throw new Error('Buffer is too short');
311
+ }
312
+ const payload = new Uint8Array(buf.slice(i, i + len));
313
+ i += len;
314
+ return [new Chest({ lockedValue, cipherText: { nonce, payload } }), i];
315
+ }
316
+ }
317
+
318
+ exports.Chest = Chest;
319
+ exports.ChestKey = ChestKey;
320
+ exports.Timelock = Timelock;
321
+ exports.TimelockProof = TimelockProof;
322
+ exports.decrypt = decrypt;
323
+ exports.encrypt = encrypt;
324
+
325
+ }));
326
+ //# sourceMappingURL=octez.js-timelock.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"octez.js-timelock.umd.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,27 @@
1
+ import { BigInteger } from 'big-integer';
2
+ import { RNG, CipherText } from './interface';
3
+ import { TimelockProof, Timelock } from './timelock-util';
4
+ export declare class ChestKey extends TimelockProof {
5
+ }
6
+ export declare function encrypt(key: Uint8Array, text: Uint8Array, rng?: RNG): CipherText;
7
+ export declare function decrypt(key: Uint8Array, c: CipherText): Uint8Array | null;
8
+ export declare class Chest {
9
+ lockedValue: BigInteger;
10
+ cipherText: CipherText;
11
+ constructor({ lockedValue, cipherText }: {
12
+ lockedValue: BigInteger;
13
+ cipherText: CipherText;
14
+ });
15
+ static newChestAndKey(payload: Uint8Array, time: number, mod?: BigInteger, rng?: RNG): {
16
+ chest: Chest;
17
+ key: ChestKey;
18
+ };
19
+ static fromTimelock(payload: Uint8Array, time: number, timelock: Timelock, rng?: RNG): {
20
+ chest: Chest;
21
+ key: ChestKey;
22
+ };
23
+ newKey(time: number, mod?: BigInteger): ChestKey;
24
+ open(key: ChestKey, time: number): Uint8Array | null;
25
+ encode(): Uint8Array;
26
+ static fromArray(buf: Uint8Array): [Chest, number];
27
+ }
@@ -0,0 +1,14 @@
1
+ import { BigInteger } from 'big-integer';
2
+ export interface RNG {
3
+ getRandomValues: (array: Uint8Array) => Uint8Array;
4
+ }
5
+ export interface TimelockInit {
6
+ lockedValue: BigInteger;
7
+ unlockedValue: BigInteger;
8
+ vdfProof: BigInteger;
9
+ modulus?: BigInteger;
10
+ }
11
+ export interface CipherText {
12
+ nonce: Uint8Array;
13
+ payload: Uint8Array;
14
+ }
@@ -0,0 +1,3 @@
1
+ export * from './chest';
2
+ export * from './interface';
3
+ export { Timelock, TimelockProof } from './timelock-util';
@@ -0,0 +1,33 @@
1
+ import bigInt, { BigInteger } from 'big-integer';
2
+ import { TimelockInit, RNG } from './interface';
3
+ export declare const RSA_MODULUS: bigInt.BigInteger;
4
+ export declare function prove(time: number, locked: BigInteger, unlocked: BigInteger, mod?: BigInteger): TimelockProof;
5
+ export declare function unlockAndProve(time: number, locked: BigInteger, mod?: BigInteger): TimelockProof;
6
+ export declare function verify(locked: BigInteger, proof: TimelockProof, time: number): boolean;
7
+ export declare class Timelock {
8
+ lockedValue: BigInteger;
9
+ unlockedValue: BigInteger;
10
+ vdfProof: BigInteger;
11
+ modulus: BigInteger;
12
+ constructor({ lockedValue, unlockedValue, vdfProof, modulus }: TimelockInit);
13
+ static precompute(time: number, mod?: BigInteger, rng?: RNG): Timelock;
14
+ getProof(time: number, rng?: RNG): {
15
+ lockedValue: BigInteger;
16
+ proof: TimelockProof;
17
+ };
18
+ encode(): Uint8Array;
19
+ static fromArray(buf: Uint8Array, mod?: BigInteger): [Timelock, number];
20
+ }
21
+ export declare class TimelockProof {
22
+ vdfTuple: Timelock;
23
+ nonce: BigInteger;
24
+ constructor({ vdfTuple, nonce }: {
25
+ vdfTuple: Timelock;
26
+ nonce: BigInteger;
27
+ });
28
+ symmetricKey(): Uint8Array;
29
+ encode(): Uint8Array;
30
+ static fromArray(buf: Uint8Array, mod?: BigInteger): [TimelockProof, number];
31
+ }
32
+ export declare function encodeBigInt(v: BigInteger): number[];
33
+ export declare function decodeBigInt(buf: ArrayLike<number>): [BigInteger, number];
@@ -0,0 +1,4 @@
1
+ export declare const VERSION: {
2
+ commitHash: string;
3
+ version: string;
4
+ };
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@tezos-x/octez.js-timelock",
3
+ "version": "0.9.0",
4
+ "description": "TypeScript implementation of the Timelock feature in Tezos",
5
+ "main": "./dist/octez.js-timelock.umd.js",
6
+ "module": "./dist/octez.js-timelock.es6.js",
7
+ "typings": "./dist/types/octez.js-timelock.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "signature.json"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "author": "Eugene Zagidullin <eugene@ecadlabs.com>",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/trilitech/octez.js.git"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "scripts": {
25
+ "test": "jest --coverage",
26
+ "test:watch": "jest --coverage --watch",
27
+ "test:prod": "npm run lint && npm run test -- --no-cache",
28
+ "lint": "eslint --ext .js,.ts .",
29
+ "precommit": "lint-staged",
30
+ "prebuild": "rimraf dist",
31
+ "version-stamp": "node ../octez.js/version-stamping.js",
32
+ "build": "tsc --project ./tsconfig.prod.json --module commonjs && rollup -c rollup.config.ts --bundleConfigAsCjs",
33
+ "start": "rollup -c rollup.config.ts --bundleConfigAsCjs -w"
34
+ },
35
+ "lint-staged": {
36
+ "{src,test}/**/*.ts": [
37
+ "prettier --write",
38
+ "eslint --fix"
39
+ ]
40
+ },
41
+ "jest": {
42
+ "transform": {
43
+ ".(ts|tsx)": "ts-jest"
44
+ },
45
+ "testEnvironment": "node",
46
+ "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
47
+ "moduleFileExtensions": [
48
+ "ts",
49
+ "tsx",
50
+ "js"
51
+ ],
52
+ "moduleNameMapper": {
53
+ "^@tezos-x/octez.js-utils$": "<rootDir>/../octez.js-utils/src/octez.js-utils.ts",
54
+ "^@tezos-x/octez.js-signer$": "<rootDir>/../octez.js-signer/src/octez.js-signer.ts",
55
+ "^@tezos-x/octez.js$": "<rootDir>/../octez.js/src/octez.ts"
56
+ },
57
+ "coveragePathIgnorePatterns": [
58
+ "/node_modules/",
59
+ "/test/"
60
+ ],
61
+ "collectCoverageFrom": [
62
+ "src/**/*.{js,ts}"
63
+ ]
64
+ },
65
+ "dependencies": {
66
+ "@stablelib/blake2b": "^1.0.1",
67
+ "@stablelib/nacl": "^1.0.4",
68
+ "big-integer": "^1.6.52"
69
+ },
70
+ "devDependencies": {
71
+ "@tezos-x/octez.js-signer": "*",
72
+ "@tezos-x/octez.js": "*",
73
+ "@tezos-x/octez.js-utils": "*",
74
+ "@types/node": "^20",
75
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
76
+ "@typescript-eslint/parser": "^6.21.0",
77
+ "eslint": "^8.57.0",
78
+ "eslint-config-standard-with-typescript": "^43.0.1",
79
+ "eslint-plugin-import": "^2.29.1",
80
+ "eslint-plugin-n": "^16.6.2",
81
+ "eslint-plugin-promise": "^6.6.0",
82
+ "jest": "^29.7.0",
83
+ "jest-config": "^29.7.0",
84
+ "jest-extended": "^4.0.2",
85
+ "lint-staged": "^15.2.7",
86
+ "lodash.camelcase": "^4.3.0",
87
+ "prettier": "^3.3.3",
88
+ "rimraf": "^6.0.1",
89
+ "rollup": "^4.22.4",
90
+ "rollup-plugin-json": "^4.0.0",
91
+ "rollup-plugin-typescript2": "^0.36.0",
92
+ "ts-jest": "^29.2.3",
93
+ "ts-loader": "^9.5.1",
94
+ "ts-node": "^10.9.2",
95
+ "ts-toolbelt": "^9.6.0",
96
+ "typescript": "^5.5.4"
97
+ },
98
+ "gitHead": "551e35aeff7d6dcde1c72284238c0ed3c3aae77e"
99
+ }