@private.me/xbind 1.3.5 → 2.3.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.
- package/LICENSES.md +212 -0
- package/README.md +388 -6
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
- package/dist-standalone/_deps/shared/cjs/errors.js +1 -639
- package/dist-standalone/_deps/shared/cjs/index.js +1 -496
- package/dist-standalone/_deps/shared/cjs/types.js +1 -317
- package/dist-standalone/_deps/shared/errors.js +1 -255
- package/dist-standalone/_deps/shared/index.js +1 -74
- package/dist-standalone/_deps/shared/types.js +1 -90
- 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 +1 -642
- package/dist-standalone/agent-sdk.js +1 -328
- package/dist-standalone/agent.d.ts +95 -5
- package/dist-standalone/agent.js +1 -1545
- package/dist-standalone/approval.js +1 -193
- package/dist-standalone/async-iterators.d.ts +275 -0
- package/dist-standalone/async-iterators.js +1 -0
- package/dist-standalone/auth.js +1 -219
- package/dist-standalone/auto-accept.js +1 -229
- package/dist-standalone/backup-config.js +1 -201
- package/dist-standalone/backup.d.ts +114 -0
- package/dist-standalone/backup.js +1 -0
- package/dist-standalone/batch-operations.d.ts +297 -0
- package/dist-standalone/batch-operations.js +1 -0
- package/dist-standalone/cancellation.d.ts +301 -0
- package/dist-standalone/cancellation.js +1 -0
- package/dist-standalone/checkpoint.js +1 -186
- package/dist-standalone/circuit-breaker.d.ts +351 -0
- package/dist-standalone/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/agent-call.js +1 -651
- package/dist-standalone/cjs/agent-sdk.js +1 -332
- package/dist-standalone/cjs/agent.js +1 -1582
- package/dist-standalone/cjs/approval.js +1 -199
- package/dist-standalone/cjs/async-iterators.js +1 -0
- package/dist-standalone/cjs/auth.js +1 -225
- package/dist-standalone/cjs/auto-accept.js +1 -233
- package/dist-standalone/cjs/backup-config.js +1 -207
- package/dist-standalone/cjs/backup.js +1 -0
- package/dist-standalone/cjs/batch-operations.js +1 -0
- package/dist-standalone/cjs/cancellation.js +1 -0
- package/dist-standalone/cjs/checkpoint.js +1 -193
- package/dist-standalone/cjs/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/cli/init.js +1 -486
- package/dist-standalone/cjs/config-validation.js +1 -0
- package/dist-standalone/cjs/connect.js +1 -312
- package/dist-standalone/cjs/connection-pool.js +1 -0
- package/dist-standalone/cjs/correlation-id.js +1 -339
- package/dist-standalone/cjs/crypto-utils.js +1 -0
- package/dist-standalone/cjs/debug-mode.js +1 -0
- package/dist-standalone/cjs/did-document.js +1 -101
- package/dist-standalone/cjs/did-privateme.js +1 -130
- package/dist-standalone/cjs/did-web.js +1 -201
- package/dist-standalone/cjs/discovery.js +1 -462
- package/dist-standalone/cjs/dual-mode.js +1 -251
- package/dist-standalone/cjs/email-templates.js +1 -313
- package/dist-standalone/cjs/email-transport.js +1 -239
- package/dist-standalone/cjs/envelope.js +1 -510
- package/dist-standalone/cjs/errors.js +1 -826
- package/dist-standalone/cjs/event-emitter.js +1 -0
- package/dist-standalone/cjs/gateway-state.js +1 -55
- package/dist-standalone/cjs/gateway-transport.js +1 -120
- package/dist-standalone/cjs/graceful-degradation.js +1 -0
- package/dist-standalone/cjs/guardrails.js +1 -223
- package/dist-standalone/cjs/health-check.js +1 -0
- package/dist-standalone/cjs/http-compat.js +1 -272
- package/dist-standalone/cjs/http-status-map.js +1 -571
- package/dist-standalone/cjs/identity.js +1 -540
- package/dist-standalone/cjs/index.js +1 -237
- package/dist-standalone/cjs/invitation.js +1 -421
- package/dist-standalone/cjs/invite.js +1 -328
- package/dist-standalone/cjs/key-agreement.js +1 -246
- package/dist-standalone/cjs/lazy-init.js +1 -300
- package/dist-standalone/cjs/logger.js +1 -0
- package/dist-standalone/cjs/mdns-discovery.js +1 -202
- package/dist-standalone/cjs/nonce-store.js +1 -66
- package/dist-standalone/cjs/pairing-manager.js +1 -223
- package/dist-standalone/cjs/plugin-system.js +1 -0
- package/dist-standalone/cjs/plugins/logging.js +1 -0
- package/dist-standalone/cjs/plugins/metrics.js +1 -0
- package/dist-standalone/cjs/plugins/validation.js +1 -0
- package/dist-standalone/cjs/policy.js +1 -320
- package/dist-standalone/cjs/progress-callbacks.js +1 -0
- package/dist-standalone/cjs/redis-nonce-store.js +1 -76
- package/dist-standalone/cjs/registry-middleware.js +1 -50
- package/dist-standalone/cjs/retry-strategies.js +1 -0
- package/dist-standalone/cjs/retry-transport.js +1 -102
- package/dist-standalone/cjs/runtime/browser.js +1 -0
- package/dist-standalone/cjs/runtime/edge.js +1 -0
- package/dist-standalone/cjs/runtime/react-native.js +1 -0
- package/dist-standalone/cjs/security-policy.js +1 -245
- package/dist-standalone/cjs/serialization.js +1 -0
- package/dist-standalone/cjs/split-channel.js +1 -177
- package/dist-standalone/cjs/subscription-proof.js +1 -230
- package/dist-standalone/cjs/succession.js +1 -148
- package/dist-standalone/cjs/timeouts.js +1 -0
- package/dist-standalone/cjs/trace-context.js +1 -0
- package/dist-standalone/cjs/trace-spans.js +1 -0
- package/dist-standalone/cjs/transport.js +1 -63
- package/dist-standalone/cjs/trust-registry.js +1 -742
- package/dist-standalone/cjs/types/error-response.js +1 -56
- package/dist-standalone/cjs/vault-auth.js +1 -0
- package/dist-standalone/cjs/vault-store-loader.js +1 -0
- package/dist-standalone/cjs/verify.js +1 -25
- package/dist-standalone/cjs/version-info.js +1 -0
- package/dist-standalone/cjs/xfetch.js +1 -252
- package/dist-standalone/cli/init.js +1 -449
- package/dist-standalone/cli/setup.js +1 -514
- package/dist-standalone/cli/types.js +1 -27
- package/dist-standalone/cli/xbind.js +1 -148
- package/dist-standalone/config-validation.d.ts +185 -0
- package/dist-standalone/config-validation.js +1 -0
- package/dist-standalone/connect.js +1 -274
- package/dist-standalone/connection-pool.d.ts +251 -0
- package/dist-standalone/connection-pool.js +1 -0
- package/dist-standalone/correlation-id.js +1 -326
- package/dist-standalone/crypto-utils.d.ts +60 -0
- package/dist-standalone/crypto-utils.js +1 -0
- package/dist-standalone/debug-mode.d.ts +286 -0
- package/dist-standalone/debug-mode.js +1 -0
- package/dist-standalone/did-document.js +1 -96
- package/dist-standalone/did-privateme.js +1 -121
- package/dist-standalone/did-web.js +1 -196
- package/dist-standalone/discovery.js +1 -458
- package/dist-standalone/dual-mode.js +1 -247
- package/dist-standalone/email-templates.js +1 -309
- package/dist-standalone/email-transport.js +1 -232
- package/dist-standalone/envelope.d.ts +29 -1
- package/dist-standalone/envelope.js +1 -497
- package/dist-standalone/errors.d.ts +10 -0
- package/dist-standalone/errors.js +1 -811
- package/dist-standalone/event-emitter.d.ts +395 -0
- package/dist-standalone/event-emitter.js +1 -0
- package/dist-standalone/gateway-state.js +1 -51
- package/dist-standalone/gateway-transport.js +1 -116
- package/dist-standalone/graceful-degradation.d.ts +246 -0
- package/dist-standalone/graceful-degradation.js +1 -0
- package/dist-standalone/guardrails.js +1 -216
- package/dist-standalone/health-check.d.ts +150 -0
- package/dist-standalone/health-check.js +1 -0
- package/dist-standalone/http-compat.js +1 -267
- package/dist-standalone/http-status-map.js +1 -561
- package/dist-standalone/identity.d.ts +64 -1
- package/dist-standalone/identity.js +1 -515
- package/dist-standalone/index.d.ts +45 -3
- package/dist-standalone/index.js +1 -52
- package/dist-standalone/invitation.js +1 -415
- package/dist-standalone/invite.js +1 -324
- package/dist-standalone/key-agreement.d.ts +61 -13
- package/dist-standalone/key-agreement.js +1 -236
- package/dist-standalone/lazy-init.js +1 -295
- package/dist-standalone/logger.d.ts +77 -0
- package/dist-standalone/logger.js +1 -0
- package/dist-standalone/mdns-discovery.js +1 -195
- package/dist-standalone/nonce-store.d.ts +16 -3
- package/dist-standalone/nonce-store.js +1 -62
- package/dist-standalone/package.json +0 -1
- package/dist-standalone/pairing-manager.js +1 -219
- package/dist-standalone/plugin-system.d.ts +145 -0
- package/dist-standalone/plugin-system.js +1 -0
- package/dist-standalone/policy.js +1 -315
- package/dist-standalone/progress-callbacks.d.ts +394 -0
- package/dist-standalone/progress-callbacks.js +1 -0
- package/dist-standalone/redis-nonce-store.js +1 -72
- package/dist-standalone/registry-middleware.js +1 -47
- package/dist-standalone/retry-strategies.d.ts +382 -0
- package/dist-standalone/retry-strategies.js +1 -0
- package/dist-standalone/retry-transport.js +1 -98
- package/dist-standalone/security-policy.js +1 -239
- package/dist-standalone/serialization.d.ts +244 -0
- package/dist-standalone/serialization.js +1 -0
- package/dist-standalone/split-channel.d.ts +49 -1
- package/dist-standalone/split-channel.js +1 -171
- package/dist-standalone/subscription-proof.js +1 -224
- package/dist-standalone/succession.js +1 -142
- package/dist-standalone/timeouts.d.ts +275 -0
- package/dist-standalone/timeouts.js +1 -0
- package/dist-standalone/trace-context.d.ts +252 -0
- package/dist-standalone/trace-context.js +1 -0
- package/dist-standalone/trace-spans.d.ts +360 -0
- package/dist-standalone/trace-spans.js +1 -0
- package/dist-standalone/transport.js +1 -59
- package/dist-standalone/trust-registry.d.ts +106 -5
- package/dist-standalone/trust-registry.js +1 -702
- package/dist-standalone/vault-auth.d.ts +91 -0
- package/dist-standalone/vault-auth.js +1 -0
- package/dist-standalone/vault-store-loader.d.ts +110 -0
- package/dist-standalone/vault-store-loader.js +1 -0
- package/dist-standalone/verify.js +1 -16
- package/dist-standalone/version-info.d.ts +259 -0
- package/dist-standalone/version-info.js +1 -0
- package/dist-standalone/xfetch.js +1 -247
- package/llms.txt +1 -0
- package/package.json +65 -5
- package/share1.dat +0 -0
- package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
- package/dist-standalone/_deps/crypto/base64.js +0 -222
- package/dist-standalone/_deps/crypto/cjs/base64.js +0 -665
- package/dist-standalone/_deps/crypto/cjs/errors.js +0 -675
- package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -473
- package/dist-standalone/_deps/crypto/cjs/index.js +0 -852
- package/dist-standalone/_deps/crypto/cjs/package.json +0 -1
- package/dist-standalone/_deps/crypto/cjs/padding.js +0 -511
- package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -372
- package/dist-standalone/_deps/crypto/cjs/shares.js +0 -874
- package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -1021
- package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -443
- package/dist-standalone/_deps/crypto/cjs/verify.js +0 -414
- package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -923
- package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
- package/dist-standalone/_deps/crypto/errors.js +0 -199
- package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
- package/dist-standalone/_deps/crypto/hmac.js +0 -134
- package/dist-standalone/_deps/crypto/index.d.ts +0 -20
- package/dist-standalone/_deps/crypto/index.js +0 -145
- package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
- package/dist-standalone/_deps/crypto/padding.js +0 -159
- package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
- package/dist-standalone/_deps/crypto/share-header.js +0 -92
- package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
- package/dist-standalone/_deps/crypto/shares.js +0 -295
- package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
- package/dist-standalone/_deps/crypto/tlv.js +0 -364
- package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
- package/dist-standalone/_deps/crypto/uuid.js +0 -136
- package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
- package/dist-standalone/_deps/crypto/verify.js +0 -71
- package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
- package/dist-standalone/_deps/crypto/xorida.js +0 -366
- package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/errors.js.map +0 -1
- package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/index.js.map +0 -1
- package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
- package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/index.js.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
- package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/types.js.map +0 -1
|
@@ -1,171 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* XorIDA split-channel bridge for @private.me/xbind.
|
|
3
|
-
*
|
|
4
|
-
* Bridges @private.me/crypto threshold sharing with the agent-sdk
|
|
5
|
-
* TransportEnvelope format. Splits plaintext into n shares with HMAC
|
|
6
|
-
* integrity, each share wrapped in its own envelope for independent routing.
|
|
7
|
-
*
|
|
8
|
-
* Pipeline:
|
|
9
|
-
* split: pad -> HMAC -> XorIDA split -> share Uint8Arrays with metadata
|
|
10
|
-
* reconstruct: collect k shares -> XorIDA reconstruct -> HMAC verify -> unpad
|
|
11
|
-
*/
|
|
12
|
-
import { ok, err } from"./_deps/shared/index.js";
|
|
13
|
-
import { splitXorIDA, reconstructXorIDA, nextOddPrime, pkcs7Pad, pkcs7Unpad, generateHMAC, verifyHMAC, toBase64, fromBase64, generateUUID, formatShareHeader, parseShareHeader, } from"./_deps/crypto/index.js";
|
|
14
|
-
/** Default split-channel configuration: 3 shares, threshold 2. */
|
|
15
|
-
export const DEFAULT_SPLIT_CONFIG = {
|
|
16
|
-
totalShares: 3,
|
|
17
|
-
threshold: 2,
|
|
18
|
-
};
|
|
19
|
-
/* ── Split ── */
|
|
20
|
-
/**
|
|
21
|
-
* Split plaintext into n shares via XorIDA with HMAC integrity.
|
|
22
|
-
*
|
|
23
|
-
* Pipeline: pad(PKCS#7) -> HMAC(padded) -> XorIDA split -> ChannelShare[]
|
|
24
|
-
*
|
|
25
|
-
* @param plaintext - Raw plaintext bytes to split
|
|
26
|
-
* @param config - Split configuration (totalShares, threshold)
|
|
27
|
-
* @returns Array of n ChannelShare objects ready for envelope wrapping
|
|
28
|
-
*/
|
|
29
|
-
export async function splitForChannel(plaintext, config = DEFAULT_SPLIT_CONFIG) {
|
|
30
|
-
const { totalShares: n, threshold: k } = config;
|
|
31
|
-
if (n < 2 || k < 2 || k > n) {
|
|
32
|
-
return err('SPLIT_FAILED:INVALID_PARAMS');
|
|
33
|
-
}
|
|
34
|
-
const groupId = generateUUID();
|
|
35
|
-
return splitForChannelWithGroupId(plaintext, config, groupId);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Split plaintext with a specific groupId (for testability).
|
|
39
|
-
*
|
|
40
|
-
* @param plaintext - Raw plaintext bytes
|
|
41
|
-
* @param config - Split configuration
|
|
42
|
-
* @param groupId - UUID to use for the share group
|
|
43
|
-
* @returns Array of ChannelShare objects
|
|
44
|
-
*/
|
|
45
|
-
export async function splitForChannelWithGroupId(plaintext, config, groupId) {
|
|
46
|
-
const { totalShares: n, threshold: k } = config;
|
|
47
|
-
if (n < 2 || k < 2 || k > n) {
|
|
48
|
-
return err('SPLIT_FAILED:INVALID_PARAMS');
|
|
49
|
-
}
|
|
50
|
-
const p = nextOddPrime(n);
|
|
51
|
-
const blockSize = p - 1;
|
|
52
|
-
const padded = pkcs7Pad(plaintext, blockSize);
|
|
53
|
-
const { key: hmacKey, signature: hmacSig } = await generateHMAC(padded);
|
|
54
|
-
let shareArrays;
|
|
55
|
-
try {
|
|
56
|
-
shareArrays = splitXorIDA(padded, n, k);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return err('SPLIT_FAILED');
|
|
60
|
-
}
|
|
61
|
-
const hmacKeyB64 = toBase64(hmacKey);
|
|
62
|
-
const hmacSigB64 = toBase64(hmacSig);
|
|
63
|
-
const shares = shareArrays.map((data, index) => ({
|
|
64
|
-
data: formatShareHeader(toBase64(data)),
|
|
65
|
-
index,
|
|
66
|
-
total: n,
|
|
67
|
-
threshold: k,
|
|
68
|
-
groupId,
|
|
69
|
-
hmacKey: hmacKeyB64,
|
|
70
|
-
hmacSig: hmacSigB64,
|
|
71
|
-
}));
|
|
72
|
-
return ok(shares);
|
|
73
|
-
}
|
|
74
|
-
/* ── Reconstruct ── */
|
|
75
|
-
/**
|
|
76
|
-
* Reconstruct plaintext from k-of-n shares.
|
|
77
|
-
*
|
|
78
|
-
* Pipeline: validate -> XorIDA reconstruct -> HMAC verify -> unpad -> plaintext
|
|
79
|
-
* HMAC verification happens BEFORE the data is trusted.
|
|
80
|
-
*
|
|
81
|
-
* @param shares - Array of at least k ChannelShare objects
|
|
82
|
-
* @returns Reconstructed plaintext bytes
|
|
83
|
-
*/
|
|
84
|
-
export async function reconstructFromChannel(shares) {
|
|
85
|
-
const validationResult = validateShares(shares);
|
|
86
|
-
if (!validationResult.ok)
|
|
87
|
-
return validationResult;
|
|
88
|
-
const { k, n } = validationResult.value;
|
|
89
|
-
const usedShares = shares.slice(0, k);
|
|
90
|
-
return reconstructValidated(usedShares, n, k);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Validate share consistency before reconstruction.
|
|
94
|
-
*
|
|
95
|
-
* @param shares - Shares to validate
|
|
96
|
-
* @returns Validated parameters or error
|
|
97
|
-
*/
|
|
98
|
-
function validateShares(shares) {
|
|
99
|
-
if (shares.length === 0) {
|
|
100
|
-
return err('INSUFFICIENT_SHARES');
|
|
101
|
-
}
|
|
102
|
-
const first = shares[0];
|
|
103
|
-
const k = first.threshold;
|
|
104
|
-
const n = first.total;
|
|
105
|
-
if (shares.length < k) {
|
|
106
|
-
return err('INSUFFICIENT_SHARES');
|
|
107
|
-
}
|
|
108
|
-
const indexSet = new Set();
|
|
109
|
-
for (const share of shares) {
|
|
110
|
-
if (share.groupId !== first.groupId) {
|
|
111
|
-
return err('INCONSISTENT_SHARES');
|
|
112
|
-
}
|
|
113
|
-
if (share.total !== n || share.threshold !== k) {
|
|
114
|
-
return err('INCONSISTENT_SHARES');
|
|
115
|
-
}
|
|
116
|
-
if (share.index < 0 || share.index >= n) {
|
|
117
|
-
return err('INVALID_SHARE_DATA');
|
|
118
|
-
}
|
|
119
|
-
if (indexSet.has(share.index)) {
|
|
120
|
-
return err('INVALID_SHARE_DATA');
|
|
121
|
-
}
|
|
122
|
-
indexSet.add(share.index);
|
|
123
|
-
}
|
|
124
|
-
return ok({ k, n, groupId: first.groupId });
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Perform XorIDA reconstruction and HMAC verification.
|
|
128
|
-
*
|
|
129
|
-
* @param usedShares - Exactly k validated shares
|
|
130
|
-
* @param n - Total shares
|
|
131
|
-
* @param k - Threshold
|
|
132
|
-
* @returns Reconstructed plaintext
|
|
133
|
-
*/
|
|
134
|
-
async function reconstructValidated(usedShares, n, k) {
|
|
135
|
-
let shareData;
|
|
136
|
-
try {
|
|
137
|
-
shareData = usedShares.map((s) => fromBase64(parseShareHeader(s.data)));
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return err('INVALID_SHARE_DATA:BASE64');
|
|
141
|
-
}
|
|
142
|
-
const indices = usedShares.map((s) => s.index);
|
|
143
|
-
let padded;
|
|
144
|
-
try {
|
|
145
|
-
padded = reconstructXorIDA(shareData, indices, n, k);
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
return err('SPLIT_FAILED:RECONSTRUCT');
|
|
149
|
-
}
|
|
150
|
-
const first = usedShares[0];
|
|
151
|
-
let hmacKey;
|
|
152
|
-
let hmacSig;
|
|
153
|
-
try {
|
|
154
|
-
hmacKey = fromBase64(first.hmacKey);
|
|
155
|
-
hmacSig = fromBase64(first.hmacSig);
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return err('INVALID_SHARE_DATA:HMAC_DECODE');
|
|
159
|
-
}
|
|
160
|
-
const hmacValid = await verifyHMAC(hmacKey, padded, hmacSig);
|
|
161
|
-
if (!hmacValid) {
|
|
162
|
-
return err('HMAC_VERIFICATION_FAILED');
|
|
163
|
-
}
|
|
164
|
-
const p = nextOddPrime(n);
|
|
165
|
-
const blockSize = p - 1;
|
|
166
|
-
const unpadResult = pkcs7Unpad(padded, blockSize);
|
|
167
|
-
if (!unpadResult.ok) {
|
|
168
|
-
return err('UNPAD_FAILED');
|
|
169
|
-
}
|
|
170
|
-
return ok(unpadResult.value);
|
|
171
|
-
}
|
|
1
|
+
import{ok,err}from"./_deps/shared/index.js";import{splitXorIDA,reconstructXorIDA,nextOddPrime,pkcs7Pad,pkcs7Unpad,generateHMAC,verifyHMAC,toBase64,fromBase64,generateUUID,formatShareHeader,parseShareHeader}from"./crypto-utils.js";export const DEFAULT_SPLIT_CONFIG={totalShares:3,threshold:2};export async function splitForChannel(r,e=DEFAULT_SPLIT_CONFIG){const{totalShares:t,threshold:n}=e;if(t<2||n<2||n>t)return err("SPLIT_FAILED:INVALID_PARAMS");return splitForChannelWithGroupId(r,e,generateUUID())}export async function splitForChannelWithGroupId(r,e,t){const{totalShares:n,threshold:a}=e;if(n<2||a<2||a>n)return err("SPLIT_FAILED:INVALID_PARAMS");const o=nextOddPrime(n),s=pkcs7Pad(r,o-1),{key:I,signature:c}=await generateHMAC(s);let i;try{i=splitXorIDA(s,n,a)}catch{return err("SPLIT_FAILED")}const A=toBase64(I),d=toBase64(c),u=i.map((r,e)=>({data:formatShareHeader(toBase64(r)),index:e,total:n,threshold:a,groupId:t,hmacKey:A,hmacSig:d}));return ok(u)}export async function reconstructFromChannel(r){const e=validateShares(r);if(!e.ok)return e;const{k:t,n:n}=e.value;return reconstructValidated(r.slice(0,t),n,t)}function validateShares(r){if(0===r.length)return err("INSUFFICIENT_SHARES");const e=r[0],t=e.threshold,n=e.total;if(r.length<t)return err("INSUFFICIENT_SHARES");const a=new Set;for(const o of r){if(o.groupId!==e.groupId)return err("INCONSISTENT_SHARES");if(o.total!==n||o.threshold!==t)return err("INCONSISTENT_SHARES");if(o.index<0||o.index>=n)return err("INVALID_SHARE_DATA");if(a.has(o.index))return err("INVALID_SHARE_DATA");a.add(o.index)}return ok({k:t,n:n,groupId:e.groupId})}async function reconstructValidated(r,e,t){let n;try{n=r.map(r=>fromBase64(parseShareHeader(r.data)))}catch{return err("INVALID_SHARE_DATA:BASE64")}const a=r.map(r=>r.index);let o;try{o=reconstructXorIDA(n,a,e,t)}catch{return err("SPLIT_FAILED:RECONSTRUCT")}const s=r[0];let I,c;try{I=fromBase64(s.hmacKey),c=fromBase64(s.hmacSig)}catch{return err("INVALID_SHARE_DATA:HMAC_DECODE")}if(!await verifyHMAC(I,o,c))return err("HMAC_VERIFICATION_FAILED");const i=nextOddPrime(e),A=pkcs7Unpad(o,i-1);return A.ok?ok(A.value):err("UNPAD_FAILED")}
|
|
@@ -1,224 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { signMlDsa65, verifyMlDsa65, ML_DSA65_SIG_BYTES } from './identity.js';
|
|
3
|
-
/* ── Internal Helpers ── */
|
|
4
|
-
/**
|
|
5
|
-
* Compute SHA-256 hash of a Uint8Array.
|
|
6
|
-
*
|
|
7
|
-
* @param data - Data to hash.
|
|
8
|
-
* @returns Hex-encoded SHA-256 hash.
|
|
9
|
-
*/
|
|
10
|
-
async function sha256Hash(data) {
|
|
11
|
-
try {
|
|
12
|
-
// SAFETY: Creating fresh copy ensures proper ArrayBuffer type (not SharedArrayBuffer)
|
|
13
|
-
const buffer = new Uint8Array(data);
|
|
14
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|
|
15
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
16
|
-
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
17
|
-
return ok(hashHex);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return err('HASH_FAILED');
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Serialize subscription proof data for signing (canonical format).
|
|
25
|
-
*
|
|
26
|
-
* Format: type|version|peer_did|bloom_filter_hash|asserted_at|expires_at
|
|
27
|
-
*
|
|
28
|
-
* @param proof - Subscription proof (without signature).
|
|
29
|
-
* @returns Canonical byte representation.
|
|
30
|
-
*/
|
|
31
|
-
function serializeForSigning(proof) {
|
|
32
|
-
const canonical = `${proof.type}|${proof.version}|${proof.peer_did}|${proof.bloom_filter_hash}|${proof.asserted_at}|${proof.expires_at}`;
|
|
33
|
-
return new TextEncoder().encode(canonical);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Base64 encode (standard, not URL-safe).
|
|
37
|
-
*/
|
|
38
|
-
function toBase64(data) {
|
|
39
|
-
// Use Buffer in Node.js, btoa in browser
|
|
40
|
-
if (typeof Buffer !== 'undefined') {
|
|
41
|
-
return Buffer.from(data).toString('base64');
|
|
42
|
-
}
|
|
43
|
-
// SAFETY: btoa is standard in browsers
|
|
44
|
-
return globalThis.btoa(String.fromCharCode(...data));
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Base64 decode (standard, not URL-safe).
|
|
48
|
-
*/
|
|
49
|
-
function fromBase64(data) {
|
|
50
|
-
// Use Buffer in Node.js, atob in browser
|
|
51
|
-
if (typeof Buffer !== 'undefined') {
|
|
52
|
-
return new Uint8Array(Buffer.from(data, 'base64'));
|
|
53
|
-
}
|
|
54
|
-
// SAFETY: atob is standard in browsers
|
|
55
|
-
const binary = globalThis.atob(data);
|
|
56
|
-
const bytes = new Uint8Array(binary.length);
|
|
57
|
-
for (let i = 0; i < binary.length; i++) {
|
|
58
|
-
bytes[i] = binary.charCodeAt(i);
|
|
59
|
-
}
|
|
60
|
-
return bytes;
|
|
61
|
-
}
|
|
62
|
-
/* ── Public API ── */
|
|
63
|
-
/**
|
|
64
|
-
* Create a subscription proof (client-side).
|
|
65
|
-
*
|
|
66
|
-
* Signs a bloom filter hash with the peer's ML-DSA-65 private key, creating
|
|
67
|
-
* a portable proof that can be used to resume subscriptions on a new gateway.
|
|
68
|
-
*
|
|
69
|
-
* @param peerDid - Subscriber's DID (must match the private key).
|
|
70
|
-
* @param bloomFilterHash - SHA-256 hash of the bloom filter (hex string).
|
|
71
|
-
* @param privateKey - 32-byte ML-DSA-65 secret key seed.
|
|
72
|
-
* @param expiresAt - Optional expiration timestamp (ms). Default: 30 days from now.
|
|
73
|
-
* @returns Subscription proof with ML-DSA-65 signature.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```typescript
|
|
77
|
-
* const proof = await createSubscriptionProof(
|
|
78
|
-
* 'did:key:z6Mk...',
|
|
79
|
-
* 'a1b2c3...',
|
|
80
|
-
* mlDsaPrivateKey,
|
|
81
|
-
* Date.now() + 30 * 24 * 60 * 60 * 1000
|
|
82
|
-
* );
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
export async function createSubscriptionProof(peerDid, bloomFilterHash, privateKey, expiresAt) {
|
|
86
|
-
const assertedAt = Date.now();
|
|
87
|
-
const expiresAtFinal = expiresAt ?? (assertedAt + 30 * 24 * 60 * 60 * 1000); // 30 days default
|
|
88
|
-
// Build proof without signature
|
|
89
|
-
const proofWithoutSig = {
|
|
90
|
-
type: 'SubscriptionProof',
|
|
91
|
-
version: '1.0',
|
|
92
|
-
peer_did: peerDid,
|
|
93
|
-
bloom_filter_hash: bloomFilterHash,
|
|
94
|
-
asserted_at: assertedAt,
|
|
95
|
-
expires_at: expiresAtFinal,
|
|
96
|
-
};
|
|
97
|
-
// Serialize and sign
|
|
98
|
-
const dataToSign = serializeForSigning(proofWithoutSig);
|
|
99
|
-
const signatureResult = await signMlDsa65(privateKey, dataToSign);
|
|
100
|
-
if (!signatureResult.ok) {
|
|
101
|
-
return err('SIGNATURE_VERIFICATION_FAILED');
|
|
102
|
-
}
|
|
103
|
-
const signature = signatureResult.value;
|
|
104
|
-
if (signature.length !== ML_DSA65_SIG_BYTES) {
|
|
105
|
-
return err('SIGNATURE_VERIFICATION_FAILED');
|
|
106
|
-
}
|
|
107
|
-
// Build final proof
|
|
108
|
-
const proof = {
|
|
109
|
-
...proofWithoutSig,
|
|
110
|
-
peer_signature_algorithm: 'ML-DSA-65',
|
|
111
|
-
peer_signature: toBase64(signature),
|
|
112
|
-
};
|
|
113
|
-
return ok(proof);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Verify a subscription proof (gateway-side).
|
|
117
|
-
*
|
|
118
|
-
* Validates the ML-DSA-65 signature and checks expiration.
|
|
119
|
-
*
|
|
120
|
-
* @param proof - Subscription proof to verify.
|
|
121
|
-
* @param peerPublicKey - 1952-byte ML-DSA-65 public key of the subscriber.
|
|
122
|
-
* @returns true if valid, false if invalid or expired.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```typescript
|
|
126
|
-
* const isValid = await verifySubscriptionProof(proof, mlDsaPublicKey);
|
|
127
|
-
* if (isValid.ok && isValid.value) {
|
|
128
|
-
* // Resume subscription
|
|
129
|
-
* }
|
|
130
|
-
* ```
|
|
131
|
-
*/
|
|
132
|
-
export async function verifySubscriptionProof(proof, peerPublicKey) {
|
|
133
|
-
// Check proof version
|
|
134
|
-
if (proof.version !== '1.0' || proof.type !== 'SubscriptionProof') {
|
|
135
|
-
return ok(false);
|
|
136
|
-
}
|
|
137
|
-
// Check expiration
|
|
138
|
-
if (Date.now() > proof.expires_at) {
|
|
139
|
-
return err('EXPIRED');
|
|
140
|
-
}
|
|
141
|
-
// Check signature algorithm
|
|
142
|
-
if (proof.peer_signature_algorithm !== 'ML-DSA-65') {
|
|
143
|
-
return ok(false);
|
|
144
|
-
}
|
|
145
|
-
// Decode signature
|
|
146
|
-
let signature;
|
|
147
|
-
try {
|
|
148
|
-
signature = fromBase64(proof.peer_signature);
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return ok(false);
|
|
152
|
-
}
|
|
153
|
-
if (signature.length !== ML_DSA65_SIG_BYTES) {
|
|
154
|
-
return ok(false);
|
|
155
|
-
}
|
|
156
|
-
// Reconstruct signed data
|
|
157
|
-
const proofWithoutSig = {
|
|
158
|
-
type: proof.type,
|
|
159
|
-
version: proof.version,
|
|
160
|
-
peer_did: proof.peer_did,
|
|
161
|
-
bloom_filter_hash: proof.bloom_filter_hash,
|
|
162
|
-
asserted_at: proof.asserted_at,
|
|
163
|
-
expires_at: proof.expires_at,
|
|
164
|
-
};
|
|
165
|
-
const dataToVerify = serializeForSigning(proofWithoutSig);
|
|
166
|
-
// Verify signature
|
|
167
|
-
const verifyResult = await verifyMlDsa65(peerPublicKey, signature, dataToVerify);
|
|
168
|
-
if (!verifyResult.ok) {
|
|
169
|
-
return err('SIGNATURE_VERIFICATION_FAILED');
|
|
170
|
-
}
|
|
171
|
-
return ok(verifyResult.value);
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Resume subscription on a new gateway using a proof.
|
|
175
|
-
*
|
|
176
|
-
* Presents the subscription proof to the new gateway, allowing the client
|
|
177
|
-
* to resume receiving trust events without re-subscribing.
|
|
178
|
-
*
|
|
179
|
-
* @param proof - Valid subscription proof.
|
|
180
|
-
* @param newGatewayUrl - Base URL of the new gateway (e.g., https://atelier2.xail.io).
|
|
181
|
-
* @returns Success or network error.
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```typescript
|
|
185
|
-
* const result = await resumeSubscription(proof, 'https://atelier2.xail.io');
|
|
186
|
-
* if (result.ok) {
|
|
187
|
-
* console.log('Subscription resumed on new gateway');
|
|
188
|
-
* }
|
|
189
|
-
* ```
|
|
190
|
-
*/
|
|
191
|
-
export async function resumeSubscription(proof, newGatewayUrl) {
|
|
192
|
-
try {
|
|
193
|
-
const baseUrl = newGatewayUrl.replace(/\/$/, '');
|
|
194
|
-
const response = await fetch(`${baseUrl}/trust/resume`, {
|
|
195
|
-
method: 'POST',
|
|
196
|
-
headers: { 'Content-Type': 'application/json' },
|
|
197
|
-
body: JSON.stringify(proof),
|
|
198
|
-
});
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
return err('NETWORK_ERROR');
|
|
201
|
-
}
|
|
202
|
-
return ok(undefined);
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
return err('NETWORK_ERROR');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Compute SHA-256 hash of a bloom filter for proof creation.
|
|
210
|
-
*
|
|
211
|
-
* @param bloomFilter - Bloom filter bytes.
|
|
212
|
-
* @returns Hex-encoded SHA-256 hash.
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```typescript
|
|
216
|
-
* const hash = await hashBloomFilter(bloomFilterBytes);
|
|
217
|
-
* if (hash.ok) {
|
|
218
|
-
* const proof = await createSubscriptionProof(did, hash.value, privateKey);
|
|
219
|
-
* }
|
|
220
|
-
* ```
|
|
221
|
-
*/
|
|
222
|
-
export async function hashBloomFilter(bloomFilter) {
|
|
223
|
-
return sha256Hash(bloomFilter);
|
|
224
|
-
}
|
|
1
|
+
import{ok,err}from"./_deps/shared/index.js";import{signMlDsa65,verifyMlDsa65,ML_DSA65_SIG_BYTES}from"./identity.js";async function sha256Hash(r){try{const e=new Uint8Array(r),t=await crypto.subtle.digest("SHA-256",e),n=Array.from(new Uint8Array(t)).map(r=>r.toString(16).padStart(2,"0")).join("");return ok(n)}catch{return err("HASH_FAILED")}}function serializeForSigning(r){const e=`${r.type}|${r.version}|${r.peer_did}|${r.bloom_filter_hash}|${r.asserted_at}|${r.expires_at}`;return(new TextEncoder).encode(e)}function toBase64(r){return"undefined"!=typeof Buffer?Buffer.from(r).toString("base64"):globalThis.btoa(String.fromCharCode(...r))}function fromBase64(r){if("undefined"!=typeof Buffer)return new Uint8Array(Buffer.from(r,"base64"));const e=globalThis.atob(r),t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e.charCodeAt(r);return t}export async function createSubscriptionProof(r,e,t,n){const o=Date.now(),i={type:"SubscriptionProof",version:"1.0",peer_did:r,bloom_filter_hash:e,asserted_at:o,expires_at:n??o+2592e6},a=serializeForSigning(i),s=await signMlDsa65(t,a);if(!s.ok)return err("SIGNATURE_VERIFICATION_FAILED");const f=s.value;if(f.length!==ML_DSA65_SIG_BYTES)return err("SIGNATURE_VERIFICATION_FAILED");const u={...i,peer_signature_algorithm:"ML-DSA-65",peer_signature:toBase64(f)};return ok(u)}export async function verifySubscriptionProof(r,e){if("1.0"!==r.version||"SubscriptionProof"!==r.type)return ok(!1);if(Date.now()>r.expires_at)return err("EXPIRED");if("ML-DSA-65"!==r.peer_signature_algorithm)return ok(!1);let t;try{t=fromBase64(r.peer_signature)}catch{return ok(!1)}if(t.length!==ML_DSA65_SIG_BYTES)return ok(!1);const n=serializeForSigning({type:r.type,version:r.version,peer_did:r.peer_did,bloom_filter_hash:r.bloom_filter_hash,asserted_at:r.asserted_at,expires_at:r.expires_at}),o=await verifyMlDsa65(e,t,n);return o.ok?ok(o.value):err("SIGNATURE_VERIFICATION_FAILED")}export async function resumeSubscription(r,e){try{const t=e.replace(/\/$/,"");return(await fetch(`${t}/trust/resume`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)})).ok?ok(void 0):err("NETWORK_ERROR")}catch{return err("NETWORK_ERROR")}}export async function hashBloomFilter(r){return sha256Hash(r)}
|
|
@@ -1,142 +1 @@
|
|
|
1
|
-
import { signMlDsa65, verifyMlDsa65 } from
|
|
2
|
-
import { ok, err } from"./_deps/shared/index.js";
|
|
3
|
-
/**
|
|
4
|
-
* Create DID succession announcement with dual signatures.
|
|
5
|
-
*
|
|
6
|
-
* Format: did:key:OLD||did:key:NEW||sequence||effective_at||expires_at||grace_period||timestamp||sig_OLD||sig_NEW
|
|
7
|
-
* Both old and new keys must sign to prove possession.
|
|
8
|
-
*
|
|
9
|
-
* @param oldDid - DID being rotated (old key)
|
|
10
|
-
* @param oldPrivateKey - ML-DSA-65 secret key (32-byte seed) for old DID
|
|
11
|
-
* @param newDid - DID replacing old DID
|
|
12
|
-
* @param newPrivateKey - ML-DSA-65 secret key (32-byte seed) for new DID
|
|
13
|
-
* @param rotationSequence - Monotonically increasing sequence number
|
|
14
|
-
* @param gracePeriodMs - Overlap window in milliseconds (default: 7 days)
|
|
15
|
-
* @returns Succession announcement with dual signatures or error
|
|
16
|
-
*/
|
|
17
|
-
export async function createSuccession(oldDid, oldPrivateKey, newDid, newPrivateKey, rotationSequence, gracePeriodMs = 7 * 24 * 60 * 60 * 1000 // 7 days default
|
|
18
|
-
) {
|
|
19
|
-
const timestamp = Date.now();
|
|
20
|
-
const effective_at = timestamp;
|
|
21
|
-
const expires_at = timestamp + 30 * 24 * 60 * 60 * 1000; // 30 days proof validity
|
|
22
|
-
const message = `${oldDid}||${newDid}||${rotationSequence}||${effective_at}||${expires_at}||${gracePeriodMs}||${timestamp}`;
|
|
23
|
-
const messageBytes = new TextEncoder().encode(message);
|
|
24
|
-
const oldSigResult = await signMlDsa65(oldPrivateKey, messageBytes);
|
|
25
|
-
if (!oldSigResult.ok) {
|
|
26
|
-
return err('SIGN_FAILED');
|
|
27
|
-
}
|
|
28
|
-
const newSigResult = await signMlDsa65(newPrivateKey, messageBytes);
|
|
29
|
-
if (!newSigResult.ok) {
|
|
30
|
-
return err('SIGN_FAILED');
|
|
31
|
-
}
|
|
32
|
-
return ok({
|
|
33
|
-
oldDid,
|
|
34
|
-
newDid,
|
|
35
|
-
rotation_sequence: rotationSequence,
|
|
36
|
-
effective_at,
|
|
37
|
-
expires_at,
|
|
38
|
-
grace_period_ms: gracePeriodMs,
|
|
39
|
-
timestamp,
|
|
40
|
-
oldSignature: oldSigResult.value,
|
|
41
|
-
newSignature: newSigResult.value
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Verify succession announcement dual signatures.
|
|
46
|
-
*
|
|
47
|
-
* Both old and new keys must have valid signatures.
|
|
48
|
-
* Also checks sequence monotonicity (new sequence must be greater than current).
|
|
49
|
-
*
|
|
50
|
-
* @param announcement - Succession announcement to verify
|
|
51
|
-
* @param oldPublicKey - ML-DSA-65 public key (1952 bytes) for old DID
|
|
52
|
-
* @param newPublicKey - ML-DSA-65 public key (1952 bytes) for new DID
|
|
53
|
-
* @returns true if both signatures valid, false otherwise
|
|
54
|
-
*/
|
|
55
|
-
export async function verifySuccession(announcement, oldPublicKey, newPublicKey) {
|
|
56
|
-
const message = `${announcement.oldDid}||${announcement.newDid}||${announcement.rotation_sequence}||${announcement.effective_at}||${announcement.expires_at}||${announcement.grace_period_ms}||${announcement.timestamp}`;
|
|
57
|
-
const messageBytes = new TextEncoder().encode(message);
|
|
58
|
-
const oldValidResult = await verifyMlDsa65(oldPublicKey, announcement.oldSignature, messageBytes);
|
|
59
|
-
if (!oldValidResult.ok) {
|
|
60
|
-
return err('VERIFY_FAILED');
|
|
61
|
-
}
|
|
62
|
-
const newValidResult = await verifyMlDsa65(newPublicKey, announcement.newSignature, messageBytes);
|
|
63
|
-
if (!newValidResult.ok) {
|
|
64
|
-
return err('VERIFY_FAILED');
|
|
65
|
-
}
|
|
66
|
-
const bothValid = oldValidResult.value && newValidResult.value;
|
|
67
|
-
return ok(bothValid);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Encode succession announcement to wire format.
|
|
71
|
-
*
|
|
72
|
-
* Format: did:key:OLD||did:key:NEW||sequence||effective_at||expires_at||grace_period||timestamp||sig_OLD||sig_NEW
|
|
73
|
-
*
|
|
74
|
-
* @param announcement - Succession announcement to encode
|
|
75
|
-
* @returns Wire format string (base64-encoded signatures)
|
|
76
|
-
*/
|
|
77
|
-
export function encodeSuccession(announcement) {
|
|
78
|
-
const parts = [
|
|
79
|
-
announcement.oldDid,
|
|
80
|
-
announcement.newDid,
|
|
81
|
-
announcement.rotation_sequence.toString(),
|
|
82
|
-
announcement.effective_at.toString(),
|
|
83
|
-
announcement.expires_at.toString(),
|
|
84
|
-
announcement.grace_period_ms.toString(),
|
|
85
|
-
announcement.timestamp.toString(),
|
|
86
|
-
Buffer.from(announcement.oldSignature).toString('base64'),
|
|
87
|
-
Buffer.from(announcement.newSignature).toString('base64')
|
|
88
|
-
];
|
|
89
|
-
return parts.join('||');
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Decode succession announcement from wire format.
|
|
93
|
-
*
|
|
94
|
-
* @param encoded - Wire format string
|
|
95
|
-
* @returns Parsed succession announcement or error
|
|
96
|
-
*/
|
|
97
|
-
export function decodeSuccession(encoded) {
|
|
98
|
-
const parts = encoded.split('||');
|
|
99
|
-
if (parts.length !== 9) {
|
|
100
|
-
return err('INVALID_FORMAT');
|
|
101
|
-
}
|
|
102
|
-
const oldDid = parts[0];
|
|
103
|
-
const newDid = parts[1];
|
|
104
|
-
const sequenceStr = parts[2];
|
|
105
|
-
const effectiveAtStr = parts[3];
|
|
106
|
-
const expiresAtStr = parts[4];
|
|
107
|
-
const gracePeriodStr = parts[5];
|
|
108
|
-
const timestampStr = parts[6];
|
|
109
|
-
const oldSigB64 = parts[7];
|
|
110
|
-
const newSigB64 = parts[8];
|
|
111
|
-
if (!oldDid || !newDid || !sequenceStr || !effectiveAtStr || !expiresAtStr || !gracePeriodStr || !timestampStr || !oldSigB64 || !newSigB64) {
|
|
112
|
-
return err('INVALID_FORMAT');
|
|
113
|
-
}
|
|
114
|
-
const rotation_sequence = parseInt(sequenceStr, 10);
|
|
115
|
-
const effective_at = parseInt(effectiveAtStr, 10);
|
|
116
|
-
const expires_at = parseInt(expiresAtStr, 10);
|
|
117
|
-
const grace_period_ms = parseInt(gracePeriodStr, 10);
|
|
118
|
-
const timestamp = parseInt(timestampStr, 10);
|
|
119
|
-
if (isNaN(rotation_sequence) || rotation_sequence < 0 ||
|
|
120
|
-
isNaN(effective_at) || effective_at < 0 ||
|
|
121
|
-
isNaN(expires_at) || expires_at < 0 ||
|
|
122
|
-
isNaN(grace_period_ms) || grace_period_ms < 0 ||
|
|
123
|
-
isNaN(timestamp) || timestamp < 0) {
|
|
124
|
-
return err('INVALID_TIMESTAMP');
|
|
125
|
-
}
|
|
126
|
-
try {
|
|
127
|
-
return ok({
|
|
128
|
-
oldDid,
|
|
129
|
-
newDid,
|
|
130
|
-
rotation_sequence,
|
|
131
|
-
effective_at,
|
|
132
|
-
expires_at,
|
|
133
|
-
grace_period_ms,
|
|
134
|
-
timestamp,
|
|
135
|
-
oldSignature: new Uint8Array(Buffer.from(oldSigB64, 'base64')),
|
|
136
|
-
newSignature: new Uint8Array(Buffer.from(newSigB64, 'base64'))
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return err('INVALID_FORMAT');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
1
|
+
import{signMlDsa65,verifyMlDsa65}from"./identity.js";import{ok,err}from"./_deps/shared/index.js";export async function createSuccession(e,r,t,n,i,o=6048e5){const a=Date.now(),s=a,c=a+2592e6,u=`${e}||${t}||${i}||${s}||${c}||${o}||${a}`,f=(new TextEncoder).encode(u),d=await signMlDsa65(r,f);if(!d.ok)return err("SIGN_FAILED");const _=await signMlDsa65(n,f);return _.ok?ok({oldDid:e,newDid:t,rotation_sequence:i,effective_at:s,expires_at:c,grace_period_ms:o,timestamp:a,oldSignature:d.value,newSignature:_.value}):err("SIGN_FAILED")}export async function verifySuccession(e,r,t){const n=`${e.oldDid}||${e.newDid}||${e.rotation_sequence}||${e.effective_at}||${e.expires_at}||${e.grace_period_ms}||${e.timestamp}`,i=(new TextEncoder).encode(n),o=await verifyMlDsa65(r,e.oldSignature,i);if(!o.ok)return err("VERIFY_FAILED");const a=await verifyMlDsa65(t,e.newSignature,i);if(!a.ok)return err("VERIFY_FAILED");const s=o.value&&a.value;return ok(s)}export function encodeSuccession(e){return[e.oldDid,e.newDid,e.rotation_sequence.toString(),e.effective_at.toString(),e.expires_at.toString(),e.grace_period_ms.toString(),e.timestamp.toString(),Buffer.from(e.oldSignature).toString("base64"),Buffer.from(e.newSignature).toString("base64")].join("||")}export function decodeSuccession(e){const r=e.split("||");if(9!==r.length)return err("INVALID_FORMAT");const t=r[0],n=r[1],i=r[2],o=r[3],a=r[4],s=r[5],c=r[6],u=r[7],f=r[8];if(!(t&&n&&i&&o&&a&&s&&c&&u&&f))return err("INVALID_FORMAT");const d=parseInt(i,10),_=parseInt(o,10),p=parseInt(a,10),g=parseInt(s,10),D=parseInt(c,10);if(isNaN(d)||d<0||isNaN(_)||_<0||isNaN(p)||p<0||isNaN(g)||g<0||isNaN(D)||D<0)return err("INVALID_TIMESTAMP");try{return ok({oldDid:t,newDid:n,rotation_sequence:d,effective_at:_,expires_at:p,grace_period_ms:g,timestamp:D,oldSignature:new Uint8Array(Buffer.from(u,"base64")),newSignature:new Uint8Array(Buffer.from(f,"base64"))})}catch{return err("INVALID_FORMAT")}}
|