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