@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
@@ -35,7 +35,7 @@ export interface ChannelShare {
35
35
  readonly hmacSig: string;
36
36
  }
37
37
  /** Error codes for split-channel operations. Sub-codes give context. */
38
- export type SplitChannelError = 'SPLIT_FAILED' | 'SPLIT_FAILED:INVALID_PARAMS' | 'SPLIT_FAILED:RECONSTRUCT' | 'INSUFFICIENT_SHARES' | 'INCONSISTENT_SHARES' | 'HMAC_VERIFICATION_FAILED' | 'UNPAD_FAILED' | 'INVALID_SHARE_DATA' | 'INVALID_SHARE_DATA:BASE64' | 'INVALID_SHARE_DATA:HMAC_DECODE';
38
+ export type SplitChannelError = 'SPLIT_FAILED' | 'SPLIT_FAILED:INVALID_PARAMS' | 'SPLIT_FAILED:RECONSTRUCT' | 'SPLIT_FAILED:CRYPTO_NOT_LOADED' | 'INSUFFICIENT_SHARES' | 'INCONSISTENT_SHARES' | 'HMAC_VERIFICATION_FAILED' | 'UNPAD_FAILED' | 'INVALID_SHARE_DATA' | 'INVALID_SHARE_DATA:BASE64' | 'INVALID_SHARE_DATA:HMAC_DECODE';
39
39
  /** Default split-channel configuration: 3 shares, threshold 2. */
40
40
  export declare const DEFAULT_SPLIT_CONFIG: SplitChannelConfig;
