@robota-sdk/agent-keyring 0.0.0-stage → 3.1.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/CHANGELOG.md +33 -0
- package/LICENSE +661 -0
- package/README.md +91 -2
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.cts +470 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +470 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +74 -3
- package/src/__tests__/authorship-constant-time.test.ts +46 -0
- package/src/__tests__/authorship.test.ts +65 -0
- package/src/__tests__/crypto-failures.test.ts +89 -0
- package/src/__tests__/fixtures/keyring-v1.json +25 -0
- package/src/__tests__/fixtures/sealed-v1.json +8 -0
- package/src/__tests__/helpers.ts +130 -0
- package/src/__tests__/kdf-bounds.test.ts +154 -0
- package/src/__tests__/keyring-document.test.ts +251 -0
- package/src/__tests__/keyring.test.ts +329 -0
- package/src/__tests__/keyslot.test.ts +98 -0
- package/src/__tests__/known-answers.test.ts +110 -0
- package/src/__tests__/link-helpers.ts +228 -0
- package/src/__tests__/owner-handover-derivation.test.ts +50 -0
- package/src/__tests__/owner-handover-dispose.test.ts +64 -0
- package/src/__tests__/owner-handover.test.ts +145 -0
- package/src/__tests__/owner-link-adversarial.test.ts +712 -0
- package/src/__tests__/owner-link-grant-wipe.test.ts +75 -0
- package/src/__tests__/owner-link-known-answers.test.ts +117 -0
- package/src/__tests__/owner-link-secret-wipe.test.ts +116 -0
- package/src/__tests__/owner-link.test.ts +229 -0
- package/src/__tests__/read-keys.test.ts +67 -0
- package/src/__tests__/recovery-key.test.ts +76 -0
- package/src/__tests__/runtime-identity.test.ts +171 -0
- package/src/__tests__/sealed-format-vectors.test.ts +91 -0
- package/src/__tests__/sealing.test.ts +376 -0
- package/src/__tests__/spawner-exchange.test.ts +127 -0
- package/src/__tests__/transient-key-pair.test.ts +36 -0
- package/src/aead.ts +62 -0
- package/src/authorship.ts +61 -0
- package/src/bytes.ts +67 -0
- package/src/errors.ts +106 -0
- package/src/fingerprint.ts +32 -0
- package/src/index.ts +77 -0
- package/src/kdf.ts +111 -0
- package/src/key-pairs.ts +148 -0
- package/src/keyring-document.ts +168 -0
- package/src/keyring-types.ts +51 -0
- package/src/keyring.ts +307 -0
- package/src/keyslot.ts +66 -0
- package/src/link-entropy.ts +24 -0
- package/src/link-frames.ts +271 -0
- package/src/link-primitives.ts +77 -0
- package/src/owner-handover.ts +155 -0
- package/src/owner-link.ts +508 -0
- package/src/read-keys.ts +78 -0
- package/src/recovery-key.ts +79 -0
- package/src/runtime-certificate.ts +129 -0
- package/src/runtime-identity.ts +166 -0
- package/src/sealing.ts +281 -0
- package/src/signatures.ts +45 -0
- package/src/spawner-exchange.ts +155 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { KeyObject } from "node:crypto";
|
|
3
|
+
//#region src/errors.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Why an operation was refused. The union is the whole error vocabulary: a caller decides on `code`,
|
|
6
|
+
* never on the message.
|
|
7
|
+
*
|
|
8
|
+
* - `wrong-credential` — the passphrase or recovery key did not open the keyslot. A mistyped
|
|
9
|
+
* credential and a tampered slot are indistinguishable by design (both fail authentication).
|
|
10
|
+
* - `malformed-keyring` / `unsupported-version` — the keyring document is not a valid version-1
|
|
11
|
+
* document, or names a version this build does not know. `unsupported-version` also covers an
|
|
12
|
+
* unknown version byte on a sealed key, record or blob.
|
|
13
|
+
* - `kdf-parameters-out-of-bounds` — a slot asks for a scrypt cost outside the accepted range; raised
|
|
14
|
+
* before any key derivation runs. `kdf-failed` means a key derivation or key generation could not
|
|
15
|
+
* run on this host (for example memory, or a crypto build without the curve), with inputs that
|
|
16
|
+
* were in range.
|
|
17
|
+
* - `empty-passphrase` / `invalid-recovery-key` — the credential has the wrong form; no key
|
|
18
|
+
* derivation ran.
|
|
19
|
+
* - `slot-not-found` — the keyring has no slot of the type the operation needs.
|
|
20
|
+
* - `malformed-ciphertext` — a sealed key, record or blob is structurally impossible (too short).
|
|
21
|
+
* - `decryption-failed` — authentication failed: wrong key, wrong AAD, or modified bytes.
|
|
22
|
+
* - `invalid-argument` — a key or buffer argument has the wrong type or length, or a key the
|
|
23
|
+
* cipher or key import refuses.
|
|
24
|
+
* - `keys-locked` — the unlocked key set was locked and no longer hands out keys.
|
|
25
|
+
* - `link-refused` — an owner-link step was refused; the error is a {@link LinkRefusedError}, whose
|
|
26
|
+
* `reason` names the check that failed. The other end of a link is never told the reason.
|
|
27
|
+
* - `link-closed` — the link was closed, by `close()` or by a frame that did not open, and protects
|
|
28
|
+
* nothing more.
|
|
29
|
+
*/
|
|
30
|
+
type TKeyringErrorCode = 'wrong-credential' | 'malformed-keyring' | 'unsupported-version' | 'kdf-parameters-out-of-bounds' | 'kdf-failed' | 'empty-passphrase' | 'invalid-recovery-key' | 'slot-not-found' | 'malformed-ciphertext' | 'decryption-failed' | 'invalid-argument' | 'keys-locked' | 'link-refused' | 'link-closed';
|
|
31
|
+
/**
|
|
32
|
+
* The only error this package throws on purpose. Messages are fixed text that names a field or a
|
|
33
|
+
* rule; no message or property ever carries a passphrase, a key, a recovery key or any value derived
|
|
34
|
+
* from them, and the original `node:crypto` error is deliberately not chained.
|
|
35
|
+
*/
|
|
36
|
+
declare class KeyringError extends Error {
|
|
37
|
+
readonly code: TKeyringErrorCode;
|
|
38
|
+
constructor(code: TKeyringErrorCode, message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Why an owner-link step was refused. Each names the one check that failed, for logs and for the
|
|
42
|
+
* caller's fixed wording; none says anything about a key.
|
|
43
|
+
*
|
|
44
|
+
* - `malformed` — a message or value of the wrong length, version, mode or encoding.
|
|
45
|
+
* - `out-of-order` — a step taken twice, or before its turn.
|
|
46
|
+
* - `spawner-unavailable` — spawner standing was asked of a runtime without a spawner key: it never
|
|
47
|
+
* ran its exchange, or its spawner key was forgotten when its trust root was installed.
|
|
48
|
+
* - `not-certified` — owner mode was asked of a runtime with no trust root.
|
|
49
|
+
* - `already-certified` — a second trust root.
|
|
50
|
+
* - `certificate-mismatch` — the certificate is not the attach key's signature over this instance
|
|
51
|
+
* and binding: made by another keyring, or for another runtime or start.
|
|
52
|
+
* - `instance-mismatch` — the runtime answered with an instance key other than its exchange's.
|
|
53
|
+
* - `bad-runtime-proof` — the runtime's proof of the transcript did not verify.
|
|
54
|
+
* - `bad-proof` — the initiator's proof of the transcript did not verify.
|
|
55
|
+
*/
|
|
56
|
+
type TLinkRefusalReason = 'malformed' | 'out-of-order' | 'spawner-unavailable' | 'not-certified' | 'already-certified' | 'certificate-mismatch' | 'instance-mismatch' | 'bad-runtime-proof' | 'bad-proof';
|
|
57
|
+
/** An owner-link refusal: a `KeyringError` with code `link-refused` and the failed check as `reason`. */
|
|
58
|
+
declare class LinkRefusedError extends KeyringError {
|
|
59
|
+
readonly reason: TLinkRefusalReason;
|
|
60
|
+
constructor(reason: TLinkRefusalReason);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/keyring-types.d.ts
|
|
64
|
+
/** The only keyring document version this build reads and writes. */
|
|
65
|
+
declare const KEYRING_VERSION = 1;
|
|
66
|
+
/**
|
|
67
|
+
* How a passphrase slot stretches the passphrase. All fields are stored in the slot so a keyring
|
|
68
|
+
* written at one cost still unlocks after the default cost changes.
|
|
69
|
+
*/
|
|
70
|
+
interface IScryptKdfParameters {
|
|
71
|
+
readonly algorithm: 'scrypt';
|
|
72
|
+
readonly N: number;
|
|
73
|
+
readonly r: number;
|
|
74
|
+
readonly p: number;
|
|
75
|
+
/** base64url, 16 bytes. */
|
|
76
|
+
readonly salt: string;
|
|
77
|
+
}
|
|
78
|
+
interface IKeyslotBase {
|
|
79
|
+
/** base64url, 16 random bytes; bound into the wrapping AAD. */
|
|
80
|
+
readonly id: string;
|
|
81
|
+
/** base64url, 12 bytes. */
|
|
82
|
+
readonly iv: string;
|
|
83
|
+
/** base64url, the AES-256-GCM-wrapped master key plus its 16-byte tag (48 bytes). */
|
|
84
|
+
readonly wrappedKey: string;
|
|
85
|
+
}
|
|
86
|
+
interface IPassphraseKeyslot extends IKeyslotBase {
|
|
87
|
+
readonly type: 'passphrase';
|
|
88
|
+
readonly kdf: IScryptKdfParameters;
|
|
89
|
+
}
|
|
90
|
+
interface IRecoveryKeyslot extends IKeyslotBase {
|
|
91
|
+
readonly type: 'recovery';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A keyslot is a tagged union on `type`: a future way to unlock (a device secret, for example) adds
|
|
95
|
+
* a member here and a branch in the parser, without touching the existing slots.
|
|
96
|
+
*/
|
|
97
|
+
type TKeyslot = IPassphraseKeyslot | IRecoveryKeyslot;
|
|
98
|
+
type TKeyslotType = TKeyslot['type'];
|
|
99
|
+
/**
|
|
100
|
+
* The persisted keyring document. There is deliberately no field for the attach public key: the
|
|
101
|
+
* file lets anyone seal to the owner, but nothing in it lets a program claim to be the owner.
|
|
102
|
+
*/
|
|
103
|
+
interface IKeyring {
|
|
104
|
+
readonly version: typeof KEYRING_VERSION;
|
|
105
|
+
/** base64url, the raw 32-byte X25519 sealing public key. */
|
|
106
|
+
readonly sealingPublicKey: string;
|
|
107
|
+
readonly slots: readonly TKeyslot[];
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/keyring-document.d.ts
|
|
111
|
+
/** Parses keyring JSON strictly; see {@link validateKeyring}. */
|
|
112
|
+
declare function parseKeyring(json: string): IKeyring;
|
|
113
|
+
/** The keyring as JSON with a fixed field order, ready to write to disk. */
|
|
114
|
+
declare function serializeKeyring(keyring: IKeyring): string;
|
|
115
|
+
/** The raw 32-byte sealing public key — all a locked process needs to seal new artifacts. */
|
|
116
|
+
declare function getSealingPublicKey(keyring: IKeyring): Buffer;
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/key-pairs.d.ts
|
|
119
|
+
interface IKeyPair {
|
|
120
|
+
/** The raw 32-byte public key. */
|
|
121
|
+
readonly publicKey: Uint8Array;
|
|
122
|
+
readonly privateKey: KeyObject;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A new X25519 sealing keypair from a random seed, for a key that lives only in the memory of the
|
|
126
|
+
* process that made it: nothing derives it again, so whatever is sealed to it opens only while that
|
|
127
|
+
* process keeps the private key. Built from the seed as the derived pairs are, so it has their shape
|
|
128
|
+
* on every runtime.
|
|
129
|
+
*/
|
|
130
|
+
declare function generateTransientSealingKeyPair(): IKeyPair;
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/read-keys.d.ts
|
|
133
|
+
/**
|
|
134
|
+
* `lock()` drops this object's references to the keys, zero-fills the authorship key (plain bytes
|
|
135
|
+
* held only here) and makes every accessor and operation throw `keys-locked`. It does NOT zero the
|
|
136
|
+
* sealing keypair: a `KeyObject` offers no way to overwrite its contents, so the private key stays
|
|
137
|
+
* in memory until the garbage collector frees it, and any reference a caller copied out earlier
|
|
138
|
+
* stays valid. Treat `lock()` as "stop handing out keys", not as erasure.
|
|
139
|
+
*/
|
|
140
|
+
interface IReadKeys {
|
|
141
|
+
readonly locked: boolean;
|
|
142
|
+
/** X25519 keypair that opens sealed artifact keys. */
|
|
143
|
+
readonly sealing: IKeyPair;
|
|
144
|
+
/**
|
|
145
|
+
* The HMAC-SHA256 tag of `message` under the authorship key, which has no public half: what an
|
|
146
|
+
* unlocked process attaches to what it wrote, since a sealed artifact opens for whoever sealed it.
|
|
147
|
+
* The key itself is never handed out.
|
|
148
|
+
*/
|
|
149
|
+
tagAuthorship(message: Uint8Array): Uint8Array;
|
|
150
|
+
/** Whether `tag` is {@link tagAuthorship} of `message`, compared in constant time. */
|
|
151
|
+
verifyAuthorship(message: Uint8Array, tag: Uint8Array): boolean;
|
|
152
|
+
lock(): void;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/keyring.d.ts
|
|
156
|
+
/** What proves the right to open a keyring. A tagged union so a future credential is one more member. */
|
|
157
|
+
type TKeyringCredential = {
|
|
158
|
+
readonly type: 'passphrase';
|
|
159
|
+
readonly passphrase: string;
|
|
160
|
+
} | {
|
|
161
|
+
readonly type: 'recoveryKey';
|
|
162
|
+
readonly recoveryKey: string;
|
|
163
|
+
};
|
|
164
|
+
interface ICreateKeyringOptions {
|
|
165
|
+
/** Any non-empty string; minimum length and strength are the caller's policy. */
|
|
166
|
+
readonly passphrase: string;
|
|
167
|
+
}
|
|
168
|
+
interface ICreatedKeyring {
|
|
169
|
+
readonly keyring: IKeyring;
|
|
170
|
+
/**
|
|
171
|
+
* The recovery key, formatted for a person to write down. It exists only here: it is not stored
|
|
172
|
+
* anywhere and cannot be shown again, so the caller must present it exactly once.
|
|
173
|
+
*/
|
|
174
|
+
readonly recoveryKey: string;
|
|
175
|
+
readonly unlocked: IReadKeys;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Creates a keyring around a new random 256-bit master key, protected by the passphrase and by a
|
|
179
|
+
* new recovery key, and returns it already unlocked. The master key is wiped before this returns.
|
|
180
|
+
*/
|
|
181
|
+
declare function createKeyring(options: ICreateKeyringOptions): Promise<ICreatedKeyring>;
|
|
182
|
+
/**
|
|
183
|
+
* Unlocks with the passphrase, for reading: the read keys and nothing that proves ownership (see
|
|
184
|
+
* `openOwnerHandover` for that). Costs one scrypt run per passphrase slot (~160 ms at the default
|
|
185
|
+
* cost, off the event loop). The stored sealing public key is NOT consulted — see
|
|
186
|
+
* {@link checkSealingPublicKey}.
|
|
187
|
+
*/
|
|
188
|
+
declare function unlockWithPassphrase(keyring: IKeyring, passphrase: string): Promise<IReadKeys>;
|
|
189
|
+
/** Unlocks with the recovery key shown at creation (case-insensitive, hyphens optional). */
|
|
190
|
+
declare function unlockWithRecoveryKey(keyring: IKeyring, recoveryKey: string): Promise<IReadKeys>;
|
|
191
|
+
/**
|
|
192
|
+
* Replaces the passphrase slot with one that wraps the same master key under `newPassphrase`, and
|
|
193
|
+
* returns the new keyring; the input is untouched. Everything encrypted under the keys stays
|
|
194
|
+
* readable because the master key does not change. The caller must prove the right to change it by
|
|
195
|
+
* supplying a working credential — the current passphrase, or the recovery key to reset a forgotten
|
|
196
|
+
* one. The new slot is written at the current default scrypt cost with a fresh salt and IV, and
|
|
197
|
+
* keeps the old slot's id. The stored sealing public key and the recovery slot are carried over
|
|
198
|
+
* as they are.
|
|
199
|
+
*/
|
|
200
|
+
declare function changePassphrase(keyring: IKeyring, credential: TKeyringCredential, newPassphrase: string): Promise<IKeyring>;
|
|
201
|
+
/**
|
|
202
|
+
* Whether the sealing public key stored in the keyring is the one the master key derives. A
|
|
203
|
+
* mismatch means the file was altered (an attacker who can write it could substitute their own key
|
|
204
|
+
* so new artifacts are sealed to them); the stored key cannot be authenticated without the master
|
|
205
|
+
* key, so this check is only possible after an unlock.
|
|
206
|
+
*/
|
|
207
|
+
declare function checkSealingPublicKey(keyring: IKeyring, keys: Pick<IReadKeys, 'sealing'>): {
|
|
208
|
+
matches: boolean;
|
|
209
|
+
};
|
|
210
|
+
/** A copy of the keyring whose stored sealing public key is the one derived from the read keys. */
|
|
211
|
+
declare function repairSealingPublicKey(keyring: IKeyring, keys: Pick<IReadKeys, 'sealing'>): IKeyring;
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/runtime-certificate.d.ts
|
|
214
|
+
/** The runtime start a link and a certificate are bound to. Public values. */
|
|
215
|
+
interface ILinkBinding {
|
|
216
|
+
readonly runtimeId: string;
|
|
217
|
+
readonly generation: string;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* What a runtime is told about its owner by its spawner: the owner's attach public key and the
|
|
221
|
+
* certificate of the runtime's own instance key. Public values, held only in runtime memory: a
|
|
222
|
+
* same-user-writable file is never a trust anchor.
|
|
223
|
+
*/
|
|
224
|
+
interface ITrustRoot {
|
|
225
|
+
readonly attachPublicKey: Uint8Array;
|
|
226
|
+
/** Ed25519 under the attach key over the certificate message (see {@link runtimeCertificateMessage}). */
|
|
227
|
+
readonly certificate: Uint8Array;
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/owner-handover.d.ts
|
|
231
|
+
interface IOwnerHandover {
|
|
232
|
+
/**
|
|
233
|
+
* Whether the keyring file holds the sealing public key the credential derives. `false` means the
|
|
234
|
+
* file was altered: a runtime given these keys seals to the derived key, and the file is repaired
|
|
235
|
+
* by `secure verify`, not here.
|
|
236
|
+
*/
|
|
237
|
+
readonly sealingKeyMatches: boolean;
|
|
238
|
+
/** The derived sealing public key's fingerprint, shown so a replaced keyring file is noticed. */
|
|
239
|
+
readonly fingerprint: string;
|
|
240
|
+
readonly disposed: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* The trust root that tells a runtime its owner: the attach public key and the certificate of
|
|
243
|
+
* `instancePublicKey` for `binding`. Give it only for an instance key and binding learned over an
|
|
244
|
+
* authenticated link, since the certificate vouches for exactly them.
|
|
245
|
+
*/
|
|
246
|
+
certify(instancePublicKey: Uint8Array, binding: ILinkBinding): ITrustRoot;
|
|
247
|
+
/** Zero-fills what it can and drops every key; later calls throw `keys-locked`. Idempotent. */
|
|
248
|
+
dispose(): void;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Opens the owner handover with `credential`: one key derivation (one scrypt run per passphrase
|
|
252
|
+
* slot, as an unlock), from whose master key the attach keypair and the read keys are derived; the
|
|
253
|
+
* master key is zero-filled before this returns. A wrong credential and a tampered slot fail alike.
|
|
254
|
+
*/
|
|
255
|
+
declare function openOwnerHandover(keyring: IKeyring, credential: TKeyringCredential): Promise<IOwnerHandover>;
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/spawner-exchange.d.ts
|
|
258
|
+
/** Only this module holds it, so only a completed exchange can make a spawner key. */
|
|
259
|
+
declare const ISSUED: unique symbol;
|
|
260
|
+
/**
|
|
261
|
+
* The key a spawner shares with the runtime it started. Opaque: it offers only `wipe()`, and the link
|
|
262
|
+
* initiator reads it through an accessor this package does not export. It has no expiry; it is
|
|
263
|
+
* wiped by whoever holds it once it is no longer needed.
|
|
264
|
+
*/
|
|
265
|
+
declare class SpawnerKey {
|
|
266
|
+
constructor(issued: typeof ISSUED, bytes: Buffer);
|
|
267
|
+
get wiped(): boolean;
|
|
268
|
+
/** Zero-fills the key and forgets it. Idempotent. */
|
|
269
|
+
wipe(): void;
|
|
270
|
+
}
|
|
271
|
+
interface ISpawnerExchangeResult {
|
|
272
|
+
readonly spawnerKey: SpawnerKey;
|
|
273
|
+
/** The runtime's instance public key, as it sent it over the inherited pair. */
|
|
274
|
+
readonly instancePublicKey: Uint8Array;
|
|
275
|
+
}
|
|
276
|
+
/** The spawner's side of one exchange. */
|
|
277
|
+
interface ISpawnerExchange {
|
|
278
|
+
/** The first message to write to the runtime: the spawner's ephemeral public key. */
|
|
279
|
+
readonly message: Uint8Array;
|
|
280
|
+
/** Derives the spawner key from the runtime's reply. Once: the ephemeral is dropped by the first call. */
|
|
281
|
+
complete(reply: Uint8Array): ISpawnerExchangeResult;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Starts the spawner's side of the exchange with a fresh ephemeral. Write `message` to the runtime
|
|
285
|
+
* over the inherited pair, read its 64-byte reply, and `complete` it.
|
|
286
|
+
*/
|
|
287
|
+
declare function startSpawnerExchange(): ISpawnerExchange;
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/runtime-identity.d.ts
|
|
290
|
+
interface IRuntimeIdentity {
|
|
291
|
+
/** The instance public key, the value the owner certifies. */
|
|
292
|
+
readonly instancePublicKey: Uint8Array;
|
|
293
|
+
/** Whether the spawner exchange ran and its key is still held. */
|
|
294
|
+
readonly spawnerKeyHeld: boolean;
|
|
295
|
+
/** Whether a trust root is installed. */
|
|
296
|
+
readonly certified: boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Answers the spawner's first message with this runtime's ephemeral and instance public key, and
|
|
299
|
+
* keeps the spawner key. Once: a second call is refused, even after a refused first one.
|
|
300
|
+
*/
|
|
301
|
+
answerSpawnerExchange(message: Uint8Array): Uint8Array;
|
|
302
|
+
/**
|
|
303
|
+
* Keeps `root` when it certifies this instance for `binding`, and forgets the spawner key. Once,
|
|
304
|
+
* and only while the spawner key is held. A refused root changes nothing.
|
|
305
|
+
*/
|
|
306
|
+
installTrustRoot(root: ITrustRoot, binding: ILinkBinding): void;
|
|
307
|
+
}
|
|
308
|
+
/** A new runtime identity with a fresh instance key, uncertified and before its spawner exchange. */
|
|
309
|
+
declare function createRuntimeIdentity(): IRuntimeIdentity;
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/link-frames.d.ts
|
|
312
|
+
/** How the initiator was admitted, which is the standing its link has. */
|
|
313
|
+
type TLinkMode = 'spawner' | 'owner';
|
|
314
|
+
type TLinkFrameKind = 'session' | 'owner' | 'grant';
|
|
315
|
+
/** The kinds a caller seals and opens as bytes; a grant has its own two operations. */
|
|
316
|
+
type TLinkDataFrameKind = Exclude<TLinkFrameKind, 'grant'>;
|
|
317
|
+
/** The kind byte and the GCM tag around every frame's ciphertext. */
|
|
318
|
+
declare const LINK_FRAME_OVERHEAD_BYTES: number;
|
|
319
|
+
/** The most one frame carries; a carrier bounds its frames below this. */
|
|
320
|
+
declare const LINK_FRAME_MAX_PLAINTEXT_BYTES: number;
|
|
321
|
+
/** A sealed read grant is always exactly this long. */
|
|
322
|
+
declare const LINK_GRANT_FRAME_BYTES: number;
|
|
323
|
+
/** The kind a frame says it is. Unauthenticated until it opens, which checks the kind too. */
|
|
324
|
+
declare function linkFrameKind(frame: Uint8Array): TLinkFrameKind;
|
|
325
|
+
/** One end of an established link. */
|
|
326
|
+
interface IEstablishedLink {
|
|
327
|
+
readonly standing: TLinkMode;
|
|
328
|
+
/** The runtime start the link is bound to: authenticated by the handshake on both ends. */
|
|
329
|
+
readonly binding: ILinkBinding;
|
|
330
|
+
/** The runtime's instance key: checked by the initiator, its own at the responder. */
|
|
331
|
+
readonly instancePublicKey: Uint8Array;
|
|
332
|
+
readonly closed: boolean;
|
|
333
|
+
seal(kind: TLinkDataFrameKind, plaintext: Uint8Array): Uint8Array;
|
|
334
|
+
/** The next frame from the other end; throws, and closes the link, unless it is exactly that. */
|
|
335
|
+
open(frame: Uint8Array): {
|
|
336
|
+
readonly kind: TLinkDataFrameKind;
|
|
337
|
+
readonly plaintext: Buffer;
|
|
338
|
+
};
|
|
339
|
+
/** Zero-fills both frame keys. Idempotent. */
|
|
340
|
+
close(): void;
|
|
341
|
+
}
|
|
342
|
+
interface IInitiatorLink extends IEstablishedLink {
|
|
343
|
+
/**
|
|
344
|
+
* The owner's read keys (the sealing key and the authorship key, never the master key or the
|
|
345
|
+
* attach key) as one grant frame. Only on an owner link, and only from the handover that opened it.
|
|
346
|
+
*/
|
|
347
|
+
sealReadGrant(handover: IOwnerHandover): Uint8Array;
|
|
348
|
+
}
|
|
349
|
+
interface IResponderLink extends IEstablishedLink {
|
|
350
|
+
/** The read keys a grant frame carries. Only on an owner-standing link; anything else closes it. */
|
|
351
|
+
openReadGrant(frame: Uint8Array): IReadKeys;
|
|
352
|
+
}
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/owner-link.d.ts
|
|
355
|
+
/** version | mode | e_i | n_i */
|
|
356
|
+
declare const LINK_HELLO_BYTES: number;
|
|
357
|
+
/** The longest challenge either mode can send. */
|
|
358
|
+
declare const LINK_CHALLENGE_MAX_BYTES: number;
|
|
359
|
+
/** The longest proof either mode can send. */
|
|
360
|
+
declare const LINK_PROOF_MAX_BYTES: number;
|
|
361
|
+
/** How the initiator proves itself: the spawner by its spawner key, the owner by its handover. */
|
|
362
|
+
type TLinkInitiatorAuth = {
|
|
363
|
+
readonly mode: 'spawner';
|
|
364
|
+
readonly spawnerKey: SpawnerKey;
|
|
365
|
+
/** The instance key the spawner exchange carried. */
|
|
366
|
+
readonly instancePublicKey: Uint8Array;
|
|
367
|
+
} | {
|
|
368
|
+
readonly mode: 'owner';
|
|
369
|
+
readonly handover: IOwnerHandover;
|
|
370
|
+
};
|
|
371
|
+
/** The client side of one handshake. */
|
|
372
|
+
interface ILinkInitiator {
|
|
373
|
+
readonly mode: TLinkMode;
|
|
374
|
+
/** The first message: the version, the mode, a fresh ephemeral and a fresh nonce. Once. */
|
|
375
|
+
hello(): Uint8Array;
|
|
376
|
+
/**
|
|
377
|
+
* Checks the runtime's challenge and, only if every check passed, returns the proof to send and
|
|
378
|
+
* the established link. Throws `LinkRefusedError` otherwise. Once, whatever the outcome.
|
|
379
|
+
*/
|
|
380
|
+
answer(challenge: Uint8Array): {
|
|
381
|
+
readonly proof: Uint8Array;
|
|
382
|
+
readonly link: IInitiatorLink;
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* The client side of a handshake. A wiped spawner key or a disposed handover is refused here, before
|
|
387
|
+
* any message exists.
|
|
388
|
+
*/
|
|
389
|
+
declare function createLinkInitiator(auth: TLinkInitiatorAuth): ILinkInitiator;
|
|
390
|
+
/** The runtime side of one handshake. */
|
|
391
|
+
interface ILinkResponder {
|
|
392
|
+
/**
|
|
393
|
+
* Answers a hello, or throws `LinkRefusedError`: a malformed hello, spawner mode without the
|
|
394
|
+
* spawner key, or owner mode without a trust root for this binding. Once.
|
|
395
|
+
*/
|
|
396
|
+
challenge(hello: Uint8Array): Uint8Array;
|
|
397
|
+
/** Checks the proof; the established link and the standing it earned. Once. */
|
|
398
|
+
accept(proof: Uint8Array): {
|
|
399
|
+
readonly standing: TLinkMode;
|
|
400
|
+
readonly link: IResponderLink;
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* The runtime side of a handshake, for `identity` at the runtime start `binding` names. Which
|
|
405
|
+
* standing may then do what is the runtime custody's decision, not this module's.
|
|
406
|
+
*/
|
|
407
|
+
declare function createLinkResponder(identity: IRuntimeIdentity, binding: ILinkBinding): ILinkResponder;
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/fingerprint.d.ts
|
|
410
|
+
/**
|
|
411
|
+
* A short string two people can read to each other to confirm they hold the same public key: the
|
|
412
|
+
* first 10 bytes of SHA-256 of the raw 32-byte key, as upper-case hex in groups of four
|
|
413
|
+
* (`A1B2-C3D4-E5F6-0718-293A`). 80 bits is for a human comparing out loud or on screen; it is a
|
|
414
|
+
* check against accidents and casual swaps, not a collision-resistant identifier.
|
|
415
|
+
*/
|
|
416
|
+
declare function fingerprint(publicKey: Uint8Array): string;
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/sealing.d.ts
|
|
419
|
+
/** A fresh random 256-bit data-encryption key (DEK) for one artifact. The caller wipes it after use. */
|
|
420
|
+
declare function generateArtifactKey(): Buffer;
|
|
421
|
+
/**
|
|
422
|
+
* Seals `artifactKey` to the sealing PUBLIC key (ECIES: fresh ephemeral X25519 key, ECDH, HKDF-SHA256,
|
|
423
|
+
* AES-256-GCM). Only the public key is needed, so a process that never unlocked the keyring can
|
|
424
|
+
* still produce artifacts that only the owner can open. Every call uses a new ephemeral key and IV,
|
|
425
|
+
* so sealing the same key twice yields different bytes.
|
|
426
|
+
*
|
|
427
|
+
* `aad` is authenticated, not stored: the caller must present the same bytes to open it. Put the
|
|
428
|
+
* artifact id in it, so a sealed key cannot be moved to another artifact.
|
|
429
|
+
*/
|
|
430
|
+
declare function sealArtifactKey(sealingPublicKey: Uint8Array, artifactKey: Uint8Array, aad: Uint8Array): Buffer;
|
|
431
|
+
/**
|
|
432
|
+
* Opens a key produced by {@link sealArtifactKey}. Needs the unlocked sealing private key. The
|
|
433
|
+
* caller owns and wipes the returned key.
|
|
434
|
+
*/
|
|
435
|
+
declare function openArtifactKey(sealingPrivateKey: KeyObject, sealed: Uint8Array, aad: Uint8Array): Buffer;
|
|
436
|
+
/**
|
|
437
|
+
* One content record: `version | 12-byte random nonce | ciphertext | 16-byte tag`, AES-256-GCM
|
|
438
|
+
* under the artifact key. Use it as often as needed with one key — a JSONL log appends one record
|
|
439
|
+
* per batch of lines, a whole-file blob is one record — but keep a key's record count far below
|
|
440
|
+
* 2^32, the safe limit for random 96-bit nonces.
|
|
441
|
+
*
|
|
442
|
+
* `aad` is authenticated, not stored. Put the artifact id and the record's sequence number in it:
|
|
443
|
+
* that is what stops records from being swapped between files or reordered. Detecting that
|
|
444
|
+
* TRAILING records were cut off is not something one record can know — a log truncated at a record
|
|
445
|
+
* boundary is a valid log — so the caller must keep the expected record count (or a final marker
|
|
446
|
+
* record) somewhere it trusts.
|
|
447
|
+
*/
|
|
448
|
+
declare function encryptRecord(artifactKey: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Buffer;
|
|
449
|
+
/** Opens a record from {@link encryptRecord}; returns plaintext only if the whole record authenticates. */
|
|
450
|
+
declare function decryptRecord(artifactKey: Uint8Array, record: Uint8Array, aad: Uint8Array): Buffer;
|
|
451
|
+
/**
|
|
452
|
+
* Seals a whole value in one step: a sealed key header followed by one record under that key.
|
|
453
|
+
* Like {@link sealArtifactKey} it needs only the sealing public key.
|
|
454
|
+
*/
|
|
455
|
+
declare function sealBlob(sealingPublicKey: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Buffer;
|
|
456
|
+
/**
|
|
457
|
+
* The two halves of a blob from {@link sealBlob}: the sealed key and the one record under it, as
|
|
458
|
+
* views into `blob`. A holder that keeps opened keys by sealed key splits with this, so the blob
|
|
459
|
+
* layout stays in this package. Checks the layout only; nothing is authenticated until each half is
|
|
460
|
+
* opened.
|
|
461
|
+
*/
|
|
462
|
+
declare function splitSealedBlob(blob: Uint8Array): {
|
|
463
|
+
readonly sealedKey: Uint8Array;
|
|
464
|
+
readonly record: Uint8Array;
|
|
465
|
+
};
|
|
466
|
+
/** Opens a blob from {@link sealBlob}; any truncation, extension or edit fails as a whole. */
|
|
467
|
+
declare function openBlob(sealingPrivateKey: KeyObject, blob: Uint8Array, aad: Uint8Array): Buffer;
|
|
468
|
+
//#endregion
|
|
469
|
+
export { type ICreateKeyringOptions, type ICreatedKeyring, type IEstablishedLink, type IInitiatorLink, type IKeyPair, type IKeyring, type ILinkBinding, type ILinkInitiator, type ILinkResponder, type IOwnerHandover, type IPassphraseKeyslot, type IReadKeys, type IRecoveryKeyslot, type IResponderLink, type IRuntimeIdentity, type IScryptKdfParameters, type ISpawnerExchange, type ISpawnerExchangeResult, type ITrustRoot, KEYRING_VERSION, KeyringError, LINK_CHALLENGE_MAX_BYTES, LINK_FRAME_MAX_PLAINTEXT_BYTES, LINK_FRAME_OVERHEAD_BYTES, LINK_GRANT_FRAME_BYTES, LINK_HELLO_BYTES, LINK_PROOF_MAX_BYTES, LinkRefusedError, SpawnerKey, type TKeyringCredential, type TKeyringErrorCode, type TKeyslot, type TKeyslotType, type TLinkDataFrameKind, type TLinkFrameKind, type TLinkInitiatorAuth, type TLinkMode, type TLinkRefusalReason, changePassphrase, checkSealingPublicKey, createKeyring, createLinkInitiator, createLinkResponder, createRuntimeIdentity, decryptRecord, encryptRecord, fingerprint, generateArtifactKey, generateTransientSealingKeyPair, getSealingPublicKey, linkFrameKind, openArtifactKey, openBlob, openOwnerHandover, parseKeyring, repairSealingPublicKey, sealArtifactKey, sealBlob, serializeKeyring, splitSealedBlob, startSpawnerExchange, unlockWithPassphrase, unlockWithRecoveryKey };
|
|
470
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/errors.ts","../../src/keyring-types.ts","../../src/keyring-document.ts","../../src/key-pairs.ts","../../src/read-keys.ts","../../src/keyring.ts","../../src/runtime-certificate.ts","../../src/owner-handover.ts","../../src/spawner-exchange.ts","../../src/runtime-identity.ts","../../src/link-frames.ts","../../src/owner-link.ts","../../src/fingerprint.ts","../../src/sealing.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0BY;;;;;;cAqBC,qBAAqB;WACvB,MAAM;EAEH,YAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;KAuB3B;;cAwBC,yBAAyB;WAC3B,QAAQ;EAEL,YAAA,QAAQ;;;;;cCnGT;;;;;UAMI;WACN;WACA;WACA;WACA;;WAEA;;UAGD;;WAEC;;WAEA;;WAEA;;UAGM,2BAA2B;WACjC;WACA,KAAK;;UAGC,yBAAyB;WAC/B;;;;;;KAOC,WAAW,qBAAqB;KAChC,eAAe;;;;;UAMV;WACN,gBAAgB;;WAEhB;WACA,gBAAgB;;;;;iBCkGX,aAAa,eAAe;;iBAa5B,iBAAiB,SAAS;;iBAK1B,oBAAoB,SAAS,WAAW;;;UC/IvC;;WAEN,WAAW;WACX,YAAY;;;;;;;;iBAmHP,mCAAmC;;;;;;;;;;UC1HlC;WACN;;WAEA,SAAS;;;;;;EAMlB,cAAc,SAAS,aAAa;;EAEpC,iBAAiB,SAAS,YAAY,KAAK;EAC3C;;;;;KCGU;WACG;WAA6B;;WAC7B;WAA8B;;UAE5B;;WAEN;;UAGM;WACN,SAAS;;;;;WAKT;WACA,UAAU;;;;;;iBAuEC,cAAc,SAAS,wBAAwB,QAAQ;;;;;;;iBAiH7D,qBAAqB,SAAS,UAAU,qBAAqB,QAAQ;;iBAKrE,sBAAsB,SAAS,UAAU,sBAAsB,QAAQ;;;;;;;;;;iBAajE,iBACpB,SAAS,UACT,YAAY,oBACZ,wBACC,QAAQ;;;;;;;iBA6BK,sBACd,SAAS,UACT,MAAM,KAAK;EACR;;;iBAWW,uBACd,SAAS,UACT,MAAM,KAAK,wBACV;;;;UC3Rc;WACN;WACA;;;;;;;UAQM;WACN,iBAAiB;;WAEjB,aAAa;;;;UCEP;;;;;;WAMN;;WAEA;WACA;;;;;;EAMT,QAAQ,mBAAmB,YAAY,SAAS,eAAe;;EAE/D;;;;;;;iBAiFoB,kBACpB,SAAS,UACT,YAAY,qBACX,QAAQ;;;;cCnGL;;;;;;cAOO;EACC,YAAA,eAAe,QAAQ,OAAO;MAOtC;;EAKJ;;UAae;WACN,YAAY;;WAEZ,mBAAmB;;;UAIb;;WAEN,SAAS;;EAElB,SAAS,OAAO,aAAa;;;;;;iBAiDf,wBAAwB;;;UClGvB;;WAEN,mBAAmB;;WAEnB;;WAEA;;;;;EAKT,sBAAsB,SAAS,aAAa;;;;;EAK5C,iBAAiB,MAAM,YAAY,SAAS;;;iBA4G9B,yBAAyB;;;;KC/H7B;KACA;;KAEA,qBAAqB,QAAQ;;cAW5B;;cAEA;;cAKA;;iBAQG,cAAc,OAAO,aAAa;;UAgBjC;WACN,UAAU;;WAEV,SAAS;;WAET,mBAAmB;WACnB;EACT,KAAK,MAAM,oBAAoB,WAAW,aAAa;;EAEvD,KAAK,OAAO;aAAwB,MAAM;aAA6B,WAAW;;;EAElF;;UAGe,uBAAuB;;;;;EAKtC,cAAc,UAAU,iBAAiB;;UAG1B,uBAAuB;;EAEtC,cAAc,OAAO,aAAa;;;;;cClBvB;;cAEA;;cASA;;KAGD;WAEG;WACA,YAAY;;WAEZ,mBAAmB;;WAEnB;WAAwB,UAAU;;;UA8IhC;WACN,MAAM;;EAEf,SAAS;;;;;EAKT,OAAO,WAAW;aAAwB,OAAO;aAAqB,MAAM;;;;;;;iBAsH9D,oBAAoB,MAAM,qBAAqB;;UAK9C;;;;;EAKf,UAAU,OAAO,aAAa;;EAE9B,OAAO,OAAO;aAAwB,UAAU;aAAoB,MAAM;;;;;;;iBA2H5D,oBACd,UAAU,kBACV,SAAS,eACR;;;;;;;;;iBC1ea,YAAY,WAAW;;;;iBC0EvB,uBAAuB;;;;;;;;;;iBAavB,gBACd,kBAAkB,YAClB,aAAa,YACb,KAAK,aACJ;;;;;iBA2Ca,gBACd,mBAAmB,WACnB,QAAQ,YACR,KAAK,aACJ;;;;;;;;;;;;;iBAyCa,cACd,aAAa,YACb,WAAW,YACX,KAAK,aACJ;;iBAUa,cACd,aAAa,YACb,QAAQ,YACR,KAAK,aACJ;;;;;iBAwBa,SACd,kBAAkB,YAClB,WAAW,YACX,KAAK,aACJ;;;;;;;iBAkBa,gBAAgB,MAAM;WAC3B,WAAW;WACX,QAAQ;;;iBAWH,SAAS,mBAAmB,WAAW,MAAM,YAAY,KAAK,aAAa"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Buffer as e}from"node:buffer";import{createCipheriv as t,createDecipheriv as n,createHash as r,createHmac as i,createPrivateKey as a,createPublicKey as o,diffieHellman as s,generateKeyPairSync as c,hkdfSync as l,randomBytes as u,scrypt as d,sign as f,timingSafeEqual as p,verify as ee}from"node:crypto";var m=class extends Error{code;constructor(e,t){super(t),this.name=`KeyringError`,this.code=e}};const te={malformed:`The owner link refused a malformed message.`,"out-of-order":`The owner link refused a step out of order.`,"spawner-unavailable":`The runtime holds no spawner key.`,"not-certified":`The runtime holds no trust root.`,"already-certified":`The runtime already holds a trust root.`,"certificate-mismatch":`The certificate does not certify this runtime for this owner.`,"instance-mismatch":`The runtime answered with another instance key.`,"bad-runtime-proof":`The runtime did not prove the transcript.`,"bad-proof":`The initiator did not prove the transcript.`};var h=class extends m{reason;constructor(e){super(`link-refused`,te[e]),this.name=`LinkRefusedError`,this.reason=e}};const ne=1,re=/^[A-Za-z0-9_-]*$/;function g(e){return Object.prototype.toString.call(e)===`[object Uint8Array]`}function _(t){return e.from(t).toString(`base64url`)}function ie(t,n){if(typeof t!=`string`||!re.test(t))return;let r=e.from(t,`base64url`);if(r.length===n)return r.toString(`base64url`)===t?r:void 0}function ae(e,t){let n=ie(e,t);if(!n)throw new m(`malformed-keyring`,`The keyring is malformed: a field is not decodable.`);return n}function v(t){return e.from(t,`utf8`)}function y(t){let n=[];for(let r of t){if(r.length>65535)throw new m(`invalid-argument`,`A value bound into the AAD exceeds 65535 bytes.`);let t=e.alloc(2);t.writeUInt16BE(r.length),n.push(t,e.from(r.buffer,r.byteOffset,r.byteLength))}return e.concat(n)}function b(...e){for(let t of e)t?.fill(0)}const oe=`aes-256-gcm`;function x(n,r,i,a){try{let o=t(oe,n,r,{authTagLength:16});o.setAAD(a);let s=e.concat([o.update(i),o.final()]);return e.concat([s,o.getAuthTag()])}catch{throw new m(`invalid-argument`,`The key or IV cannot be used for encryption.`)}}function S(t,r,i,a){if(i.length<16)throw new m(`decryption-failed`,`Authentication of the ciphertext failed.`);let o=i.length-16,s=i.subarray(0,o),c=i.subarray(o),l=[];try{let i=n(oe,t,r,{authTagLength:16});return i.setAAD(a),i.setAuthTag(c),l.push(i.update(s)),l.push(i.final()),e.concat(l)}catch{throw new m(`decryption-failed`,`Authentication of the ciphertext failed.`)}finally{b(...l)}}const se={N:2**17,r:8,p:1},C={minN:2**15,maxN:2**19,minR:8,maxR:16,p:1,maxMemoryBytes:2**29};function ce(e){let{N:t,r:n,p:r}=e,i=Number.isInteger(t)&&t>=C.minN&&t<=C.maxN&&(t&t-1)==0,a=Number.isInteger(n)&&n>=C.minR&&n<=C.maxR,o=128*n*t<=C.maxMemoryBytes;if(!i||!a||!o||r!==C.p)throw new m(`kdf-parameters-out-of-bounds`,`A passphrase slot requests scrypt parameters outside the accepted range.`)}function w(e){if(typeof e!=`string`)throw new m(`invalid-argument`,`The passphrase must be a string.`);let t=e.normalize(`NFKC`);if(t.length===0)throw new m(`empty-passphrase`,`The passphrase is empty.`);return v(t)}function le(e,t,n){ce(n);let{N:r,r:i,p:a}=n,o=128*i*(r+a+2)+1048576;return new Promise((n,s)=>{try{d(e,t,32,{N:r,r:i,p:a,maxmem:o},(e,t)=>{e?s(T()):n(t)})}catch{s(T())}})}function T(){return new m(`kdf-failed`,`The key derivation could not run on this host.`)}function E(t,n,r=new Uint8Array){try{return e.from(l(`sha256`,t,r,n,32))}catch{throw T()}}const ue=[`version`,`sealingPublicKey`,`slots`],de=[`id`,`type`,`iv`,`wrappedKey`],fe=[...de,`kdf`],pe=[`algorithm`,`N`,`r`,`p`,`salt`];function D(e){return new m(`malformed-keyring`,`The keyring is malformed: ${e}.`)}function me(e,t){if(typeof e!=`object`||!e||Array.isArray(e))throw D(`${t} must be an object`);let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw D(`${t} must be a plain object`);return e}function O(e,t,n){let r=Object.keys(e);if(r.length!==t.length||r.some(e=>!t.includes(e)))throw D(`${n} must have exactly the fields ${t.join(`, `)}`)}function k(e,t,n){if(ie(e,t)===void 0)throw D(`${n} must be canonical base64url of ${t} bytes`);return e}function he(e,t){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<1)throw D(`${t} must be a positive integer`);return e}function ge(e,t){O(e,fe,t);let n=me(e.kdf,`${t}.kdf`);if(O(n,pe,`${t}.kdf`),n.algorithm!==`scrypt`)throw D(`${t}.kdf.algorithm must be "scrypt"`);return{id:k(e.id,16,`${t}.id`),type:`passphrase`,kdf:Object.freeze({algorithm:`scrypt`,N:he(n.N,`${t}.kdf.N`),r:he(n.r,`${t}.kdf.r`),p:he(n.p,`${t}.kdf.p`),salt:k(n.salt,16,`${t}.kdf.salt`)}),iv:k(e.iv,12,`${t}.iv`),wrappedKey:k(e.wrappedKey,48,`${t}.wrappedKey`)}}function _e(e,t){return O(e,de,t),{id:k(e.id,16,`${t}.id`),type:`recovery`,iv:k(e.iv,12,`${t}.iv`),wrappedKey:k(e.wrappedKey,48,`${t}.wrappedKey`)}}function ve(e,t){let n=`slots[${t}]`,r=me(e,n);switch(r.type){case`passphrase`:return Object.freeze(ge(r,n));case`recovery`:return Object.freeze(_e(r,n));default:throw D(`${n}.type is not a known slot type`)}}function A(e){let t=me(e,`the document`);if(t.version!==1)throw typeof t.version==`number`&&Number.isSafeInteger(t.version)?new m(`unsupported-version`,`The keyring was written by a version this build does not support.`):D(`version must be an integer`);O(t,ue,`the document`);let n=k(t.sealingPublicKey,32,`sealingPublicKey`);if(!Array.isArray(t.slots)||t.slots.length<1||t.slots.length>8)throw D(`slots must be an array of 1 to 8 entries`);let r=t.slots.map((e,t)=>ve(e,t));if(new Set(r.map(e=>e.id)).size!==r.length)throw D(`slot ids must be unique`);return Object.freeze({version:1,sealingPublicKey:n,slots:Object.freeze(r)})}function ye(e){if(typeof e!=`string`)throw D(`the document must be a JSON string`);if(e.length>16384)throw D(`the document is too large`);let t;try{t=JSON.parse(e)}catch{throw D(`the document is not valid JSON`)}return A(t)}function be(e){return JSON.stringify(A(e),null,2)}function xe(t){return e.from(A(t).sealingPublicKey,`base64url`)}function j(e){return E(e,`robota/keyring/v1/authorship-hmac`)}function M(t){return e.from(t.buffer,t.byteOffset,t.byteLength)}function Se(e,t){if(!g(e)||!g(t))throw new m(`invalid-argument`,`An authorship tag is made over bytes.`);try{return i(`sha256`,M(e)).update(M(t)).digest()}catch{throw new m(`invalid-argument`,`The authorship tag could not be computed.`)}}function Ce(e,t,n){if(!g(n))throw new m(`invalid-argument`,`An authorship tag is bytes.`);let r=Se(e,t);try{return n.length===r.length&&p(r,M(n))}finally{r.fill(0)}}const we=`robota/keyring/v1/sealing-x25519`,Te=`robota/keyring/v1/attach-ed25519`,N=e.from(`302e020100300506032b656e04220420`,`hex`),Ee=e.from(`302e020100300506032b657004220420`,`hex`),De=e.from(`302a300506032b656e032100`,`hex`),Oe=e.from(`302a300506032b6570032100`,`hex`),ke=De.length;function Ae(t){try{let n=(t.type===`public`?t:o(t)).export({format:`der`,type:`spki`});return e.from(n.subarray(ke))}catch{throw new m(`invalid-argument`,`The key has no exportable public key.`)}}function je(t){if(!g(t)||t.length!==32)throw new m(`invalid-argument`,`A public key must be exactly 32 bytes.`);try{return o({key:e.concat([De,t]),format:`der`,type:`spki`})}catch{throw new m(`invalid-argument`,`The public key is not a valid X25519 key.`)}}function Me(t){if(!g(t)||t.length!==32)throw new m(`invalid-argument`,`A public key must be exactly 32 bytes.`);try{return o({key:e.concat([Oe,t]),format:`der`,type:`spki`})}catch{throw new m(`invalid-argument`,`The public key is not a valid Ed25519 key.`)}}function P(t,n){let r=e.concat([t,n]);try{let e=a({key:r,format:`der`,type:`pkcs8`});return{publicKey:Ae(e),privateKey:e}}catch{throw new m(`kdf-failed`,`The key derivation could not run on this host.`)}finally{b(r)}}function F(e){return P(N,e)}function Ne(e){return P(Ee,e)}function Pe(e){return E(e,we)}function Fe(e){let t=E(e,Te);try{return P(Ee,t)}finally{b(t)}}function Ie(e){let t=E(e,we),n=E(e,Te);try{return{sealing:P(N,t),attach:P(Ee,n)}}finally{b(t,n)}}function Le(){let e=u(32);try{return P(N,e)}finally{b(e)}}function Re(){return _(u(16))}function ze(e){return E(e,`robota/keyring/v1/recovery-kek`)}function Be(t,n){return y([e.from([1]),v(t),v(n)])}function Ve(e,t,n,r){let i=u(12),a=x(e,i,t,Be(n,r));return{iv:_(i),wrappedKey:_(a)}}function He(e,t){let n=ae(t.iv,12),r=ae(t.wrappedKey,48);try{return S(e,n,r,Be(t.id,t.type))}catch{throw new m(`wrong-credential`,`The credential did not open the keyring.`)}}const Ue=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,We=/^[0-9A-Za-z\s-]*$/;function Ge(){return new m(`invalid-recovery-key`,`The recovery key is not in the expected format.`)}function Ke(e){if(e.length!==20)throw new m(`invalid-argument`,`A recovery key is exactly 20 bytes.`);let t=``,n=0,r=0;for(let i of e){for(n=n<<8|i,r+=8;r>=5;)t+=Ue[n>>>r-5&31],r-=5;n&=(1<<r)-1}let i=[];for(let e=0;e<t.length;e+=4)i.push(t.slice(e,e+4));return i.join(`-`)}function qe(t){if(typeof t!=`string`||t.length>128||!We.test(t))throw Ge();let n=t.toUpperCase().replace(/[\s-]/g,``).replace(/O/g,`0`).replace(/[IL]/g,`1`);if(n.length!==32)throw Ge();let r=e.alloc(20),i=0,a=0,o=0;for(let e of n){let t=Ue.indexOf(e);if(t<0)throw r.fill(0),Ge();i=i<<5|t,a+=5,a>=8&&(r[o]=i>>>a-8&255,o+=1,a-=8,i&=(1<<a)-1)}return r}var Je=class{#e;constructor(e,t){this.#e={sealing:e,authorship:t}}get locked(){return this.#e===void 0}get sealing(){return this.#t().sealing}tagAuthorship(e){return Se(this.#t().authorship,e)}verifyAuthorship(e,t){return Ce(this.#t().authorship,e,t)}lock(){this.#e?.authorship.fill(0),this.#e=void 0}#t(){if(this.#e===void 0)throw new m(`keys-locked`,`The keys are locked.`);return this.#e}};function Ye(e,t){return new Je(e,t)}function Xe(e){return e.type===`passphrase`}function Ze(e){return e.type===`recovery`}async function Qe(e,t,n){let r=u(16),i=await le(t,r,se);try{let t=Ve(i,e,n,`passphrase`);return{id:n,type:`passphrase`,kdf:{algorithm:`scrypt`,...se,salt:_(r)},...t}}finally{b(i)}}function $e(e,t){let n=Re(),r=ze(t);try{return{id:n,type:`recovery`,...Ve(r,e,n,`recovery`)}}finally{b(r)}}async function et(e){let t=Ie(e.masterKey),n=await Qe(e.masterKey,e.passphrase,Re()),r=$e(e.masterKey,e.recoveryKeyBytes);return{keyring:A({version:1,sealingPublicKey:_(t.sealing.publicKey),slots:[n,r]}),pairs:t}}async function tt(e){let t=w(e.passphrase),n=u(32),r=u(20);try{let{keyring:e,pairs:i}=await et({masterKey:n,passphrase:t,recoveryKeyBytes:r});return{keyring:e,recoveryKey:Ke(r),unlocked:Ye(i.sealing,j(n))}}finally{b(t,n,r)}}function nt(e){return e instanceof m&&e.code===`wrong-credential`}async function rt(e,t){let n=w(t);try{let t=e.slots.filter(Xe);if(t.length===0)throw new m(`slot-not-found`,`The keyring has no passphrase slot.`);for(let e of t)ce(e.kdf);for(let e of t){let t=await le(n,ae(e.kdf.salt,16),e.kdf);try{return{masterKey:He(t,e),slotId:e.id}}catch(e){if(!nt(e))throw e}finally{b(t)}}throw new m(`wrong-credential`,`The credential did not open the keyring.`)}finally{b(n)}}function it(e,t){let n=qe(t);try{let t=e.slots.filter(Ze);if(t.length===0)throw new m(`slot-not-found`,`The keyring has no recovery slot.`);for(let e of t){let t=ze(n);try{return{masterKey:He(t,e),slotId:e.id}}catch(e){if(!nt(e))throw e}finally{b(t)}}throw new m(`wrong-credential`,`The credential did not open the keyring.`)}finally{b(n)}}async function at(e,t){switch(t?.type){case`passphrase`:return rt(e,t.passphrase);case`recoveryKey`:return it(e,t.recoveryKey);default:throw new m(`invalid-argument`,`The credential type is not recognised.`)}}async function ot(e,t){let{masterKey:n}=await at(A(e),t),r=Pe(n);try{return Ye(F(r),j(n))}finally{b(n,r)}}function st(e,t){return ot(e,{type:`passphrase`,passphrase:t})}function ct(e,t){return ot(e,{type:`recoveryKey`,recoveryKey:t})}async function lt(e,t,n){let r=A(e),i=w(n),a;try{a=await at(r,t);let e=t.type===`passphrase`?a.slotId:r.slots.find(Xe)?.id;if(e===void 0)throw new m(`slot-not-found`,`The keyring has no passphrase slot.`);let n=await Qe(a.masterKey,i,e);return A({...r,slots:r.slots.map(t=>t.id===e?n:t)})}finally{b(i,a?.masterKey)}}function ut(e,t){return{matches:dt(e,t.sealing.publicKey)}}function dt(e,t){let n=xe(e);return n.length===t.length&&p(n,t)}function ft(e,t){return A({...A(e),sealingPublicKey:_(t.sealing.publicKey)})}function pt(e){if(!g(e)||e.length!==32)throw new m(`invalid-argument`,`A public key must be a Uint8Array of exactly 32 bytes.`);let t=r(`sha256`).update(e).digest(`hex`).slice(0,20),n=[];for(let e=0;e<t.length;e+=4)n.push(t.slice(e,e+4).toUpperCase());return n.join(`-`)}function I(e,t){try{return f(null,t,e)}catch{throw new m(`kdf-failed`,`The key operation could not run on this host.`)}}function L(e,t,n){if(!g(n)||n.length!==64||!g(t))return!1;try{return ee(null,t,Me(e),n)}catch{return!1}}const mt=/^[A-Za-z0-9._:-]+$/u,ht=[`generation`,`runtimeId`];function R(e){return typeof e==`string`&&e.length>0&&e.length<=128&&mt.test(e)}function z(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new m(`invalid-argument`,`A link binding is a runtime id and a generation.`);let t=e,n=Object.keys(t).sort();if(n.length!==ht.length||n.some((e,t)=>e!==ht[t])||!R(t.runtimeId)||!R(t.generation))throw new m(`invalid-argument`,`A link binding's runtime id and generation are 1-128 characters of [A-Za-z0-9._:-].`);return{runtimeId:t.runtimeId,generation:t.generation}}function gt(t){if(t.length===0||t.length>128)return;let n=e.from(t.buffer,t.byteOffset,t.byteLength).toString(`latin1`);return R(n)?n:void 0}function _t(e){if(!g(e)||e.length!==32)throw new m(`invalid-argument`,`An instance public key is exactly 32 bytes.`);return e}function vt(e,t){let n=_t(e),r=z(t);return y([v(`robota/link/v1/runtime-cert`),n,v(r.runtimeId),v(r.generation)])}function yt(e,t,n){return!g(e.attachPublicKey)||e.attachPublicKey.length!==32||!g(e.certificate)||e.certificate.length!==64?!1:L(e.attachPublicKey,vt(t,n),e.certificate)}const bt=new WeakMap;var xt=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t,bt.set(this,()=>this.#n())}get sealingKeyMatches(){return this.#n(),this.#t}get fingerprint(){return pt(this.#n().sealingPublicKey)}get disposed(){return this.#e===void 0}certify(e,t){let{attach:n}=this.#n(),r=vt(e,t);return{attachPublicKey:Uint8Array.from(n.publicKey),certificate:Uint8Array.from(I(n.privateKey,r))}}dispose(){let e=this.#e;e!==void 0&&(this.#e=void 0,b(e.sealingSeed,e.authorship))}#n(){if(this.#e===void 0)throw new m(`keys-locked`,`The owner handover was disposed.`);return this.#e}};function B(e){let t=bt.get(e);if(t===void 0)throw new m(`invalid-argument`,`This is not an owner handover this package opened.`);return t()}async function St(e,t){let n=A(e),{masterKey:r}=await at(n,t),i,a;try{i=Pe(r),a=j(r);let e=Fe(r),t=F(i).publicKey;return new xt({attach:e,sealingSeed:i,sealingPublicKey:t,authorship:a},dt(n,t))}catch(e){throw b(i,a),e}finally{b(r)}}const V={seed:()=>u(32),nonce:()=>u(32)};function H(e){let t=e.seed();try{return F(t)}finally{b(t)}}function U(e,t){if(!g(t)||t.length!==32)throw new h(`malformed`);try{return s({privateKey:e,publicKey:je(t)})}catch{throw new h(`malformed`)}}function Ct(e){return r(`sha256`).update(y(e)).digest()}function W(e,...t){let n=i(`sha256`,e);for(let e of t)n.update(e);return n.digest()}function G(e,t){return e.length===t.length&&p(e,t)}function wt(t,n,r,i){try{return e.from(l(`sha256`,t,n,r,i))}catch{throw new m(`kdf-failed`,`The key derivation could not run on this host.`)}}const K=new WeakMap,Tt=Symbol(`spawner-key`);var Et=class{constructor(e,t){if(e!==Tt)throw new m(`invalid-argument`,`A spawner key comes only from an exchange.`);K.set(this,t)}get wiped(){return!K.has(this)}wipe(){K.get(this)?.fill(0),K.delete(this)}};function Dt(e){let t=K.get(e);if(t===void 0)throw new m(`keys-locked`,`The spawner key was wiped.`);return t}function Ot(e,t,n,r){return wt(e,Ct([v(`robota/link/v1/spawner-exchange`),t,n,r]),`robota/link/v1/spawner`,32)}function kt(e){let t=H(e);return{message:Uint8Array.from(t.publicKey),complete(e){let n=t;if(t=void 0,n===void 0)throw new h(`out-of-order`);if(!g(e)||e.length!==64)throw new h(`malformed`);let r=e.subarray(0,32),i=Uint8Array.from(e.subarray(32)),a=U(n.privateKey,r);try{let e=Ot(a,n.publicKey,r,i);return{spawnerKey:new Et(Tt,e),instancePublicKey:i}}finally{b(a)}}}}function At(){return kt(V)}function jt(t,n,r){if(!g(t)||t.length!==32)throw new h(`malformed`);let i=H(r),a=U(i.privateKey,t);try{return{reply:e.concat([i.publicKey,n]),spawnerKey:Ot(a,t,i.publicKey,n)}}finally{b(a)}}const Mt=new WeakMap;function Nt(e){let t=e;if(typeof t!=`object`||!t||!g(t.attachPublicKey)||t.attachPublicKey.length!==32||!g(t.certificate)||t.certificate.length!==64)throw new m(`invalid-argument`,`A trust root is a 32-byte attach public key and a 64-byte certificate.`);return{attachPublicKey:Uint8Array.from(t.attachPublicKey),certificate:Uint8Array.from(t.certificate)}}var Pt=class{#e;#t;#n=!1;#r;#i;constructor(e){this.#t=e;let t=e.seed();try{this.#e=Ne(t)}finally{b(t)}Mt.set(this,{instance:this.#e,entropy:e,spawnerKey:()=>this.#r,trust:()=>this.#i})}get instancePublicKey(){return Uint8Array.from(this.#e.publicKey)}get spawnerKeyHeld(){return this.#r!==void 0}get certified(){return this.#i!==void 0}answerSpawnerExchange(e){if(this.#n)throw new h(`out-of-order`);this.#n=!0;let{reply:t,spawnerKey:n}=jt(e,this.#e.publicKey,this.#t);return this.#r=n,t}installTrustRoot(e,t){let n=Nt(e),r=z(t);if(this.#i!==void 0)throw new h(`already-certified`);if(this.#r===void 0)throw new h(`spawner-unavailable`);if(!yt(n,this.#e.publicKey,r))throw new h(`certificate-mismatch`);this.#i={root:n,binding:r},b(this.#r),this.#r=void 0}};function Ft(e){return new Pt(e)}function It(){return Ft(V)}function q(e){let t=Mt.get(e);if(t===void 0)throw new m(`invalid-argument`,`This is not a runtime identity this package made.`);return t}const J={session:1,owner:2,grant:3},Lt=new Map(Object.entries(J).map(([e,t])=>[t,e])),Rt={initiator:1,responder:2},zt=17,Bt=16*1024*1024,Vt=82;function Ht(e){let t=g(e)&&e.length>=17?Lt.get(e[0]):void 0;if(t===void 0)throw new h(`malformed`);return t}function Ut(t){let n=e.alloc(12);return n.writeBigUInt64BE(BigInt(t),4),n}var Wt=class{standing;binding;#e;#t;#n;#r;#i=0;#a=0;constructor(e,t,n,r,i){this.#e=e,this.standing=t,this.binding=Object.freeze({runtimeId:n.runtimeId,generation:n.generation}),this.#t=Uint8Array.from(r),this.#n=e===`initiator`?i.initiatorToResponder:i.responderToInitiator,this.#r=e===`initiator`?i.responderToInitiator:i.initiatorToResponder}get instancePublicKey(){return Uint8Array.from(this.#t)}get closed(){return this.#n===void 0}seal(e,t){if(e!==`session`&&e!==`owner`)throw new m(`invalid-argument`,`A frame is a session or an owner frame.`);return this.sealKind(e,t)}open(e){let t=this.kindOf(e);if(t===`grant`)throw this.close(),new m(`invalid-argument`,`A read grant opens only as a grant.`);return{kind:t,plaintext:this.openKind(e,t)}}close(){b(this.#n,this.#r),this.#n=void 0,this.#r=void 0}sealKind(t,n){if(!g(n)||n.length>16777216)throw new m(`invalid-argument`,`A frame carries 0-${Bt} bytes.`);let r=this.#o(this.#n);if(!Number.isSafeInteger(this.#i+1))throw this.close(),new m(`link-closed`,`The link has sent all the frames it can.`);let i=e.from([Rt[this.#e],J[t]]),a=x(r,Ut(this.#i),n,i);return this.#i+=1,e.concat([e.from([J[t]]),a])}kindOf(e){this.#o(this.#r);try{return Ht(e)}catch(e){throw this.close(),e}}openKind(t,n){let r=this.#o(this.#r),i=this.#e===`initiator`?`responder`:`initiator`,a=e.from([Rt[i],J[n]]),o;try{o=S(r,Ut(this.#a),t.subarray(1),a)}catch(e){throw this.close(),e}return this.#a+=1,o}#o(e){if(e===void 0)throw new m(`link-closed`,`The link is closed.`);return e}},Gt=class extends Wt{#e;constructor(e,t,n,r,i){super(`initiator`,e,t,n,r),this.#e=i}sealReadGrant(t){if(this.standing!==`owner`||t!==this.#e)throw new m(`invalid-argument`,`A read grant goes only over an owner link, from the handover that opened it.`);let{sealingSeed:n,authorship:r}=B(t),i=e.alloc(65);try{return i[0]=1,n.copy(i,1),r.copy(i,33),this.sealKind(`grant`,i)}finally{b(i)}}},Kt=class extends Wt{constructor(e,t,n,r){super(`responder`,e,t,n,r)}openReadGrant(t){if(this.standing!==`owner`)throw this.close(),new m(`invalid-argument`,`A read grant is accepted only on an owner link.`);if(this.kindOf(t)!==`grant`)throw this.close(),new m(`invalid-argument`,`Only a read grant opens as a grant.`);let n=this.openKind(t,`grant`);try{if(n.length!==65||n[0]!==1)throw this.close(),new h(`malformed`);return Ye(F(n.subarray(1,33)),e.from(n.subarray(33)))}finally{b(n)}}};const Y={spawner:1,owner:2},qt={spawner:0,owner:64},Jt={spawner:96,owner:64},Yt={spawner:32,owner:64},X=v(`r`),Z=v(`i`),Xt=66,Zt=358+Math.max(...Object.values(qt))+Math.max(...Object.values(Jt)),Qt=Math.max(...Object.values(Yt));function $t(t){return Ct([v(`robota/link/v1/handshake`),e.from([Y[t.mode]]),t.initiatorEphemeral,t.initiatorNonce,t.responderEphemeral,t.responderNonce,t.instancePublicKey,v(t.binding.runtimeId),v(t.binding.generation),t.certificate])}function en(t,n){let r=wt(t,n,`robota/link/v1/frames`,64);try{return{initiatorToResponder:e.from(r.subarray(0,32)),responderToInitiator:e.from(r.subarray(32))}}finally{b(r)}}function tn(e,t,n){let r=U(e.privateKey,t);try{return en(r,n)}finally{b(r)}}function nn(t){return e.concat([t.responderEphemeral,t.responderNonce,t.instancePublicKey,y([v(t.binding.runtimeId),v(t.binding.generation),t.certificate]),t.runtimeProof])}function rn(e,t){if(!g(e)||e.length>Zt)throw new h(`malformed`);let n=0,r=t=>{if(n+t>e.length)throw new h(`malformed`);let r=Uint8Array.from(e.subarray(n,n+t));return n+=t,r},i=()=>{let e=r(2);return r(e[0]<<8|e[1])},a=r(32),o=r(32),s=r(32),c=gt(i()),l=gt(i()),u=i(),d=r(e.length-n);if(c===void 0||l===void 0||u.length!==qt[t]||d.length!==Jt[t])throw new h(`malformed`);return{responderEphemeral:a,responderNonce:o,instancePublicKey:s,binding:{runtimeId:c,generation:l},certificate:u,runtimeProof:d}}function an(e){if(!g(e)||e.length!==66||e[0]!==1)throw new h(`malformed`);let t=e[1]===Y.spawner?`spawner`:e[1]===Y.owner?`owner`:void 0;if(t===void 0)throw new h(`malformed`);return{mode:t,ephemeral:Uint8Array.from(e.subarray(2,34)),nonce:Uint8Array.from(e.subarray(34))}}var on=class{mode;#e;#t;#n;#r={step:`new`};constructor(e,t){if(e?.mode===`spawner`)Dt(e.spawnerKey),this.#n=Uint8Array.from(_t(e.instancePublicKey));else if(e?.mode===`owner`)B(e.handover);else throw new m(`invalid-argument`,`A link initiator is a spawner or an owner.`);this.mode=e.mode,this.#e=e,this.#t=t}hello(){if(this.#r.step!==`new`)throw new h(`out-of-order`);let t=H(this.#t),n=this.#t.nonce();return this.#r={step:`waiting`,ephemeral:t,nonce:n},e.concat([e.from([1,Y[this.mode]]),t.publicKey,n])}answer(t){let n=this.#r;if(n.step!==`waiting`)throw new h(`out-of-order`);this.#r={step:`spent`};let r=rn(t,this.mode);if(this.#e.mode===`spawner`&&!G(r.instancePublicKey,this.#n))throw new h(`instance-mismatch`);if(this.#e.mode===`owner`){let{attach:e}=B(this.#e.handover);if(!yt({attachPublicKey:e.publicKey,certificate:r.certificate},r.instancePublicKey,r.binding))throw new h(`certificate-mismatch`)}let i=$t({mode:this.mode,initiatorEphemeral:n.ephemeral.publicKey,initiatorNonce:n.nonce,responderEphemeral:r.responderEphemeral,responderNonce:r.responderNonce,instancePublicKey:r.instancePublicKey,binding:r.binding,certificate:r.certificate}),a=e.concat([X,i]);if(this.#e.mode===`spawner`){let e=G(W(Dt(this.#e.spawnerKey),X,i),r.runtimeProof.subarray(0,32)),t=L(r.instancePublicKey,a,r.runtimeProof.subarray(32));if(!e||!t)throw new h(`bad-runtime-proof`)}else if(!L(r.instancePublicKey,a,r.runtimeProof))throw new h(`bad-runtime-proof`);let o=tn(n.ephemeral,r.responderEphemeral,i),s=this.#e.mode===`spawner`?W(Dt(this.#e.spawnerKey),Z,i):I(B(this.#e.handover).attach.privateKey,e.concat([Z,i])),c=this.#e.mode===`owner`?this.#e.handover:void 0;return{proof:s,link:new Gt(this.mode,r.binding,r.instancePublicKey,o,c)}}};function sn(e,t){return new on(e,t)}function cn(e){return sn(e,V)}var ln=class{#e;#t;#n=`new`;#r;constructor(e,t){q(e),this.#e=e,this.#t=z(t)}challenge(t){if(this.#n!==`new`)throw new h(`out-of-order`);this.#n=`spent`;let{mode:n,ephemeral:r,nonce:i}=an(t),a=q(this.#e),o=a.spawnerKey(),s=a.trust();if(n===`spawner`&&o===void 0)throw new h(`spawner-unavailable`);if(n===`owner`&&(s===void 0||s.binding.runtimeId!==this.#t.runtimeId||s.binding.generation!==this.#t.generation))throw new h(`not-certified`);let c=H(a.entropy),l=a.entropy.nonce(),u=n===`owner`?s.root.certificate:new Uint8Array,d=$t({mode:n,initiatorEphemeral:r,initiatorNonce:i,responderEphemeral:c.publicKey,responderNonce:l,instancePublicKey:a.instance.publicKey,binding:this.#t,certificate:u}),f=tn(c,r,d),p=I(a.instance.privateKey,e.concat([X,d])),ee=n===`spawner`?e.concat([W(o,X,d),p]):p;return this.#r={mode:n,th:d,keys:f,...n===`spawner`?{expectedProof:W(o,Z,d)}:{attachPublicKey:Uint8Array.from(s.root.attachPublicKey)}},this.#n=`waiting`,nn({responderEphemeral:c.publicKey,responderNonce:l,instancePublicKey:a.instance.publicKey,binding:this.#t,certificate:u,runtimeProof:ee})}accept(t){let n=this.#r;if(this.#n!==`waiting`||n===void 0)throw new h(`out-of-order`);this.#r=void 0,this.#n=`spent`;try{if(!g(t)||t.length!==Yt[n.mode])throw new h(`malformed`);if(n.mode===`spawner`){if(q(this.#e).spawnerKey()===void 0)throw new h(`spawner-unavailable`);if(!G(n.expectedProof,t))throw new h(`bad-proof`)}else if(!L(n.attachPublicKey,e.concat([Z,n.th]),t))throw new h(`bad-proof`)}catch(e){throw b(n.keys.initiatorToResponder,n.keys.responderToInitiator,n.expectedProof),e}b(n.expectedProof);let r=q(this.#e).instance.publicKey;return{standing:n.mode,link:new Kt(n.mode,this.#t,r,n.keys)}}};function un(e,t){return new ln(e,t)}function Q(e,t,n){if(!g(e)||n!==void 0&&e.length!==n)throw new m(`invalid-argument`,n===void 0?`The ${t} must be a Uint8Array.`:`The ${t} must be a Uint8Array of exactly ${n} bytes.`);return e}function dn(e){let t=e;if(typeof t!=`object`||!t||t.type!==`private`||t.asymmetricKeyType!==`x25519`)throw new m(`invalid-argument`,`The sealing private key must be an X25519 private KeyObject.`);return t}function $(t){return y([e.from([1]),Q(t,`aad`)])}function fn(t,n,r){let i=e.concat([n,r]);try{return E(t,`robota/keyring/v1/dek-wrap`,i)}finally{b(t)}}function pn(){try{let e=c(`x25519`);return{privateKey:e.privateKey,publicKey:Ae(e.publicKey)}}catch{throw new m(`kdf-failed`,`The key derivation could not run on this host.`)}}function mn(){return u(32)}function hn(t,n,r){let i=Q(t,`sealing public key`,32);Q(n,`artifact key`,32);let a=$(r),o=je(i),c=pn(),l=c.publicKey,d;try{d=s({privateKey:c.privateKey,publicKey:o})}catch{throw new m(`invalid-argument`,`The sealing public key cannot be used for key agreement.`)}let f=fn(d,l,i);try{let t=u(12),r=x(f,t,n,a);return e.concat([e.from([1]),l,t,r])}finally{b(f)}}function gn(e){if(e.length===0)throw new m(`malformed-ciphertext`,`The sealed data is empty.`);if(e[0]!==1)throw new m(`unsupported-version`,`The sealed data uses a format this build does not support.`)}function _n(e,t,n){let r=dn(e);Q(t,`sealed key`);let i=$(n);if(gn(t),t.length!==93)throw new m(`malformed-ciphertext`,`The sealed key has the wrong length.`);let a=t.subarray(1,33),o=t.subarray(33,45),c=t.subarray(45),l;try{l=s({privateKey:r,publicKey:je(a)})}catch{throw new m(`decryption-failed`,`Authentication of the ciphertext failed.`)}let u=fn(l,a,Ae(r));try{return S(u,o,c,i)}finally{b(u)}}function vn(t,n,r){Q(t,`artifact key`,32),Q(n,`plaintext`);let i=$(r),a=u(12),o=x(t,a,n,i);return e.concat([e.from([1]),a,o])}function yn(e,t,n){Q(e,`artifact key`,32),Q(t,`record`);let r=$(n);if(gn(t),t.length<29)throw new m(`malformed-ciphertext`,`The record is shorter than its fixed overhead.`);return S(e,t.subarray(1,13),t.subarray(13),r)}function bn(t,n,r){let i=mn();try{return e.concat([hn(t,i,r),vn(i,n,r)])}finally{b(i)}}function xn(e){if(Q(e,`blob`),gn(e),e.length<122)throw new m(`malformed-ciphertext`,`The blob is shorter than its fixed overhead.`);return{sealedKey:e.subarray(0,93),record:e.subarray(93)}}function Sn(e,t,n){dn(e);let{sealedKey:r,record:i}=xn(t),a=_n(e,r,n);try{return yn(a,i,n)}finally{b(a)}}export{ne as KEYRING_VERSION,m as KeyringError,Zt as LINK_CHALLENGE_MAX_BYTES,Bt as LINK_FRAME_MAX_PLAINTEXT_BYTES,zt as LINK_FRAME_OVERHEAD_BYTES,Vt as LINK_GRANT_FRAME_BYTES,Xt as LINK_HELLO_BYTES,Qt as LINK_PROOF_MAX_BYTES,h as LinkRefusedError,Et as SpawnerKey,lt as changePassphrase,ut as checkSealingPublicKey,tt as createKeyring,cn as createLinkInitiator,un as createLinkResponder,It as createRuntimeIdentity,yn as decryptRecord,vn as encryptRecord,pt as fingerprint,mn as generateArtifactKey,Le as generateTransientSealingKeyPair,xe as getSealingPublicKey,Ht as linkFrameKind,_n as openArtifactKey,Sn as openBlob,St as openOwnerHandover,ye as parseKeyring,ft as repairSealingPublicKey,hn as sealArtifactKey,bn as sealBlob,be as serializeKeyring,xn as splitSealedBlob,At as startSpawnerExchange,st as unlockWithPassphrase,ct as unlockWithRecoveryKey};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|