@private.me/xbind 3.0.2 → 3.0.4

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 (222) hide show
  1. package/README.md +2366 -204
  2. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
  3. package/dist-standalone/_deps/shared/cjs/errors.js +1 -729
  4. package/dist-standalone/_deps/shared/cjs/index.js +1 -463
  5. package/dist-standalone/_deps/shared/cjs/types.js +1 -315
  6. package/dist-standalone/_deps/shared/errors.js +1 -244
  7. package/dist-standalone/_deps/shared/index.js +1 -72
  8. package/dist-standalone/_deps/shared/types.js +1 -86
  9. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  10. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  20. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  21. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  28. package/dist-standalone/_deps/xchange/errors.js +1 -1
  29. package/dist-standalone/_deps/xchange/index.js +1 -1
  30. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  31. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  32. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  33. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  34. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  40. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  41. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  42. package/dist-standalone/_deps/xregistry/index.js +1 -1
  43. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  44. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  45. package/dist-standalone/_deps/xregistry/types.js +1 -1
  46. package/dist-standalone/agent-call.d.ts +2 -2
  47. package/dist-standalone/agent-call.js +1 -659
  48. package/dist-standalone/agent-sdk.js +1 -328
  49. package/dist-standalone/agent.d.ts +2 -0
  50. package/dist-standalone/agent.js +1 -1800
  51. package/dist-standalone/approval.js +1 -193
  52. package/dist-standalone/async-iterators.d.ts +3 -3
  53. package/dist-standalone/async-iterators.js +1 -382
  54. package/dist-standalone/auth.js +1 -219
  55. package/dist-standalone/auto-accept.js +1 -229
  56. package/dist-standalone/backup-config.js +1 -201
  57. package/dist-standalone/backup.js +1 -326
  58. package/dist-standalone/batch-operations.js +1 -388
  59. package/dist-standalone/cancellation.js +1 -477
  60. package/dist-standalone/checkpoint.js +1 -186
  61. package/dist-standalone/circuit-breaker.js +1 -468
  62. package/dist-standalone/cjs/agent-call.js +1 -701
  63. package/dist-standalone/cjs/agent-sdk.js +1 -332
  64. package/dist-standalone/cjs/agent.js +1 -1837
  65. package/dist-standalone/cjs/approval.js +1 -199
  66. package/dist-standalone/cjs/async-iterators.js +1 -392
  67. package/dist-standalone/cjs/auth.js +1 -225
  68. package/dist-standalone/cjs/auto-accept.js +1 -233
  69. package/dist-standalone/cjs/backup-config.js +1 -207
  70. package/dist-standalone/cjs/backup.js +1 -330
  71. package/dist-standalone/cjs/batch-operations.js +1 -397
  72. package/dist-standalone/cjs/cancellation.js +1 -490
  73. package/dist-standalone/cjs/checkpoint.js +1 -193
  74. package/dist-standalone/cjs/circuit-breaker.js +1 -476
  75. package/dist-standalone/cjs/cli/init.js +1 -492
  76. package/dist-standalone/cjs/config-validation.js +1 -522
  77. package/dist-standalone/cjs/connect.js +1 -312
  78. package/dist-standalone/cjs/connection-pool.js +1 -506
  79. package/dist-standalone/cjs/correlation-id.js +1 -339
  80. package/dist-standalone/cjs/crypto-utils.js +1 -176
  81. package/dist-standalone/cjs/debug-mode.js +1 -534
  82. package/dist-standalone/cjs/did-document.js +1 -101
  83. package/dist-standalone/cjs/did-privateme.js +1 -130
  84. package/dist-standalone/cjs/did-web.js +1 -201
  85. package/dist-standalone/cjs/discovery.js +1 -462
  86. package/dist-standalone/cjs/dual-mode.js +1 -251
  87. package/dist-standalone/cjs/email-templates.js +1 -313
  88. package/dist-standalone/cjs/email-transport.js +1 -239
  89. package/dist-standalone/cjs/envelope.js +1 -538
  90. package/dist-standalone/cjs/errors.js +1 -913
  91. package/dist-standalone/cjs/event-emitter.js +1 -461
  92. package/dist-standalone/cjs/gateway-state.js +1 -55
  93. package/dist-standalone/cjs/gateway-transport.js +1 -120
  94. package/dist-standalone/cjs/graceful-degradation.js +1 -403
  95. package/dist-standalone/cjs/guardrails.js +1 -223
  96. package/dist-standalone/cjs/health-check.js +1 -336
  97. package/dist-standalone/cjs/http-compat.js +1 -272
  98. package/dist-standalone/cjs/http-status-map.js +1 -571
  99. package/dist-standalone/cjs/identity.js +1 -645
  100. package/dist-standalone/cjs/index.js +1 -406
  101. package/dist-standalone/cjs/invitation.js +1 -421
  102. package/dist-standalone/cjs/invite.js +1 -328
  103. package/dist-standalone/cjs/key-agreement.js +1 -335
  104. package/dist-standalone/cjs/lazy-init.js +1 -300
  105. package/dist-standalone/cjs/logger.js +1 -291
  106. package/dist-standalone/cjs/loopback-transport.js +1 -0
  107. package/dist-standalone/cjs/mdns-discovery.js +1 -202
  108. package/dist-standalone/cjs/nonce-store.js +1 -80
  109. package/dist-standalone/cjs/pairing-manager.js +1 -223
  110. package/dist-standalone/cjs/plugin-system.js +1 -264
  111. package/dist-standalone/cjs/plugins/logging.js +1 -168
  112. package/dist-standalone/cjs/plugins/metrics.js +1 -181
  113. package/dist-standalone/cjs/plugins/validation.js +1 -302
  114. package/dist-standalone/cjs/policy.js +1 -320
  115. package/dist-standalone/cjs/progress-callbacks.js +1 -583
  116. package/dist-standalone/cjs/redis-nonce-store.js +1 -76
  117. package/dist-standalone/cjs/registry-middleware.js +1 -50
  118. package/dist-standalone/cjs/retry-strategies.js +1 -544
  119. package/dist-standalone/cjs/retry-transport.js +1 -102
  120. package/dist-standalone/cjs/runtime/browser.js +1 -533
  121. package/dist-standalone/cjs/runtime/edge.js +1 -526
  122. package/dist-standalone/cjs/runtime/react-native.js +1 -394
  123. package/dist-standalone/cjs/security-policy.js +1 -245
  124. package/dist-standalone/cjs/serialization.js +1 -1040
  125. package/dist-standalone/cjs/split-channel.js +1 -225
  126. package/dist-standalone/cjs/subscription-proof.js +1 -230
  127. package/dist-standalone/cjs/succession.js +1 -148
  128. package/dist-standalone/cjs/timeouts.js +1 -412
  129. package/dist-standalone/cjs/trace-context.js +1 -424
  130. package/dist-standalone/cjs/trace-spans.js +1 -495
  131. package/dist-standalone/cjs/transport.js +1 -63
  132. package/dist-standalone/cjs/trust-registry.js +1 -991
  133. package/dist-standalone/cjs/types/error-response.js +1 -56
  134. package/dist-standalone/cjs/vault-auth.js +1 -178
  135. package/dist-standalone/cjs/vault-store-loader.js +1 -194
  136. package/dist-standalone/cjs/verify.js +1 -25
  137. package/dist-standalone/cjs/version-info.js +1 -543
  138. package/dist-standalone/cjs/xfetch.js +1 -340
  139. package/dist-standalone/cli/init.js +1 -455
  140. package/dist-standalone/cli/setup.js +1 -514
  141. package/dist-standalone/cli/types.js +1 -27
  142. package/dist-standalone/cli/xbind.js +1 -148
  143. package/dist-standalone/config-validation.js +1 -513
  144. package/dist-standalone/connect.js +1 -274
  145. package/dist-standalone/connection-pool.js +1 -500
  146. package/dist-standalone/correlation-id.js +1 -326
  147. package/dist-standalone/crypto-utils.d.ts +2 -7
  148. package/dist-standalone/crypto-utils.js +1 -157
  149. package/dist-standalone/debug-mode.js +1 -510
  150. package/dist-standalone/did-document.js +1 -96
  151. package/dist-standalone/did-privateme.js +1 -121
  152. package/dist-standalone/did-web.js +1 -196
  153. package/dist-standalone/discovery.js +1 -458
  154. package/dist-standalone/dual-mode.js +1 -247
  155. package/dist-standalone/email-templates.js +1 -309
  156. package/dist-standalone/email-transport.d.ts +2 -2
  157. package/dist-standalone/email-transport.js +1 -232
  158. package/dist-standalone/envelope.js +1 -525
  159. package/dist-standalone/errors.d.ts +13 -3
  160. package/dist-standalone/errors.js +1 -896
  161. package/dist-standalone/event-emitter.js +1 -456
  162. package/dist-standalone/gateway-state.d.ts +1 -1
  163. package/dist-standalone/gateway-state.js +1 -51
  164. package/dist-standalone/gateway-transport.js +1 -116
  165. package/dist-standalone/graceful-degradation.js +1 -396
  166. package/dist-standalone/guardrails.js +1 -216
  167. package/dist-standalone/health-check.d.ts +5 -1
  168. package/dist-standalone/health-check.js +1 -332
  169. package/dist-standalone/http-compat.d.ts +1 -1
  170. package/dist-standalone/http-compat.js +1 -267
  171. package/dist-standalone/http-status-map.js +1 -561
  172. package/dist-standalone/identity.js +1 -619
  173. package/dist-standalone/index.d.ts +15 -4
  174. package/dist-standalone/index.js +1 -78
  175. package/dist-standalone/invitation.js +1 -415
  176. package/dist-standalone/invite.js +1 -324
  177. package/dist-standalone/key-agreement.js +1 -325
  178. package/dist-standalone/lazy-init.d.ts +11 -6
  179. package/dist-standalone/lazy-init.js +1 -295
  180. package/dist-standalone/logger.js +1 -285
  181. package/dist-standalone/loopback-transport.d.ts +87 -0
  182. package/dist-standalone/loopback-transport.js +1 -0
  183. package/dist-standalone/mdns-discovery.js +1 -195
  184. package/dist-standalone/nonce-store.js +1 -76
  185. package/dist-standalone/pairing-manager.js +1 -219
  186. package/dist-standalone/plugin-system.js +1 -257
  187. package/dist-standalone/plugins/logging.js +1 -163
  188. package/dist-standalone/plugins/metrics.d.ts +4 -4
  189. package/dist-standalone/plugins/metrics.js +1 -176
  190. package/dist-standalone/plugins/validation.js +1 -297
  191. package/dist-standalone/policy.js +1 -315
  192. package/dist-standalone/progress-callbacks.js +1 -576
  193. package/dist-standalone/redis-nonce-store.js +1 -72
  194. package/dist-standalone/registry-middleware.js +1 -47
  195. package/dist-standalone/retry-strategies.js +1 -534
  196. package/dist-standalone/retry-transport.js +1 -98
  197. package/dist-standalone/runtime/browser.js +1 -516
  198. package/dist-standalone/runtime/edge.js +1 -511
  199. package/dist-standalone/runtime/react-native.d.ts +1 -1
  200. package/dist-standalone/runtime/react-native.js +1 -383
  201. package/dist-standalone/security-policy.js +1 -239
  202. package/dist-standalone/serialization.js +1 -1031
  203. package/dist-standalone/split-channel.d.ts +1 -1
  204. package/dist-standalone/split-channel.js +1 -219
  205. package/dist-standalone/subscription-proof.js +1 -224
  206. package/dist-standalone/succession.js +1 -142
  207. package/dist-standalone/timeouts.js +1 -398
  208. package/dist-standalone/trace-context.js +1 -414
  209. package/dist-standalone/trace-spans.js +1 -488
  210. package/dist-standalone/transport.d.ts +1 -1
  211. package/dist-standalone/transport.js +1 -59
  212. package/dist-standalone/trust-registry.d.ts +3 -3
  213. package/dist-standalone/trust-registry.js +1 -950
  214. package/dist-standalone/types/error-response.js +1 -52
  215. package/dist-standalone/vault-auth.js +1 -174
  216. package/dist-standalone/vault-store-loader.d.ts +9 -0
  217. package/dist-standalone/vault-store-loader.js +1 -187
  218. package/dist-standalone/verify.js +1 -16
  219. package/dist-standalone/version-info.js +1 -530
  220. package/dist-standalone/xfetch.js +1 -335
  221. package/package.json +1 -1
  222. package/share1.dat +0 -0
