@verbeth/sdk 0.1.4 → 0.1.6

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 (190) hide show
  1. package/README.md +20 -168
  2. package/dist/esm/src/addresses.d.ts +20 -0
  3. package/dist/esm/src/addresses.d.ts.map +1 -0
  4. package/dist/esm/src/addresses.js +33 -0
  5. package/dist/esm/src/client/HsrTagIndex.d.ts +77 -0
  6. package/dist/esm/src/client/HsrTagIndex.d.ts.map +1 -0
  7. package/dist/esm/src/client/HsrTagIndex.js +157 -0
  8. package/dist/esm/src/client/PendingManager.d.ts +65 -0
  9. package/dist/esm/src/client/PendingManager.d.ts.map +1 -0
  10. package/dist/esm/src/client/PendingManager.js +84 -0
  11. package/dist/esm/src/client/SessionManager.d.ts +65 -0
  12. package/dist/esm/src/client/SessionManager.d.ts.map +1 -0
  13. package/dist/esm/src/client/SessionManager.js +146 -0
  14. package/dist/esm/src/client/VerbethClient.d.ts +153 -99
  15. package/dist/esm/src/client/VerbethClient.d.ts.map +1 -1
  16. package/dist/esm/src/client/VerbethClient.js +429 -123
  17. package/dist/esm/src/client/VerbethClientBuilder.d.ts +105 -0
  18. package/dist/esm/src/client/VerbethClientBuilder.d.ts.map +1 -0
  19. package/dist/esm/src/client/VerbethClientBuilder.js +146 -0
  20. package/dist/esm/src/client/hsrMatcher.d.ts +22 -0
  21. package/dist/esm/src/client/hsrMatcher.d.ts.map +1 -0
  22. package/dist/esm/src/client/hsrMatcher.js +31 -0
  23. package/dist/esm/src/client/index.d.ts +6 -1
  24. package/dist/esm/src/client/index.d.ts.map +1 -1
  25. package/dist/esm/src/client/index.js +2 -0
  26. package/dist/esm/src/client/types.d.ts +151 -10
  27. package/dist/esm/src/client/types.d.ts.map +1 -1
  28. package/dist/esm/src/crypto(old).d.ts +46 -0
  29. package/dist/esm/src/crypto(old).d.ts.map +1 -0
  30. package/dist/esm/src/crypto(old).js +137 -0
  31. package/dist/esm/src/crypto.d.ts +7 -29
  32. package/dist/esm/src/crypto.d.ts.map +1 -1
  33. package/dist/esm/src/crypto.js +36 -72
  34. package/dist/esm/src/executor.d.ts +17 -18
  35. package/dist/esm/src/executor.d.ts.map +1 -1
  36. package/dist/esm/src/executor.js +54 -70
  37. package/dist/esm/src/handshake.d.ts +51 -0
  38. package/dist/esm/src/handshake.d.ts.map +1 -0
  39. package/dist/esm/src/handshake.js +105 -0
  40. package/dist/esm/src/identity.d.ts +24 -18
  41. package/dist/esm/src/identity.d.ts.map +1 -1
  42. package/dist/esm/src/identity.js +126 -31
  43. package/dist/esm/src/index.d.ts +11 -7
  44. package/dist/esm/src/index.d.ts.map +1 -1
  45. package/dist/esm/src/index.js +10 -7
  46. package/dist/esm/src/payload.d.ts +3 -30
  47. package/dist/esm/src/payload.d.ts.map +1 -1
  48. package/dist/esm/src/payload.js +3 -77
  49. package/dist/esm/src/pq/kem.d.ts +33 -0
  50. package/dist/esm/src/pq/kem.d.ts.map +1 -0
  51. package/dist/esm/src/pq/kem.js +40 -0
  52. package/dist/esm/src/ratchet/auth.d.ts +34 -0
  53. package/dist/esm/src/ratchet/auth.d.ts.map +1 -0
  54. package/dist/esm/src/ratchet/auth.js +88 -0
  55. package/dist/esm/src/ratchet/codec.d.ts +52 -0
  56. package/dist/esm/src/ratchet/codec.d.ts.map +1 -0
  57. package/dist/esm/src/ratchet/codec.js +127 -0
  58. package/dist/esm/src/ratchet/decrypt.d.ts +28 -0
  59. package/dist/esm/src/ratchet/decrypt.d.ts.map +1 -0
  60. package/dist/esm/src/ratchet/decrypt.js +255 -0
  61. package/dist/esm/src/ratchet/encrypt.d.ts +17 -0
  62. package/dist/esm/src/ratchet/encrypt.d.ts.map +1 -0
  63. package/dist/esm/src/ratchet/encrypt.js +78 -0
  64. package/dist/esm/src/ratchet/index.d.ts +8 -0
  65. package/dist/esm/src/ratchet/index.d.ts.map +1 -0
  66. package/dist/esm/src/ratchet/index.js +8 -0
  67. package/dist/esm/src/ratchet/kdf.d.ts +60 -0
  68. package/dist/esm/src/ratchet/kdf.d.ts.map +1 -0
  69. package/dist/esm/src/ratchet/kdf.js +91 -0
  70. package/dist/esm/src/ratchet/session.d.ts +43 -0
  71. package/dist/esm/src/ratchet/session.d.ts.map +1 -0
  72. package/dist/esm/src/ratchet/session.js +139 -0
  73. package/dist/esm/src/ratchet/types.d.ts +168 -0
  74. package/dist/esm/src/ratchet/types.d.ts.map +1 -0
  75. package/dist/esm/src/ratchet/types.js +27 -0
  76. package/dist/esm/src/safeSessionSigner.d.ts +35 -0
  77. package/dist/esm/src/safeSessionSigner.d.ts.map +1 -0
  78. package/dist/esm/src/safeSessionSigner.js +59 -0
  79. package/dist/esm/src/send.d.ts +32 -24
  80. package/dist/esm/src/send.d.ts.map +1 -1
  81. package/dist/esm/src/send.js +84 -39
  82. package/dist/esm/src/types.d.ts +8 -13
  83. package/dist/esm/src/types.d.ts.map +1 -1
  84. package/dist/esm/src/utils/safeSessionSigner.d.ts +23 -0
  85. package/dist/esm/src/utils/safeSessionSigner.d.ts.map +1 -0
  86. package/dist/esm/src/utils/safeSessionSigner.js +59 -0
  87. package/dist/esm/src/utils/txQueue.d.ts +12 -0
  88. package/dist/esm/src/utils/txQueue.d.ts.map +1 -0
  89. package/dist/esm/src/utils/txQueue.js +25 -0
  90. package/dist/esm/src/utils.d.ts +2 -3
  91. package/dist/esm/src/utils.d.ts.map +1 -1
  92. package/dist/esm/src/utils.js +5 -5
  93. package/dist/esm/src/verify.d.ts +9 -25
  94. package/dist/esm/src/verify.d.ts.map +1 -1
  95. package/dist/esm/src/verify.js +49 -50
  96. package/dist/src/addresses.d.ts +20 -0
  97. package/dist/src/addresses.d.ts.map +1 -0
  98. package/dist/src/addresses.js +33 -0
  99. package/dist/src/client/HsrTagIndex.d.ts +77 -0
  100. package/dist/src/client/HsrTagIndex.d.ts.map +1 -0
  101. package/dist/src/client/HsrTagIndex.js +157 -0
  102. package/dist/src/client/PendingManager.d.ts +65 -0
  103. package/dist/src/client/PendingManager.d.ts.map +1 -0
  104. package/dist/src/client/PendingManager.js +84 -0
  105. package/dist/src/client/SessionManager.d.ts +65 -0
  106. package/dist/src/client/SessionManager.d.ts.map +1 -0
  107. package/dist/src/client/SessionManager.js +146 -0
  108. package/dist/src/client/VerbethClient.d.ts +153 -99
  109. package/dist/src/client/VerbethClient.d.ts.map +1 -1
  110. package/dist/src/client/VerbethClient.js +429 -123
  111. package/dist/src/client/VerbethClientBuilder.d.ts +105 -0
  112. package/dist/src/client/VerbethClientBuilder.d.ts.map +1 -0
  113. package/dist/src/client/VerbethClientBuilder.js +146 -0
  114. package/dist/src/client/hsrMatcher.d.ts +22 -0
  115. package/dist/src/client/hsrMatcher.d.ts.map +1 -0
  116. package/dist/src/client/hsrMatcher.js +31 -0
  117. package/dist/src/client/index.d.ts +6 -1
  118. package/dist/src/client/index.d.ts.map +1 -1
  119. package/dist/src/client/index.js +2 -0
  120. package/dist/src/client/types.d.ts +151 -10
  121. package/dist/src/client/types.d.ts.map +1 -1
  122. package/dist/src/crypto(old).d.ts +46 -0
  123. package/dist/src/crypto(old).d.ts.map +1 -0
  124. package/dist/src/crypto(old).js +137 -0
  125. package/dist/src/crypto.d.ts +7 -29
  126. package/dist/src/crypto.d.ts.map +1 -1
  127. package/dist/src/crypto.js +36 -72
  128. package/dist/src/executor.d.ts +17 -18
  129. package/dist/src/executor.d.ts.map +1 -1
  130. package/dist/src/executor.js +54 -70
  131. package/dist/src/handshake.d.ts +51 -0
  132. package/dist/src/handshake.d.ts.map +1 -0
  133. package/dist/src/handshake.js +105 -0
  134. package/dist/src/identity.d.ts +24 -18
  135. package/dist/src/identity.d.ts.map +1 -1
  136. package/dist/src/identity.js +126 -31
  137. package/dist/src/index.d.ts +11 -7
  138. package/dist/src/index.d.ts.map +1 -1
  139. package/dist/src/index.js +10 -7
  140. package/dist/src/payload.d.ts +3 -30
  141. package/dist/src/payload.d.ts.map +1 -1
  142. package/dist/src/payload.js +3 -77
  143. package/dist/src/pq/kem.d.ts +33 -0
  144. package/dist/src/pq/kem.d.ts.map +1 -0
  145. package/dist/src/pq/kem.js +40 -0
  146. package/dist/src/ratchet/auth.d.ts +34 -0
  147. package/dist/src/ratchet/auth.d.ts.map +1 -0
  148. package/dist/src/ratchet/auth.js +88 -0
  149. package/dist/src/ratchet/codec.d.ts +52 -0
  150. package/dist/src/ratchet/codec.d.ts.map +1 -0
  151. package/dist/src/ratchet/codec.js +127 -0
  152. package/dist/src/ratchet/decrypt.d.ts +28 -0
  153. package/dist/src/ratchet/decrypt.d.ts.map +1 -0
  154. package/dist/src/ratchet/decrypt.js +255 -0
  155. package/dist/src/ratchet/encrypt.d.ts +17 -0
  156. package/dist/src/ratchet/encrypt.d.ts.map +1 -0
  157. package/dist/src/ratchet/encrypt.js +78 -0
  158. package/dist/src/ratchet/index.d.ts +8 -0
  159. package/dist/src/ratchet/index.d.ts.map +1 -0
  160. package/dist/src/ratchet/index.js +8 -0
  161. package/dist/src/ratchet/kdf.d.ts +60 -0
  162. package/dist/src/ratchet/kdf.d.ts.map +1 -0
  163. package/dist/src/ratchet/kdf.js +91 -0
  164. package/dist/src/ratchet/session.d.ts +43 -0
  165. package/dist/src/ratchet/session.d.ts.map +1 -0
  166. package/dist/src/ratchet/session.js +139 -0
  167. package/dist/src/ratchet/types.d.ts +168 -0
  168. package/dist/src/ratchet/types.d.ts.map +1 -0
  169. package/dist/src/ratchet/types.js +27 -0
  170. package/dist/src/safeSessionSigner.d.ts +35 -0
  171. package/dist/src/safeSessionSigner.d.ts.map +1 -0
  172. package/dist/src/safeSessionSigner.js +59 -0
  173. package/dist/src/send.d.ts +32 -24
  174. package/dist/src/send.d.ts.map +1 -1
  175. package/dist/src/send.js +84 -39
  176. package/dist/src/types.d.ts +8 -13
  177. package/dist/src/types.d.ts.map +1 -1
  178. package/dist/src/utils/safeSessionSigner.d.ts +23 -0
  179. package/dist/src/utils/safeSessionSigner.d.ts.map +1 -0
  180. package/dist/src/utils/safeSessionSigner.js +59 -0
  181. package/dist/src/utils/txQueue.d.ts +12 -0
  182. package/dist/src/utils/txQueue.d.ts.map +1 -0
  183. package/dist/src/utils/txQueue.js +25 -0
  184. package/dist/src/utils.d.ts +2 -3
  185. package/dist/src/utils.d.ts.map +1 -1
  186. package/dist/src/utils.js +5 -5
  187. package/dist/src/verify.d.ts +9 -25
  188. package/dist/src/verify.d.ts.map +1 -1
  189. package/dist/src/verify.js +49 -50
  190. package/package.json +2 -1
