@private.me/xbind 1.3.5 → 3.0.0

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 (306) hide show
  1. package/LICENSES.md +212 -0
  2. package/README.md +388 -6
  3. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
  4. package/dist-standalone/_deps/shared/cjs/errors.js +1 -639
  5. package/dist-standalone/_deps/shared/cjs/index.js +1 -496
  6. package/dist-standalone/_deps/shared/cjs/types.js +1 -317
  7. package/dist-standalone/_deps/shared/errors.js +1 -255
  8. package/dist-standalone/_deps/shared/index.js +1 -74
  9. package/dist-standalone/_deps/shared/types.js +1 -90
  10. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  20. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  21. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  28. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  29. package/dist-standalone/_deps/xchange/errors.js +1 -1
  30. package/dist-standalone/_deps/xchange/index.js +1 -1
  31. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  32. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  33. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  34. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  40. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  41. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  42. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  43. package/dist-standalone/_deps/xregistry/index.js +1 -1
  44. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  45. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  46. package/dist-standalone/_deps/xregistry/types.js +1 -1
  47. package/dist-standalone/agent-call.js +1 -642
  48. package/dist-standalone/agent-sdk.js +1 -328
  49. package/dist-standalone/agent.d.ts +95 -5
  50. package/dist-standalone/agent.js +1 -1545
  51. package/dist-standalone/approval.js +1 -193
  52. package/dist-standalone/async-iterators.d.ts +275 -0
  53. package/dist-standalone/async-iterators.js +1 -0
  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.d.ts +114 -0
  58. package/dist-standalone/backup.js +1 -0
  59. package/dist-standalone/batch-operations.d.ts +297 -0
  60. package/dist-standalone/batch-operations.js +1 -0
  61. package/dist-standalone/cancellation.d.ts +301 -0
  62. package/dist-standalone/cancellation.js +1 -0
  63. package/dist-standalone/checkpoint.js +1 -186
  64. package/dist-standalone/circuit-breaker.d.ts +351 -0
  65. package/dist-standalone/circuit-breaker.js +1 -0
  66. package/dist-standalone/cjs/agent-call.js +1 -651
  67. package/dist-standalone/cjs/agent-sdk.js +1 -332
  68. package/dist-standalone/cjs/agent.js +1 -1582
  69. package/dist-standalone/cjs/approval.js +1 -199
  70. package/dist-standalone/cjs/async-iterators.js +1 -0
  71. package/dist-standalone/cjs/auth.js +1 -225
  72. package/dist-standalone/cjs/auto-accept.js +1 -233
  73. package/dist-standalone/cjs/backup-config.js +1 -207
  74. package/dist-standalone/cjs/backup.js +1 -0
  75. package/dist-standalone/cjs/batch-operations.js +1 -0
  76. package/dist-standalone/cjs/cancellation.js +1 -0
  77. package/dist-standalone/cjs/checkpoint.js +1 -193
  78. package/dist-standalone/cjs/circuit-breaker.js +1 -0
  79. package/dist-standalone/cjs/cli/init.js +1 -486
  80. package/dist-standalone/cjs/config-validation.js +1 -0
  81. package/dist-standalone/cjs/connect.js +1 -312
  82. package/dist-standalone/cjs/connection-pool.js +1 -0
  83. package/dist-standalone/cjs/correlation-id.js +1 -339
  84. package/dist-standalone/cjs/crypto-utils.js +1 -0
  85. package/dist-standalone/cjs/debug-mode.js +1 -0
  86. package/dist-standalone/cjs/did-document.js +1 -101
  87. package/dist-standalone/cjs/did-privateme.js +1 -130
  88. package/dist-standalone/cjs/did-web.js +1 -201
  89. package/dist-standalone/cjs/discovery.js +1 -462
  90. package/dist-standalone/cjs/dual-mode.js +1 -251
  91. package/dist-standalone/cjs/email-templates.js +1 -313
  92. package/dist-standalone/cjs/email-transport.js +1 -239
  93. package/dist-standalone/cjs/envelope.js +1 -510
  94. package/dist-standalone/cjs/errors.js +1 -826
  95. package/dist-standalone/cjs/event-emitter.js +1 -0
  96. package/dist-standalone/cjs/gateway-state.js +1 -55
  97. package/dist-standalone/cjs/gateway-transport.js +1 -120
  98. package/dist-standalone/cjs/graceful-degradation.js +1 -0
  99. package/dist-standalone/cjs/guardrails.js +1 -223
  100. package/dist-standalone/cjs/health-check.js +1 -0
  101. package/dist-standalone/cjs/http-compat.js +1 -272
  102. package/dist-standalone/cjs/http-status-map.js +1 -571
  103. package/dist-standalone/cjs/identity.js +1 -540
  104. package/dist-standalone/cjs/index.js +1 -237
  105. package/dist-standalone/cjs/invitation.js +1 -421
  106. package/dist-standalone/cjs/invite.js +1 -328
  107. package/dist-standalone/cjs/key-agreement.js +1 -246
  108. package/dist-standalone/cjs/lazy-init.js +1 -300
  109. package/dist-standalone/cjs/logger.js +1 -0
  110. package/dist-standalone/cjs/mdns-discovery.js +1 -202
  111. package/dist-standalone/cjs/nonce-store.js +1 -66
  112. package/dist-standalone/cjs/pairing-manager.js +1 -223
  113. package/dist-standalone/cjs/plugin-system.js +1 -0
  114. package/dist-standalone/cjs/plugins/logging.js +1 -0
  115. package/dist-standalone/cjs/plugins/metrics.js +1 -0
  116. package/dist-standalone/cjs/plugins/validation.js +1 -0
  117. package/dist-standalone/cjs/policy.js +1 -320
  118. package/dist-standalone/cjs/progress-callbacks.js +1 -0
  119. package/dist-standalone/cjs/redis-nonce-store.js +1 -76
  120. package/dist-standalone/cjs/registry-middleware.js +1 -50
  121. package/dist-standalone/cjs/retry-strategies.js +1 -0
  122. package/dist-standalone/cjs/retry-transport.js +1 -102
  123. package/dist-standalone/cjs/runtime/browser.js +1 -0
  124. package/dist-standalone/cjs/runtime/edge.js +1 -0
  125. package/dist-standalone/cjs/runtime/react-native.js +1 -0
  126. package/dist-standalone/cjs/security-policy.js +1 -245
  127. package/dist-standalone/cjs/serialization.js +1 -0
  128. package/dist-standalone/cjs/split-channel.js +1 -177
  129. package/dist-standalone/cjs/subscription-proof.js +1 -230
  130. package/dist-standalone/cjs/succession.js +1 -148
  131. package/dist-standalone/cjs/timeouts.js +1 -0
  132. package/dist-standalone/cjs/trace-context.js +1 -0
  133. package/dist-standalone/cjs/trace-spans.js +1 -0
  134. package/dist-standalone/cjs/transport.js +1 -63
  135. package/dist-standalone/cjs/trust-registry.js +1 -742
  136. package/dist-standalone/cjs/types/error-response.js +1 -56
  137. package/dist-standalone/cjs/vault-auth.js +1 -0
  138. package/dist-standalone/cjs/vault-store-loader.js +1 -0
  139. package/dist-standalone/cjs/verify.js +1 -25
  140. package/dist-standalone/cjs/version-info.js +1 -0
  141. package/dist-standalone/cjs/xfetch.js +1 -252
  142. package/dist-standalone/cli/init.js +1 -449
  143. package/dist-standalone/cli/setup.js +1 -514
  144. package/dist-standalone/cli/types.js +1 -27
  145. package/dist-standalone/cli/xbind.js +1 -148
  146. package/dist-standalone/config-validation.d.ts +185 -0
  147. package/dist-standalone/config-validation.js +1 -0
  148. package/dist-standalone/connect.js +1 -274
  149. package/dist-standalone/connection-pool.d.ts +251 -0
  150. package/dist-standalone/connection-pool.js +1 -0
  151. package/dist-standalone/correlation-id.js +1 -326
  152. package/dist-standalone/crypto-utils.d.ts +60 -0
  153. package/dist-standalone/crypto-utils.js +1 -0
  154. package/dist-standalone/debug-mode.d.ts +286 -0
  155. package/dist-standalone/debug-mode.js +1 -0
  156. package/dist-standalone/did-document.js +1 -96
  157. package/dist-standalone/did-privateme.js +1 -121
  158. package/dist-standalone/did-web.js +1 -196
  159. package/dist-standalone/discovery.js +1 -458
  160. package/dist-standalone/dual-mode.js +1 -247
  161. package/dist-standalone/email-templates.js +1 -309
  162. package/dist-standalone/email-transport.js +1 -232
  163. package/dist-standalone/envelope.d.ts +29 -1
  164. package/dist-standalone/envelope.js +1 -497
  165. package/dist-standalone/errors.d.ts +10 -0
  166. package/dist-standalone/errors.js +1 -811
  167. package/dist-standalone/event-emitter.d.ts +395 -0
  168. package/dist-standalone/event-emitter.js +1 -0
  169. package/dist-standalone/gateway-state.js +1 -51
  170. package/dist-standalone/gateway-transport.js +1 -116
  171. package/dist-standalone/graceful-degradation.d.ts +246 -0
  172. package/dist-standalone/graceful-degradation.js +1 -0
  173. package/dist-standalone/guardrails.js +1 -216
  174. package/dist-standalone/health-check.d.ts +150 -0
  175. package/dist-standalone/health-check.js +1 -0
  176. package/dist-standalone/http-compat.js +1 -267
  177. package/dist-standalone/http-status-map.js +1 -561
  178. package/dist-standalone/identity.d.ts +64 -1
  179. package/dist-standalone/identity.js +1 -515
  180. package/dist-standalone/index.d.ts +45 -3
  181. package/dist-standalone/index.js +1 -52
  182. package/dist-standalone/invitation.js +1 -415
  183. package/dist-standalone/invite.js +1 -324
  184. package/dist-standalone/key-agreement.d.ts +61 -13
  185. package/dist-standalone/key-agreement.js +1 -236
  186. package/dist-standalone/lazy-init.js +1 -295
  187. package/dist-standalone/logger.d.ts +77 -0
  188. package/dist-standalone/logger.js +1 -0
  189. package/dist-standalone/mdns-discovery.js +1 -195
  190. package/dist-standalone/nonce-store.d.ts +16 -3
  191. package/dist-standalone/nonce-store.js +1 -62
  192. package/dist-standalone/package.json +0 -1
  193. package/dist-standalone/pairing-manager.js +1 -219
  194. package/dist-standalone/plugin-system.d.ts +145 -0
  195. package/dist-standalone/plugin-system.js +1 -0
  196. package/dist-standalone/policy.js +1 -315
  197. package/dist-standalone/progress-callbacks.d.ts +394 -0
  198. package/dist-standalone/progress-callbacks.js +1 -0
  199. package/dist-standalone/redis-nonce-store.js +1 -72
  200. package/dist-standalone/registry-middleware.js +1 -47
  201. package/dist-standalone/retry-strategies.d.ts +382 -0
  202. package/dist-standalone/retry-strategies.js +1 -0
  203. package/dist-standalone/retry-transport.js +1 -98
  204. package/dist-standalone/security-policy.js +1 -239
  205. package/dist-standalone/serialization.d.ts +244 -0
  206. package/dist-standalone/serialization.js +1 -0
  207. package/dist-standalone/split-channel.d.ts +49 -1
  208. package/dist-standalone/split-channel.js +1 -171
  209. package/dist-standalone/subscription-proof.js +1 -224
  210. package/dist-standalone/succession.js +1 -142
  211. package/dist-standalone/timeouts.d.ts +275 -0
  212. package/dist-standalone/timeouts.js +1 -0
  213. package/dist-standalone/trace-context.d.ts +252 -0
  214. package/dist-standalone/trace-context.js +1 -0
  215. package/dist-standalone/trace-spans.d.ts +360 -0
  216. package/dist-standalone/trace-spans.js +1 -0
  217. package/dist-standalone/transport.js +1 -59
  218. package/dist-standalone/trust-registry.d.ts +106 -5
  219. package/dist-standalone/trust-registry.js +1 -702
  220. package/dist-standalone/vault-auth.d.ts +91 -0
  221. package/dist-standalone/vault-auth.js +1 -0
  222. package/dist-standalone/vault-store-loader.d.ts +110 -0
  223. package/dist-standalone/vault-store-loader.js +1 -0
  224. package/dist-standalone/verify.js +1 -16
  225. package/dist-standalone/version-info.d.ts +259 -0
  226. package/dist-standalone/version-info.js +1 -0
  227. package/dist-standalone/xfetch.js +1 -247
  228. package/llms.txt +1 -0
  229. package/package.json +65 -5
  230. package/share1.dat +0 -0
  231. package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
  232. package/dist-standalone/_deps/crypto/base64.js +0 -222
  233. package/dist-standalone/_deps/crypto/cjs/base64.js +0 -665
  234. package/dist-standalone/_deps/crypto/cjs/errors.js +0 -675
  235. package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -473
  236. package/dist-standalone/_deps/crypto/cjs/index.js +0 -852
  237. package/dist-standalone/_deps/crypto/cjs/package.json +0 -1
  238. package/dist-standalone/_deps/crypto/cjs/padding.js +0 -511
  239. package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -372
  240. package/dist-standalone/_deps/crypto/cjs/shares.js +0 -874
  241. package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -1021
  242. package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -443
  243. package/dist-standalone/_deps/crypto/cjs/verify.js +0 -414
  244. package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -923
  245. package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
  246. package/dist-standalone/_deps/crypto/errors.js +0 -199
  247. package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
  248. package/dist-standalone/_deps/crypto/hmac.js +0 -134
  249. package/dist-standalone/_deps/crypto/index.d.ts +0 -20
  250. package/dist-standalone/_deps/crypto/index.js +0 -145
  251. package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
  252. package/dist-standalone/_deps/crypto/padding.js +0 -159
  253. package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
  254. package/dist-standalone/_deps/crypto/share-header.js +0 -92
  255. package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
  256. package/dist-standalone/_deps/crypto/shares.js +0 -295
  257. package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
  258. package/dist-standalone/_deps/crypto/tlv.js +0 -364
  259. package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
  260. package/dist-standalone/_deps/crypto/uuid.js +0 -136
  261. package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
  262. package/dist-standalone/_deps/crypto/verify.js +0 -71
  263. package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
  264. package/dist-standalone/_deps/crypto/xorida.js +0 -366
  265. package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
  266. package/dist-standalone/_deps/shared/errors.js.map +0 -1
  267. package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
  268. package/dist-standalone/_deps/shared/index.js.map +0 -1
  269. package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
  270. package/dist-standalone/_deps/shared/types.js.map +0 -1
  271. package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
  272. package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
  273. package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
  274. package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
  275. package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
  276. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
  277. package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
  278. package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
  279. package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
  280. package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
  281. package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
  282. package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
  283. package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
  284. package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
  285. package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
  286. package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
  287. package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
  288. package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
  289. package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
  290. package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
  291. package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
  292. package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
  293. package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
  294. package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
  295. package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
  296. package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
  297. package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
  298. package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
  299. package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
  300. package/dist-standalone/_deps/xregistry/index.js.map +0 -1
  301. package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
  302. package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
  303. package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
  304. package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
  305. package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
  306. package/dist-standalone/_deps/xregistry/types.js.map +0 -1
