nervur 0.19.2 → 0.20.1
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 +156 -104
- package/dist/being/being.d.ts +8 -15
- package/dist/being/being.js +20 -67
- package/dist/being/digest.d.ts +1 -1
- package/dist/being/digest.js +7 -27
- package/dist/being/faculty.d.ts +10 -0
- package/dist/being/faculty.js +74 -0
- package/dist/being/index.d.ts +5 -5
- package/dist/being/index.js +6 -5
- package/dist/being/kind.d.ts +4 -0
- package/dist/being/kind.js +28 -0
- package/dist/being/types.d.ts +35 -63
- package/dist/being/types.js +3 -57
- package/dist/being/words.d.ts +13 -0
- package/dist/being/words.js +14 -0
- package/dist/cli/command.d.ts +13 -0
- package/dist/cli/command.js +194 -0
- package/dist/cli/harbor.d.ts +21 -0
- package/dist/cli/harbor.js +141 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +14 -0
- package/dist/contract/index.d.ts +39 -0
- package/dist/contract/index.js +33 -0
- package/dist/crypto/aes.d.ts +3 -0
- package/dist/crypto/aes.js +24 -0
- package/dist/crypto/bytes.d.ts +6 -0
- package/dist/crypto/bytes.js +33 -0
- package/dist/crypto/ed25519.d.ts +3 -0
- package/dist/crypto/ed25519.js +85 -0
- package/dist/crypto/hash.d.ts +2 -0
- package/dist/crypto/hash.js +11 -0
- package/dist/crypto/index.d.ts +7 -0
- package/dist/crypto/index.js +10 -0
- package/dist/crypto/json.d.ts +8 -0
- package/dist/crypto/json.js +250 -0
- package/dist/crypto/mlkem.d.ts +12 -0
- package/dist/crypto/mlkem.js +36 -0
- package/dist/crypto/subtle.d.ts +10 -0
- package/dist/crypto/subtle.js +29 -0
- package/dist/crypto/x25519.d.ts +2 -0
- package/dist/crypto/x25519.js +21 -0
- package/dist/folder/index.d.ts +14 -0
- package/dist/folder/index.js +87 -0
- package/dist/harbor/catalogue.d.ts +25 -0
- package/dist/harbor/catalogue.js +93 -0
- package/dist/harbor/dock.d.ts +29 -0
- package/dist/harbor/dock.js +122 -0
- package/dist/harbor/harbor.d.ts +31 -0
- package/dist/harbor/harbor.js +146 -0
- package/dist/harbor/index.d.ts +7 -10
- package/dist/harbor/index.js +9 -18
- package/dist/harbor/package.d.ts +10 -0
- package/dist/harbor/package.js +115 -0
- package/dist/harbor/registry.d.ts +9 -0
- package/dist/harbor/registry.js +78 -0
- package/dist/harbor/root-line.d.ts +9 -0
- package/dist/harbor/root-line.js +36 -0
- package/dist/harbor/terrain.d.ts +9 -0
- package/dist/harbor/terrain.js +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/pointer/bodies.d.ts +26 -0
- package/dist/pointer/bodies.js +64 -0
- package/dist/pointer/index.d.ts +2 -0
- package/dist/pointer/index.js +4 -0
- package/dist/pointer/world.d.ts +35 -0
- package/dist/pointer/world.js +85 -0
- package/dist/quo/door.d.ts +34 -0
- package/dist/quo/door.js +172 -0
- package/dist/quo/index.d.ts +8 -0
- package/dist/quo/index.js +11 -0
- package/dist/quo/invitation.d.ts +7 -0
- package/dist/quo/invitation.js +15 -0
- package/dist/quo/keys.d.ts +40 -0
- package/dist/quo/keys.js +79 -0
- package/dist/quo/payload.d.ts +15 -0
- package/dist/quo/payload.js +55 -0
- package/dist/quo/relations.d.ts +40 -0
- package/dist/quo/relations.js +33 -0
- package/dist/quo/reply.d.ts +13 -0
- package/dist/quo/reply.js +35 -0
- package/dist/quo/seal.d.ts +42 -0
- package/dist/quo/seal.js +78 -0
- package/dist/quo/standing.d.ts +39 -0
- package/dist/quo/standing.js +90 -0
- package/dist/tcp/frame.d.ts +29 -0
- package/dist/tcp/frame.js +74 -0
- package/dist/tcp/index.d.ts +18 -0
- package/dist/tcp/index.js +164 -0
- package/dist/ward/allowance.d.ts +13 -9
- package/dist/ward/allowance.js +28 -67
- package/dist/ward/cells.d.ts +6 -6
- package/dist/ward/cells.js +67 -179
- package/dist/ward/index.d.ts +6 -10
- package/dist/ward/index.js +7 -13
- package/dist/ward/partition.d.ts +44 -60
- package/dist/ward/partition.js +56 -285
- package/dist/ward/pilot.d.ts +8 -0
- package/dist/ward/pilot.js +50 -0
- package/dist/ward/stance.d.ts +27 -20
- package/dist/ward/stance.js +174 -406
- package/dist/ward/ward-being.d.ts +35 -0
- package/dist/ward/ward-being.js +82 -0
- package/dist/ward/ward.d.ts +45 -11
- package/dist/ward/ward.js +239 -357
- package/package.json +24 -31
- package/src/being/being.ts +35 -78
- package/src/being/digest.ts +7 -35
- package/src/being/faculty.ts +73 -0
- package/src/being/index.ts +6 -6
- package/src/being/kind.ts +30 -0
- package/src/being/types.ts +50 -146
- package/src/being/words.ts +31 -0
- package/src/cli/command.ts +188 -0
- package/src/cli/harbor.ts +137 -0
- package/src/cli/main.ts +13 -0
- package/src/contract/index.ts +73 -0
- package/src/crypto/aes.ts +26 -0
- package/src/crypto/bytes.ts +37 -0
- package/src/crypto/ed25519.ts +84 -0
- package/src/crypto/hash.ts +14 -0
- package/src/crypto/index.ts +10 -0
- package/src/crypto/json.ts +241 -0
- package/src/crypto/mlkem.ts +38 -0
- package/src/crypto/subtle.ts +33 -0
- package/src/crypto/x25519.ts +21 -0
- package/src/folder/index.ts +92 -0
- package/src/harbor/catalogue.ts +110 -0
- package/src/harbor/dock.ts +137 -0
- package/src/harbor/harbor.ts +180 -0
- package/src/harbor/index.ts +9 -19
- package/src/harbor/package.ts +131 -0
- package/src/harbor/registry.ts +73 -0
- package/src/harbor/root-line.ts +36 -0
- package/src/harbor/terrain.ts +15 -0
- package/src/index.ts +20 -0
- package/src/pointer/bodies.ts +65 -0
- package/src/pointer/index.ts +4 -0
- package/src/pointer/world.ts +94 -0
- package/src/quo/door.ts +178 -0
- package/src/quo/index.ts +11 -0
- package/src/quo/invitation.ts +15 -0
- package/src/quo/keys.ts +91 -0
- package/src/quo/payload.ts +61 -0
- package/src/quo/relations.ts +62 -0
- package/src/quo/reply.ts +38 -0
- package/src/quo/seal.ts +101 -0
- package/src/quo/standing.ts +111 -0
- package/src/stand/main.ts +20 -0
- package/src/stand/stand.ts +220 -0
- package/src/tcp/frame.ts +78 -0
- package/src/tcp/index.ts +173 -0
- package/src/ward/allowance.ts +37 -75
- package/src/ward/cells.ts +63 -176
- package/src/ward/index.ts +7 -17
- package/src/ward/partition.ts +86 -326
- package/src/ward/pilot.ts +50 -0
- package/src/ward/stance.ts +186 -420
- package/src/ward/ward-being.ts +103 -0
- package/src/ward/ward.ts +256 -359
- package/dist/being/lent.d.ts +0 -32
- package/dist/being/lent.js +0 -72
- package/dist/being/silence.d.ts +0 -12
- package/dist/being/silence.js +0 -41
- package/dist/conformance/assert.d.ts +0 -11
- package/dist/conformance/assert.js +0 -106
- package/dist/conformance/beings.d.ts +0 -199
- package/dist/conformance/beings.js +0 -188
- package/dist/conformance/estate.d.ts +0 -5
- package/dist/conformance/estate.js +0 -388
- package/dist/conformance/index.d.ts +0 -80
- package/dist/conformance/index.js +0 -819
- package/dist/conformance/reach.d.ts +0 -10
- package/dist/conformance/reach.js +0 -72
- package/dist/conformance/store.d.ts +0 -5
- package/dist/conformance/store.js +0 -113
- package/dist/harbor/box.d.ts +0 -121
- package/dist/harbor/box.js +0 -121
- package/dist/harbor/core.d.ts +0 -55
- package/dist/harbor/core.js +0 -662
- package/dist/harbor/dial.d.ts +0 -9
- package/dist/harbor/dial.js +0 -81
- package/dist/harbor/memory.d.ts +0 -29
- package/dist/harbor/memory.js +0 -104
- package/dist/harbor/reach.d.ts +0 -36
- package/dist/harbor/reach.js +0 -199
- package/dist/harbor/store.d.ts +0 -35
- package/dist/harbor/store.js +0 -62
- package/dist/vector/cases.d.ts +0 -42
- package/dist/vector/cases.js +0 -223
- package/dist/vector/index.d.ts +0 -6
- package/dist/vector/index.js +0 -8
- package/dist/vector/stand.d.ts +0 -9
- package/dist/vector/stand.js +0 -77
- package/dist/vector/world.d.ts +0 -144
- package/dist/vector/world.js +0 -209
- package/dist/ward/arithmetic.d.ts +0 -31
- package/dist/ward/arithmetic.js +0 -249
- package/dist/ward/door.d.ts +0 -18
- package/dist/ward/door.js +0 -186
- package/dist/ward/ground.d.ts +0 -29
- package/dist/ward/ground.js +0 -56
- package/dist/ward/heirs.d.ts +0 -13
- package/dist/ward/heirs.js +0 -117
- package/dist/ward/json.d.ts +0 -2
- package/dist/ward/json.js +0 -163
- package/dist/ward/owner.d.ts +0 -13
- package/dist/ward/owner.js +0 -220
- package/dist/ward/seal.d.ts +0 -52
- package/dist/ward/seal.js +0 -150
- package/src/being/lent.ts +0 -72
- package/src/being/silence.ts +0 -46
- package/src/conformance/assert.ts +0 -100
- package/src/conformance/beings.ts +0 -188
- package/src/conformance/estate.ts +0 -412
- package/src/conformance/index.ts +0 -965
- package/src/conformance/reach.ts +0 -83
- package/src/conformance/store.ts +0 -125
- package/src/harbor/box.ts +0 -131
- package/src/harbor/core.ts +0 -699
- package/src/harbor/dial.ts +0 -112
- package/src/harbor/memory.ts +0 -123
- package/src/harbor/reach.ts +0 -221
- package/src/harbor/store.ts +0 -91
- package/src/vector/cases.ts +0 -257
- package/src/vector/index.ts +0 -11
- package/src/vector/stand.ts +0 -76
- package/src/vector/world.ts +0 -232
- package/src/ward/arithmetic.ts +0 -251
- package/src/ward/door.ts +0 -186
- package/src/ward/ground.ts +0 -142
- package/src/ward/heirs.ts +0 -116
- package/src/ward/json.ts +0 -144
- package/src/ward/owner.ts +0 -214
- package/src/ward/seal.ts +0 -178
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Drawn bytes.
|
|
2
|
+
export class Entropy {
|
|
3
|
+
// The same source, as the function `quo/` takes.
|
|
4
|
+
get drawer() {
|
|
5
|
+
return (length) => this.draw(length);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
// The moment, and a wait that ends.
|
|
9
|
+
export class Clock {
|
|
10
|
+
}
|
|
11
|
+
// A harbor's memory as a terrain keeps it: sealed entries it cannot read,
|
|
12
|
+
// in places, each entry by name. Every name is lowercase hex the kit
|
|
13
|
+
// chose, so a body may use it as a file name as it stands.
|
|
14
|
+
export class Memory {
|
|
15
|
+
}
|
|
16
|
+
// The harbor's seed: thirty-two bytes, the same every time. It is the one
|
|
17
|
+
// secret a terrain holds.
|
|
18
|
+
export class Custody {
|
|
19
|
+
}
|
|
20
|
+
// Code, by the name a harbor keeps for it, or null where this loader has
|
|
21
|
+
// none. Code never crosses a door and never lives in a cell, so it is the
|
|
22
|
+
// one thing beside the seed a terrain hands in: a map, an import, a bundle.
|
|
23
|
+
export class Loader {
|
|
24
|
+
}
|
|
25
|
+
// Bytes to a ward pk in; bytes, or null for nothing, out.
|
|
26
|
+
export class Carrier {
|
|
27
|
+
// Where a ward pk is reached, or null to forget it: true when this
|
|
28
|
+
// carrier dials by address and took it. The addresses live in the dock's
|
|
29
|
+
// cells, and the harbor hands them here as it unpacks.
|
|
30
|
+
route(_pk, _at) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const TAG = 16;
|
|
2
|
+
export declare const encrypt: (key: Uint8Array, nonce: Uint8Array, additional: Uint8Array, plaintext: Uint8Array) => Promise<Uint8Array>;
|
|
3
|
+
export declare const decrypt: (key: Uint8Array, nonce: Uint8Array, additional: Uint8Array, ciphertext: Uint8Array) => Promise<Uint8Array | null>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// AES-256-GCM with a sixteen-byte tag at the end of the ciphertext.
|
|
3
|
+
import { own, subtle } from './subtle.js';
|
|
4
|
+
export const TAG = 16;
|
|
5
|
+
const cipher = (key, nonce, additional) => ({
|
|
6
|
+
params: { name: 'AES-GCM', iv: own(nonce), additionalData: own(additional), tagLength: TAG * 8 },
|
|
7
|
+
key: subtle().importKey('raw', own(key), 'AES-GCM', false, ['encrypt', 'decrypt']),
|
|
8
|
+
});
|
|
9
|
+
export const encrypt = async (key, nonce, additional, plaintext) => {
|
|
10
|
+
const c = cipher(key, nonce, additional);
|
|
11
|
+
return new Uint8Array(await subtle().encrypt(c.params, await c.key, own(plaintext)));
|
|
12
|
+
};
|
|
13
|
+
// The plaintext, or null when the ciphertext does not open.
|
|
14
|
+
export const decrypt = async (key, nonce, additional, ciphertext) => {
|
|
15
|
+
if (ciphertext.length < TAG)
|
|
16
|
+
return null;
|
|
17
|
+
const c = cipher(key, nonce, additional);
|
|
18
|
+
try {
|
|
19
|
+
return new Uint8Array(await subtle().decrypt(c.params, await c.key, own(ciphertext)));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const hex: (bytes: Uint8Array) => string;
|
|
2
|
+
export declare const isHex: (text: unknown, length?: number) => text is string;
|
|
3
|
+
export declare const unhex: (text: string) => Uint8Array;
|
|
4
|
+
export declare const concat: (...parts: Uint8Array[]) => Uint8Array;
|
|
5
|
+
export declare const zero: (bytes: Uint8Array) => boolean;
|
|
6
|
+
export declare const utf8: (text: string) => Uint8Array;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Bytes and the one text spelling Quo gives them: hex, two lowercase digits
|
|
3
|
+
// a byte.
|
|
4
|
+
const DIGITS = Array.from({ length: 256 }, (_, at) => at.toString(16).padStart(2, '0'));
|
|
5
|
+
const HEX = /^(?:[0-9a-f]{2})*$/;
|
|
6
|
+
export const hex = (bytes) => {
|
|
7
|
+
let out = '';
|
|
8
|
+
for (const byte of bytes)
|
|
9
|
+
out += DIGITS[byte];
|
|
10
|
+
return out;
|
|
11
|
+
};
|
|
12
|
+
// Hex of `length` bytes, or any length when none is named.
|
|
13
|
+
export const isHex = (text, length) => typeof text === 'string' && HEX.test(text) && (length === undefined || text.length === length * 2);
|
|
14
|
+
export const unhex = (text) => {
|
|
15
|
+
if (!isHex(text))
|
|
16
|
+
throw new TypeError('not hex');
|
|
17
|
+
const out = new Uint8Array(text.length / 2);
|
|
18
|
+
for (let at = 0; at < out.length; at += 1)
|
|
19
|
+
out[at] = parseInt(text.slice(at * 2, at * 2 + 2), 16);
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
export const concat = (...parts) => {
|
|
23
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
24
|
+
let at = 0;
|
|
25
|
+
for (const part of parts) {
|
|
26
|
+
out.set(part, at);
|
|
27
|
+
at += part.length;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
};
|
|
31
|
+
export const zero = (bytes) => bytes.every((b) => b === 0);
|
|
32
|
+
const encoder = new TextEncoder();
|
|
33
|
+
export const utf8 = (text) => encoder.encode(text);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const ed25519Public: (secret: Uint8Array) => Promise<Uint8Array>;
|
|
2
|
+
export declare const sign: (secret: Uint8Array, message: Uint8Array) => Promise<Uint8Array>;
|
|
3
|
+
export declare const verify: (pk: Uint8Array, message: Uint8Array, signature: Uint8Array) => Promise<boolean>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Ed25519 signs, as RFC 8032 defines it, and verifies with the cofactorless
|
|
3
|
+
// check. The spec names exactly where a check fails, and Web Crypto promises
|
|
4
|
+
// none of the decoding rules, so they are held here before Web Crypto runs:
|
|
5
|
+
//
|
|
6
|
+
// a signature of any length but sixty-four
|
|
7
|
+
// an `s` at or above the group order
|
|
8
|
+
// an `R` that does not decode, or whose bytes are not its point's encoding
|
|
9
|
+
// a public key that does not decode, or whose y is at or above the prime
|
|
10
|
+
// a public key of small order, in any spelling
|
|
11
|
+
import { importSecret, own, publicOf, subtle } from './subtle.js';
|
|
12
|
+
const P = 2n ** 255n - 19n;
|
|
13
|
+
const L = 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
14
|
+
const mod = (a) => ((a % P) + P) % P;
|
|
15
|
+
const power = (base, exponent) => {
|
|
16
|
+
let out = 1n;
|
|
17
|
+
let b = mod(base);
|
|
18
|
+
for (let e = exponent; e > 0n; e >>= 1n) {
|
|
19
|
+
if (e & 1n)
|
|
20
|
+
out = (out * b) % P;
|
|
21
|
+
b = (b * b) % P;
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
};
|
|
25
|
+
const D = mod(-121665n * power(121666n, P - 2n));
|
|
26
|
+
const ROOT_MINUS_ONE = power(2n, (P - 1n) / 4n);
|
|
27
|
+
const little = (bytes) => bytes.reduceRight((n, b) => (n << 8n) | BigInt(b), 0n);
|
|
28
|
+
// A point from its thirty-two bytes, or null where RFC 8032 section 5.1.3
|
|
29
|
+
// refuses it, the sign bit set on x = 0 included.
|
|
30
|
+
const decode = (bytes) => {
|
|
31
|
+
if (bytes.length !== 32)
|
|
32
|
+
return null;
|
|
33
|
+
const sign = (bytes[31] & 0x80) !== 0;
|
|
34
|
+
const y = little(bytes) & ((1n << 255n) - 1n);
|
|
35
|
+
if (y >= P)
|
|
36
|
+
return null;
|
|
37
|
+
const u = mod(y * y - 1n);
|
|
38
|
+
const v = mod(D * y * y + 1n);
|
|
39
|
+
let x = mod(u * power(v, 3n) * power(u * power(v, 7n), (P - 5n) / 8n));
|
|
40
|
+
const check = mod(v * x * x);
|
|
41
|
+
if (check === mod(-u))
|
|
42
|
+
x = mod(x * ROOT_MINUS_ONE);
|
|
43
|
+
else if (check !== u)
|
|
44
|
+
return null;
|
|
45
|
+
if (x === 0n && sign)
|
|
46
|
+
return null;
|
|
47
|
+
if ((x & 1n) !== (sign ? 1n : 0n))
|
|
48
|
+
x = mod(-x);
|
|
49
|
+
return { x, y, z: 1n, t: mod(x * y) };
|
|
50
|
+
};
|
|
51
|
+
const add = (a, b) => {
|
|
52
|
+
const A = mod((a.y - a.x) * (b.y - b.x));
|
|
53
|
+
const B = mod((a.y + a.x) * (b.y + b.x));
|
|
54
|
+
const C = mod(2n * D * a.t * b.t);
|
|
55
|
+
const E = mod(2n * a.z * b.z);
|
|
56
|
+
const [e, f, g, h] = [B - A, E - C, E + C, B + A];
|
|
57
|
+
return { x: mod(e * f), y: mod(g * h), z: mod(f * g), t: mod(e * h) };
|
|
58
|
+
};
|
|
59
|
+
// Small order: eight times the point is the identity.
|
|
60
|
+
const smallOrder = (p) => {
|
|
61
|
+
let q = p;
|
|
62
|
+
for (let i = 0; i < 3; i += 1)
|
|
63
|
+
q = add(q, q);
|
|
64
|
+
return q.x === 0n && q.y === q.z;
|
|
65
|
+
};
|
|
66
|
+
export const ed25519Public = async (secret) => publicOf(await importSecret('Ed25519', secret, ['sign']));
|
|
67
|
+
export const sign = async (secret, message) => new Uint8Array(await subtle().sign('Ed25519', await importSecret('Ed25519', secret, ['sign']), own(message)));
|
|
68
|
+
export const verify = async (pk, message, signature) => {
|
|
69
|
+
if (signature.length !== 64)
|
|
70
|
+
return false;
|
|
71
|
+
const key = decode(pk);
|
|
72
|
+
if (key === null || smallOrder(key))
|
|
73
|
+
return false;
|
|
74
|
+
if (decode(signature.subarray(0, 32)) === null)
|
|
75
|
+
return false;
|
|
76
|
+
if (little(signature.subarray(32)) >= L)
|
|
77
|
+
return false;
|
|
78
|
+
try {
|
|
79
|
+
const imported = await subtle().importKey('raw', own(pk), { name: 'Ed25519' }, false, ['verify']);
|
|
80
|
+
return await subtle().verify('Ed25519', imported, own(signature), own(message));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SHA-256 hashes, and HKDF-SHA-256 derives under the zero-length salt, with
|
|
3
|
+
// the label's ASCII bytes as `info`.
|
|
4
|
+
import { own, subtle } from './subtle.js';
|
|
5
|
+
const SALT = new Uint8Array(0);
|
|
6
|
+
export const sha256 = async (bytes) => new Uint8Array(await subtle().digest('SHA-256', own(bytes)));
|
|
7
|
+
export const hkdf = async (ikm, info, length) => {
|
|
8
|
+
const material = await subtle().importKey('raw', own(ikm), 'HKDF', false, ['deriveBits']);
|
|
9
|
+
const bits = await subtle().deriveBits({ name: 'HKDF', hash: 'SHA-256', salt: SALT, info: own(info) }, material, length * 8);
|
|
10
|
+
return new Uint8Array(bits);
|
|
11
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { concat, hex, isHex, unhex, utf8, zero } from './bytes.ts';
|
|
2
|
+
export { hkdf, sha256 } from './hash.ts';
|
|
3
|
+
export { isCount, readObject, readValue, writeValue, type Fields, type Json } from './json.ts';
|
|
4
|
+
export { decrypt, encrypt, TAG } from './aes.ts';
|
|
5
|
+
export { agree, x25519Public } from './x25519.ts';
|
|
6
|
+
export { ed25519Public, sign, verify } from './ed25519.ts';
|
|
7
|
+
export { CIPHERTEXT, decapsulate, EK, encapsulate, lockFrom, type Lock } from './mlkem.ts';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// The six algorithms Quo names, and bytes. Arithmetic only: no label, no
|
|
3
|
+
// key of Quo's, no decision.
|
|
4
|
+
export { concat, hex, isHex, unhex, utf8, zero } from './bytes.js';
|
|
5
|
+
export { hkdf, sha256 } from './hash.js';
|
|
6
|
+
export { isCount, readObject, readValue, writeValue } from './json.js';
|
|
7
|
+
export { decrypt, encrypt, TAG } from './aes.js';
|
|
8
|
+
export { agree, x25519Public } from './x25519.js';
|
|
9
|
+
export { ed25519Public, sign, verify } from './ed25519.js';
|
|
10
|
+
export { CIPHERTEXT, decapsulate, EK, encapsulate, lockFrom } from './mlkem.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
2
|
+
[key: string]: Json;
|
|
3
|
+
};
|
|
4
|
+
export type Fields = ReadonlyMap<string, string>;
|
|
5
|
+
export declare const readObject: (input: Uint8Array | string) => Fields | null;
|
|
6
|
+
export declare const readValue: (input: Uint8Array | string, depth: number) => Json | undefined;
|
|
7
|
+
export declare const writeValue: (value: unknown, depth: number) => string | undefined;
|
|
8
|
+
export declare const isCount: (literal: string) => boolean;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
2
|
+
const NUMBER = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/y;
|
|
3
|
+
const ESCAPE = /\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})/y;
|
|
4
|
+
const LONE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
5
|
+
const LITERALS = ['true', 'false', 'null'];
|
|
6
|
+
const skip = (s, at) => {
|
|
7
|
+
while (at < s.length && ' \t\n\r'.includes(s[at]))
|
|
8
|
+
at += 1;
|
|
9
|
+
return at;
|
|
10
|
+
};
|
|
11
|
+
// The end of the string opening at `at`, or -1.
|
|
12
|
+
const stringEnd = (s, at) => {
|
|
13
|
+
if (s[at] !== '"')
|
|
14
|
+
return -1;
|
|
15
|
+
for (at += 1; at < s.length;) {
|
|
16
|
+
const c = s.charCodeAt(at);
|
|
17
|
+
if (c === 0x22)
|
|
18
|
+
return at + 1;
|
|
19
|
+
if (c < 0x20)
|
|
20
|
+
return -1;
|
|
21
|
+
if (c !== 0x5c) {
|
|
22
|
+
at += 1;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
ESCAPE.lastIndex = at;
|
|
26
|
+
if (!ESCAPE.test(s))
|
|
27
|
+
return -1;
|
|
28
|
+
at = ESCAPE.lastIndex;
|
|
29
|
+
}
|
|
30
|
+
return -1;
|
|
31
|
+
};
|
|
32
|
+
const scalarEnd = (s, at) => {
|
|
33
|
+
if (s[at] === '"')
|
|
34
|
+
return stringEnd(s, at);
|
|
35
|
+
const literal = LITERALS.find((word) => s.startsWith(word, at));
|
|
36
|
+
if (literal)
|
|
37
|
+
return at + literal.length;
|
|
38
|
+
NUMBER.lastIndex = at;
|
|
39
|
+
return NUMBER.test(s) ? NUMBER.lastIndex : -1;
|
|
40
|
+
};
|
|
41
|
+
// A key, its colon and the space after it: the start of the key's value, or -1.
|
|
42
|
+
const keyEnd = (s, at) => {
|
|
43
|
+
const end = stringEnd(s, at);
|
|
44
|
+
if (end < 0)
|
|
45
|
+
return -1;
|
|
46
|
+
const colon = skip(s, end);
|
|
47
|
+
return s[colon] === ':' ? skip(s, colon + 1) : -1;
|
|
48
|
+
};
|
|
49
|
+
// The end of the value starting at `at`, held to the grammar at any depth
|
|
50
|
+
// without recursion, or -1.
|
|
51
|
+
const valueEnd = (s, at) => {
|
|
52
|
+
const open = [];
|
|
53
|
+
for (;;) {
|
|
54
|
+
const c = s[at];
|
|
55
|
+
if (c === '{' || c === '[') {
|
|
56
|
+
at = skip(s, at + 1);
|
|
57
|
+
if (s[at] === (c === '{' ? '}' : ']'))
|
|
58
|
+
at += 1;
|
|
59
|
+
else {
|
|
60
|
+
open.push(c);
|
|
61
|
+
if (c === '{' && (at = keyEnd(s, at)) < 0)
|
|
62
|
+
return -1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if ((at = scalarEnd(s, at)) < 0)
|
|
67
|
+
return -1;
|
|
68
|
+
for (;;) {
|
|
69
|
+
if (open.length === 0)
|
|
70
|
+
return at;
|
|
71
|
+
at = skip(s, at);
|
|
72
|
+
const top = open.at(-1);
|
|
73
|
+
if (s[at] === ',') {
|
|
74
|
+
at = skip(s, at + 1);
|
|
75
|
+
if (top === '{' && (at = keyEnd(s, at)) < 0)
|
|
76
|
+
return -1;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
if (s[at] !== (top === '{' ? '}' : ']'))
|
|
80
|
+
return -1;
|
|
81
|
+
open.pop();
|
|
82
|
+
at += 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const text = (input) => {
|
|
87
|
+
if (typeof input === 'string')
|
|
88
|
+
return input;
|
|
89
|
+
try {
|
|
90
|
+
return decoder.decode(input);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
export const readObject = (input) => {
|
|
97
|
+
const s = text(input);
|
|
98
|
+
if (s === null)
|
|
99
|
+
return null;
|
|
100
|
+
const fields = new Map();
|
|
101
|
+
let at = skip(s, 0);
|
|
102
|
+
if (s[at] !== '{')
|
|
103
|
+
return null;
|
|
104
|
+
at = skip(s, at + 1);
|
|
105
|
+
if (s[at] === '}')
|
|
106
|
+
return skip(s, at + 1) === s.length ? fields : null;
|
|
107
|
+
for (;;) {
|
|
108
|
+
const keyStart = at;
|
|
109
|
+
const valueStart = keyEnd(s, at);
|
|
110
|
+
if (valueStart < 0)
|
|
111
|
+
return null;
|
|
112
|
+
const key = JSON.parse(s.slice(keyStart, stringEnd(s, keyStart)));
|
|
113
|
+
if (fields.has(key))
|
|
114
|
+
return null;
|
|
115
|
+
const end = valueEnd(s, valueStart);
|
|
116
|
+
if (end < 0)
|
|
117
|
+
return null;
|
|
118
|
+
fields.set(key, s.slice(valueStart, end));
|
|
119
|
+
at = skip(s, end);
|
|
120
|
+
if (s[at] === '}')
|
|
121
|
+
return skip(s, at + 1) === s.length ? fields : null;
|
|
122
|
+
if (s[at] !== ',')
|
|
123
|
+
return null;
|
|
124
|
+
at = skip(s, at + 1);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
// A number text a double holds as written: a fraction, or a whole number
|
|
128
|
+
// the double it reads as is exactly, never past the largest double, never
|
|
129
|
+
// a value that rounds to zero, never minus zero.
|
|
130
|
+
const exactNumber = (literal) => {
|
|
131
|
+
const [, sign, whole, frac = '', exp = '0'] = /^(-?)(0|[1-9]\d*)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(literal);
|
|
132
|
+
const v = Number(literal);
|
|
133
|
+
if (!Number.isFinite(v))
|
|
134
|
+
return false;
|
|
135
|
+
const digits = (whole + frac).replace(/^0+/, '');
|
|
136
|
+
if (v === 0)
|
|
137
|
+
return digits === '' && sign === '';
|
|
138
|
+
const trimmed = digits.replace(/0+$/, '');
|
|
139
|
+
const e = BigInt(exp) - BigInt(frac.length) + BigInt(digits.length - trimmed.length);
|
|
140
|
+
if (e < 0n)
|
|
141
|
+
return true;
|
|
142
|
+
return BigInt(trimmed) * 10n ** e === (v < 0 ? -BigInt(v) : BigInt(v)) || literal === String(v);
|
|
143
|
+
};
|
|
144
|
+
const depthOf = (s) => {
|
|
145
|
+
let depth = 0;
|
|
146
|
+
let deepest = 0;
|
|
147
|
+
for (let at = 0; at < s.length; at += 1) {
|
|
148
|
+
const c = s[at];
|
|
149
|
+
if (c === '"')
|
|
150
|
+
at = stringEnd(s, at) - 1;
|
|
151
|
+
else if (c === '{' || c === '[')
|
|
152
|
+
deepest = Math.max(deepest, (depth += 1));
|
|
153
|
+
else if (c === '}' || c === ']')
|
|
154
|
+
depth -= 1;
|
|
155
|
+
}
|
|
156
|
+
return deepest;
|
|
157
|
+
};
|
|
158
|
+
// Parses a text already held to the grammar and to the depth bound.
|
|
159
|
+
const parse = (s) => {
|
|
160
|
+
let at = 0;
|
|
161
|
+
const value = () => {
|
|
162
|
+
at = skip(s, at);
|
|
163
|
+
const c = s[at];
|
|
164
|
+
if (c === '{' || c === '[') {
|
|
165
|
+
const object = c === '{';
|
|
166
|
+
const out = object ? {} : [];
|
|
167
|
+
at = skip(s, at + 1);
|
|
168
|
+
if (s[at] === (object ? '}' : ']')) {
|
|
169
|
+
at += 1;
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
for (;;) {
|
|
173
|
+
if (object) {
|
|
174
|
+
const key = str();
|
|
175
|
+
if (Object.hasOwn(out, key))
|
|
176
|
+
throw new Error('two keys of one name');
|
|
177
|
+
at = skip(s, at) + 1;
|
|
178
|
+
Object.defineProperty(out, key, { value: value(), enumerable: true, writable: true, configurable: true });
|
|
179
|
+
}
|
|
180
|
+
else
|
|
181
|
+
out.push(value());
|
|
182
|
+
at = skip(s, at);
|
|
183
|
+
if (s[at++] !== ',')
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (c === '"')
|
|
188
|
+
return str();
|
|
189
|
+
const end = scalarEnd(s, at);
|
|
190
|
+
const literal = s.slice(at, end);
|
|
191
|
+
at = end;
|
|
192
|
+
if (LITERALS.includes(literal))
|
|
193
|
+
return JSON.parse(literal);
|
|
194
|
+
if (!exactNumber(literal))
|
|
195
|
+
throw new Error('a number no double holds as written');
|
|
196
|
+
return Number(literal);
|
|
197
|
+
};
|
|
198
|
+
const str = () => {
|
|
199
|
+
at = skip(s, at);
|
|
200
|
+
const end = stringEnd(s, at);
|
|
201
|
+
const out = JSON.parse(s.slice(at, end));
|
|
202
|
+
if (LONE.test(out))
|
|
203
|
+
throw new Error('a lone surrogate');
|
|
204
|
+
at = end;
|
|
205
|
+
return out;
|
|
206
|
+
};
|
|
207
|
+
return value();
|
|
208
|
+
};
|
|
209
|
+
export const readValue = (input, depth) => {
|
|
210
|
+
const s = text(input);
|
|
211
|
+
if (s === null)
|
|
212
|
+
return undefined;
|
|
213
|
+
const start = skip(s, 0);
|
|
214
|
+
const end = valueEnd(s, start);
|
|
215
|
+
if (end < 0 || skip(s, end) !== s.length || depthOf(s) > depth)
|
|
216
|
+
return undefined;
|
|
217
|
+
try {
|
|
218
|
+
return parse(s);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
// A value as JSON text, or undefined for one that JSON would drop, rewrite
|
|
225
|
+
// or refuse: anything but null, a boolean, a finite number other than minus
|
|
226
|
+
// zero, well-formed text, a list with no holes, or a plain object of own
|
|
227
|
+
// string keys, nested no deeper than `depth`, with no cycle.
|
|
228
|
+
export const writeValue = (value, depth) => {
|
|
229
|
+
const fits = (v, room, path) => {
|
|
230
|
+
if (v === null || typeof v === 'boolean')
|
|
231
|
+
return true;
|
|
232
|
+
if (typeof v === 'string')
|
|
233
|
+
return !LONE.test(v);
|
|
234
|
+
if (typeof v === 'number')
|
|
235
|
+
return Number.isFinite(v) && !Object.is(v, -0);
|
|
236
|
+
if (typeof v !== 'object' || room === 0 || path.has(v))
|
|
237
|
+
return false;
|
|
238
|
+
path.add(v);
|
|
239
|
+
const ok = Array.isArray(v)
|
|
240
|
+
? Object.keys(v).length === v.length && v.every((x) => fits(x, room - 1, path))
|
|
241
|
+
: [Object.prototype, null].includes(Object.getPrototypeOf(v)) &&
|
|
242
|
+
Object.getOwnPropertySymbols(v).length === 0 &&
|
|
243
|
+
Object.entries(Object.getOwnPropertyDescriptors(v)).every(([k, d]) => !LONE.test(k) && d.enumerable === true && 'value' in d && fits(d.value, room - 1, path));
|
|
244
|
+
path.delete(v);
|
|
245
|
+
return ok;
|
|
246
|
+
};
|
|
247
|
+
return fits(value, depth, new Set()) ? JSON.stringify(value) : undefined;
|
|
248
|
+
};
|
|
249
|
+
// A count number: no sign, no fraction, no exponent, from 1 to 2^53 - 1.
|
|
250
|
+
export const isCount = (literal) => /^[1-9]\d{0,15}$/.test(literal) && Number(literal) <= Number.MAX_SAFE_INTEGER;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const EK = 1184;
|
|
2
|
+
export declare const CIPHERTEXT = 1088;
|
|
3
|
+
export type Lock = {
|
|
4
|
+
ek: Uint8Array;
|
|
5
|
+
dk: Uint8Array;
|
|
6
|
+
};
|
|
7
|
+
export declare const lockFrom: (d: Uint8Array, z: Uint8Array) => Lock;
|
|
8
|
+
export declare const encapsulate: (ek: Uint8Array, m: Uint8Array) => {
|
|
9
|
+
ciphertext: Uint8Array;
|
|
10
|
+
shared: Uint8Array;
|
|
11
|
+
} | null;
|
|
12
|
+
export declare const decapsulate: (dk: Uint8Array, ciphertext: Uint8Array) => Uint8Array | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// ML-KEM-768 of FIPS 203 encapsulates. Web Crypto has none on every terrain,
|
|
3
|
+
// so it comes from `@noble/post-quantum`.
|
|
4
|
+
import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';
|
|
5
|
+
export const EK = 1184;
|
|
6
|
+
export const CIPHERTEXT = 1088;
|
|
7
|
+
// KeyGen_internal(d, z): the lock from sixty-four drawn bytes, d then z.
|
|
8
|
+
export const lockFrom = (d, z) => {
|
|
9
|
+
const { publicKey, secretKey } = ml_kem768.keygen(Uint8Array.from([...d, ...z]));
|
|
10
|
+
return { ek: publicKey, dk: secretKey };
|
|
11
|
+
};
|
|
12
|
+
// Encaps_internal(ek, m), or null for an encapsulation key the modulus check
|
|
13
|
+
// of FIPS 203 section 7.2 refuses.
|
|
14
|
+
export const encapsulate = (ek, m) => {
|
|
15
|
+
if (ek.length !== EK)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const { cipherText, sharedSecret } = ml_kem768.encapsulate(ek, m);
|
|
19
|
+
return { ciphertext: cipherText, shared: sharedSecret };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// Decapsulation is implicit rejection: a ciphertext for another lock gives a
|
|
26
|
+
// shared secret that opens nothing.
|
|
27
|
+
export const decapsulate = (dk, ciphertext) => {
|
|
28
|
+
if (ciphertext.length !== CIPHERTEXT)
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
return ml_kem768.decapsulate(ciphertext, dk);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const subtle: () => SubtleCrypto;
|
|
2
|
+
export type Bytes = Uint8Array<ArrayBuffer>;
|
|
3
|
+
export declare const own: (bytes: Uint8Array) => Bytes;
|
|
4
|
+
declare const PKCS8: {
|
|
5
|
+
readonly Ed25519: '302e020100300506032b657004220420';
|
|
6
|
+
readonly X25519: '302e020100300506032b656e04220420';
|
|
7
|
+
};
|
|
8
|
+
export declare const importSecret: (curve: keyof typeof PKCS8, secret: Uint8Array, uses: KeyUsage[]) => Promise<CryptoKey>;
|
|
9
|
+
export declare const publicOf: (key: CryptoKey) => Promise<Uint8Array>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Web Crypto, read at every use. A page on a plain http origin has `crypto`
|
|
3
|
+
// without `subtle`, and a terrain may install it after this module loads.
|
|
4
|
+
export const subtle = () => {
|
|
5
|
+
const found = globalThis.crypto?.subtle;
|
|
6
|
+
if (!found)
|
|
7
|
+
throw new Error('this terrain has no crypto.subtle: a ward needs a secure context');
|
|
8
|
+
return found;
|
|
9
|
+
};
|
|
10
|
+
// Web Crypto takes a buffer it owns, so every input is copied into one.
|
|
11
|
+
export const own = (bytes) => Uint8Array.from(bytes);
|
|
12
|
+
// The PKCS#8 wrapping of a thirty-two byte secret, which is the whole of it
|
|
13
|
+
// for both curves.
|
|
14
|
+
const PKCS8 = { Ed25519: '302e020100300506032b657004220420', X25519: '302e020100300506032b656e04220420' };
|
|
15
|
+
export const importSecret = (curve, secret, uses) => {
|
|
16
|
+
if (secret.length !== 32)
|
|
17
|
+
throw new TypeError('a secret is thirty-two bytes');
|
|
18
|
+
const prefix = Uint8Array.from(PKCS8[curve].match(/../g), (b) => parseInt(b, 16));
|
|
19
|
+
const wrapped = new Uint8Array(48);
|
|
20
|
+
wrapped.set(prefix);
|
|
21
|
+
wrapped.set(secret, 16);
|
|
22
|
+
return subtle().importKey('pkcs8', wrapped, { name: curve }, true, uses);
|
|
23
|
+
};
|
|
24
|
+
// Web Crypto gives a private key's public half only through a JWK.
|
|
25
|
+
export const publicOf = async (key) => {
|
|
26
|
+
const { x } = await subtle().exportKey('jwk', key);
|
|
27
|
+
const binary = atob(x.replaceAll('-', '+').replaceAll('_', '/'));
|
|
28
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
29
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// X25519 agrees. A secret is thirty-two bytes, clamped by the function and
|
|
3
|
+
// never before.
|
|
4
|
+
import { zero } from './bytes.js';
|
|
5
|
+
import { importSecret, own, publicOf, subtle } from './subtle.js';
|
|
6
|
+
export const x25519Public = async (secret) => publicOf(await importSecret('X25519', secret, ['deriveBits']));
|
|
7
|
+
// The agreement, or null for a public key that takes no seal: one whose
|
|
8
|
+
// agreement is thirty-two zero bytes, or one the terrain refuses to read.
|
|
9
|
+
export const agree = async (secret, pk) => {
|
|
10
|
+
if (pk.length !== 32)
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const mine = await importSecret('X25519', secret, ['deriveBits']);
|
|
14
|
+
const theirs = await subtle().importKey('raw', own(pk), { name: 'X25519' }, false, []);
|
|
15
|
+
const shared = new Uint8Array(await subtle().deriveBits({ name: 'X25519', public: theirs }, mine, 256));
|
|
16
|
+
return zero(shared) ? null : shared;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Custody, Memory, type Entropy } from '../contract/index.ts';
|
|
2
|
+
export declare class FolderMemory extends Memory {
|
|
3
|
+
#private;
|
|
4
|
+
readonly root: string;
|
|
5
|
+
constructor(root: string);
|
|
6
|
+
read(place: string): Promise<Map<string, Uint8Array>>;
|
|
7
|
+
write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare class FolderCustody extends Custody {
|
|
10
|
+
#private;
|
|
11
|
+
readonly root: string;
|
|
12
|
+
constructor(root: string, entropy: Entropy);
|
|
13
|
+
seed(): Promise<Uint8Array>;
|
|
14
|
+
}
|