nervur 0.19.2 → 0.20.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 +66 -101
- package/dist/being/being.d.ts +7 -15
- package/dist/being/being.js +16 -67
- package/dist/being/digest.d.ts +1 -1
- package/dist/being/digest.js +7 -27
- package/dist/being/faculty.d.ts +9 -0
- package/dist/being/faculty.js +68 -0
- package/dist/being/index.d.ts +4 -5
- package/dist/being/index.js +5 -5
- package/dist/being/types.d.ts +35 -62
- package/dist/being/types.js +3 -57
- package/dist/being/words.d.ts +13 -0
- package/dist/being/words.js +14 -0
- package/dist/contract/index.d.ts +26 -0
- package/dist/contract/index.js +19 -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 +13 -0
- package/dist/folder/index.js +125 -0
- package/dist/harbor/dock.d.ts +19 -0
- package/dist/harbor/dock.js +92 -0
- package/dist/harbor/harbor.d.ts +22 -0
- package/dist/harbor/harbor.js +84 -0
- package/dist/harbor/index.d.ts +4 -10
- package/dist/harbor/index.js +5 -18
- package/dist/harbor/registry.d.ts +7 -0
- package/dist/harbor/registry.js +13 -0
- package/dist/harbor/terrain.d.ts +8 -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 +20 -0
- package/dist/pointer/bodies.js +45 -0
- package/dist/pointer/index.d.ts +2 -0
- package/dist/pointer/index.js +4 -0
- package/dist/pointer/world.d.ts +34 -0
- package/dist/pointer/world.js +76 -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/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 +5 -10
- package/dist/ward/index.js +6 -13
- package/dist/ward/partition.d.ts +44 -60
- package/dist/ward/partition.js +56 -285
- package/dist/ward/stance.d.ts +29 -20
- package/dist/ward/stance.js +173 -406
- package/dist/ward/ward-being.d.ts +33 -0
- package/dist/ward/ward-being.js +76 -0
- package/dist/ward/ward.d.ts +43 -11
- package/dist/ward/ward.js +215 -363
- package/package.json +19 -33
- package/src/being/being.ts +31 -78
- package/src/being/digest.ts +7 -35
- package/src/being/faculty.ts +66 -0
- package/src/being/index.ts +5 -6
- package/src/being/types.ts +47 -145
- package/src/being/words.ts +31 -0
- package/src/contract/index.ts +44 -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 +134 -0
- package/src/harbor/dock.ts +101 -0
- package/src/harbor/harbor.ts +105 -0
- package/src/harbor/index.ts +5 -19
- package/src/harbor/registry.ts +20 -0
- package/src/harbor/terrain.ts +13 -0
- package/src/index.ts +20 -0
- package/src/pointer/bodies.ts +47 -0
- package/src/pointer/index.ts +4 -0
- package/src/pointer/world.ts +91 -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 +19 -0
- package/src/stand/stand.ts +178 -0
- package/src/ward/allowance.ts +37 -75
- package/src/ward/cells.ts +63 -176
- package/src/ward/index.ts +6 -17
- package/src/ward/partition.ts +86 -326
- package/src/ward/stance.ts +185 -420
- package/src/ward/ward-being.ts +97 -0
- package/src/ward/ward.ts +229 -363
- 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
package/dist/being/types.js
CHANGED
|
@@ -1,58 +1,4 @@
|
|
|
1
|
-
// The ids the ward speaks under, and no being may mint. An asker id is hers:
|
|
2
|
-
// she chooses the strings, and the ward has no business in her namespace. But
|
|
3
|
-
// the ward must sometimes stand at her door itself — its describe asks every
|
|
4
|
-
// being under OWNER — and an id it shares with one of her occupants is two
|
|
5
|
-
// different parties wearing one name. She could not tell them apart, and the
|
|
6
|
-
// ward would shape its describe through an occupant's gate.
|
|
7
|
-
//
|
|
8
|
-
// So the ward's words are shouted, and refused at the mint. A word can only
|
|
9
|
-
// be reserved before anyone has used it: a partition already holding an
|
|
10
|
-
// occupant of that name could never have it taken back. PUBLIC guards nothing
|
|
11
|
-
// today — a public asker is {} and carries no id at all — and that is exactly
|
|
12
|
-
// why it is claimed now, while claiming it is free.
|
|
13
1
|
export const OWNER = 'OWNER';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// who hands one on sends it as args, and args are values; this is the one
|
|
18
|
-
// place that is spelled, so nobody spells it with a cast. Absent stays absent:
|
|
19
|
-
// an invitation to a public being carries no heir and no secret, and a key
|
|
20
|
-
// present and undefined is not the same object once JSON has been through it.
|
|
21
|
-
export const invitationArgs = (inv) => ({
|
|
22
|
-
ward: inv.ward,
|
|
23
|
-
...(inv.heir !== undefined ? { heir: inv.heir } : {}),
|
|
24
|
-
...(inv.secret !== undefined ? { secret: inv.secret } : {}),
|
|
25
|
-
});
|
|
26
|
-
// Whether a value that arrived is an invitation. The shape is the spec's, so
|
|
27
|
-
// the reading of it is too, and it is one reading: a form that
|
|
28
|
-
// asks a guest for one, a lent being handed one to wake her maker by, and a
|
|
29
|
-
// shell that finds one in a link all ask the same question, and a kinder
|
|
30
|
-
// answer in one of them is a value that fails at a door instead of at the
|
|
31
|
-
// edge it came in by. A ward pk is a hundred and twenty-eight lowercase hex,
|
|
32
|
-
// and a heir comes with its secret or neither comes: an invitation to a
|
|
33
|
-
// public being carries no heir, and a heir with no secret opens nothing.
|
|
34
|
-
export const isInvitation = (v) => {
|
|
35
|
-
if (v === null || typeof v !== 'object' || Array.isArray(v))
|
|
36
|
-
return false;
|
|
37
|
-
const o = v;
|
|
38
|
-
if (typeof o.ward !== 'string' || !/^[0-9a-f]{128}$/.test(o.ward))
|
|
39
|
-
return false;
|
|
40
|
-
const heir = typeof o.heir === 'string', secret = typeof o.secret === 'string';
|
|
41
|
-
return (heir && secret) || (!heir && !secret && !('heir' in o) && !('secret' in o));
|
|
42
|
-
};
|
|
43
|
-
// Whether what came back from an empty ask is a blueprint. Every describe on
|
|
44
|
-
// the far side of a door is somebody else's code, so nothing may be written
|
|
45
|
-
// into a standing's record as a blueprint without being read as one first: a
|
|
46
|
-
// side walks `asks` by name, and a list of anything else, or no list at all,
|
|
47
|
-
// is a side that breaks on a far ward's answer. The asks are checked to the
|
|
48
|
-
// depth a side reads them, a name and an input each, and no further: what a
|
|
49
|
-
// far being puts beside those is hers.
|
|
50
|
-
export const isBlueprint = (v) => {
|
|
51
|
-
if (v === null || typeof v !== 'object' || Array.isArray(v))
|
|
52
|
-
return false;
|
|
53
|
-
const { asks, notes } = v;
|
|
54
|
-
if (!Array.isArray(asks) || notes === undefined)
|
|
55
|
-
return false;
|
|
56
|
-
return asks.every((a) => a !== null && typeof a === 'object' && !Array.isArray(a) && typeof a.name === 'string' && a.input !== null && typeof a.input === 'object' && !Array.isArray(a.input));
|
|
57
|
-
};
|
|
58
|
-
export const WORD_KEY = Symbol.for('quo.word');
|
|
2
|
+
// The ward-being's key in her ward, and so the id every being she makes
|
|
3
|
+
// knows her by.
|
|
4
|
+
export const WARD = 'ward';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Word as DoorWord } from '../quo/index.ts';
|
|
2
|
+
export declare const silence: unique symbol;
|
|
3
|
+
export type Silence = typeof silence;
|
|
4
|
+
export declare const isSilence: (x: unknown) => x is Silence;
|
|
5
|
+
export type WordName = DoorWord | 'unreached' | 'late' | 'dropped' | 'invitation';
|
|
6
|
+
declare const KEY: unique symbol;
|
|
7
|
+
export type Word<W extends WordName = WordName> = {
|
|
8
|
+
readonly [KEY]: W;
|
|
9
|
+
};
|
|
10
|
+
export declare const word: <W extends WordName>(name: W) => Word<W>;
|
|
11
|
+
export declare const isWord: (x: unknown) => x is Word;
|
|
12
|
+
export declare const told: (x: unknown) => unknown;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const silence = Symbol.for('nervur.silence');
|
|
2
|
+
export const isSilence = (x) => x === silence;
|
|
3
|
+
const KEY = Symbol.for('nervur.word');
|
|
4
|
+
const made = new Map();
|
|
5
|
+
export const word = (name) => {
|
|
6
|
+
let w = made.get(name);
|
|
7
|
+
if (!w)
|
|
8
|
+
made.set(name, (w = Object.freeze({ [KEY]: name })));
|
|
9
|
+
return w;
|
|
10
|
+
};
|
|
11
|
+
export const isWord = (x) => typeof x === 'object' && x !== null && typeof x[KEY] === 'string';
|
|
12
|
+
// A word by its name, and anything else as it is, so one comparison says
|
|
13
|
+
// which answer came back.
|
|
14
|
+
export const told = (x) => (isWord(x) ? x[KEY] : x);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Draw } from '../quo/index.ts';
|
|
2
|
+
export declare abstract class Entropy {
|
|
3
|
+
abstract draw(length: number): Uint8Array;
|
|
4
|
+
get drawer(): Draw;
|
|
5
|
+
}
|
|
6
|
+
export declare abstract class Clock {
|
|
7
|
+
abstract now(): number;
|
|
8
|
+
abstract wait(ms: number): {
|
|
9
|
+
done: Promise<void>;
|
|
10
|
+
cancel(): void;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface Rows {
|
|
14
|
+
readonly rows: Record<string, unknown>;
|
|
15
|
+
told(row: string): void;
|
|
16
|
+
kept(): Promise<boolean>;
|
|
17
|
+
}
|
|
18
|
+
export declare abstract class Memory {
|
|
19
|
+
abstract open(name: string): Promise<Rows>;
|
|
20
|
+
}
|
|
21
|
+
export declare abstract class Custody {
|
|
22
|
+
abstract seed(name: string): Promise<Uint8Array>;
|
|
23
|
+
}
|
|
24
|
+
export declare abstract class Carrier {
|
|
25
|
+
abstract carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | null>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
export class Memory {
|
|
12
|
+
}
|
|
13
|
+
// A ward's seed by the harbor's name for it: thirty-two bytes, the same
|
|
14
|
+
// every time that name is asked.
|
|
15
|
+
export class Custody {
|
|
16
|
+
}
|
|
17
|
+
// Bytes to a ward pk in; bytes, or null for nothing, out.
|
|
18
|
+
export class Carrier {
|
|
19
|
+
}
|
|
@@ -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 {};
|