@@ -1,324 +1 @@
1
- /**
2
- * @module invite
3
- * Viral invite system for XBind connections.
4
- *
5
- * Enables one-click invites:
6
- * - Generate: `xbind invite payments-service`
7
- * - Accept: Click link or `xbind accept https://xbind.to/invite/abc123`
8
- *
9
- * The viral loop:
10
- * 1. Service A invites Service B
11
- * 2. Service B clicks link (< 60 sec setup)
12
- * 3. Connection established
13
- * 4. Service B invites Service C
14
- */
15
- import { ok, err } from"./_deps/shared/index.js";
16
- /**
17
- * Invite error codes.
18
- */
19
- export var InviteErrorCode;
20
- (function (InviteErrorCode) {
21
- InviteErrorCode["INVALID_INVITE"] = "INVITE_INVALID";
22
- InviteErrorCode["EXPIRED_INVITE"] = "INVITE_EXPIRED";
23
- InviteErrorCode["INVITE_NOT_FOUND"] = "INVITE_NOT_FOUND";
24
- InviteErrorCode["NETWORK_ERROR"] = "INVITE_NETWORK_ERROR";
25
- InviteErrorCode["ALREADY_CONNECTED"] = "INVITE_ALREADY_CONNECTED";
26
- })(InviteErrorCode || (InviteErrorCode = {}));
27
- /**
28
- * Invite service for creating and accepting invites.
29
- */
30
- export class InviteService {
31
- inviteApiUrl;
32
- /**
33
- * Create invite service.
34
- *
35
- * @param options - Configuration
36
- * @param options.inviteApiUrl - Invite API URL (default: https://xbind.to)
37
- */
38
- constructor(options = {}) {
39
- this.inviteApiUrl = options.inviteApiUrl || 'https://xbind.to';
40
- }
41
- /**
42
- * Create an invite.
43
- *
44
- * @param options - Invite options
45
- * @param options.from - Sender service info
46
- * @param options.target - Target service (optional)
47
- * @param options.template - Suggested template (e.g., "payment-processing")
48
- * @param options.permissions - Custom permissions
49
- * @param options.expiresIn - Expiration in seconds (default: 7 days)
50
- * @returns Invite or error
51
- *
52
- * @example
53
- * ```ts
54
- * const inviteService = new InviteService();
55
- * const invite = await inviteService.create({
56
- * from: {
57
- * name: 'billing-service',
58
- * did: 'did:key:z6Mk...',
59
- * endpoint: 'https://billing.example.com',
60
- * publicKey: '...',
61
- * },
62
- * target: {
63
- * name: 'payments-service',
64
- * },
65
- * template: 'payment-processing',
66
- * });
67
- *
68
- * // Share invite.value.url or invite.value.qr
69
- * ```
70
- */
71
- async create(options) {
72
- const expiresIn = options.expiresIn || 7 * 24 * 60 * 60; // 7 days
73
- const expires = new Date(Date.now() + expiresIn * 1000).toISOString();
74
- try {
75
- const response = await fetch(`${this.inviteApiUrl}/invites/create`, {
76
- method: 'POST',
77
- headers: {
78
- 'Content-Type': 'application/json',
79
- },
80
- body: JSON.stringify({
81
- from: {
82
- name: options.from.name,
83
- did: options.from.did,
84
- endpoint: options.from.endpoint,
85
- publicKey: options.from.publicKey,
86
- x25519PublicKey: options.from.x25519PublicKey,
87
- mlKemPublicKey: options.from.mlKemPublicKey,
88
- },
89
- to: options.target,
90
- template: options.template,
91
- permissions: options.permissions,
92
- expires,
93
- }),
94
- });
95
- if (!response.ok) {
96
- return err({
97
- code: InviteErrorCode.NETWORK_ERROR,
98
- message: `Failed to create invite: ${response.status}`,
99
- });
100
- }
101
- const data = await response.json();
102
- return ok(data);
103
- }
104
- catch (error) {
105
- return err({
106
- code: InviteErrorCode.NETWORK_ERROR,
107
- message: error instanceof Error ? error.message : 'Network error',
108
- });
109
- }
110
- }
111
- /**
112
- * Accept an invite.
113
- *
114
- * @param options - Accept options
115
- * @param options.inviteUrl - Invite URL (e.g., https://xbind.to/invite/abc123)
116
- * @param options.acceptor - Acceptor service info
117
- * @returns Connection info or error
118
- *
119
- * @example
120
- * ```ts
121
- * const inviteService = new InviteService();
122
- * const result = await inviteService.accept({
123
- * inviteUrl: 'https://xbind.to/invite/abc123',
124
- * acceptor: {
125
- * name: 'payments-service',
126
- * did: 'did:key:z6Mk...',
127
- * endpoint: 'https://payments.example.com',
128
- * publicKey: '...',
129
- * }
130
- * });
131
- *
132
- * if (result.ok) {
133
- * console.log('Connected!');
134
- * }
135
- * ```
136
- */
137
- async accept(options) {
138
- try {
139
- // Extract invite ID from URL
140
- const inviteId = this.extractInviteId(options.inviteUrl);
141
- if (!inviteId) {
142
- return err({
143
- code: InviteErrorCode.INVALID_INVITE,
144
- message: 'Invalid invite URL',
145
- hint: 'URL should be like: https://xbind.to/invite/abc123',
146
- });
147
- }
148
- // Accept invite
149
- const response = await fetch(`${this.inviteApiUrl}/invites/${inviteId}/accept`, {
150
- method: 'POST',
151
- headers: {
152
- 'Content-Type': 'application/json',
153
- },
154
- body: JSON.stringify({
155
- acceptor: {
156
- name: options.acceptor.name,
157
- did: options.acceptor.did,
158
- endpoint: options.acceptor.endpoint,
159
- publicKey: options.acceptor.publicKey,
160
- x25519PublicKey: options.acceptor.x25519PublicKey,
161
- mlKemPublicKey: options.acceptor.mlKemPublicKey,
162
- },
163
- }),
164
- });
165
- if (response.status === 404) {
166
- return err({
167
- code: InviteErrorCode.INVITE_NOT_FOUND,
168
- message: 'Invite not found or expired',
169
- hint: 'The invite may have been revoked or expired',
170
- });
171
- }
172
- if (response.status === 410) {
173
- const errorData = await response.json().catch(() => ({}));
174
- return err({
175
- code: InviteErrorCode.EXPIRED_INVITE,
176
- message: errorData.message || 'Invite has expired',
177
- hint: errorData.hint || 'Ask the sender to create a new invite',
178
- });
179
- }
180
- if (response.status === 409) {
181
- return err({
182
- code: InviteErrorCode.ALREADY_CONNECTED,
183
- message: 'Services are already connected',
184
- hint: 'You are already connected to this service',
185
- });
186
- }
187
- if (!response.ok) {
188
- return err({
189
- code: InviteErrorCode.NETWORK_ERROR,
190
- message: `Failed to accept invite: ${response.status}`,
191
- });
192
- }
193
- const data = await response.json();
194
- return ok(data);
195
- }
196
- catch (error) {
197
- return err({
198
- code: InviteErrorCode.NETWORK_ERROR,
199
- message: error instanceof Error ? error.message : 'Network error',
200
- hint: 'Check your network connection and try again',
201
- });
202
- }
203
- }
204
- /**
205
- * Get invite details without accepting.
206
- *
207
- * @param inviteUrl - Invite URL
208
- * @returns Invite details or error
209
- */
210
- async get(inviteUrl) {
211
- try {
212
- const inviteId = this.extractInviteId(inviteUrl);
213
- if (!inviteId) {
214
- return err({
215
- code: InviteErrorCode.INVALID_INVITE,
216
- message: 'Invalid invite URL',
217
- });
218
- }
219
- const response = await fetch(`${this.inviteApiUrl}/invites/${inviteId}`, {
220
- headers: {
221
- 'Accept': 'application/json',
222
- },
223
- });
224
- if (response.status === 404) {
225
- return err({
226
- code: InviteErrorCode.INVITE_NOT_FOUND,
227
- message: 'Invite not found',
228
- });
229
- }
230
- if (!response.ok) {
231
- return err({
232
- code: InviteErrorCode.NETWORK_ERROR,
233
- message: `Failed to get invite: ${response.status}`,
234
- });
235
- }
236
- const data = await response.json();
237
- return ok(data);
238
- }
239
- catch (error) {
240
- return err({
241
- code: InviteErrorCode.NETWORK_ERROR,
242
- message: error instanceof Error ? error.message : 'Network error',
243
- });
244
- }
245
- }
246
- /**
247
- * Revoke an invite.
248
- *
249
- * @param inviteId - Invite ID to revoke
250
- * @returns Success or error
251
- *
252
- * @example
253
- * ```ts
254
- * const inviteService = new InviteService();
255
- * const result = await inviteService.revoke('inv_abc123');
256
- *
257
- * if (result.ok) {
258
- * console.log('Invite revoked');
259
- * }
260
- * ```
261
- */
262
- async revoke(inviteId) {
263
- try {
264
- const response = await fetch(`${this.inviteApiUrl}/invites/${inviteId}/revoke`, {
265
- method: 'POST',
266
- headers: {
267
- 'Content-Type': 'application/json',
268
- },
269
- });
270
- if (response.status === 404) {
271
- return err({
272
- code: InviteErrorCode.INVITE_NOT_FOUND,
273
- message: 'Invite not found',
274
- hint: 'The invite may have already been revoked or expired',
275
- });
276
- }
277
- if (!response.ok) {
278
- return err({
279
- code: InviteErrorCode.NETWORK_ERROR,
280
- message: `Failed to revoke invite: ${response.status}`,
281
- });
282
- }
283
- const data = await response.json();
284
- return ok(data);
285
- }
286
- catch (error) {
287
- return err({
288
- code: InviteErrorCode.NETWORK_ERROR,
289
- message: error instanceof Error ? error.message : 'Network error',
290
- });
291
- }
292
- }
293
- /**
294
- * Extract invite ID from URL.
295
- *
296
- * @param url - Invite URL
297
- * @returns Invite ID or null
298
- *
299
- * @example
300
- * ```ts
301
- * extractInviteId('https://xbind.to/invite/abc123') // 'abc123'
302
- * extractInviteId('https://xbind.to/billing/invite/xyz') // 'xyz'
303
- * ```
304
- */
305
- extractInviteId(url) {
306
- try {
307
- const parsed = new URL(url);
308
- const segments = parsed.pathname.split('/').filter(s => s.length > 0);
309
- // Look for 'invite' segment followed by ID
310
- const inviteIndex = segments.indexOf('invite');
311
- if (inviteIndex !== -1 && segments[inviteIndex + 1]) {
312
- return segments[inviteIndex + 1] ?? null;
313
- }
314
- // Fallback: last segment
315
- if (segments.length > 0) {
316
- return segments[segments.length - 1] ?? null;
317
- }
318
- return null;
319
- }
320
- catch {
321
- return null;
322
- }
323
- }
324
- }
1
+ import{ok,err}from"./_deps/shared/index.js";export var InviteErrorCode;!function(e){e.INVALID_INVITE="INVITE_INVALID",e.EXPIRED_INVITE="INVITE_EXPIRED",e.INVITE_NOT_FOUND="INVITE_NOT_FOUND",e.NETWORK_ERROR="INVITE_NETWORK_ERROR",e.ALREADY_CONNECTED="INVITE_ALREADY_CONNECTED"}(InviteErrorCode||(InviteErrorCode={}));export class InviteService{inviteApiUrl;constructor(e={}){this.inviteApiUrl=e.inviteApiUrl||"https://xbind.to"}async create(e){const r=e.expiresIn||604800,t=new Date(Date.now()+1e3*r).toISOString();try{const r=await fetch(`${this.inviteApiUrl}/invites/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:{name:e.from.name,did:e.from.did,endpoint:e.from.endpoint,publicKey:e.from.publicKey,x25519PublicKey:e.from.x25519PublicKey,mlKemPublicKey:e.from.mlKemPublicKey},to:e.target,template:e.template,permissions:e.permissions,expires:t})});if(!r.ok)return err({code:InviteErrorCode.NETWORK_ERROR,message:`Failed to create invite: ${r.status}`});const i=await r.json();return ok(i)}catch(e){return err({code:InviteErrorCode.NETWORK_ERROR,message:e instanceof Error?e.message:"Network error"})}}async accept(e){try{const r=this.extractInviteId(e.inviteUrl);if(!r)return err({code:InviteErrorCode.INVALID_INVITE,message:"Invalid invite URL",hint:"URL should be like: https://xbind.to/invite/abc123"});const t=await fetch(`${this.inviteApiUrl}/invites/${r}/accept`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({acceptor:{name:e.acceptor.name,did:e.acceptor.did,endpoint:e.acceptor.endpoint,publicKey:e.acceptor.publicKey,x25519PublicKey:e.acceptor.x25519PublicKey,mlKemPublicKey:e.acceptor.mlKemPublicKey}})});if(404===t.status)return err({code:InviteErrorCode.INVITE_NOT_FOUND,message:"Invite not found or expired",hint:"The invite may have been revoked or expired"});if(410===t.status){const e=await t.json().catch(()=>({}));return err({code:InviteErrorCode.EXPIRED_INVITE,message:e.message||"Invite has expired",hint:e.hint||"Ask the sender to create a new invite"})}if(409===t.status)return err({code:InviteErrorCode.ALREADY_CONNECTED,message:"Services are already connected",hint:"You are already connected to this service"});if(!t.ok)return err({code:InviteErrorCode.NETWORK_ERROR,message:`Failed to accept invite: ${t.status}`});const i=await t.json();return ok(i)}catch(e){return err({code:InviteErrorCode.NETWORK_ERROR,message:e instanceof Error?e.message:"Network error",hint:"Check your network connection and try again"})}}async get(e){try{const r=this.extractInviteId(e);if(!r)return err({code:InviteErrorCode.INVALID_INVITE,message:"Invalid invite URL"});const t=await fetch(`${this.inviteApiUrl}/invites/${r}`,{headers:{Accept:"application/json"}});if(404===t.status)return err({code:InviteErrorCode.INVITE_NOT_FOUND,message:"Invite not found"});if(!t.ok)return err({code:InviteErrorCode.NETWORK_ERROR,message:`Failed to get invite: ${t.status}`});const i=await t.json();return ok(i)}catch(e){return err({code:InviteErrorCode.NETWORK_ERROR,message:e instanceof Error?e.message:"Network error"})}}async revoke(e){try{const r=await fetch(`${this.inviteApiUrl}/invites/${e}/revoke`,{method:"POST",headers:{"Content-Type":"application/json"}});if(404===r.status)return err({code:InviteErrorCode.INVITE_NOT_FOUND,message:"Invite not found",hint:"The invite may have already been revoked or expired"});if(!r.ok)return err({code:InviteErrorCode.NETWORK_ERROR,message:`Failed to revoke invite: ${r.status}`});const t=await r.json();return ok(t)}catch(e){return err({code:InviteErrorCode.NETWORK_ERROR,message:e instanceof Error?e.message:"Network error"})}}extractInviteId(e){try{const r=new URL(e).pathname.split("/").filter(e=>e.length>0),t=r.indexOf("invite");return-1!==t&&r[t+1]?r[t+1]??null:r.length>0?r[r.length-1]??null:null}catch{return null}}}
@@ -25,7 +25,7 @@ export interface HybridKeyAgreementResult {
25
25
  readonly kemCiphertext: Uint8Array;
26
26
  }
27
27
  /** Error codes for key agreement operations. Sub-codes give context. */
28
- export type KeyAgreementError = 'KEYGEN_FAILED' | 'DERIVE_FAILED' | 'IMPORT_FAILED' | 'INVALID_KEY_LENGTH' | 'INVALID_KEY_LENGTH:EXPECTED_32' | 'KEM_ENCAPSULATE_FAILED' | 'KEM_DECAPSULATE_FAILED' | 'HKDF_FAILED';
28
+ export type KeyAgreementError = 'KEYGEN_FAILED' | 'DERIVE_FAILED' | 'IMPORT_FAILED' | 'INVALID_KEY_LENGTH' | 'INVALID_KEY_LENGTH:EXPECTED_32' | 'KEM_ENCAPSULATE_FAILED' | 'KEM_DECAPSULATE_FAILED' | 'HKDF_FAILED' | 'EXPORT_KEY_FAILED';
29
29
  /**
30
30
  * Generate an ephemeral X25519 key pair for ECDH key agreement.
31
31
  *
@@ -34,6 +34,19 @@ export type KeyAgreementError = 'KEYGEN_FAILED' | 'DERIVE_FAILED' | 'IMPORT_FAIL
34
34
  * key agreement and then discarded.
35
35
  *
36
36
  * @returns Result containing the ephemeral key pair or error.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { generateEphemeralKeyPair } from '@private.me/xbind';
41
+ *
42
+ * const ephemeral = await generateEphemeralKeyPair();
43
+ * if (!ephemeral.ok) {
44
+ * throw new Error(`Key generation failed: ${ephemeral.error}`);
45
+ * }
46
+ *
47
+ * console.log('Ephemeral public key (32 bytes):', ephemeral.value.rawPublicKey);
48
+ * // Use ephemeral.value.privateKey for ECDH, then discard
49
+ * ```
37
50
  */
38
51
  export declare function generateEphemeralKeyPair(): Promise<Result<EphemeralKeyPair, KeyAgreementError>>;
39
52
  /**
@@ -68,6 +81,24 @@ export declare function deriveSharedKeyECDH(localPrivateKey: CryptoKey, remotePu
68
81
  *
69
82
  * @param recipientX25519PubKey - Recipient's static X25519 public key.
70
83
  * @returns Result containing the key agreement result or error.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { senderKeyAgreement, importX25519PublicKey } from '@private.me/xbind';
88
+ *
89
+ * // Import recipient's X25519 public key
90
+ * const recipientPubKeyRaw = new Uint8Array(32); // From recipient's identity
91
+ * const recipientPubKey = await importX25519PublicKey(recipientPubKeyRaw);
92
+ * if (!recipientPubKey.ok) throw new Error(recipientPubKey.error);
93
+ *
94
+ * // Perform key agreement
95
+ * const agreement = await senderKeyAgreement(recipientPubKey.value);
96
+ * if (!agreement.ok) throw new Error(agreement.error);
97
+ *
98
+ * // Use shared key for encryption
99
+ * const { sharedKey, ephemeralPublicKey } = agreement.value;
100
+ * // Include ephemeralPublicKey in envelope header
101
+ * ```
71
102
  */
72
103
  export declare function senderKeyAgreement(recipientX25519PubKey: CryptoKey): Promise<Result<KeyAgreementResult, KeyAgreementError>>;
73
104
  /**
@@ -83,17 +114,30 @@ export declare function senderKeyAgreement(recipientX25519PubKey: CryptoKey): Pr
83
114
  */
84
115
  export declare function receiverKeyAgreement(receiverPrivateKey: CryptoKey, senderEphemeralPubRaw: Uint8Array): Promise<Result<CryptoKey, KeyAgreementError>>;
85
116
  /**
86
- * Combine X25519 and ML-KEM shared secrets via HKDF-SHA256.
87
- *
88
- * Concatenates 32B X25519 + 32B ML-KEM shared secrets, runs HKDF
89
- * with zero salt and 'xail-hybrid-kem-v2' info, outputs 256-bit AES key.
90
- * Secure as long as either X25519 OR ML-KEM remains unbroken.
91
- *
92
- * @param x25519Shared - 32-byte raw X25519 ECDH shared bits.
93
- * @param mlKemShared - 32-byte ML-KEM-768 shared secret.
94
- * @returns AES-256-GCM CryptoKey derived from both secrets.
117
+ * Combine X25519 + ML-KEM shared secrets using IETF X-Wing combiner.
118
+ *
119
+ * IETF draft-connolly-cfrg-xwing-kem-10 compliant combiner that includes:
120
+ * - ML-KEM shared secret (32B)
121
+ * - ML-KEM ciphertext (1088B for ML-KEM-768)
122
+ * - X25519 shared secret (32B)
123
+ * - X25519 ephemeral public key (32B)
124
+ * - X25519 recipient public key (32B)
125
+ * - ML-KEM recipient public key (1184B for ML-KEM-768)
126
+ *
127
+ * This prevents Unknown Key Share (UKS) attacks, transcript manipulation,
128
+ * and session confusion by binding the key material to the full protocol transcript.
129
+ *
130
+ * Session 159 - CRYPTO-1: Upgraded from naive concatenation to IETF standard.
131
+ *
132
+ * @param mlKemShared - 32-byte ML-KEM-768 shared secret
133
+ * @param mlKemCiphertext - 1088-byte ML-KEM-768 ciphertext
134
+ * @param x25519Shared - 32-byte raw X25519 ECDH shared secret
135
+ * @param x25519EphemeralPub - 32-byte sender's ephemeral X25519 public key
136
+ * @param x25519RecipientPub - 32-byte recipient's static X25519 public key
137
+ * @param mlKemRecipientPub - 1184-byte recipient's ML-KEM-768 public key
138
+ * @returns AES-256-GCM CryptoKey derived using IETF X-Wing combiner
95
139
  */
96
- export declare function combineSharedSecrets(x25519Shared: Uint8Array, mlKemShared: Uint8Array): Promise<Result<CryptoKey, KeyAgreementError>>;
140
+ export declare function combineSharedSecrets(mlKemShared: Uint8Array, mlKemCiphertext: Uint8Array, x25519Shared: Uint8Array, x25519EphemeralPub: Uint8Array, x25519RecipientPub: Uint8Array, mlKemRecipientPub: Uint8Array): Promise<Result<CryptoKey, KeyAgreementError>>;
97
141
  /**
98
142
  * Perform sender-side hybrid key agreement (X25519 + ML-KEM-768).
99
143
  *
@@ -111,12 +155,16 @@ export declare function senderHybridKeyAgreement(recipientX25519Pub: CryptoKey,
111
155
  *
112
156
  * 1. Import ephemeral X25519, ECDH → x25519SharedBits
113
157
  * 2. ML-KEM-768 decapsulate → mlKemShared
114
- * 3. HKDF(x25519Shared || mlKemShared) → same AES-256-GCM key
158
+ * 3. IETF X-Wing combiner → same AES-256-GCM key
159
+ *
160
+ * Session 159 - CRYPTO-1: Updated to use IETF X-Wing combiner with public keys.
115
161
  *
116
162
  * @param x25519PrivateKey - Receiver's static X25519 private key.
163
+ * @param x25519PublicKeyRaw - Receiver's static X25519 public key (32B raw bytes).
117
164
  * @param ephPubRaw - Sender's ephemeral X25519 public key (32B).
118
165
  * @param kemCiphertext - ML-KEM-768 ciphertext from sender (1088B).
119
166
  * @param mlKemSecretKey - Receiver's ML-KEM-768 secret key (2400B).
167
+ * @param mlKemPublicKey - Receiver's ML-KEM-768 public key (1184B).
120
168
  * @returns AES-256-GCM shared key or error.
121
169
  */
122
- export declare function receiverHybridKeyAgreement(x25519PrivateKey: CryptoKey, ephPubRaw: Uint8Array, kemCiphertext: Uint8Array, mlKemSecretKey: Uint8Array): Promise<Result<CryptoKey, KeyAgreementError>>;
170
+ export declare function receiverHybridKeyAgreement(x25519PrivateKey: CryptoKey, x25519PublicKeyRaw: Uint8Array, ephPubRaw: Uint8Array, kemCiphertext: Uint8Array, mlKemSecretKey: Uint8Array, mlKemPublicKey: Uint8Array): Promise<Result<CryptoKey, KeyAgreementError>>;
@@ -1,236 +1 @@
1
- import { ok, err } from"./_deps/shared/index.js";
2
- import { MlKem768 } from 'mlkem';
3
- /* -- Constants -- */
4
- const X25519_RAW_KEY_BYTES = 32;
5
- const AES_KEY_BITS = 256;
6
- /* -- Helpers -- */
7
- /** Copy Uint8Array to fresh ArrayBuffer. */
8
- function toArrayBuffer(data) {
9
- const buf = new ArrayBuffer(data.byteLength);
10
- new Uint8Array(buf).set(data);
11
- return buf;
12
- }
13
- /* -- Key Generation -- */
14
- /**
15
- * Generate an ephemeral X25519 key pair for ECDH key agreement.
16
- *
17
- * Uses Web Crypto API to generate a fresh X25519 key pair.
18
- * The key pair is ephemeral -- it should be used for a single
19
- * key agreement and then discarded.
20
- *
21
- * @returns Result containing the ephemeral key pair or error.
22
- */
23
- export async function generateEphemeralKeyPair() {
24
- try {
25
- // SAFETY: X25519 generateKey always returns CryptoKeyPair
26
- const keyPair = await crypto.subtle.generateKey({ name: 'X25519' }, true, ['deriveBits']);
27
- const rawPub = new Uint8Array(await crypto.subtle.exportKey('raw', keyPair.publicKey));
28
- return ok({
29
- privateKey: keyPair.privateKey,
30
- publicKey: keyPair.publicKey,
31
- rawPublicKey: rawPub,
32
- });
33
- }
34
- catch {
35
- return err('KEYGEN_FAILED');
36
- }
37
- }
38
- /* -- Key Import -- */
39
- /**
40
- * Import a raw 32-byte X25519 public key into a CryptoKey.
41
- *
42
- * Used by the receiver to import the sender's ephemeral public
43
- * key from the envelope for ECDH derivation.
44
- *
45
- * @param rawPublicKey - Raw 32-byte X25519 public key.
46
- * @returns Result containing the imported CryptoKey or error.
47
- */
48
- export async function importX25519PublicKey(rawPublicKey) {
49
- if (rawPublicKey.length !== X25519_RAW_KEY_BYTES) {
50
- return err('INVALID_KEY_LENGTH:EXPECTED_32');
51
- }
52
- try {
53
- const key = await crypto.subtle.importKey('raw', toArrayBuffer(rawPublicKey), { name: 'X25519' }, true, []);
54
- return ok(key);
55
- }
56
- catch {
57
- return err('IMPORT_FAILED');
58
- }
59
- }
60
- /* -- ECDH Key Derivation -- */
61
- /**
62
- * Derive a shared AES-256-GCM key via X25519 ECDH.
63
- *
64
- * Performs ECDH key agreement between a local private key and a
65
- * remote public key, producing 256 bits of shared secret that
66
- * are imported as an AES-256-GCM key.
67
- *
68
- * @param localPrivateKey - Local X25519 private key.
69
- * @param remotePublicKey - Remote X25519 public key (CryptoKey).
70
- * @returns Result containing the derived AES-256-GCM key or error.
71
- */
72
- export async function deriveSharedKeyECDH(localPrivateKey, remotePublicKey) {
73
- try {
74
- const sharedBits = await crypto.subtle.deriveBits({ name: 'X25519', public: remotePublicKey }, localPrivateKey, AES_KEY_BITS);
75
- const aesKey = await crypto.subtle.importKey('raw', sharedBits, { name: 'AES-GCM', length: AES_KEY_BITS }, false, ['encrypt', 'decrypt']);
76
- return ok(aesKey);
77
- }
78
- catch {
79
- return err('DERIVE_FAILED');
80
- }
81
- }
82
- /* -- High-Level API -- */
83
- /**
84
- * Perform sender-side ECDH key agreement.
85
- *
86
- * Generates an ephemeral X25519 key pair, derives a shared key
87
- * with the recipient's static X25519 public key, and returns
88
- * both the shared key and the ephemeral public key to include
89
- * in the envelope.
90
- *
91
- * @param recipientX25519PubKey - Recipient's static X25519 public key.
92
- * @returns Result containing the key agreement result or error.
93
- */
94
- export async function senderKeyAgreement(recipientX25519PubKey) {
95
- const ephemeral = await generateEphemeralKeyPair();
96
- if (!ephemeral.ok)
97
- return ephemeral;
98
- const shared = await deriveSharedKeyECDH(ephemeral.value.privateKey, recipientX25519PubKey);
99
- if (!shared.ok)
100
- return shared;
101
- return ok({
102
- sharedKey: shared.value,
103
- ephemeralPublicKey: ephemeral.value.rawPublicKey,
104
- });
105
- }
106
- /**
107
- * Perform receiver-side ECDH key agreement.
108
- *
109
- * Imports the sender's ephemeral public key from the envelope
110
- * and derives the shared key using the receiver's static X25519
111
- * private key.
112
- *
113
- * @param receiverPrivateKey - Receiver's static X25519 private key.
114
- * @param senderEphemeralPubRaw - Sender's ephemeral public key (raw bytes).
115
- * @returns Result containing the derived AES-256-GCM key or error.
116
- */
117
- export async function receiverKeyAgreement(receiverPrivateKey, senderEphemeralPubRaw) {
118
- const imported = await importX25519PublicKey(senderEphemeralPubRaw);
119
- if (!imported.ok)
120
- return imported;
121
- return deriveSharedKeyECDH(receiverPrivateKey, imported.value);
122
- }
123
- /* -- Hybrid Key Agreement (X25519 + ML-KEM-768) -- */
124
- const HYBRID_HKDF_INFO = 'xail-hybrid-kem-v2';
125
- /**
126
- * Combine X25519 and ML-KEM shared secrets via HKDF-SHA256.
127
- *
128
- * Concatenates 32B X25519 + 32B ML-KEM shared secrets, runs HKDF
129
- * with zero salt and 'xail-hybrid-kem-v2' info, outputs 256-bit AES key.
130
- * Secure as long as either X25519 OR ML-KEM remains unbroken.
131
- *
132
- * @param x25519Shared - 32-byte raw X25519 ECDH shared bits.
133
- * @param mlKemShared - 32-byte ML-KEM-768 shared secret.
134
- * @returns AES-256-GCM CryptoKey derived from both secrets.
135
- */
136
- export async function combineSharedSecrets(x25519Shared, mlKemShared) {
137
- try {
138
- const combined = new Uint8Array(x25519Shared.length + mlKemShared.length);
139
- combined.set(x25519Shared);
140
- combined.set(mlKemShared, x25519Shared.length);
141
- const baseKey = await crypto.subtle.importKey('raw', toArrayBuffer(combined), 'HKDF', false, ['deriveBits']);
142
- const derived = await crypto.subtle.deriveBits({
143
- name: 'HKDF',
144
- hash: 'SHA-256',
145
- salt: new Uint8Array(32),
146
- info: new TextEncoder().encode(HYBRID_HKDF_INFO),
147
- }, baseKey, AES_KEY_BITS);
148
- const aesKey = await crypto.subtle.importKey('raw', derived, { name: 'AES-GCM', length: AES_KEY_BITS }, false, ['encrypt', 'decrypt']);
149
- return ok(aesKey);
150
- }
151
- catch {
152
- return err('HKDF_FAILED');
153
- }
154
- }
155
- /**
156
- * Perform sender-side hybrid key agreement (X25519 + ML-KEM-768).
157
- *
158
- * 1. Generate ephemeral X25519, ECDH → x25519SharedBits
159
- * 2. ML-KEM-768 encapsulate → kemCiphertext + mlKemShared
160
- * 3. HKDF(x25519Shared || mlKemShared) → AES-256-GCM key
161
- *
162
- * @param recipientX25519Pub - Recipient's static X25519 public key.
163
- * @param recipientMlKemPub - Recipient's ML-KEM-768 public key (1184B).
164
- * @returns Hybrid key agreement result with shared key, ephemeral pub, and KEM ciphertext.
165
- */
166
- export async function senderHybridKeyAgreement(recipientX25519Pub, recipientMlKemPub) {
167
- // Step 1: Ephemeral X25519 ECDH
168
- const ephemeral = await generateEphemeralKeyPair();
169
- if (!ephemeral.ok)
170
- return ephemeral;
171
- let x25519Shared;
172
- try {
173
- x25519Shared = new Uint8Array(await crypto.subtle.deriveBits({ name: 'X25519', public: recipientX25519Pub }, ephemeral.value.privateKey, AES_KEY_BITS));
174
- }
175
- catch {
176
- return err('DERIVE_FAILED');
177
- }
178
- // Step 2: ML-KEM-768 encapsulate
179
- let kemCipherText;
180
- let kemSharedSecret;
181
- try {
182
- const mlkem = new MlKem768();
183
- const [cipherText, sharedSecret] = await mlkem.encap(recipientMlKemPub);
184
- kemCipherText = cipherText;
185
- kemSharedSecret = sharedSecret;
186
- }
187
- catch {
188
- return err('KEM_ENCAPSULATE_FAILED');
189
- }
190
- // Step 3: Combine via HKDF
191
- const combined = await combineSharedSecrets(x25519Shared, kemSharedSecret);
192
- if (!combined.ok)
193
- return combined;
194
- return ok({
195
- sharedKey: combined.value,
196
- ephemeralPublicKey: ephemeral.value.rawPublicKey,
197
- kemCiphertext: kemCipherText,
198
- });
199
- }
200
- /**
201
- * Perform receiver-side hybrid key agreement (X25519 + ML-KEM-768).
202
- *
203
- * 1. Import ephemeral X25519, ECDH → x25519SharedBits
204
- * 2. ML-KEM-768 decapsulate → mlKemShared
205
- * 3. HKDF(x25519Shared || mlKemShared) → same AES-256-GCM key
206
- *
207
- * @param x25519PrivateKey - Receiver's static X25519 private key.
208
- * @param ephPubRaw - Sender's ephemeral X25519 public key (32B).
209
- * @param kemCiphertext - ML-KEM-768 ciphertext from sender (1088B).
210
- * @param mlKemSecretKey - Receiver's ML-KEM-768 secret key (2400B).
211
- * @returns AES-256-GCM shared key or error.
212
- */
213
- export async function receiverHybridKeyAgreement(x25519PrivateKey, ephPubRaw, kemCiphertext, mlKemSecretKey) {
214
- // Step 1: X25519 ECDH
215
- const imported = await importX25519PublicKey(ephPubRaw);
216
- if (!imported.ok)
217
- return imported;
218
- let x25519Shared;
219
- try {
220
- x25519Shared = new Uint8Array(await crypto.subtle.deriveBits({ name: 'X25519', public: imported.value }, x25519PrivateKey, AES_KEY_BITS));
221
- }
222
- catch {
223
- return err('DERIVE_FAILED');
224
- }
225
- // Step 2: ML-KEM-768 decapsulate
226
- let mlKemShared;
227
- try {
228
- const mlkem = new MlKem768();
229
- mlKemShared = await mlkem.decap(kemCiphertext, mlKemSecretKey);
230
- }
231
- catch {
232
- return err('KEM_DECAPSULATE_FAILED');
233
- }
234
- // Step 3: Combine via HKDF
235
- return combineSharedSecrets(x25519Shared, mlKemShared);
236
- }
1
+ import{ok,err}from"./_deps/shared/index.js";import{createMlKem768}from"mlkem";const X25519_RAW_KEY_BYTES=32,AES_KEY_BITS=256;function toArrayBuffer(e){const r=new ArrayBuffer(e.byteLength);return new Uint8Array(r).set(e),r}export async function generateEphemeralKeyPair(){try{const e=await crypto.subtle.generateKey({name:"X25519"},!0,["deriveBits"]),r=new Uint8Array(await crypto.subtle.exportKey("raw",e.publicKey));return ok({privateKey:e.privateKey,publicKey:e.publicKey,rawPublicKey:r})}catch{return err("KEYGEN_FAILED")}}export async function importX25519PublicKey(e){if(32!==e.length)return err("INVALID_KEY_LENGTH:EXPECTED_32");try{const r=await crypto.subtle.importKey("raw",toArrayBuffer(e),{name:"X25519"},!0,[]);return ok(r)}catch{return err("IMPORT_FAILED")}}export async function deriveSharedKeyECDH(e,r){try{const t=await crypto.subtle.deriveBits({name:"X25519",public:r},e,256),a=await crypto.subtle.importKey("raw",t,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return ok(a)}catch{return err("DERIVE_FAILED")}}export async function senderKeyAgreement(e){const r=await generateEphemeralKeyPair();if(!r.ok)return r;const t=await deriveSharedKeyECDH(r.value.privateKey,e);return t.ok?ok({sharedKey:t.value,ephemeralPublicKey:r.value.rawPublicKey}):t}export async function receiverKeyAgreement(e,r){const t=await importX25519PublicKey(r);return t.ok?deriveSharedKeyECDH(e,t.value):t}const HYBRID_HKDF_INFO="xail-hybrid-kem-v2";export async function combineSharedSecrets(e,r,t,a,n,c){try{const i=e.length+r.length+t.length+a.length+n.length+c.length,o=new Uint8Array(i);let y=0;o.set(e,y),y+=e.length,o.set(r,y),y+=r.length,o.set(t,y),y+=t.length,o.set(a,y),y+=a.length,o.set(n,y),y+=n.length,o.set(c,y);const u=(new TextEncoder).encode("XWingKEM"),l=await crypto.subtle.importKey("raw",toArrayBuffer(o),"HKDF",!1,["deriveBits"]),s=await crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:u,info:(new TextEncoder).encode(HYBRID_HKDF_INFO)},l,256),p=await crypto.subtle.importKey("raw",s,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return ok(p)}catch{return err("HKDF_FAILED")}}export async function senderHybridKeyAgreement(e,r){const t=await generateEphemeralKeyPair();if(!t.ok)return t;let a,n,c,i;try{a=new Uint8Array(await crypto.subtle.deriveBits({name:"X25519",public:e},t.value.privateKey,256))}catch{return err("DERIVE_FAILED")}try{const e=await createMlKem768(),[t,a]=e.encap(r);n=t,c=a}catch{return err("KEM_ENCAPSULATE_FAILED")}try{const r=await crypto.subtle.exportKey("raw",e);i=new Uint8Array(r)}catch{return err("EXPORT_KEY_FAILED")}const o=await combineSharedSecrets(c,n,a,t.value.rawPublicKey,i,r);return o.ok?ok({sharedKey:o.value,ephemeralPublicKey:t.value.rawPublicKey,kemCiphertext:n}):o}export async function receiverHybridKeyAgreement(e,r,t,a,n,c){const i=await importX25519PublicKey(t);if(!i.ok)return i;let o,y;try{o=new Uint8Array(await crypto.subtle.deriveBits({name:"X25519",public:i.value},e,256))}catch{return err("DERIVE_FAILED")}try{y=(await createMlKem768()).decap(a,n)}catch{return err("KEM_DECAPSULATE_FAILED")}return combineSharedSecrets(y,a,o,t,r,c)}