@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +661 -0
  3. package/README.md +91 -2
  4. package/dist/node/index.cjs +1 -0
  5. package/dist/node/index.d.cts +470 -0
  6. package/dist/node/index.d.cts.map +1 -0
  7. package/dist/node/index.d.ts +470 -0
  8. package/dist/node/index.d.ts.map +1 -0
  9. package/dist/node/index.js +2 -0
  10. package/dist/node/index.js.map +1 -0
  11. package/package.json +74 -3
  12. package/src/__tests__/authorship-constant-time.test.ts +46 -0
  13. package/src/__tests__/authorship.test.ts +65 -0
  14. package/src/__tests__/crypto-failures.test.ts +89 -0
  15. package/src/__tests__/fixtures/keyring-v1.json +25 -0
  16. package/src/__tests__/fixtures/sealed-v1.json +8 -0
  17. package/src/__tests__/helpers.ts +130 -0
  18. package/src/__tests__/kdf-bounds.test.ts +154 -0
  19. package/src/__tests__/keyring-document.test.ts +251 -0
  20. package/src/__tests__/keyring.test.ts +329 -0
  21. package/src/__tests__/keyslot.test.ts +98 -0
  22. package/src/__tests__/known-answers.test.ts +110 -0
  23. package/src/__tests__/link-helpers.ts +228 -0
  24. package/src/__tests__/owner-handover-derivation.test.ts +50 -0
  25. package/src/__tests__/owner-handover-dispose.test.ts +64 -0
  26. package/src/__tests__/owner-handover.test.ts +145 -0
  27. package/src/__tests__/owner-link-adversarial.test.ts +712 -0
  28. package/src/__tests__/owner-link-grant-wipe.test.ts +75 -0
  29. package/src/__tests__/owner-link-known-answers.test.ts +117 -0
  30. package/src/__tests__/owner-link-secret-wipe.test.ts +116 -0
  31. package/src/__tests__/owner-link.test.ts +229 -0
  32. package/src/__tests__/read-keys.test.ts +67 -0
  33. package/src/__tests__/recovery-key.test.ts +76 -0
  34. package/src/__tests__/runtime-identity.test.ts +171 -0
  35. package/src/__tests__/sealed-format-vectors.test.ts +91 -0
  36. package/src/__tests__/sealing.test.ts +376 -0
  37. package/src/__tests__/spawner-exchange.test.ts +127 -0
  38. package/src/__tests__/transient-key-pair.test.ts +36 -0
  39. package/src/aead.ts +62 -0
  40. package/src/authorship.ts +61 -0
  41. package/src/bytes.ts +67 -0
  42. package/src/errors.ts +106 -0
  43. package/src/fingerprint.ts +32 -0
  44. package/src/index.ts +77 -0
  45. package/src/kdf.ts +111 -0
  46. package/src/key-pairs.ts +148 -0
  47. package/src/keyring-document.ts +168 -0
  48. package/src/keyring-types.ts +51 -0
  49. package/src/keyring.ts +307 -0
  50. package/src/keyslot.ts +66 -0
  51. package/src/link-entropy.ts +24 -0
  52. package/src/link-frames.ts +271 -0
  53. package/src/link-primitives.ts +77 -0
  54. package/src/owner-handover.ts +155 -0
  55. package/src/owner-link.ts +508 -0
  56. package/src/read-keys.ts +78 -0
  57. package/src/recovery-key.ts +79 -0
  58. package/src/runtime-certificate.ts +129 -0
  59. package/src/runtime-identity.ts +166 -0
  60. package/src/sealing.ts +281 -0
  61. package/src/signatures.ts +45 -0
  62. package/src/spawner-exchange.ts +155 -0