@@ -1,326 +1 @@
1
- /**
2
- * Encrypted Backup and Restore (RCV-3)
3
- *
4
- * Exports and imports encrypted identity and cryptographic state.
5
- * Uses PBKDF2 (NIST SP 800-132) for key derivation and AES-256-GCM
6
- * for authenticated encryption.
7
- *
8
- * @module backup
9
- */
10
- import { ok, err } from"./_deps/shared/index.js";
11
- import { toBase64, fromBase64 } from './crypto-utils.js';
12
- import { exportPKCS8, exportX25519PKCS8, importIdentity, exportMlKemSecretKey, exportMlKemPublicKey, exportMlDsaSecretKey, exportMlDsaPublicKey, } from './identity.js';
13
- /* ── Constants ── */
14
- /** PBKDF2 iterations for key derivation (NIST SP 800-132 minimum: 100,000). */
15
- const PBKDF2_ITERATIONS = 310_000;
16
- /** PBKDF2 salt length in bytes. */
17
- const SALT_LENGTH = 16;
18
- /** AES-256-GCM IV length in bytes. */
19
- const IV_LENGTH = 12;
20
- /** AES-256-GCM key length in bytes. */
21
- const KEY_LENGTH = 32;
22
- /* ── Utility Functions ── */
23
- /**
24
- * Copy Uint8Array to fresh ArrayBuffer (avoids SharedArrayBuffer type issues).
25
- */
26
- function toArrayBuffer(data) {
27
- const buf = new ArrayBuffer(data.byteLength);
28
- new Uint8Array(buf).set(data);
29
- return buf;
30
- }
31
- /**
32
- * Derive a 256-bit AES key from password using PBKDF2-SHA256.
33
- *
34
- * Uses NIST SP 800-132 recommended parameters:
35
- * - 310,000 iterations (as of 2025, recommended for 100ms delay on modern hardware)
36
- * - SHA-256 as PRF
37
- * - 16-byte salt
38
- *
39
- * @param password - User password (UTF-8 string).
40
- * @param salt - 16-byte random salt.
41
- * @returns AES-256 key or error.
42
- */
43
- async function deriveKey(password, salt) {
44
- try {
45
- if (salt.length !== SALT_LENGTH) {
46
- return err('INVALID_BACKUP');
47
- }
48
- // Import password as PBKDF2 base key
49
- const baseKey = await crypto.subtle.importKey('raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveBits']);
50
- // Derive 256 bits (32 bytes) for AES-256
51
- const derivedBits = new Uint8Array(await crypto.subtle.deriveBits({
52
- name: 'PBKDF2',
53
- hash: 'SHA-256',
54
- salt: toArrayBuffer(salt),
55
- iterations: PBKDF2_ITERATIONS,
56
- }, baseKey, 256));
57
- // Import derived key as AES-GCM key
58
- const aesKey = await crypto.subtle.importKey('raw', toArrayBuffer(derivedBits), { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);
59
- return ok(aesKey);
60
- }
61
- catch {
62
- return err('PBKDF2_FAILED');
63
- }
64
- }
65
- /**
66
- * Serialize an AgentIdentity to a plaintext backup payload.
67
- * (The payload is then encrypted before storage.)
68
- */
69
- async function serializeIdentity(identity) {
70
- try {
71
- // Export Ed25519 and X25519 private keys as PKCS8
72
- const ed25519PkResult = await exportPKCS8(identity.privateKey);
73
- if (!ed25519PkResult.ok)
74
- return err('EXPORT_FAILED');
75
- const x25519PkResult = await exportX25519PKCS8(identity.x25519PrivateKey);
76
- if (!x25519PkResult.ok)
77
- return err('EXPORT_FAILED');
78
- // Collect ML-KEM and ML-DSA keys if present
79
- const mlKemSecretKey = exportMlKemSecretKey(identity);
80
- const mlKemPublicKey = exportMlKemPublicKey(identity);
81
- const mlDsaSecretKey = exportMlDsaSecretKey(identity);
82
- const mlDsaPublicKey = exportMlDsaPublicKey(identity);
83
- // Serialize rotated keys
84
- const rotatedKeys = identity.rotatedKeys
85
- ? await Promise.all(identity.rotatedKeys.map(async (rotated) => {
86
- const x25519Pk = await exportX25519PKCS8(rotated.x25519PrivateKey);
87
- if (!x25519Pk.ok)
88
- throw new Error('Failed to export rotated X25519 key');
89
- return {
90
- rotatedAt: rotated.rotatedAt,
91
- x25519Pkcs8: toBase64(x25519Pk.value),
92
- ...(rotated.mlKemSecretKey ? { mlKemSecretKey: toBase64(rotated.mlKemSecretKey) } : {}),
93
- };
94
- }))
95
- : undefined;
96
- return ok({
97
- did: identity.did,
98
- rawPublicKey: toBase64(identity.rawPublicKey),
99
- ed25519Pkcs8: toBase64(ed25519PkResult.value),
100
- x25519Pkcs8: toBase64(x25519PkResult.value),
101
- ...(mlKemSecretKey ? { mlKemSecretKey: toBase64(mlKemSecretKey) } : {}),
102
- ...(mlKemPublicKey ? { mlKemPublicKey: toBase64(mlKemPublicKey) } : {}),
103
- ...(mlDsaSecretKey ? { mlDsaSecretKey: toBase64(mlDsaSecretKey) } : {}),
104
- ...(mlDsaPublicKey ? { mlDsaPublicKey: toBase64(mlDsaPublicKey) } : {}),
105
- ...(rotatedKeys ? { rotatedKeys } : {}),
106
- exportedAt: Date.now(),
107
- });
108
- }
109
- catch {
110
- return err('EXPORT_FAILED');
111
- }
112
- }
113
- /* ── Export: Encrypt Identity to Backup ── */
114
- /**
115
- * Export an identity as an encrypted backup blob.
116
- *
117
- * Encrypts the complete identity (Ed25519, X25519, ML-KEM, ML-DSA keys,
118
- * rotated keys) with a password using PBKDF2 + AES-256-GCM.
119
- *
120
- * The backup can be stored safely in cloud storage, version control, or
121
- * transmitted over untrusted channels. Only the password holder can decrypt.
122
- *
123
- * Security:
124
- * - PBKDF2-SHA256 with 310,000 iterations (≈100ms on modern hardware)
125
- * - AES-256-GCM for authenticated encryption
126
- * - Random salt + IV for each backup (no two backups are identical)
127
- * - 16-byte GCM tag prevents tampering
128
- *
129
- * @param identity - Agent identity to backup.
130
- * @param password - User password (plaintext). Will be PBKDF2 derived.
131
- * @returns Encrypted backup blob or error.
132
- *
133
- * @example
134
- * ```typescript
135
- * import { generateIdentity } from '@private.me/xbind';
136
- * import { exportBackup } from '@private.me/xbind';
137
- *
138
- * const identity = await generateIdentity();
139
- * if (!identity.ok) throw identity.error;
140
- *
141
- * const backup = await exportBackup(identity.value, 'user-password');
142
- * if (!backup.ok) throw backup.error;
143
- *
144
- * // Store backup securely
145
- * const json = JSON.stringify(backup.value);
146
- * localStorage.setItem('backup', json);
147
- * ```
148
- */
149
- export async function exportBackup(identity, password) {
150
- try {
151
- // Serialize identity to plaintext payload
152
- const payloadResult = await serializeIdentity(identity);
153
- if (!payloadResult.ok)
154
- return payloadResult;
155
- // Generate random salt and IV
156
- const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
157
- const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
158
- // Derive AES key from password
159
- const keyResult = await deriveKey(password, salt);
160
- if (!keyResult.ok)
161
- return keyResult;
162
- // Encrypt payload as JSON
163
- const payloadJson = JSON.stringify(payloadResult.value);
164
- const plaintext = new TextEncoder().encode(payloadJson);
165
- // Encrypt with AES-256-GCM
166
- const encryptResult = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: toArrayBuffer(iv) }, keyResult.value, plaintext);
167
- const ciphertext = new Uint8Array(encryptResult);
168
- // GCM includes authentication tag in the ciphertext.
169
- // Split: ciphertext is everything except last 16 bytes, tag is last 16 bytes.
170
- // (Web Crypto API appends the tag to the ciphertext)
171
- if (ciphertext.length < 16) {
172
- return err('ENCRYPTION_FAILED');
173
- }
174
- const ctLen = ciphertext.length - 16;
175
- const ct = ciphertext.slice(0, ctLen);
176
- const tag = ciphertext.slice(ctLen);
177
- return ok({
178
- version: 1,
179
- salt: toBase64(salt),
180
- iv: toBase64(iv),
181
- ciphertext: toBase64(ct),
182
- tag: toBase64(tag),
183
- });
184
- }
185
- catch {
186
- return err('ENCRYPTION_FAILED');
187
- }
188
- }
189
- /* ── Import: Decrypt Backup to Identity ── */
190
- /**
191
- * Restore an identity from an encrypted backup blob.
192
- *
193
- * Decrypts the backup using PBKDF2-derived AES key. Verifies authenticity
194
- * with GCM tag before returning plaintext keys.
195
- *
196
- * @param backup - Encrypted backup blob.
197
- * @param password - User password (plaintext). Will be PBKDF2 derived.
198
- * @returns Restored AgentIdentity or error.
199
- *
200
- * @example
201
- * ```typescript
202
- * import { importBackup } from '@private.me/xbind';
203
- *
204
- * const json = localStorage.getItem('backup');
205
- * const backup = JSON.parse(json);
206
- *
207
- * const identity = await importBackup(backup, 'user-password');
208
- * if (!identity.ok) {
209
- * if (identity.error === 'INVALID_PASSWORD') {
210
- * console.error('Wrong password!');
211
- * } else {
212
- * throw identity.error;
213
- * }
214
- * }
215
- *
216
- * // Now use restored identity
217
- * const agent = new Agent(identity.value);
218
- * ```
219
- */
220
- export async function importBackup(backup, password) {
221
- try {
222
- // Validate version
223
- if (backup.version !== 1) {
224
- return err('INVALID_BACKUP');
225
- }
226
- // Decode base64 fields
227
- let salt;
228
- let iv;
229
- let ciphertext;
230
- let tag;
231
- try {
232
- salt = fromBase64(backup.salt);
233
- iv = fromBase64(backup.iv);
234
- ciphertext = fromBase64(backup.ciphertext);
235
- tag = fromBase64(backup.tag);
236
- }
237
- catch {
238
- return err('INVALID_BACKUP');
239
- }
240
- // Validate lengths
241
- if (salt.length !== SALT_LENGTH || iv.length !== IV_LENGTH || tag.length !== 16) {
242
- return err('INVALID_BACKUP');
243
- }
244
- // Derive AES key from password
245
- const keyResult = await deriveKey(password, salt);
246
- if (!keyResult.ok)
247
- return keyResult;
248
- // Reconstruct full GCM input (ciphertext + tag)
249
- const gcmInput = new Uint8Array(ciphertext.length + tag.length);
250
- gcmInput.set(ciphertext);
251
- gcmInput.set(tag, ciphertext.length);
252
- // Decrypt with AES-256-GCM
253
- let plaintext;
254
- try {
255
- plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: toArrayBuffer(iv) }, keyResult.value, toArrayBuffer(gcmInput));
256
- }
257
- catch (decryptErr) {
258
- // GCM authentication failure (wrong password or tampered backup)
259
- console.warn('[xBind] GCM verification failed:', decryptErr);
260
- return err('INVALID_PASSWORD');
261
- }
262
- // Parse JSON payload
263
- let payload;
264
- try {
265
- const jsonStr = new TextDecoder().decode(plaintext);
266
- payload = JSON.parse(jsonStr);
267
- }
268
- catch {
269
- return err('INVALID_BACKUP');
270
- }
271
- // Validate payload structure
272
- if (!payload.did || !payload.ed25519Pkcs8 || !payload.x25519Pkcs8) {
273
- return err('INVALID_BACKUP');
274
- }
275
- // Decode base64 keys
276
- let ed25519Pkcs8;
277
- let x25519Pkcs8;
278
- let mlKemSecretKey;
279
- let mlKemPublicKey;
280
- let mlDsaSecretKey;
281
- let mlDsaPublicKey;
282
- try {
283
- ed25519Pkcs8 = fromBase64(payload.ed25519Pkcs8);
284
- x25519Pkcs8 = fromBase64(payload.x25519Pkcs8);
285
- if (payload.mlKemSecretKey)
286
- mlKemSecretKey = fromBase64(payload.mlKemSecretKey);
287
- if (payload.mlKemPublicKey)
288
- mlKemPublicKey = fromBase64(payload.mlKemPublicKey);
289
- if (payload.mlDsaSecretKey)
290
- mlDsaSecretKey = fromBase64(payload.mlDsaSecretKey);
291
- if (payload.mlDsaPublicKey)
292
- mlDsaPublicKey = fromBase64(payload.mlDsaPublicKey);
293
- }
294
- catch {
295
- return err('INVALID_BACKUP');
296
- }
297
- // Import identity from decoded keys
298
- const identity = await importIdentity(ed25519Pkcs8, x25519Pkcs8, mlKemSecretKey, mlKemPublicKey, mlDsaSecretKey, mlDsaPublicKey);
299
- if (!identity.ok)
300
- return err('IMPORT_FAILED');
301
- // Restore rotated keys if present
302
- if (payload.rotatedKeys && payload.rotatedKeys.length > 0) {
303
- const restored = identity.value;
304
- const rotatedKeys = await Promise.all(payload.rotatedKeys.map(async (rk) => {
305
- const x25519Pk = fromBase64(rk.x25519Pkcs8);
306
- const x25519PrivateKey = await crypto.subtle.importKey('pkcs8', toArrayBuffer(x25519Pk), { name: 'X25519' }, true, ['deriveBits']);
307
- const mlKemSecretKeyRk = rk.mlKemSecretKey ? fromBase64(rk.mlKemSecretKey) : undefined;
308
- return {
309
- rotatedAt: rk.rotatedAt,
310
- x25519PrivateKey,
311
- ...(mlKemSecretKeyRk ? { mlKemSecretKey: mlKemSecretKeyRk } : {}),
312
- };
313
- }));
314
- // Return identity with restored rotated keys
315
- return ok({
316
- ...restored,
317
- rotatedKeys: rotatedKeys,
318
- });
319
- }
320
- return identity;
321
- }
322
- catch (importErr) {
323
- console.warn('[xBind] Import backup failed:', importErr);
324
- return err('DECRYPTION_FAILED');
325
- }
326
- }
1
+ import{ok,err}from"./_deps/shared/index.js";import{toBase64,fromBase64}from"./crypto-utils.js";import{exportPKCS8,exportX25519PKCS8,importIdentity,exportMlKemSecretKey,exportMlKemPublicKey,exportMlDsaSecretKey,exportMlDsaPublicKey}from"./identity.js";const PBKDF2_ITERATIONS=31e4,SALT_LENGTH=16,IV_LENGTH=12;function toArrayBuffer(e){const t=new ArrayBuffer(e.byteLength);return new Uint8Array(t).set(e),t}async function deriveKey(e,t){try{if(16!==t.length)return err("INVALID_BACKUP");const r=await crypto.subtle.importKey("raw",(new TextEncoder).encode(e),"PBKDF2",!1,["deriveBits"]),a=new Uint8Array(await crypto.subtle.deriveBits({name:"PBKDF2",hash:"SHA-256",salt:toArrayBuffer(t),iterations:31e4},r,256)),o=await crypto.subtle.importKey("raw",toArrayBuffer(a),{name:"AES-GCM"},!1,["encrypt","decrypt"]);return ok(o)}catch{return err("PBKDF2_FAILED")}}async function serializeIdentity(e){try{const t=await exportPKCS8(e.privateKey);if(!t.ok)return err("EXPORT_FAILED");const r=await exportX25519PKCS8(e.x25519PrivateKey);if(!r.ok)return err("EXPORT_FAILED");const a=exportMlKemSecretKey(e),o=exportMlKemPublicKey(e),n=exportMlDsaSecretKey(e),s=exportMlDsaPublicKey(e),i=e.rotatedKeys?await Promise.all(e.rotatedKeys.map(async e=>{const t=await exportX25519PKCS8(e.x25519PrivateKey);if(!t.ok)throw new Error("Failed to export rotated X25519 key");return{rotatedAt:e.rotatedAt,x25519Pkcs8:toBase64(t.value),...e.mlKemSecretKey?{mlKemSecretKey:toBase64(e.mlKemSecretKey)}:{}}})):void 0;return ok({did:e.did,rawPublicKey:toBase64(e.rawPublicKey),ed25519Pkcs8:toBase64(t.value),x25519Pkcs8:toBase64(r.value),...a?{mlKemSecretKey:toBase64(a)}:{},...o?{mlKemPublicKey:toBase64(o)}:{},...n?{mlDsaSecretKey:toBase64(n)}:{},...s?{mlDsaPublicKey:toBase64(s)}:{},...i?{rotatedKeys:i}:{},exportedAt:Date.now()})}catch{return err("EXPORT_FAILED")}}export async function exportBackup(e,t){try{const r=await serializeIdentity(e);if(!r.ok)return r;const a=crypto.getRandomValues(new Uint8Array(16)),o=crypto.getRandomValues(new Uint8Array(12)),n=await deriveKey(t,a);if(!n.ok)return n;const s=JSON.stringify(r.value),i=(new TextEncoder).encode(s),c=await crypto.subtle.encrypt({name:"AES-GCM",iv:toArrayBuffer(o)},n.value,i),y=new Uint8Array(c);if(y.length<16)return err("ENCRYPTION_FAILED");const l=y.length-16,K=y.slice(0,l),m=y.slice(l);return ok({version:1,salt:toBase64(a),iv:toBase64(o),ciphertext:toBase64(K),tag:toBase64(m)})}catch{return err("ENCRYPTION_FAILED")}}export async function importBackup(e,t){try{if(1!==e.version)return err("INVALID_BACKUP");let r,a,o,n;try{r=fromBase64(e.salt),a=fromBase64(e.iv),o=fromBase64(e.ciphertext),n=fromBase64(e.tag)}catch{return err("INVALID_BACKUP")}if(16!==r.length||12!==a.length||16!==n.length)return err("INVALID_BACKUP");const s=await deriveKey(t,r);if(!s.ok)return s;const i=new Uint8Array(o.length+n.length);let c,y,l,K,m,u,f,B;i.set(o),i.set(n,o.length);try{c=await crypto.subtle.decrypt({name:"AES-GCM",iv:toArrayBuffer(a)},s.value,toArrayBuffer(i))}catch{return err("INVALID_PASSWORD")}try{const e=(new TextDecoder).decode(c);y=JSON.parse(e)}catch{return err("INVALID_BACKUP")}if(!y.did||!y.ed25519Pkcs8||!y.x25519Pkcs8)return err("INVALID_BACKUP");try{l=fromBase64(y.ed25519Pkcs8),K=fromBase64(y.x25519Pkcs8),y.mlKemSecretKey&&(m=fromBase64(y.mlKemSecretKey)),y.mlKemPublicKey&&(u=fromBase64(y.mlKemPublicKey)),y.mlDsaSecretKey&&(f=fromBase64(y.mlDsaSecretKey)),y.mlDsaPublicKey&&(B=fromBase64(y.mlDsaPublicKey))}catch{return err("INVALID_BACKUP")}const d=await importIdentity(l,K,m,u,f,B);if(!d.ok)return err("IMPORT_FAILED");if(y.rotatedKeys&&y.rotatedKeys.length>0){const e=d.value,t=await Promise.all(y.rotatedKeys.map(async e=>{const t=fromBase64(e.x25519Pkcs8),r=await crypto.subtle.importKey("pkcs8",toArrayBuffer(t),{name:"X25519"},!0,["deriveBits"]),a=e.mlKemSecretKey?fromBase64(e.mlKemSecretKey):void 0;return{rotatedAt:e.rotatedAt,x25519PrivateKey:r,...a?{mlKemSecretKey:a}:{}}}));return ok({...e,rotatedKeys:t})}return d}catch{return err("DECRYPTION_FAILED")}}