@@ -0,0 +1,91 @@
1
+ // packages/sdk/src/ratchet/kdf.ts
2
+ /**
3
+ * Key Derivation Functions for Double Ratchet.
4
+ *
5
+ * Uses HKDF-SHA256 for root key derivation and HMAC-SHA256 for chain key derivation,
6
+ * matching Signal protocol specifications.
7
+ */
8
+ import { hkdf } from '@noble/hashes/hkdf';
9
+ import { sha256 } from '@noble/hashes/sha2';
10
+ import { hmac } from '@noble/hashes/hmac';
11
+ import { keccak256 } from 'ethers';
12
+ import nacl from 'tweetnacl';
13
+ /**
14
+ * Derive new root key and chain key from DH output.
15
+ *
16
+ * @param rootKey - Current root key (32 bytes)
17
+ * @param dhOutput - DH shared secret (32 bytes)
18
+ * @returns New root key and chain key
19
+ */
20
+ export function kdfRootKey(rootKey, dhOutput) {
21
+ const output = hkdf(sha256, dhOutput, rootKey, 'VerbethRatchet', 64);
22
+ return {
23
+ rootKey: output.slice(0, 32),
24
+ chainKey: output.slice(32, 64),
25
+ };
26
+ }
27
+ /**
28
+ * Derive message key and advance chain key.
29
+ *
30
+ * @param chainKey - Current chain key (32 bytes)
31
+ * @returns New chain key and message key for encryption/decryption
32
+ */
33
+ export function kdfChainKey(chainKey) {
34
+ // Message key derived with constant 0x01
35
+ const messageKey = hmac(sha256, chainKey, new Uint8Array([0x01]));
36
+ // New chain key derived with constant 0x02
37
+ const newChainKey = hmac(sha256, chainKey, new Uint8Array([0x02]));
38
+ return {
39
+ chainKey: newChainKey,
40
+ messageKey: messageKey,
41
+ };
42
+ }
43
+ /**
44
+ * Perform X25519 Diffie-Hellman key exchange.
45
+ *
46
+ * @param mySecretKey - My X25519 secret key (32 bytes)
47
+ * @param theirPublicKey - Their X25519 public key (32 bytes)
48
+ * @returns Shared secret (32 bytes)
49
+ */
50
+ export function dh(mySecretKey, theirPublicKey) {
51
+ return nacl.scalarMult(mySecretKey, theirPublicKey);
52
+ }
53
+ /**
54
+ * Generate new X25519 keypair for DH ratchet step.
55
+ *
56
+ * @returns New keypair with secretKey and publicKey
57
+ */
58
+ export function generateDHKeyPair() {
59
+ const kp = nacl.box.keyPair();
60
+ return { secretKey: kp.secretKey, publicKey: kp.publicKey };
61
+ }
62
+ /**
63
+ * Derive topic from DH output using rootKey as PQ-secure salt.
64
+ *
65
+ * The rootKey (PQ-secure from hybrid handshake) acts as HKDF salt,
66
+ * providing quantum-resistant topic unlinkability even if dhOutput
67
+ * is later computed by a quantum adversary.
68
+ *
69
+ * @param rootKey - Current root key (32 bytes, PQ-secure)
70
+ * @param dhOutput - DH shared secret from ratchet step (32 bytes)
71
+ * @param direction - 'outbound' or 'inbound' for topic direction
72
+ * @returns bytes32 topic as hex string
73
+ */
74
+ export function deriveTopic(rootKey, dhOutput, direction) {
75
+ const info = `verbeth:topic-${direction}:v3`;
76
+ const okm = hkdf(sha256, dhOutput, rootKey, info, 32);
77
+ return keccak256(okm);
78
+ }
79
+ /**
80
+ * Combines classical (X25519) and post-quantum (ML-KEM-768) key exchange
81
+ *
82
+ * @param x25519Secret - X25519 DH shared secret (32 bytes)
83
+ * @param kemSecret - ML-KEM-768 shared secret (32 bytes)
84
+ * @returns Hybrid shared secret (32 bytes)
85
+ */
86
+ export function hybridInitialSecret(x25519Secret, kemSecret) {
87
+ const combined = new Uint8Array(x25519Secret.length + kemSecret.length);
88
+ combined.set(x25519Secret, 0);
89
+ combined.set(kemSecret, x25519Secret.length);
90
+ return hkdf(sha256, combined, new Uint8Array(32), 'VerbethHybrid', 32);
91
+ }
@@ -0,0 +1,43 @@
1
+ import { RatchetSession, InitResponderParams, InitInitiatorParams } from './types.js';
2
+ /**
3
+ * Compute deterministic conversation ID from topics.
4
+ * Sorting ensures both parties derive the same ID regardless of perspective.
5
+ *
6
+ * @param topicA - First topic
7
+ * @param topicB - Second topic
8
+ * @returns Unique conversation identifier
9
+ */
10
+ export declare function computeConversationId(topicA: string, topicB: string): string;
11
+ /**
12
+ * Initialize session as responder (Bob).
13
+ *
14
+ * Called after receiving handshake, before/during sending response.
15
+ * The responder must persist myResponderEphemeralSecret immediately.
16
+ * This becomes dhMySecretKey and is required for all future ratchet operations.
17
+ *
18
+ * Responder starts at epoch 0 (handshake topics).
19
+ *
20
+ * If kemSecret is provided (from ML-KEM encapsulation), uses hybrid KDF
21
+ * combining X25519 DH and ML-KEM for post-quantum security.
22
+ *
23
+ * @param params - Initialization parameters
24
+ * @returns Initialized ratchet session
25
+ */
26
+ export declare function initSessionAsResponder(params: InitResponderParams): RatchetSession;
27
+ /**
28
+ * Initialize session as initiator (Alice).
29
+ *
30
+ * Called after receiving and validating handshake response.
31
+ *
32
+ * Initiator precomputes epoch 1 topics from its first post-handshake DH step.
33
+ * Outbound should use epoch 1 as soon as we introduce a new DH pubkey.
34
+ * Inbound stays on epoch 0 until the responder ratchets.
35
+ *
36
+ * If kemSecret is provided (from ML-KEM decapsulation), uses hybrid KDF
37
+ * combining X25519 DH and ML-KEM for post-quantum security.
38
+ *
39
+ * @param params - Initialization parameters
40
+ * @returns Initialized ratchet session
41
+ */
42
+ export declare function initSessionAsInitiator(params: InitInitiatorParams): RatchetSession;
43
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/ratchet/session.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,CA6DlF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,CA4ElF"}
@@ -0,0 +1,139 @@
1
+ // packages/sdk/src/ratchet/session.ts
2
+ /**
3
+ * Ratchet Session Initialization.
4
+ *
5
+ * Provides functions to initialize ratchet sessions for both
6
+ * initiator (Alice) and responder (Bob) roles.
7
+ *
8
+ * Initial shared secret is derived from ephemeral-to-ephemeral DH only.
9
+ */
10
+ import { keccak256, toUtf8Bytes } from 'ethers';
11
+ import { kdfRootKey, dh, generateDHKeyPair, deriveTopic, hybridInitialSecret } from './kdf.js';
12
+ /**
13
+ * Compute deterministic conversation ID from topics.
14
+ * Sorting ensures both parties derive the same ID regardless of perspective.
15
+ *
16
+ * @param topicA - First topic
17
+ * @param topicB - Second topic
18
+ * @returns Unique conversation identifier
19
+ */
20
+ export function computeConversationId(topicA, topicB) {
21
+ const sorted = [topicA.toLowerCase(), topicB.toLowerCase()].sort();
22
+ return keccak256(toUtf8Bytes(sorted.join(':')));
23
+ }
24
+ /**
25
+ * Initialize session as responder (Bob).
26
+ *
27
+ * Called after receiving handshake, before/during sending response.
28
+ * The responder must persist myResponderEphemeralSecret immediately.
29
+ * This becomes dhMySecretKey and is required for all future ratchet operations.
30
+ *
31
+ * Responder starts at epoch 0 (handshake topics).
32
+ *
33
+ * If kemSecret is provided (from ML-KEM encapsulation), uses hybrid KDF
34
+ * combining X25519 DH and ML-KEM for post-quantum security.
35
+ *
36
+ * @param params - Initialization parameters
37
+ * @returns Initialized ratchet session
38
+ */
39
+ export function initSessionAsResponder(params) {
40
+ const { myAddress, contactAddress, myResponderEphemeralSecret, myResponderEphemeralPublic, theirHandshakeEphemeralPubKey, topicOutbound, topicInbound, kemSecret, } = params;
41
+ const x25519Secret = dh(myResponderEphemeralSecret, theirHandshakeEphemeralPubKey);
42
+ const sharedSecret = kemSecret
43
+ ? hybridInitialSecret(x25519Secret, kemSecret)
44
+ : x25519Secret;
45
+ // Derive initial root key and sending chain key
46
+ const { rootKey, chainKey: sendingChainKey } = kdfRootKey(new Uint8Array(32), // Initial salt (zeros)
47
+ sharedSecret);
48
+ const now = Date.now();
49
+ return {
50
+ conversationId: computeConversationId(topicOutbound, topicInbound),
51
+ topicOutbound,
52
+ topicInbound,
53
+ myAddress,
54
+ contactAddress,
55
+ rootKey,
56
+ // Reuse responder ephemeral as first DH ratchet key
57
+ dhMySecretKey: myResponderEphemeralSecret,
58
+ dhMyPublicKey: myResponderEphemeralPublic,
59
+ dhTheirPublicKey: theirHandshakeEphemeralPubKey,
60
+ sendingChainKey,
61
+ sendingMsgNumber: 0,
62
+ // Receiving chain not yet established (Alice sends first post-handshake)
63
+ receivingChainKey: null,
64
+ receivingMsgNumber: 0,
65
+ previousChainLength: 0,
66
+ skippedKeys: [],
67
+ // Topic Ratcheting - Epoch 0: use handshake-derived topics
68
+ currentTopicOutbound: topicOutbound,
69
+ currentTopicInbound: topicInbound,
70
+ previousTopicInbound: undefined,
71
+ previousTopicExpiry: undefined,
72
+ topicEpoch: 0,
73
+ createdAt: now,
74
+ updatedAt: now,
75
+ epoch: 0,
76
+ };
77
+ }
78
+ /**
79
+ * Initialize session as initiator (Alice).
80
+ *
81
+ * Called after receiving and validating handshake response.
82
+ *
83
+ * Initiator precomputes epoch 1 topics from its first post-handshake DH step.
84
+ * Outbound should use epoch 1 as soon as we introduce a new DH pubkey.
85
+ * Inbound stays on epoch 0 until the responder ratchets.
86
+ *
87
+ * If kemSecret is provided (from ML-KEM decapsulation), uses hybrid KDF
88
+ * combining X25519 DH and ML-KEM for post-quantum security.
89
+ *
90
+ * @param params - Initialization parameters
91
+ * @returns Initialized ratchet session
92
+ */
93
+ export function initSessionAsInitiator(params) {
94
+ const { myAddress, contactAddress, myHandshakeEphemeralSecret, theirResponderEphemeralPubKey, topicOutbound, topicInbound, kemSecret, } = params;
95
+ const x25519Secret = dh(myHandshakeEphemeralSecret, theirResponderEphemeralPubKey);
96
+ const sharedSecret = kemSecret
97
+ ? hybridInitialSecret(x25519Secret, kemSecret)
98
+ : x25519Secret;
99
+ // Derive same initial root key as responder
100
+ const { rootKey: initialRootKey, chainKey: bobsSendingChain } = kdfRootKey(new Uint8Array(32), sharedSecret);
101
+ // Generate first DH keypair for sending (Alice performs first DH ratchet)
102
+ const myDHKeyPair = generateDHKeyPair();
103
+ const dhSend = dh(myDHKeyPair.secretKey, theirResponderEphemeralPubKey);
104
+ const { rootKey: finalRootKey, chainKey: sendingChainKey } = kdfRootKey(initialRootKey, dhSend);
105
+ const conversationId = computeConversationId(topicOutbound, topicInbound);
106
+ // Pre-compute epoch 1 topics (for when our first message is sent)
107
+ // Use finalRootKey as PQ-secure salt for quantum-resistant topic unlinkability
108
+ const epoch1TopicOut = deriveTopic(finalRootKey, dhSend, 'outbound');
109
+ const epoch1TopicIn = deriveTopic(finalRootKey, dhSend, 'inbound');
110
+ const now = Date.now();
111
+ return {
112
+ conversationId,
113
+ topicOutbound,
114
+ topicInbound,
115
+ myAddress,
116
+ contactAddress,
117
+ rootKey: finalRootKey,
118
+ dhMySecretKey: myDHKeyPair.secretKey,
119
+ dhMyPublicKey: myDHKeyPair.publicKey,
120
+ dhTheirPublicKey: theirResponderEphemeralPubKey,
121
+ sendingChainKey,
122
+ sendingMsgNumber: 0,
123
+ // Alice can receive Bob's messages immediately
124
+ receivingChainKey: bobsSendingChain,
125
+ receivingMsgNumber: 0,
126
+ previousChainLength: 0,
127
+ skippedKeys: [],
128
+ // Start with handshake topics
129
+ currentTopicOutbound: topicOutbound,
130
+ currentTopicInbound: topicInbound,
131
+ // Pre-computed next topics (will be promoted when we send)
132
+ nextTopicOutbound: epoch1TopicOut,
133
+ nextTopicInbound: epoch1TopicIn,
134
+ topicEpoch: 0,
135
+ createdAt: now,
136
+ updatedAt: now,
137
+ epoch: 0,
138
+ };
139
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Double Ratchet types and constants.
3
+ */
4
+ /**
5
+ * Sanity cap per ratchet step, protects against malicious peers or corrupted state.
6
+ */
7
+ export declare const MAX_SKIP_PER_MESSAGE = 100000;
8
+ /**
9
+ * When exceeded, oldest keys are pruned.
10
+ */
11
+ export declare const MAX_STORED_SKIPPED_KEYS = 1000;
12
+ /**
13
+ * Skipped keys TTL (24 hours is sufficient for reorg tolerance).
14
+ * (this is not message expiry as sequential messages don't use skipped keys)
15
+ */
16
+ export declare const MAX_SKIPPED_KEYS_AGE_MS: number;
17
+ /**
18
+ * Yield to UI every N derivations during large backlog processing.
19
+ */
20
+ export declare const SYNC_BATCH_SIZE = 10000;
21
+ /**
22
+ * Binary payload version byte.
23
+ */
24
+ export declare const RATCHET_VERSION_V1 = 1;
25
+ export declare const TOPIC_TRANSITION_WINDOW_MS: number;
26
+ /**
27
+ * Ratchet session state.
28
+ *
29
+ * This is stateful - must be persisted after every encrypt/decrypt.
30
+ * Session is identified by conversationId (derived from topics), enabling
31
+ * correct handling of Safe addresses vs EOAs.
32
+ */
33
+ export interface RatchetSession {
34
+ /** Primary key: keccak256(sort([topicOut, topicIn])) */
35
+ conversationId: string;
36
+ /** Original handshake-derived outbound topic (immutable reference) */
37
+ topicOutbound: `0x${string}`;
38
+ /** Original handshake-derived inbound topic (immutable reference) */
39
+ topicInbound: `0x${string}`;
40
+ /** My EOA address (for convenience/lookup) */
41
+ myAddress: string;
42
+ /** Their EOA address (for convenience/lookup) */
43
+ contactAddress: string;
44
+ /** Current root key (32 bytes) */
45
+ rootKey: Uint8Array;
46
+ /** My current DH secret key (32 bytes) */
47
+ dhMySecretKey: Uint8Array;
48
+ /** My current DH public key (32 bytes) */
49
+ dhMyPublicKey: Uint8Array;
50
+ /** Their last received DH public key (32 bytes) */
51
+ dhTheirPublicKey: Uint8Array;
52
+ /** Current sending chain key (null until first DH ratchet as sender) */
53
+ sendingChainKey: Uint8Array | null;
54
+ /** Next sending message number (Ns) */
55
+ sendingMsgNumber: number;
56
+ /** Current receiving chain key (null until first message received) */
57
+ receivingChainKey: Uint8Array | null;
58
+ /** Next expected receiving message number (Nr) */
59
+ receivingMsgNumber: number;
60
+ /** Message count in previous sending chain (PN header field) */
61
+ previousChainLength: number;
62
+ /** Stored keys for out-of-order messages */
63
+ skippedKeys: SkippedKey[];
64
+ /** Current outbound topic (may differ from original after ratcheting) */
65
+ currentTopicOutbound: `0x${string}`;
66
+ /** Current inbound topic (may differ from original after ratcheting) */
67
+ currentTopicInbound: `0x${string}`;
68
+ /** Pre-computed next outbound topic (for our next DH ratchet) */
69
+ nextTopicOutbound?: `0x${string}`;
70
+ /** Pre-computed next inbound topic (for their next DH ratchet) */
71
+ nextTopicInbound?: `0x${string}`;
72
+ /** Previous inbound topic (grace period for late messages) */
73
+ previousTopicInbound?: `0x${string}`;
74
+ /** Expiry timestamp for previous topic */
75
+ previousTopicExpiry?: number;
76
+ /** Topic epoch counter */
77
+ topicEpoch: number;
78
+ /** Session creation timestamp */
79
+ createdAt: number;
80
+ /** Last state update timestamp */
81
+ updatedAt: number;
82
+ /** Increments on session reset (internal bookkeeping only) */
83
+ epoch: number;
84
+ }
85
+ /**
86
+ * Stored key for out-of-order message decryption.
87
+ */
88
+ export interface SkippedKey {
89
+ /** DH epoch identifier (hex of their DH pubkey) */
90
+ dhPubKeyHex: string;
91
+ /** Message number in that epoch */
92
+ msgNumber: number;
93
+ /** Derived message key (32 bytes) */
94
+ messageKey: Uint8Array;
95
+ /** Creation timestamp for TTL pruning */
96
+ createdAt: number;
97
+ }
98
+ /**
99
+ * Message header (40 bytes when encoded).
100
+ */
101
+ export interface MessageHeader {
102
+ /** Sender's current DH ratchet public key (32 bytes) */
103
+ dh: Uint8Array;
104
+ /** Previous chain length - messages in sender's previous sending chain */
105
+ pn: number;
106
+ /** Message number in current sending chain */
107
+ n: number;
108
+ }
109
+ /**
110
+ * Result of ratchetEncrypt - includes new session state for two-phase commit.
111
+ */
112
+ export interface EncryptResult {
113
+ /** Updated session state (MUST be persisted) */
114
+ session: RatchetSession;
115
+ header: MessageHeader;
116
+ ciphertext: Uint8Array;
117
+ signature: Uint8Array;
118
+ topic: `0x${string}`;
119
+ }
120
+ /**
121
+ * Result of ratchetDecrypt.
122
+ */
123
+ export interface DecryptResult {
124
+ /** Updated session state (MUST be persisted) */
125
+ session: RatchetSession;
126
+ plaintext: Uint8Array;
127
+ }
128
+ /**
129
+ * Parsed binary ratchet payload.
130
+ */
131
+ export interface ParsedRatchetPayload {
132
+ version: number;
133
+ signature: Uint8Array;
134
+ header: MessageHeader;
135
+ ciphertext: Uint8Array;
136
+ }
137
+ /**
138
+ * Parameters for initializing session as responder.
139
+ */
140
+ export interface InitResponderParams {
141
+ myAddress: string;
142
+ contactAddress: string;
143
+ /** Ephemeral secret used in HandshakeResponse (becomes dhMySecretKey) */
144
+ myResponderEphemeralSecret: Uint8Array;
145
+ myResponderEphemeralPublic: Uint8Array;
146
+ /** Initiator's ephemeral from Handshake event */
147
+ theirHandshakeEphemeralPubKey: Uint8Array;
148
+ topicOutbound: `0x${string}`;
149
+ topicInbound: `0x${string}`;
150
+ /** ML-KEM shared secret for PQ-hybrid handshake */
151
+ kemSecret?: Uint8Array;
152
+ }
153
+ /**
154
+ * Parameters for initializing session as initiator.
155
+ */
156
+ export interface InitInitiatorParams {
157
+ myAddress: string;
158
+ contactAddress: string;
159
+ /** Handshake ephemeral secret (must persist until response arrives) */
160
+ myHandshakeEphemeralSecret: Uint8Array;
161
+ /** Responder's ephemeral from HandshakeResponse (inside encrypted payload) */
162
+ theirResponderEphemeralPubKey: Uint8Array;
163
+ topicOutbound: `0x${string}`;
164
+ topicInbound: `0x${string}`;
165
+ /** ML-KEM shared secret for PQ-hybrid handshake */
166
+ kemSecret?: Uint8Array;
167
+ }
168
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/ratchet/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ // packages/sdk/src/ratchet/types.ts
2
+ /**
3
+ * Double Ratchet types and constants.
4
+ */
5
+ /**
6
+ * Sanity cap per ratchet step, protects against malicious peers or corrupted state.
7
+ */
8
+ export const MAX_SKIP_PER_MESSAGE = 100000;
9
+ /**
10
+ * When exceeded, oldest keys are pruned.
11
+ */
12
+ export const MAX_STORED_SKIPPED_KEYS = 1000;
13
+ /**
14
+ * Skipped keys TTL (24 hours is sufficient for reorg tolerance).
15
+ * (this is not message expiry as sequential messages don't use skipped keys)
16
+ */
17
+ export const MAX_SKIPPED_KEYS_AGE_MS = 24 * 60 * 60 * 1000;
18
+ /**
19
+ * Yield to UI every N derivations during large backlog processing.
20
+ */
21
+ export const SYNC_BATCH_SIZE = 10000;
22
+ /**
23
+ * Binary payload version byte.
24
+ */
25
+ export const RATCHET_VERSION_V1 = 0x01;
26
+ // Previous topic remains valid for this duration after promotion.
27
+ export const TOPIC_TRANSITION_WINDOW_MS = 5 * 60 * 1000;
@@ -0,0 +1,35 @@
1
+ import { AbstractSigner, type Provider, type Signer, type TransactionRequest, type TransactionResponse, type TypedDataDomain, type TypedDataField } from "ethers";
2
+ export interface SafeSessionSignerOptions {
3
+ provider: Provider;
4
+ safeAddress: string;
5
+ /** Safe module enabled on the Safe */
6
+ moduleAddress: string;
7
+ /** only allowed target */
8
+ logChainAddress: string;
9
+ /** The EOA session signer (pays gas, calls the module). Must be connected to a provider. */
10
+ sessionSigner: Signer;
11
+ /** Default: execute(address,uint256,bytes,uint8) */
12
+ moduleAbi?: readonly string[];
13
+ /** Default: "execute" */
14
+ executeMethod?: string;
15
+ }
16
+ /**
17
+ * Ethers v6 Signer adapter:
18
+ * - Exposes address = Safe
19
+ * - Intercepts txs to LogChain and routes them through the Safe module
20
+ *
21
+ * sessionSigner is an EOA that directly sends the module tx.
22
+ */
23
+ export declare class SafeSessionSigner extends AbstractSigner {
24
+ private module;
25
+ private executeMethod;
26
+ private opts;
27
+ constructor(opts: SafeSessionSignerOptions);
28
+ getAddress(): Promise<string>;
29
+ signMessage(message: string | Uint8Array): Promise<string>;
30
+ signTransaction(_tx: TransactionRequest): Promise<string>;
31
+ signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
32
+ sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>;
33
+ connect(provider: Provider): SafeSessionSigner;
34
+ }
35
+ //# sourceMappingURL=safeSessionSigner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeSessionSigner.d.ts","sourceRoot":"","sources":["../../../src/safeSessionSigner.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EAEd,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,QAAQ,CAAC;AAEhB,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,aAAa,EAAE,MAAM,CAAC;IAEtB,oDAAoD;IACpD,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,yBAAyB;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;IACnD,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,IAAI,CAA2B;gBAE3B,IAAI,EAAE,wBAAwB;IAkB3B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,eAAe,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIzD,aAAa,CAC1B,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,EAC5C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACzB,OAAO,CAAC,MAAM,CAAC;IASH,eAAe,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsB3E,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,iBAAiB;CAGxD"}
@@ -0,0 +1,59 @@
1
+ // packages/sdk/src/safeSessionSigner.ts
2
+ import { AbstractSigner, Contract, } from "ethers";
3
+ const DEFAULT_ABI = [
4
+ "function execute(address to, uint256 value, bytes data, uint8 operation)",
5
+ ];
6
+ /**
7
+ * Ethers v6 Signer adapter:
8
+ * - Exposes address = Safe
9
+ * - Intercepts txs to LogChain and routes them through the Safe module
10
+ *
11
+ * sessionSigner is an EOA that directly sends the module tx.
12
+ */
13
+ export class SafeSessionSigner extends AbstractSigner {
14
+ constructor(opts) {
15
+ super(opts.provider);
16
+ this.opts = opts;
17
+ if (!opts.sessionSigner.provider) {
18
+ throw new Error("SafeSessionSigner: sessionSigner must be connected to a Provider (e.g., new Wallet(pk, provider)).");
19
+ }
20
+ this.module = new Contract(opts.moduleAddress, (opts.moduleAbi ?? DEFAULT_ABI), opts.sessionSigner);
21
+ this.executeMethod = opts.executeMethod ?? "execute";
22
+ }
23
+ async getAddress() {
24
+ return this.opts.safeAddress;
25
+ }
26
+ async signMessage(message) {
27
+ return this.opts.sessionSigner.signMessage(message);
28
+ }
29
+ async signTransaction(_tx) {
30
+ throw new Error("SafeSessionSigner: signTransaction not supported; use sendTransaction().");
31
+ }
32
+ async signTypedData(domain, types, value) {
33
+ // delegate
34
+ const anySigner = this.opts.sessionSigner;
35
+ if (typeof anySigner.signTypedData === "function") {
36
+ return anySigner.signTypedData(domain, types, value);
37
+ }
38
+ throw new Error("SafeSessionSigner: underlying sessionSigner does not support signTypedData.");
39
+ }
40
+ async sendTransaction(tx) {
41
+ if (!tx.to)
42
+ throw new Error("SafeSessionSigner: tx.to is required");
43
+ const to = String(tx.to).toLowerCase();
44
+ const logChain = this.opts.logChainAddress.toLowerCase();
45
+ if (to !== logChain) {
46
+ throw new Error(`SafeSessionSigner: only LogChain txs are supported. Got to=${tx.to}`);
47
+ }
48
+ const data = tx.data ?? "0x";
49
+ const fn = this.module[this.executeMethod];
50
+ if (typeof fn !== "function") {
51
+ throw new Error(`SafeSessionSigner: module execute method "${this.executeMethod}" not found on ${this.opts.moduleAddress}`);
52
+ }
53
+ // operation: 0 = CALL, value: 0
54
+ return fn(this.opts.logChainAddress, 0n, data, 0);
55
+ }
56
+ connect(provider) {
57
+ return new SafeSessionSigner({ ...this.opts, provider });
58
+ }
59
+ }
@@ -2,49 +2,57 @@ import { Signer } from "ethers";
2
2
  import nacl from 'tweetnacl';
3
3
  import { IdentityKeyPair, IdentityProof } from './types.js';
4
4
  import { IExecutor } from './executor.js';
5
- /**
6
- * Sends an encrypted message assuming recipient's keys were already obtained via handshake.
7
- * Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint (for tests)
8
- */
9
- export declare function sendEncryptedMessage({ executor, topic, message, recipientPubKey, senderAddress, senderSignKeyPair, timestamp }: {
10
- executor: IExecutor;
11
- topic: string;
12
- message: string;
13
- recipientPubKey: Uint8Array;
14
- senderAddress: string;
15
- senderSignKeyPair: nacl.SignKeyPair;
16
- timestamp: number;
17
- }): Promise<any>;
5
+ /** ML-KEM keypair for PQ-hybrid handshake */
6
+ export interface KemKeyPair {
7
+ publicKey: Uint8Array;
8
+ secretKey: Uint8Array;
9
+ }
18
10
  /**
19
11
  * Initiates an on-chain handshake with unified keys and mandatory identity proof.
20
12
  * Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint (for tests)
13
+ *
14
+ * Includes ML-KEM-768 public key for post-quantum hybrid key exchange.
15
+ *
16
+ * @returns Transaction, ephemeral keypair, and KEM keypair (MUST be persisted for session init)
21
17
  */
22
- export declare function initiateHandshake({ executor, recipientAddress, identityKeyPair, ephemeralPubKey, plaintextPayload, identityProof, signer }: {
18
+ export declare function initiateHandshake({ executor, recipientAddress, identityKeyPair, plaintextPayload, identityProof, }: {
23
19
  executor: IExecutor;
24
20
  recipientAddress: string;
25
21
  identityKeyPair: IdentityKeyPair;
26
- ephemeralPubKey: Uint8Array;
27
22
  plaintextPayload: string;
28
23
  identityProof: IdentityProof;
29
- signer: Signer;
30
- }): Promise<any>;
24
+ signer?: Signer;
25
+ }): Promise<{
26
+ tx: any;
27
+ ephemeralKeyPair: nacl.BoxKeyPair;
28
+ kemKeyPair: KemKeyPair;
29
+ }>;
31
30
  /**
32
31
  * Responds to a handshake with unified keys and mandatory identity proof.
33
32
  * Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint (for tests)
33
+ *
34
+ * Supports PQ-hybrid handshake: if initiator includes KEM public key,
35
+ * encapsulates a shared secret and includes ciphertext in response.
36
+ *
37
+ * @returns Transaction, tag, salt, ephemeral keys, and KEM secret
34
38
  */
35
- export declare function respondToHandshake({ executor, initiatorPubKey, // X25519 key from initiator (ephemeral)
36
- responderIdentityKeyPair, responderEphemeralKeyPair, note, identityProof, signer, initiatorIdentityPubKey, }: {
39
+ export declare function respondToHandshake({ executor, initiatorEphemeralPubKey, responderIdentityKeyPair, note, identityProof, }: {
37
40
  executor: IExecutor;
38
- initiatorPubKey: Uint8Array;
41
+ /** Initiator's ephemeral key (32 bytes X25519) OR extended key (1216 bytes: X25519 + ML-KEM) */
42
+ initiatorEphemeralPubKey: Uint8Array;
39
43
  responderIdentityKeyPair: IdentityKeyPair;
40
- responderEphemeralKeyPair?: nacl.BoxKeyPair;
41
44
  note?: string;
42
45
  identityProof: IdentityProof;
43
- signer: Signer;
44
- initiatorIdentityPubKey?: Uint8Array;
46
+ signer?: Signer;
45
47
  }): Promise<{
46
48
  tx: any;
47
- salt: Uint8Array<ArrayBufferLike>;
49
+ salt: Uint8Array;
48
50
  tag: `0x${string}`;
51
+ /** Responder's DH ratchet secret - must persist as dhMySecretKey in ratchet session */
52
+ responderEphemeralSecret: Uint8Array;
53
+ /** Responder's DH ratchet public - inside encrypted payload, not on-chain */
54
+ responderEphemeralPublic: Uint8Array;
55
+ /** ML-KEM shared secret (32 bytes) - MUST persist for hybrid KDF, undefined if no KEM in handshake */
56
+ kemSharedSecret?: Uint8Array;
49
57
  }>;
50
58
  //# sourceMappingURL=send.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../../src/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,MAAM,EAEP,MAAM,QAAQ,CAAC;AAChB,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,QAAQ,EACR,KAAK,EACL,OAAO,EACP,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,SAAS,EACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,IAAI,CAAC,WAAW,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB,gBAmBA;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,MAAM,EACP,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,eAAe,EAAE,UAAU,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,gBA4BA;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,QAAQ,EACR,eAAe,EAAE,wCAAwC;AACzD,wBAAwB,EACxB,yBAAyB,EACzB,IAAI,EACJ,aAAa,EACb,MAAM,EACN,uBAAuB,GACxB,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,eAAe,EAAE,UAAU,CAAC;IAC5B,wBAAwB,EAAE,eAAe,CAAC;IAC1C,yBAAyB,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,CAAC,EAAE,UAAU,CAAC;CACtC;;;;GAuDA"}
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../../src/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,MAAM,EAEP,MAAM,QAAQ,CAAC;AAChB,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI1C,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,gBAAgB,EAAE,IAAI,CAAC,UAAU,CAAC;IAClC,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC,CA6CD;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,QAAQ,EACR,wBAAwB,EACxB,wBAAwB,EACxB,IAAI,EACJ,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,gGAAgG;IAChG,wBAAwB,EAAE,UAAU,CAAC;IACrC,wBAAwB,EAAE,eAAe,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,KAAK,MAAM,EAAE,CAAC;IACnB,uFAAuF;IACvF,wBAAwB,EAAE,UAAU,CAAC;IACrC,6EAA6E;IAC7E,wBAAwB,EAAE,UAAU,CAAC;IACrC,sGAAsG;IACtG,eAAe,CAAC,EAAE,UAAU,CAAC;CAC9B,CAAC,CA+FD"}