package/README.md CHANGED
@@ -1,3 +1,92 @@
1
- # Temporary Holding Version
1
+ # @robota-sdk/agent-keyring
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Key custody and sealing primitives for conversations that must stay unreadable to other programs
4
+ running as the same operating-system user. A passphrase and a one-time recovery key each unwrap one
5
+ master key. The master key derives an X25519 sealing keypair, an Ed25519 attach keypair and an
6
+ HMAC-SHA256 authorship key. Artifacts are encrypted under per-artifact data keys that are sealed to
7
+ the **public** sealing key, so a locked process can keep writing and only an unlocked keyring can
8
+ read; only an unlocked keyring can tag what it wrote (`tagAuthorship`/`verifyAuthorship` on the
9
+ read keys), since an artifact that opens says nothing about who sealed it.
10
+
11
+ An unlock yields the read keys only. The attach key stays inside a short-lived owner handover, which
12
+ certifies a served runtime's instance key and proves the owner on the **owner link**: a handshake in
13
+ which the runtime proves itself first, so a program on a taken-over socket gets nothing, followed by
14
+ AES-256-GCM frames and a read grant that hands a runtime the read keys and never the attach key or
15
+ the master key. The carrier that runs the link over a socket is `@robota-sdk/agent-transport-link`.
16
+
17
+ It uses `node:crypto` only (AES-256-GCM, scrypt, HKDF-SHA256, HMAC-SHA256, SHA-256, X25519,
18
+ Ed25519), has no native module and no `@robota-sdk` dependency, and runs on Node.js 22.12 or later
19
+ and on Bun.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @robota-sdk/agent-keyring
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```ts
30
+ import {
31
+ createKeyring,
32
+ getSealingPublicKey,
33
+ openBlob,
34
+ parseKeyring,
35
+ sealBlob,
36
+ serializeKeyring,
37
+ unlockWithPassphrase,
38
+ } from '@robota-sdk/agent-keyring';
39
+
40
+ // Once: create the keyring, show `recoveryKey` to the user (it cannot be shown again), and store
41
+ // the serialized document.
42
+ const { keyring, recoveryKey, unlocked } = await createKeyring({ passphrase: 'a long passphrase' });
43
+ const document = serializeKeyring(keyring);
44
+
45
+ // A locked process holds only the file: it can seal, but not open.
46
+ const aad = new TextEncoder().encode('artifact:7f3a|seq:0');
47
+ const sealed = sealBlob(
48
+ getSealingPublicKey(parseKeyring(document)),
49
+ new TextEncoder().encode('hello'),
50
+ aad,
51
+ );
52
+
53
+ // Later, with the passphrase (or `unlockWithRecoveryKey`), the owner can open it.
54
+ const keys = await unlockWithPassphrase(parseKeyring(document), 'a long passphrase');
55
+ const plaintext = openBlob(keys.sealing.privateKey, sealed, aad);
56
+ keys.lock();
57
+ unlocked.lock();
58
+ ```
59
+
60
+ For artifacts with many records (a JSONL log), seal one data key with `sealArtifactKey`, encrypt each
61
+ batch with `encryptRecord`, and put the artifact id and a sequence number in the `aad` of every call.
62
+
63
+ ## API
64
+
65
+ | Export | Description |
66
+ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
67
+ | `createKeyring` | New keyring with a passphrase slot and a recovery slot; returns it unlocked |
68
+ | `unlockWithPassphrase`, `unlockWithRecoveryKey` | Recover the keys; scrypt runs off the event loop |
69
+ | `changePassphrase` | Re-wrap the passphrase slot under a new passphrase, proven by the old one or the recovery key |
70
+ | `checkSealingPublicKey`, `repairSealingPublicKey` | Detect and repair a sealing public key swapped in the file |
71
+ | `parseKeyring`, `serializeKeyring`, `getSealingPublicKey` | Strict keyring document I/O and the public key a locked writer needs |
72
+ | `generateArtifactKey`, `sealArtifactKey`, `openArtifactKey` | Per-artifact data key, sealed to the public key and opened with the private key |
73
+ | `encryptRecord`, `decryptRecord` | AES-256-GCM records under a data key, many per key |
74
+ | `sealBlob`, `openBlob` | A sealed key header plus one record, in one step |
75
+ | `splitSealedBlob` | A blob's sealed key and record, for a caller that keeps opened keys by sealed key |
76
+ | `generateTransientSealingKeyPair` | A random X25519 sealing keypair for a key that lives only in one process's memory |
77
+ | `fingerprint` | Short human-comparable string for a public key |
78
+ | `openOwnerHandover` | The owner for a moment: certifies runtimes and proves the owner; hands out no key |
79
+ | `startSpawnerExchange`, `SpawnerKey` | The spawner's side of the key exchange over the runtime's inherited channel |
80
+ | `createRuntimeIdentity` | A runtime's memory-only instance key, its spawner key until certified, and its trust root |
81
+ | `createLinkInitiator`, `createLinkResponder` | The owner link handshake, as a client and as a runtime |
82
+ | `linkFrameKind` | The kind of a sealed link frame, before it is opened |
83
+ | `KeyringError`, `LinkRefusedError` | The only errors thrown deliberately; `code` and `reason` name the refusal, never a secret |
84
+
85
+ See [`docs/SPEC.md`](./docs/SPEC.md) for the contract and its limits: `lock()` drops references and
86
+ does not erase the keypairs, detecting truncated record sequences is the caller's job, a swapped
87
+ sealing public key is detected rather than prevented, sealed data keys have no forward secrecy, and
88
+ a runtime's spawner key admits whoever holds it until the runtime is certified.
89
+
90
+ ## License
91
+
92
+ This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:buffer"),t=require("node:crypto");var n=class extends Error{code;constructor(e,t){super(t),this.name=`KeyringError`,this.code=e}};const r={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 i=class extends n{reason;constructor(e){super(`link-refused`,r[e]),this.name=`LinkRefusedError`,this.reason=e}};const a=/^[A-Za-z0-9_-]*$/;function o(e){return Object.prototype.toString.call(e)===`[object Uint8Array]`}function s(t){return e.Buffer.from(t).toString(`base64url`)}function c(t,n){if(typeof t!=`string`||!a.test(t))return;let r=e.Buffer.from(t,`base64url`);if(r.length===n)return r.toString(`base64url`)===t?r:void 0}function l(e,t){let r=c(e,t);if(!r)throw new n(`malformed-keyring`,`The keyring is malformed: a field is not decodable.`);return r}function u(t){return e.Buffer.from(t,`utf8`)}function d(t){let r=[];for(let i of t){if(i.length>65535)throw new n(`invalid-argument`,`A value bound into the AAD exceeds 65535 bytes.`);let t=e.Buffer.alloc(2);t.writeUInt16BE(i.length),r.push(t,e.Buffer.from(i.buffer,i.byteOffset,i.byteLength))}return e.Buffer.concat(r)}function f(...e){for(let t of e)t?.fill(0)}const p=`aes-256-gcm`;function m(r,i,a,o){try{let n=(0,t.createCipheriv)(p,r,i,{authTagLength:16});n.setAAD(o);let s=e.Buffer.concat([n.update(a),n.final()]);return e.Buffer.concat([s,n.getAuthTag()])}catch{throw new n(`invalid-argument`,`The key or IV cannot be used for encryption.`)}}function h(r,i,a,o){if(a.length<16)throw new n(`decryption-failed`,`Authentication of the ciphertext failed.`);let s=a.length-16,c=a.subarray(0,s),l=a.subarray(s),u=[];try{let n=(0,t.createDecipheriv)(p,r,i,{authTagLength:16});return n.setAAD(o),n.setAuthTag(l),u.push(n.update(c)),u.push(n.final()),e.Buffer.concat(u)}catch{throw new n(`decryption-failed`,`Authentication of the ciphertext failed.`)}finally{f(...u)}}const ee={N:2**17,r:8,p:1},g={minN:2**15,maxN:2**19,minR:8,maxR:16,p:1,maxMemoryBytes:2**29};function te(e){let{N:t,r,p:i}=e,a=Number.isInteger(t)&&t>=g.minN&&t<=g.maxN&&(t&t-1)==0,o=Number.isInteger(r)&&r>=g.minR&&r<=g.maxR,s=128*r*t<=g.maxMemoryBytes;if(!a||!o||!s||i!==g.p)throw new n(`kdf-parameters-out-of-bounds`,`A passphrase slot requests scrypt parameters outside the accepted range.`)}function _(e){if(typeof e!=`string`)throw new n(`invalid-argument`,`The passphrase must be a string.`);let t=e.normalize(`NFKC`);if(t.length===0)throw new n(`empty-passphrase`,`The passphrase is empty.`);return u(t)}function ne(e,n,r){te(r);let{N:i,r:a,p:o}=r,s=128*a*(i+o+2)+1048576;return new Promise((r,c)=>{try{(0,t.scrypt)(e,n,32,{N:i,r:a,p:o,maxmem:s},(e,t)=>{e?c(re()):r(t)})}catch{c(re())}})}function re(){return new n(`kdf-failed`,`The key derivation could not run on this host.`)}function v(n,r,i=new Uint8Array){try{return e.Buffer.from((0,t.hkdfSync)(`sha256`,n,i,r,32))}catch{throw re()}}const ie=[`version`,`sealingPublicKey`,`slots`],ae=[`id`,`type`,`iv`,`wrappedKey`],oe=[...ae,`kdf`],se=[`algorithm`,`N`,`r`,`p`,`salt`];function y(e){return new n(`malformed-keyring`,`The keyring is malformed: ${e}.`)}function b(e,t){if(typeof e!=`object`||!e||Array.isArray(e))throw y(`${t} must be an object`);let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw y(`${t} must be a plain object`);return e}function x(e,t,n){let r=Object.keys(e);if(r.length!==t.length||r.some(e=>!t.includes(e)))throw y(`${n} must have exactly the fields ${t.join(`, `)}`)}function S(e,t,n){if(c(e,t)===void 0)throw y(`${n} must be canonical base64url of ${t} bytes`);return e}function ce(e,t){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<1)throw y(`${t} must be a positive integer`);return e}function le(e,t){x(e,oe,t);let n=b(e.kdf,`${t}.kdf`);if(x(n,se,`${t}.kdf`),n.algorithm!==`scrypt`)throw y(`${t}.kdf.algorithm must be "scrypt"`);return{id:S(e.id,16,`${t}.id`),type:`passphrase`,kdf:Object.freeze({algorithm:`scrypt`,N:ce(n.N,`${t}.kdf.N`),r:ce(n.r,`${t}.kdf.r`),p:ce(n.p,`${t}.kdf.p`),salt:S(n.salt,16,`${t}.kdf.salt`)}),iv:S(e.iv,12,`${t}.iv`),wrappedKey:S(e.wrappedKey,48,`${t}.wrappedKey`)}}function ue(e,t){return x(e,ae,t),{id:S(e.id,16,`${t}.id`),type:`recovery`,iv:S(e.iv,12,`${t}.iv`),wrappedKey:S(e.wrappedKey,48,`${t}.wrappedKey`)}}function de(e,t){let n=`slots[${t}]`,r=b(e,n);switch(r.type){case`passphrase`:return Object.freeze(le(r,n));case`recovery`:return Object.freeze(ue(r,n));default:throw y(`${n}.type is not a known slot type`)}}function C(e){let t=b(e,`the document`);if(t.version!==1)throw typeof t.version==`number`&&Number.isSafeInteger(t.version)?new n(`unsupported-version`,`The keyring was written by a version this build does not support.`):y(`version must be an integer`);x(t,ie,`the document`);let r=S(t.sealingPublicKey,32,`sealingPublicKey`);if(!Array.isArray(t.slots)||t.slots.length<1||t.slots.length>8)throw y(`slots must be an array of 1 to 8 entries`);let i=t.slots.map((e,t)=>de(e,t));if(new Set(i.map(e=>e.id)).size!==i.length)throw y(`slot ids must be unique`);return Object.freeze({version:1,sealingPublicKey:r,slots:Object.freeze(i)})}function fe(e){if(typeof e!=`string`)throw y(`the document must be a JSON string`);if(e.length>16384)throw y(`the document is too large`);let t;try{t=JSON.parse(e)}catch{throw y(`the document is not valid JSON`)}return C(t)}function pe(e){return JSON.stringify(C(e),null,2)}function me(t){return e.Buffer.from(C(t).sealingPublicKey,`base64url`)}function w(e){return v(e,`robota/keyring/v1/authorship-hmac`)}function T(t){return e.Buffer.from(t.buffer,t.byteOffset,t.byteLength)}function he(e,r){if(!o(e)||!o(r))throw new n(`invalid-argument`,`An authorship tag is made over bytes.`);try{return(0,t.createHmac)(`sha256`,T(e)).update(T(r)).digest()}catch{throw new n(`invalid-argument`,`The authorship tag could not be computed.`)}}function ge(e,r,i){if(!o(i))throw new n(`invalid-argument`,`An authorship tag is bytes.`);let a=he(e,r);try{return i.length===a.length&&(0,t.timingSafeEqual)(a,T(i))}finally{a.fill(0)}}const _e=`robota/keyring/v1/sealing-x25519`,ve=`robota/keyring/v1/attach-ed25519`,E=e.Buffer.from(`302e020100300506032b656e04220420`,`hex`),D=e.Buffer.from(`302e020100300506032b657004220420`,`hex`),ye=e.Buffer.from(`302a300506032b656e032100`,`hex`),be=e.Buffer.from(`302a300506032b6570032100`,`hex`),xe=ye.length;function O(r){try{let n=(r.type===`public`?r:(0,t.createPublicKey)(r)).export({format:`der`,type:`spki`});return e.Buffer.from(n.subarray(xe))}catch{throw new n(`invalid-argument`,`The key has no exportable public key.`)}}function k(r){if(!o(r)||r.length!==32)throw new n(`invalid-argument`,`A public key must be exactly 32 bytes.`);try{return(0,t.createPublicKey)({key:e.Buffer.concat([ye,r]),format:`der`,type:`spki`})}catch{throw new n(`invalid-argument`,`The public key is not a valid X25519 key.`)}}function Se(r){if(!o(r)||r.length!==32)throw new n(`invalid-argument`,`A public key must be exactly 32 bytes.`);try{return(0,t.createPublicKey)({key:e.Buffer.concat([be,r]),format:`der`,type:`spki`})}catch{throw new n(`invalid-argument`,`The public key is not a valid Ed25519 key.`)}}function A(r,i){let a=e.Buffer.concat([r,i]);try{let e=(0,t.createPrivateKey)({key:a,format:`der`,type:`pkcs8`});return{publicKey:O(e),privateKey:e}}catch{throw new n(`kdf-failed`,`The key derivation could not run on this host.`)}finally{f(a)}}function j(e){return A(E,e)}function Ce(e){return A(D,e)}function we(e){return v(e,_e)}function Te(e){let t=v(e,ve);try{return A(D,t)}finally{f(t)}}function Ee(e){let t=v(e,_e),n=v(e,ve);try{return{sealing:A(E,t),attach:A(D,n)}}finally{f(t,n)}}function De(){let e=(0,t.randomBytes)(32);try{return A(E,e)}finally{f(e)}}function Oe(){return s((0,t.randomBytes)(16))}function ke(e){return v(e,`robota/keyring/v1/recovery-kek`)}function Ae(t,n){return d([e.Buffer.from([1]),u(t),u(n)])}function je(e,n,r,i){let a=(0,t.randomBytes)(12),o=m(e,a,n,Ae(r,i));return{iv:s(a),wrappedKey:s(o)}}function Me(e,t){let r=l(t.iv,12),i=l(t.wrappedKey,48);try{return h(e,r,i,Ae(t.id,t.type))}catch{throw new n(`wrong-credential`,`The credential did not open the keyring.`)}}const Ne=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,Pe=/^[0-9A-Za-z\s-]*$/;function M(){return new n(`invalid-recovery-key`,`The recovery key is not in the expected format.`)}function Fe(e){if(e.length!==20)throw new n(`invalid-argument`,`A recovery key is exactly 20 bytes.`);let t=``,r=0,i=0;for(let n of e){for(r=r<<8|n,i+=8;i>=5;)t+=Ne[r>>>i-5&31],i-=5;r&=(1<<i)-1}let a=[];for(let e=0;e<t.length;e+=4)a.push(t.slice(e,e+4));return a.join(`-`)}function Ie(t){if(typeof t!=`string`||t.length>128||!Pe.test(t))throw M();let n=t.toUpperCase().replace(/[\s-]/g,``).replace(/O/g,`0`).replace(/[IL]/g,`1`);if(n.length!==32)throw M();let r=e.Buffer.alloc(20),i=0,a=0,o=0;for(let e of n){let t=Ne.indexOf(e);if(t<0)throw r.fill(0),M();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 Le=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 he(this.#t().authorship,e)}verifyAuthorship(e,t){return ge(this.#t().authorship,e,t)}lock(){this.#e?.authorship.fill(0),this.#e=void 0}#t(){if(this.#e===void 0)throw new n(`keys-locked`,`The keys are locked.`);return this.#e}};function N(e,t){return new Le(e,t)}function Re(e){return e.type===`passphrase`}function ze(e){return e.type===`recovery`}async function Be(e,n,r){let i=(0,t.randomBytes)(16),a=await ne(n,i,ee);try{let t=je(a,e,r,`passphrase`);return{id:r,type:`passphrase`,kdf:{algorithm:`scrypt`,...ee,salt:s(i)},...t}}finally{f(a)}}function Ve(e,t){let n=Oe(),r=ke(t);try{return{id:n,type:`recovery`,...je(r,e,n,`recovery`)}}finally{f(r)}}async function He(e){let t=Ee(e.masterKey),n=await Be(e.masterKey,e.passphrase,Oe()),r=Ve(e.masterKey,e.recoveryKeyBytes);return{keyring:C({version:1,sealingPublicKey:s(t.sealing.publicKey),slots:[n,r]}),pairs:t}}async function Ue(e){let n=_(e.passphrase),r=(0,t.randomBytes)(32),i=(0,t.randomBytes)(20);try{let{keyring:e,pairs:t}=await He({masterKey:r,passphrase:n,recoveryKeyBytes:i});return{keyring:e,recoveryKey:Fe(i),unlocked:N(t.sealing,w(r))}}finally{f(n,r,i)}}function We(e){return e instanceof n&&e.code===`wrong-credential`}async function Ge(e,t){let r=_(t);try{let t=e.slots.filter(Re);if(t.length===0)throw new n(`slot-not-found`,`The keyring has no passphrase slot.`);for(let e of t)te(e.kdf);for(let e of t){let t=await ne(r,l(e.kdf.salt,16),e.kdf);try{return{masterKey:Me(t,e),slotId:e.id}}catch(e){if(!We(e))throw e}finally{f(t)}}throw new n(`wrong-credential`,`The credential did not open the keyring.`)}finally{f(r)}}function Ke(e,t){let r=Ie(t);try{let t=e.slots.filter(ze);if(t.length===0)throw new n(`slot-not-found`,`The keyring has no recovery slot.`);for(let e of t){let t=ke(r);try{return{masterKey:Me(t,e),slotId:e.id}}catch(e){if(!We(e))throw e}finally{f(t)}}throw new n(`wrong-credential`,`The credential did not open the keyring.`)}finally{f(r)}}async function P(e,t){switch(t?.type){case`passphrase`:return Ge(e,t.passphrase);case`recoveryKey`:return Ke(e,t.recoveryKey);default:throw new n(`invalid-argument`,`The credential type is not recognised.`)}}async function qe(e,t){let{masterKey:n}=await P(C(e),t),r=we(n);try{return N(j(r),w(n))}finally{f(n,r)}}function Je(e,t){return qe(e,{type:`passphrase`,passphrase:t})}function Ye(e,t){return qe(e,{type:`recoveryKey`,recoveryKey:t})}async function Xe(e,t,r){let i=C(e),a=_(r),o;try{o=await P(i,t);let e=t.type===`passphrase`?o.slotId:i.slots.find(Re)?.id;if(e===void 0)throw new n(`slot-not-found`,`The keyring has no passphrase slot.`);let r=await Be(o.masterKey,a,e);return C({...i,slots:i.slots.map(t=>t.id===e?r:t)})}finally{f(a,o?.masterKey)}}function Ze(e,t){return{matches:Qe(e,t.sealing.publicKey)}}function Qe(e,n){let r=me(e);return r.length===n.length&&(0,t.timingSafeEqual)(r,n)}function $e(e,t){return C({...C(e),sealingPublicKey:s(t.sealing.publicKey)})}function et(e){if(!o(e)||e.length!==32)throw new n(`invalid-argument`,`A public key must be a Uint8Array of exactly 32 bytes.`);let r=(0,t.createHash)(`sha256`).update(e).digest(`hex`).slice(0,20),i=[];for(let e=0;e<r.length;e+=4)i.push(r.slice(e,e+4).toUpperCase());return i.join(`-`)}function F(e,r){try{return(0,t.sign)(null,r,e)}catch{throw new n(`kdf-failed`,`The key operation could not run on this host.`)}}function I(e,n,r){if(!o(r)||r.length!==64||!o(n))return!1;try{return(0,t.verify)(null,n,Se(e),r)}catch{return!1}}const tt=/^[A-Za-z0-9._:-]+$/u,nt=[`generation`,`runtimeId`];function L(e){return typeof e==`string`&&e.length>0&&e.length<=128&&tt.test(e)}function R(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new n(`invalid-argument`,`A link binding is a runtime id and a generation.`);let t=e,r=Object.keys(t).sort();if(r.length!==nt.length||r.some((e,t)=>e!==nt[t])||!L(t.runtimeId)||!L(t.generation))throw new n(`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 rt(t){if(t.length===0||t.length>128)return;let n=e.Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString(`latin1`);return L(n)?n:void 0}function it(e){if(!o(e)||e.length!==32)throw new n(`invalid-argument`,`An instance public key is exactly 32 bytes.`);return e}function at(e,t){let n=it(e),r=R(t);return d([u(`robota/link/v1/runtime-cert`),n,u(r.runtimeId),u(r.generation)])}function ot(e,t,n){return!o(e.attachPublicKey)||e.attachPublicKey.length!==32||!o(e.certificate)||e.certificate.length!==64?!1:I(e.attachPublicKey,at(t,n),e.certificate)}const st=new WeakMap;var ct=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t,st.set(this,()=>this.#n())}get sealingKeyMatches(){return this.#n(),this.#t}get fingerprint(){return et(this.#n().sealingPublicKey)}get disposed(){return this.#e===void 0}certify(e,t){let{attach:n}=this.#n(),r=at(e,t);return{attachPublicKey:Uint8Array.from(n.publicKey),certificate:Uint8Array.from(F(n.privateKey,r))}}dispose(){let e=this.#e;e!==void 0&&(this.#e=void 0,f(e.sealingSeed,e.authorship))}#n(){if(this.#e===void 0)throw new n(`keys-locked`,`The owner handover was disposed.`);return this.#e}};function z(e){let t=st.get(e);if(t===void 0)throw new n(`invalid-argument`,`This is not an owner handover this package opened.`);return t()}async function lt(e,t){let n=C(e),{masterKey:r}=await P(n,t),i,a;try{i=we(r),a=w(r);let e=Te(r),t=j(i).publicKey;return new ct({attach:e,sealingSeed:i,sealingPublicKey:t,authorship:a},Qe(n,t))}catch(e){throw f(i,a),e}finally{f(r)}}const B={seed:()=>(0,t.randomBytes)(32),nonce:()=>(0,t.randomBytes)(32)};function V(e){let t=e.seed();try{return j(t)}finally{f(t)}}function H(e,n){if(!o(n)||n.length!==32)throw new i(`malformed`);try{return(0,t.diffieHellman)({privateKey:e,publicKey:k(n)})}catch{throw new i(`malformed`)}}function ut(e){return(0,t.createHash)(`sha256`).update(d(e)).digest()}function U(e,...n){let r=(0,t.createHmac)(`sha256`,e);for(let e of n)r.update(e);return r.digest()}function W(e,n){return e.length===n.length&&(0,t.timingSafeEqual)(e,n)}function dt(r,i,a,o){try{return e.Buffer.from((0,t.hkdfSync)(`sha256`,r,i,a,o))}catch{throw new n(`kdf-failed`,`The key derivation could not run on this host.`)}}const G=new WeakMap,ft=Symbol(`spawner-key`);var pt=class{constructor(e,t){if(e!==ft)throw new n(`invalid-argument`,`A spawner key comes only from an exchange.`);G.set(this,t)}get wiped(){return!G.has(this)}wipe(){G.get(this)?.fill(0),G.delete(this)}};function K(e){let t=G.get(e);if(t===void 0)throw new n(`keys-locked`,`The spawner key was wiped.`);return t}function mt(e,t,n,r){return dt(e,ut([u(`robota/link/v1/spawner-exchange`),t,n,r]),`robota/link/v1/spawner`,32)}function ht(e){let t=V(e);return{message:Uint8Array.from(t.publicKey),complete(e){let n=t;if(t=void 0,n===void 0)throw new i(`out-of-order`);if(!o(e)||e.length!==64)throw new i(`malformed`);let r=e.subarray(0,32),a=Uint8Array.from(e.subarray(32)),s=H(n.privateKey,r);try{let e=mt(s,n.publicKey,r,a);return{spawnerKey:new pt(ft,e),instancePublicKey:a}}finally{f(s)}}}}function gt(){return ht(B)}function _t(t,n,r){if(!o(t)||t.length!==32)throw new i(`malformed`);let a=V(r),s=H(a.privateKey,t);try{return{reply:e.Buffer.concat([a.publicKey,n]),spawnerKey:mt(s,t,a.publicKey,n)}}finally{f(s)}}const vt=new WeakMap;function yt(e){let t=e;if(typeof t!=`object`||!t||!o(t.attachPublicKey)||t.attachPublicKey.length!==32||!o(t.certificate)||t.certificate.length!==64)throw new n(`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 bt=class{#e;#t;#n=!1;#r;#i;constructor(e){this.#t=e;let t=e.seed();try{this.#e=Ce(t)}finally{f(t)}vt.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 i(`out-of-order`);this.#n=!0;let{reply:t,spawnerKey:n}=_t(e,this.#e.publicKey,this.#t);return this.#r=n,t}installTrustRoot(e,t){let n=yt(e),r=R(t);if(this.#i!==void 0)throw new i(`already-certified`);if(this.#r===void 0)throw new i(`spawner-unavailable`);if(!ot(n,this.#e.publicKey,r))throw new i(`certificate-mismatch`);this.#i={root:n,binding:r},f(this.#r),this.#r=void 0}};function xt(e){return new bt(e)}function St(){return xt(B)}function q(e){let t=vt.get(e);if(t===void 0)throw new n(`invalid-argument`,`This is not a runtime identity this package made.`);return t}const J={session:1,owner:2,grant:3},Ct=new Map(Object.entries(J).map(([e,t])=>[t,e])),wt={initiator:1,responder:2},Tt=16*1024*1024;function Et(e){let t=o(e)&&e.length>=17?Ct.get(e[0]):void 0;if(t===void 0)throw new i(`malformed`);return t}function Dt(t){let n=e.Buffer.alloc(12);return n.writeBigUInt64BE(BigInt(t),4),n}var Ot=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 n(`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 n(`invalid-argument`,`A read grant opens only as a grant.`);return{kind:t,plaintext:this.openKind(e,t)}}close(){f(this.#n,this.#r),this.#n=void 0,this.#r=void 0}sealKind(t,r){if(!o(r)||r.length>16777216)throw new n(`invalid-argument`,`A frame carries 0-${Tt} bytes.`);let i=this.#o(this.#n);if(!Number.isSafeInteger(this.#i+1))throw this.close(),new n(`link-closed`,`The link has sent all the frames it can.`);let a=e.Buffer.from([wt[this.#e],J[t]]),s=m(i,Dt(this.#i),r,a);return this.#i+=1,e.Buffer.concat([e.Buffer.from([J[t]]),s])}kindOf(e){this.#o(this.#r);try{return Et(e)}catch(e){throw this.close(),e}}openKind(t,n){let r=this.#o(this.#r),i=this.#e===`initiator`?`responder`:`initiator`,a=e.Buffer.from([wt[i],J[n]]),o;try{o=h(r,Dt(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 n(`link-closed`,`The link is closed.`);return e}},kt=class extends Ot{#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 n(`invalid-argument`,`A read grant goes only over an owner link, from the handover that opened it.`);let{sealingSeed:r,authorship:i}=z(t),a=e.Buffer.alloc(65);try{return a[0]=1,r.copy(a,1),i.copy(a,33),this.sealKind(`grant`,a)}finally{f(a)}}},At=class extends Ot{constructor(e,t,n,r){super(`responder`,e,t,n,r)}openReadGrant(t){if(this.standing!==`owner`)throw this.close(),new n(`invalid-argument`,`A read grant is accepted only on an owner link.`);if(this.kindOf(t)!==`grant`)throw this.close(),new n(`invalid-argument`,`Only a read grant opens as a grant.`);let r=this.openKind(t,`grant`);try{if(r.length!==65||r[0]!==1)throw this.close(),new i(`malformed`);return N(j(r.subarray(1,33)),e.Buffer.from(r.subarray(33)))}finally{f(r)}}};const Y={spawner:1,owner:2},jt={spawner:0,owner:64},Mt={spawner:96,owner:64},Nt={spawner:32,owner:64},X=u(`r`),Z=u(`i`),Pt=358+Math.max(...Object.values(jt))+Math.max(...Object.values(Mt)),Ft=Math.max(...Object.values(Nt));function It(t){return ut([u(`robota/link/v1/handshake`),e.Buffer.from([Y[t.mode]]),t.initiatorEphemeral,t.initiatorNonce,t.responderEphemeral,t.responderNonce,t.instancePublicKey,u(t.binding.runtimeId),u(t.binding.generation),t.certificate])}function Lt(t,n){let r=dt(t,n,`robota/link/v1/frames`,64);try{return{initiatorToResponder:e.Buffer.from(r.subarray(0,32)),responderToInitiator:e.Buffer.from(r.subarray(32))}}finally{f(r)}}function Rt(e,t,n){let r=H(e.privateKey,t);try{return Lt(r,n)}finally{f(r)}}function zt(t){return e.Buffer.concat([t.responderEphemeral,t.responderNonce,t.instancePublicKey,d([u(t.binding.runtimeId),u(t.binding.generation),t.certificate]),t.runtimeProof])}function Bt(e,t){if(!o(e)||e.length>Pt)throw new i(`malformed`);let n=0,r=t=>{if(n+t>e.length)throw new i(`malformed`);let r=Uint8Array.from(e.subarray(n,n+t));return n+=t,r},a=()=>{let e=r(2);return r(e[0]<<8|e[1])},s=r(32),c=r(32),l=r(32),u=rt(a()),d=rt(a()),f=a(),p=r(e.length-n);if(u===void 0||d===void 0||f.length!==jt[t]||p.length!==Mt[t])throw new i(`malformed`);return{responderEphemeral:s,responderNonce:c,instancePublicKey:l,binding:{runtimeId:u,generation:d},certificate:f,runtimeProof:p}}function Vt(e){if(!o(e)||e.length!==66||e[0]!==1)throw new i(`malformed`);let t=e[1]===Y.spawner?`spawner`:e[1]===Y.owner?`owner`:void 0;if(t===void 0)throw new i(`malformed`);return{mode:t,ephemeral:Uint8Array.from(e.subarray(2,34)),nonce:Uint8Array.from(e.subarray(34))}}var Ht=class{mode;#e;#t;#n;#r={step:`new`};constructor(e,t){if(e?.mode===`spawner`)K(e.spawnerKey),this.#n=Uint8Array.from(it(e.instancePublicKey));else if(e?.mode===`owner`)z(e.handover);else throw new n(`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 i(`out-of-order`);let t=V(this.#t),n=this.#t.nonce();return this.#r={step:`waiting`,ephemeral:t,nonce:n},e.Buffer.concat([e.Buffer.from([1,Y[this.mode]]),t.publicKey,n])}answer(t){let n=this.#r;if(n.step!==`waiting`)throw new i(`out-of-order`);this.#r={step:`spent`};let r=Bt(t,this.mode);if(this.#e.mode===`spawner`&&!W(r.instancePublicKey,this.#n))throw new i(`instance-mismatch`);if(this.#e.mode===`owner`){let{attach:e}=z(this.#e.handover);if(!ot({attachPublicKey:e.publicKey,certificate:r.certificate},r.instancePublicKey,r.binding))throw new i(`certificate-mismatch`)}let a=It({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}),o=e.Buffer.concat([X,a]);if(this.#e.mode===`spawner`){let e=W(U(K(this.#e.spawnerKey),X,a),r.runtimeProof.subarray(0,32)),t=I(r.instancePublicKey,o,r.runtimeProof.subarray(32));if(!e||!t)throw new i(`bad-runtime-proof`)}else if(!I(r.instancePublicKey,o,r.runtimeProof))throw new i(`bad-runtime-proof`);let s=Rt(n.ephemeral,r.responderEphemeral,a),c=this.#e.mode===`spawner`?U(K(this.#e.spawnerKey),Z,a):F(z(this.#e.handover).attach.privateKey,e.Buffer.concat([Z,a])),l=this.#e.mode===`owner`?this.#e.handover:void 0;return{proof:c,link:new kt(this.mode,r.binding,r.instancePublicKey,s,l)}}};function Ut(e,t){return new Ht(e,t)}function Wt(e){return Ut(e,B)}var Gt=class{#e;#t;#n=`new`;#r;constructor(e,t){q(e),this.#e=e,this.#t=R(t)}challenge(t){if(this.#n!==`new`)throw new i(`out-of-order`);this.#n=`spent`;let{mode:n,ephemeral:r,nonce:a}=Vt(t),o=q(this.#e),s=o.spawnerKey(),c=o.trust();if(n===`spawner`&&s===void 0)throw new i(`spawner-unavailable`);if(n===`owner`&&(c===void 0||c.binding.runtimeId!==this.#t.runtimeId||c.binding.generation!==this.#t.generation))throw new i(`not-certified`);let l=V(o.entropy),u=o.entropy.nonce(),d=n===`owner`?c.root.certificate:new Uint8Array,f=It({mode:n,initiatorEphemeral:r,initiatorNonce:a,responderEphemeral:l.publicKey,responderNonce:u,instancePublicKey:o.instance.publicKey,binding:this.#t,certificate:d}),p=Rt(l,r,f),m=F(o.instance.privateKey,e.Buffer.concat([X,f])),h=n===`spawner`?e.Buffer.concat([U(s,X,f),m]):m;return this.#r={mode:n,th:f,keys:p,...n===`spawner`?{expectedProof:U(s,Z,f)}:{attachPublicKey:Uint8Array.from(c.root.attachPublicKey)}},this.#n=`waiting`,zt({responderEphemeral:l.publicKey,responderNonce:u,instancePublicKey:o.instance.publicKey,binding:this.#t,certificate:d,runtimeProof:h})}accept(t){let n=this.#r;if(this.#n!==`waiting`||n===void 0)throw new i(`out-of-order`);this.#r=void 0,this.#n=`spent`;try{if(!o(t)||t.length!==Nt[n.mode])throw new i(`malformed`);if(n.mode===`spawner`){if(q(this.#e).spawnerKey()===void 0)throw new i(`spawner-unavailable`);if(!W(n.expectedProof,t))throw new i(`bad-proof`)}else if(!I(n.attachPublicKey,e.Buffer.concat([Z,n.th]),t))throw new i(`bad-proof`)}catch(e){throw f(n.keys.initiatorToResponder,n.keys.responderToInitiator,n.expectedProof),e}f(n.expectedProof);let r=q(this.#e).instance.publicKey;return{standing:n.mode,link:new At(n.mode,this.#t,r,n.keys)}}};function Kt(e,t){return new Gt(e,t)}function Q(e,t,r){if(!o(e)||r!==void 0&&e.length!==r)throw new n(`invalid-argument`,r===void 0?`The ${t} must be a Uint8Array.`:`The ${t} must be a Uint8Array of exactly ${r} bytes.`);return e}function qt(e){let t=e;if(typeof t!=`object`||!t||t.type!==`private`||t.asymmetricKeyType!==`x25519`)throw new n(`invalid-argument`,`The sealing private key must be an X25519 private KeyObject.`);return t}function $(t){return d([e.Buffer.from([1]),Q(t,`aad`)])}function Jt(t,n,r){let i=e.Buffer.concat([n,r]);try{return v(t,`robota/keyring/v1/dek-wrap`,i)}finally{f(t)}}function Yt(){try{let e=(0,t.generateKeyPairSync)(`x25519`);return{privateKey:e.privateKey,publicKey:O(e.publicKey)}}catch{throw new n(`kdf-failed`,`The key derivation could not run on this host.`)}}function Xt(){return(0,t.randomBytes)(32)}function Zt(r,i,a){let o=Q(r,`sealing public key`,32);Q(i,`artifact key`,32);let s=$(a),c=k(o),l=Yt(),u=l.publicKey,d;try{d=(0,t.diffieHellman)({privateKey:l.privateKey,publicKey:c})}catch{throw new n(`invalid-argument`,`The sealing public key cannot be used for key agreement.`)}let p=Jt(d,u,o);try{let n=(0,t.randomBytes)(12),r=m(p,n,i,s);return e.Buffer.concat([e.Buffer.from([1]),u,n,r])}finally{f(p)}}function Qt(e){if(e.length===0)throw new n(`malformed-ciphertext`,`The sealed data is empty.`);if(e[0]!==1)throw new n(`unsupported-version`,`The sealed data uses a format this build does not support.`)}function $t(e,r,i){let a=qt(e);Q(r,`sealed key`);let o=$(i);if(Qt(r),r.length!==93)throw new n(`malformed-ciphertext`,`The sealed key has the wrong length.`);let s=r.subarray(1,33),c=r.subarray(33,45),l=r.subarray(45),u;try{u=(0,t.diffieHellman)({privateKey:a,publicKey:k(s)})}catch{throw new n(`decryption-failed`,`Authentication of the ciphertext failed.`)}let d=Jt(u,s,O(a));try{return h(d,c,l,o)}finally{f(d)}}function en(n,r,i){Q(n,`artifact key`,32),Q(r,`plaintext`);let a=$(i),o=(0,t.randomBytes)(12),s=m(n,o,r,a);return e.Buffer.concat([e.Buffer.from([1]),o,s])}function tn(e,t,r){Q(e,`artifact key`,32),Q(t,`record`);let i=$(r);if(Qt(t),t.length<29)throw new n(`malformed-ciphertext`,`The record is shorter than its fixed overhead.`);return h(e,t.subarray(1,13),t.subarray(13),i)}function nn(t,n,r){let i=Xt();try{return e.Buffer.concat([Zt(t,i,r),en(i,n,r)])}finally{f(i)}}function rn(e){if(Q(e,`blob`),Qt(e),e.length<122)throw new n(`malformed-ciphertext`,`The blob is shorter than its fixed overhead.`);return{sealedKey:e.subarray(0,93),record:e.subarray(93)}}function an(e,t,n){qt(e);let{sealedKey:r,record:i}=rn(t),a=$t(e,r,n);try{return tn(a,i,n)}finally{f(a)}}exports.KEYRING_VERSION=1,exports.KeyringError=n,exports.LINK_CHALLENGE_MAX_BYTES=Pt,exports.LINK_FRAME_MAX_PLAINTEXT_BYTES=Tt,exports.LINK_FRAME_OVERHEAD_BYTES=17,exports.LINK_GRANT_FRAME_BYTES=82,exports.LINK_HELLO_BYTES=66,exports.LINK_PROOF_MAX_BYTES=Ft,exports.LinkRefusedError=i,exports.SpawnerKey=pt,exports.changePassphrase=Xe,exports.checkSealingPublicKey=Ze,exports.createKeyring=Ue,exports.createLinkInitiator=Wt,exports.createLinkResponder=Kt,exports.createRuntimeIdentity=St,exports.decryptRecord=tn,exports.encryptRecord=en,exports.fingerprint=et,exports.generateArtifactKey=Xt,exports.generateTransientSealingKeyPair=De,exports.getSealingPublicKey=me,exports.linkFrameKind=Et,exports.openArtifactKey=$t,exports.openBlob=an,exports.openOwnerHandover=lt,exports.parseKeyring=fe,exports.repairSealingPublicKey=$e,exports.sealArtifactKey=Zt,exports.sealBlob=nn,exports.serializeKeyring=pe,exports.splitSealedBlob=rn,exports.startSpawnerExchange=gt,exports.unlockWithPassphrase=Je,exports.unlockWithRecoveryKey=Ye;
@@ -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.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","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"}