41
41
  /**
@@ -1,219 +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 './crypto-utils.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
- * Information-theoretic security: Any k shares can reconstruct the plaintext,
26
- * but k-1 shares reveal ZERO information (proven secure).
27
- *
28
- * @param plaintext - Raw plaintext bytes to split
29
- * @param config - Split configuration (totalShares, threshold)
30
- * @returns Array of n ChannelShare objects ready for envelope wrapping
31
- *
32
- * @example
33
- * ```typescript
34
- * import { splitForChannel, DEFAULT_SPLIT_CONFIG } from '@private.me/xbind';
35
- *
36
- * const plaintext = new TextEncoder().encode('Sensitive data');
37
- *
38
- * // Default: 3 shares, need 2 to reconstruct
39
- * const shares = await splitForChannel(plaintext);
40
- * if (!shares.ok) throw new Error(shares.error);
41
- *
42
- * console.log('Created shares:', shares.value.length);
43
- * // Send each share through different channels (email, SMS, push)
44
- * shares.value.forEach((share, i) => {
45
- * console.log(`Share ${i}: ${share.groupId}`);
46
- * });
47
- *
48
- * // Custom configuration: 5 shares, need 3
49
- * const customShares = await splitForChannel(plaintext, {
50
- * totalShares: 5,
51
- * threshold: 3
52
- * });
53
- * ```
54
- */
55
- export async function splitForChannel(plaintext, config = DEFAULT_SPLIT_CONFIG) {
56
- const { totalShares: n, threshold: k } = config;
57
- if (n < 2 || k < 2 || k > n) {
58
- return err('SPLIT_FAILED:INVALID_PARAMS');
59
- }
60
- const groupId = generateUUID();
61
- return splitForChannelWithGroupId(plaintext, config, groupId);
62
- }
63
- /**
64
- * Split plaintext with a specific groupId (for testability).
65
- *
66
- * @param plaintext - Raw plaintext bytes
67
- * @param config - Split configuration
68
- * @param groupId - UUID to use for the share group
69
- * @returns Array of ChannelShare objects
70
- */
71
- export async function splitForChannelWithGroupId(plaintext, config, groupId) {
72
- const { totalShares: n, threshold: k } = config;
73
- if (n < 2 || k < 2 || k > n) {
74
- return err('SPLIT_FAILED:INVALID_PARAMS');
75
- }
76
- const p = nextOddPrime(n);
77
- const blockSize = p - 1;
78
- const padded = pkcs7Pad(plaintext, blockSize);
79
- const { key: hmacKey, signature: hmacSig } = await generateHMAC(padded);
80
- let shareArrays;
81
- try {
82
- shareArrays = splitXorIDA(padded, n, k);
83
- }
84
- catch {
85
- return err('SPLIT_FAILED');
86
- }
87
- const hmacKeyB64 = toBase64(hmacKey);
88
- const hmacSigB64 = toBase64(hmacSig);
89
- const shares = shareArrays.map((data, index) => ({
90
- data: formatShareHeader(toBase64(data)),
91
- index,
92
- total: n,
93
- threshold: k,
94
- groupId,
95
- hmacKey: hmacKeyB64,
96
- hmacSig: hmacSigB64,
97
- }));
98
- return ok(shares);
99
- }
100
- /* ── Reconstruct ── */
101
- /**
102
- * Reconstruct plaintext from k-of-n shares.
103
- *
104
- * Pipeline: validate -> XorIDA reconstruct -> HMAC verify -> unpad -> plaintext
105
- * HMAC verification happens BEFORE the data is trusted.
106
- *
107
- * @param shares - Array of at least k ChannelShare objects (must have same groupId)
108
- * @returns Reconstructed plaintext bytes
109
- *
110
- * @example
111
- * ```typescript
112
- * import { reconstructFromChannel, type ChannelShare } from '@private.me/xbind';
113
- *
114
- * // Collect shares from different channels
115
- * const receivedShares: ChannelShare[] = [
116
- * emailShare, // Received via email
117
- * smsShare, // Received via SMS
118
- * // Only need 2 shares (threshold = 2)
119
- * ];
120
- *
121
- * // Reconstruct original message
122
- * const plaintext = await reconstructFromChannel(receivedShares);
123
- * if (!plaintext.ok) {
124
- * console.error('Reconstruction failed:', plaintext.error);
125
- * return;
126
- * }
127
- *
128
- * const message = new TextDecoder().decode(plaintext.value);
129
- * console.log('Reconstructed message:', message);
130
- * ```
131
- */
132
- export async function reconstructFromChannel(shares) {
133
- const validationResult = validateShares(shares);
134
- if (!validationResult.ok)
135
- return validationResult;
136
- const { k, n } = validationResult.value;
137
- const usedShares = shares.slice(0, k);
138
- return reconstructValidated(usedShares, n, k);
139
- }
140
- /**
141
- * Validate share consistency before reconstruction.
142
- *
143
- * @param shares - Shares to validate
144
- * @returns Validated parameters or error
145
- */
146
- function validateShares(shares) {
147
- if (shares.length === 0) {
148
- return err('INSUFFICIENT_SHARES');
149
- }
150
- const first = shares[0];
151
- const k = first.threshold;
152
- const n = first.total;
153
- if (shares.length < k) {
154
- return err('INSUFFICIENT_SHARES');
155
- }
156
- const indexSet = new Set();
157
- for (const share of shares) {
158
- if (share.groupId !== first.groupId) {
159
- return err('INCONSISTENT_SHARES');
160
- }
161
- if (share.total !== n || share.threshold !== k) {
162
- return err('INCONSISTENT_SHARES');
163
- }
164
- if (share.index < 0 || share.index >= n) {
165
- return err('INVALID_SHARE_DATA');
166
- }
167
- if (indexSet.has(share.index)) {
168
- return err('INVALID_SHARE_DATA');
169
- }
170
- indexSet.add(share.index);
171
- }
172
- return ok({ k, n, groupId: first.groupId });
173
- }
174
- /**
175
- * Perform XorIDA reconstruction and HMAC verification.
176
- *
177
- * @param usedShares - Exactly k validated shares
178
- * @param n - Total shares
179
- * @param k - Threshold
180
- * @returns Reconstructed plaintext
181
- */
182
- async function reconstructValidated(usedShares, n, k) {
183
- let shareData;
184
- try {
185
- shareData = usedShares.map((s) => fromBase64(parseShareHeader(s.data)));
186
- }
187
- catch {
188
- return err('INVALID_SHARE_DATA:BASE64');
189
- }
190
- const indices = usedShares.map((s) => s.index);
191
- let padded;
192
- try {
193
- padded = reconstructXorIDA(shareData, indices, n, k);
194
- }
195
- catch {
196
- return err('SPLIT_FAILED:RECONSTRUCT');
197
- }
198
- const first = usedShares[0];
199
- let hmacKey;
200
- let hmacSig;
201
- try {
202
- hmacKey = fromBase64(first.hmacKey);
203
- hmacSig = fromBase64(first.hmacSig);
204
- }
205
- catch {
206
- return err('INVALID_SHARE_DATA:HMAC_DECODE');
207
- }
208
- const hmacValid = await verifyHMAC(hmacKey, padded, hmacSig);
209
- if (!hmacValid) {
210
- return err('HMAC_VERIFICATION_FAILED');
211
- }
212
- const p = nextOddPrime(n);
213
- const blockSize = p - 1;
214
- const unpadResult = pkcs7Unpad(padded, blockSize);
215
- if (!unpadResult.ok) {
216
- return err('UNPAD_FAILED');
217
- }
218
- return ok(unpadResult.value);
219
- }
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");let o,s,I;try{const e=nextOddPrime(n),t=pkcs7Pad(r,e-1),c=await generateHMAC(t);s=c.key,I=c.signature,o=splitXorIDA(t,n,a)}catch(r){return err("SPLIT_FAILED:CRYPTO_NOT_LOADED")}const c=toBase64(s),A=toBase64(I),i=o.map((r,e)=>({data:formatShareHeader(toBase64(r)),index:e,total:n,threshold:a,groupId:t,hmacKey:c,hmacSig:A}));return ok(i)}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),o=r[0];let s,I;try{s=fromBase64(o.hmacKey),I=fromBase64(o.hmacSig)}catch{return err("INVALID_SHARE_DATA:HMAC_DECODE")}try{const r=reconstructXorIDA(n,a,e,t);if(!await verifyHMAC(s,r,I))return err("HMAC_VERIFICATION_FAILED");const o=nextOddPrime(e),c=pkcs7Unpad(r,o-1);return c.ok?ok(c.value):err("UNPAD_FAILED")}catch(r){return err("SPLIT_FAILED:CRYPTO_NOT_LOADED")}}
@@ -1,224 +1 @@
1
- import { ok, err } from"./_deps/shared/index.js";
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 './identity.js';
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")}}