@verbeth/sdk 0.1.4 → 0.1.5

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 (184) hide show
  1. package/README.md +16 -167
  2. package/dist/esm/src/client/HsrTagIndex.d.ts +77 -0
  3. package/dist/esm/src/client/HsrTagIndex.d.ts.map +1 -0
  4. package/dist/esm/src/client/HsrTagIndex.js +157 -0
  5. package/dist/esm/src/client/PendingManager.d.ts +65 -0
  6. package/dist/esm/src/client/PendingManager.d.ts.map +1 -0
  7. package/dist/esm/src/client/PendingManager.js +84 -0
  8. package/dist/esm/src/client/SessionManager.d.ts +65 -0
  9. package/dist/esm/src/client/SessionManager.d.ts.map +1 -0
  10. package/dist/esm/src/client/SessionManager.js +146 -0
  11. package/dist/esm/src/client/VerbethClient.d.ts +153 -99
  12. package/dist/esm/src/client/VerbethClient.d.ts.map +1 -1
  13. package/dist/esm/src/client/VerbethClient.js +429 -123
  14. package/dist/esm/src/client/VerbethClientBuilder.d.ts +105 -0
  15. package/dist/esm/src/client/VerbethClientBuilder.d.ts.map +1 -0
  16. package/dist/esm/src/client/VerbethClientBuilder.js +146 -0
  17. package/dist/esm/src/client/hsrMatcher.d.ts +22 -0
  18. package/dist/esm/src/client/hsrMatcher.d.ts.map +1 -0
  19. package/dist/esm/src/client/hsrMatcher.js +31 -0
  20. package/dist/esm/src/client/index.d.ts +6 -1
  21. package/dist/esm/src/client/index.d.ts.map +1 -1
  22. package/dist/esm/src/client/index.js +2 -0
  23. package/dist/esm/src/client/types.d.ts +151 -10
  24. package/dist/esm/src/client/types.d.ts.map +1 -1
  25. package/dist/esm/src/crypto(old).d.ts +46 -0
  26. package/dist/esm/src/crypto(old).d.ts.map +1 -0
  27. package/dist/esm/src/crypto(old).js +137 -0
  28. package/dist/esm/src/crypto.d.ts +7 -29
  29. package/dist/esm/src/crypto.d.ts.map +1 -1
  30. package/dist/esm/src/crypto.js +36 -72
  31. package/dist/esm/src/executor.d.ts +1 -2
  32. package/dist/esm/src/executor.d.ts.map +1 -1
  33. package/dist/esm/src/executor.js +8 -24
  34. package/dist/esm/src/handshake.d.ts +51 -0
  35. package/dist/esm/src/handshake.d.ts.map +1 -0
  36. package/dist/esm/src/handshake.js +105 -0
  37. package/dist/esm/src/identity.d.ts +24 -18
  38. package/dist/esm/src/identity.d.ts.map +1 -1
  39. package/dist/esm/src/identity.js +126 -31
  40. package/dist/esm/src/index.d.ts +10 -7
  41. package/dist/esm/src/index.d.ts.map +1 -1
  42. package/dist/esm/src/index.js +9 -7
  43. package/dist/esm/src/payload.d.ts +3 -30
  44. package/dist/esm/src/payload.d.ts.map +1 -1
  45. package/dist/esm/src/payload.js +3 -77
  46. package/dist/esm/src/pq/kem.d.ts +33 -0
  47. package/dist/esm/src/pq/kem.d.ts.map +1 -0
  48. package/dist/esm/src/pq/kem.js +40 -0
  49. package/dist/esm/src/ratchet/auth.d.ts +34 -0
  50. package/dist/esm/src/ratchet/auth.d.ts.map +1 -0
  51. package/dist/esm/src/ratchet/auth.js +88 -0
  52. package/dist/esm/src/ratchet/codec.d.ts +52 -0
  53. package/dist/esm/src/ratchet/codec.d.ts.map +1 -0
  54. package/dist/esm/src/ratchet/codec.js +127 -0
  55. package/dist/esm/src/ratchet/decrypt.d.ts +28 -0
  56. package/dist/esm/src/ratchet/decrypt.d.ts.map +1 -0
  57. package/dist/esm/src/ratchet/decrypt.js +255 -0
  58. package/dist/esm/src/ratchet/encrypt.d.ts +17 -0
  59. package/dist/esm/src/ratchet/encrypt.d.ts.map +1 -0
  60. package/dist/esm/src/ratchet/encrypt.js +78 -0
  61. package/dist/esm/src/ratchet/index.d.ts +8 -0
  62. package/dist/esm/src/ratchet/index.d.ts.map +1 -0
  63. package/dist/esm/src/ratchet/index.js +8 -0
  64. package/dist/esm/src/ratchet/kdf.d.ts +60 -0
  65. package/dist/esm/src/ratchet/kdf.d.ts.map +1 -0
  66. package/dist/esm/src/ratchet/kdf.js +91 -0
  67. package/dist/esm/src/ratchet/session.d.ts +43 -0
  68. package/dist/esm/src/ratchet/session.d.ts.map +1 -0
  69. package/dist/esm/src/ratchet/session.js +139 -0
  70. package/dist/esm/src/ratchet/types.d.ts +168 -0
  71. package/dist/esm/src/ratchet/types.d.ts.map +1 -0
  72. package/dist/esm/src/ratchet/types.js +27 -0
  73. package/dist/esm/src/safeSessionSigner.d.ts +35 -0
  74. package/dist/esm/src/safeSessionSigner.d.ts.map +1 -0
  75. package/dist/esm/src/safeSessionSigner.js +59 -0
  76. package/dist/esm/src/send.d.ts +32 -24
  77. package/dist/esm/src/send.d.ts.map +1 -1
  78. package/dist/esm/src/send.js +84 -39
  79. package/dist/esm/src/types.d.ts +8 -13
  80. package/dist/esm/src/types.d.ts.map +1 -1
  81. package/dist/esm/src/utils/safeSessionSigner.d.ts +23 -0
  82. package/dist/esm/src/utils/safeSessionSigner.d.ts.map +1 -0
  83. package/dist/esm/src/utils/safeSessionSigner.js +59 -0
  84. package/dist/esm/src/utils/txQueue.d.ts +12 -0
  85. package/dist/esm/src/utils/txQueue.d.ts.map +1 -0
  86. package/dist/esm/src/utils/txQueue.js +25 -0
  87. package/dist/esm/src/utils.d.ts +2 -3
  88. package/dist/esm/src/utils.d.ts.map +1 -1
  89. package/dist/esm/src/utils.js +5 -5
  90. package/dist/esm/src/verify.d.ts +9 -25
  91. package/dist/esm/src/verify.d.ts.map +1 -1
  92. package/dist/esm/src/verify.js +49 -50
  93. package/dist/src/client/HsrTagIndex.d.ts +77 -0
  94. package/dist/src/client/HsrTagIndex.d.ts.map +1 -0
  95. package/dist/src/client/HsrTagIndex.js +157 -0
  96. package/dist/src/client/PendingManager.d.ts +65 -0
  97. package/dist/src/client/PendingManager.d.ts.map +1 -0
  98. package/dist/src/client/PendingManager.js +84 -0
  99. package/dist/src/client/SessionManager.d.ts +65 -0
  100. package/dist/src/client/SessionManager.d.ts.map +1 -0
  101. package/dist/src/client/SessionManager.js +146 -0
  102. package/dist/src/client/VerbethClient.d.ts +153 -99
  103. package/dist/src/client/VerbethClient.d.ts.map +1 -1
  104. package/dist/src/client/VerbethClient.js +429 -123
  105. package/dist/src/client/VerbethClientBuilder.d.ts +105 -0
  106. package/dist/src/client/VerbethClientBuilder.d.ts.map +1 -0
  107. package/dist/src/client/VerbethClientBuilder.js +146 -0
  108. package/dist/src/client/hsrMatcher.d.ts +22 -0
  109. package/dist/src/client/hsrMatcher.d.ts.map +1 -0
  110. package/dist/src/client/hsrMatcher.js +31 -0
  111. package/dist/src/client/index.d.ts +6 -1
  112. package/dist/src/client/index.d.ts.map +1 -1
  113. package/dist/src/client/index.js +2 -0
  114. package/dist/src/client/types.d.ts +151 -10
  115. package/dist/src/client/types.d.ts.map +1 -1
  116. package/dist/src/crypto(old).d.ts +46 -0
  117. package/dist/src/crypto(old).d.ts.map +1 -0
  118. package/dist/src/crypto(old).js +137 -0
  119. package/dist/src/crypto.d.ts +7 -29
  120. package/dist/src/crypto.d.ts.map +1 -1
  121. package/dist/src/crypto.js +36 -72
  122. package/dist/src/executor.d.ts +1 -2
  123. package/dist/src/executor.d.ts.map +1 -1
  124. package/dist/src/executor.js +8 -24
  125. package/dist/src/handshake.d.ts +51 -0
  126. package/dist/src/handshake.d.ts.map +1 -0
  127. package/dist/src/handshake.js +105 -0
  128. package/dist/src/identity.d.ts +24 -18
  129. package/dist/src/identity.d.ts.map +1 -1
  130. package/dist/src/identity.js +126 -31
  131. package/dist/src/index.d.ts +10 -7
  132. package/dist/src/index.d.ts.map +1 -1
  133. package/dist/src/index.js +9 -7
  134. package/dist/src/payload.d.ts +3 -30
  135. package/dist/src/payload.d.ts.map +1 -1
  136. package/dist/src/payload.js +3 -77
  137. package/dist/src/pq/kem.d.ts +33 -0
  138. package/dist/src/pq/kem.d.ts.map +1 -0
  139. package/dist/src/pq/kem.js +40 -0
  140. package/dist/src/ratchet/auth.d.ts +34 -0
  141. package/dist/src/ratchet/auth.d.ts.map +1 -0
  142. package/dist/src/ratchet/auth.js +88 -0
  143. package/dist/src/ratchet/codec.d.ts +52 -0
  144. package/dist/src/ratchet/codec.d.ts.map +1 -0
  145. package/dist/src/ratchet/codec.js +127 -0
  146. package/dist/src/ratchet/decrypt.d.ts +28 -0
  147. package/dist/src/ratchet/decrypt.d.ts.map +1 -0
  148. package/dist/src/ratchet/decrypt.js +255 -0
  149. package/dist/src/ratchet/encrypt.d.ts +17 -0
  150. package/dist/src/ratchet/encrypt.d.ts.map +1 -0
  151. package/dist/src/ratchet/encrypt.js +78 -0
  152. package/dist/src/ratchet/index.d.ts +8 -0
  153. package/dist/src/ratchet/index.d.ts.map +1 -0
  154. package/dist/src/ratchet/index.js +8 -0
  155. package/dist/src/ratchet/kdf.d.ts +60 -0
  156. package/dist/src/ratchet/kdf.d.ts.map +1 -0
  157. package/dist/src/ratchet/kdf.js +91 -0
  158. package/dist/src/ratchet/session.d.ts +43 -0
  159. package/dist/src/ratchet/session.d.ts.map +1 -0
  160. package/dist/src/ratchet/session.js +139 -0
  161. package/dist/src/ratchet/types.d.ts +168 -0
  162. package/dist/src/ratchet/types.d.ts.map +1 -0
  163. package/dist/src/ratchet/types.js +27 -0
  164. package/dist/src/safeSessionSigner.d.ts +35 -0
  165. package/dist/src/safeSessionSigner.d.ts.map +1 -0
  166. package/dist/src/safeSessionSigner.js +59 -0
  167. package/dist/src/send.d.ts +32 -24
  168. package/dist/src/send.d.ts.map +1 -1
  169. package/dist/src/send.js +84 -39
  170. package/dist/src/types.d.ts +8 -13
  171. package/dist/src/types.d.ts.map +1 -1
  172. package/dist/src/utils/safeSessionSigner.d.ts +23 -0
  173. package/dist/src/utils/safeSessionSigner.d.ts.map +1 -0
  174. package/dist/src/utils/safeSessionSigner.js +59 -0
  175. package/dist/src/utils/txQueue.d.ts +12 -0
  176. package/dist/src/utils/txQueue.d.ts.map +1 -0
  177. package/dist/src/utils/txQueue.js +25 -0
  178. package/dist/src/utils.d.ts +2 -3
  179. package/dist/src/utils.d.ts.map +1 -1
  180. package/dist/src/utils.js +5 -5
  181. package/dist/src/verify.d.ts +9 -25
  182. package/dist/src/verify.d.ts.map +1 -1
  183. package/dist/src/verify.js +49 -50
  184. package/package.json +2 -1
@@ -0,0 +1,105 @@
1
+ // packages/sdk/src/handshake.ts
2
+ import { keccak256, toUtf8Bytes, hexlify, getBytes } from "ethers";
3
+ import nacl from 'tweetnacl';
4
+ import { encryptStructuredPayload } from './crypto.js';
5
+ import { serializeHandshakeContent, encodeUnifiedPubKeys, createHandshakeResponseContent, } from './payload.js';
6
+ import { computeHybridTagFromResponder } from './crypto.js';
7
+ import { kem } from './pq/kem.js';
8
+ /**
9
+ * Initiates an on-chain handshake with unified keys and mandatory identity proof.
10
+ * Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
11
+ *
12
+ * @returns Transaction, ephemeral keypair, and KEM keypair (must be persisted for session init)
13
+ */
14
+ export async function initiateHandshake({ executor, recipientAddress, identityKeyPair, plaintextPayload, identityProof, }) {
15
+ if (!executor) {
16
+ throw new Error("Executor must be provided");
17
+ }
18
+ // Generate ephemeral keypair for this handshake
19
+ const ephemeralKeyPair = nacl.box.keyPair();
20
+ // Generate ML-KEM-768 keypair for PQ-hybrid key exchange
21
+ const kemKeyPair = kem.generateKeyPair();
22
+ const recipientHash = keccak256(toUtf8Bytes('contact:' + recipientAddress.toLowerCase()));
23
+ const handshakeContent = {
24
+ plaintextPayload,
25
+ identityProof
26
+ };
27
+ const serializedPayload = serializeHandshakeContent(handshakeContent);
28
+ const unifiedPubKeys = encodeUnifiedPubKeys(identityKeyPair.publicKey, identityKeyPair.signingPublicKey);
29
+ // Ephemeral public key now includes KEM public key (32 + 1184 = 1216 bytes)
30
+ const ephemeralWithKem = new Uint8Array(32 + kem.publicKeyBytes);
31
+ ephemeralWithKem.set(ephemeralKeyPair.publicKey, 0);
32
+ ephemeralWithKem.set(kemKeyPair.publicKey, 32);
33
+ const tx = await executor.initiateHandshake(recipientHash, hexlify(unifiedPubKeys), hexlify(ephemeralWithKem), toUtf8Bytes(serializedPayload));
34
+ return {
35
+ tx,
36
+ ephemeralKeyPair, // Caller must persist secretKey for ratchet session init
37
+ kemKeyPair, // Caller must also persist secretKey for KEM decapsulation
38
+ };
39
+ }
40
+ /**
41
+ * Responds to a handshake with unified keys and mandatory identity proof.
42
+ * Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
43
+ *
44
+ * If initiator includes KEM public key, encapsulates a shared secret and includes ciphertext in response.
45
+ *
46
+ * @returns Transaction, tag, salt, ephemeral keys, and KEM secret
47
+ */
48
+ export async function respondToHandshake({ executor, initiatorEphemeralPubKey, responderIdentityKeyPair, note, identityProof, }) {
49
+ if (!executor) {
50
+ throw new Error("Executor must be provided");
51
+ }
52
+ // =========================================================================
53
+ // TWO SEPARATE KEYPAIRS for unlinkability:
54
+ //
55
+ // 1. tagKeyPair (R, r): only for tag computation
56
+ // - R goes on-chain as responderEphemeralR
57
+ // - Used by Alice to verify the tag
58
+ // - not used for ratchet
59
+ //
60
+ // 2. ratchetKeyPair: For post-handshake encryption and first DH ratchet key
61
+ // - Public key goes inside encrypted payload
62
+ // - Becomes dhMySecretKey/dhMyPublicKey in ratchet session
63
+ //
64
+ // Why this matters: With a single keypair, the on-chain R would equal the
65
+ // first message's DH header, allowing observers to link HandshakeResponse
66
+ // to subsequent conversation. With two keypairs, there's no on-chain link.
67
+ // =========================================================================
68
+ const tagKeyPair = nacl.box.keyPair();
69
+ const ratchetKeyPair = nacl.box.keyPair();
70
+ // Check if initiator included KEM public key (extended format: 32 + 1184 = 1216 bytes)
71
+ const hasKem = initiatorEphemeralPubKey.length === 32 + kem.publicKeyBytes;
72
+ const initiatorX25519Pub = hasKem
73
+ ? initiatorEphemeralPubKey.slice(0, 32)
74
+ : initiatorEphemeralPubKey;
75
+ // KEM encapsulation needed for hybrid tag
76
+ let kemCiphertext;
77
+ let kemSharedSecret;
78
+ if (hasKem) {
79
+ const initiatorKemPub = initiatorEphemeralPubKey.slice(32, 32 + kem.publicKeyBytes);
80
+ const { ciphertext, sharedSecret } = kem.encapsulate(initiatorKemPub);
81
+ kemCiphertext = ciphertext;
82
+ kemSharedSecret = sharedSecret;
83
+ }
84
+ if (!kemSharedSecret) {
85
+ throw new Error("KEM is required for PQ-secure handshake");
86
+ }
87
+ // Hybrid tag combines ECDH(r, viewPubA) + kemSecret
88
+ const inResponseTo = computeHybridTagFromResponder(tagKeyPair.secretKey, initiatorX25519Pub, kemSharedSecret);
89
+ const salt = getBytes(inResponseTo);
90
+ const responseContent = createHandshakeResponseContent(responderIdentityKeyPair.publicKey, responderIdentityKeyPair.signingPublicKey, ratchetKeyPair.publicKey, // first DH ratchet key inside payload
91
+ note, identityProof, kemCiphertext);
92
+ // Encrypt using ratchetKeyPair (the epk in encrypted payload = ratchetKeyPair.publicKey)
93
+ const payload = encryptStructuredPayload(responseContent, initiatorX25519Pub, ratchetKeyPair.secretKey, ratchetKeyPair.publicKey);
94
+ // tagKeyPair.publicKey goes on-chain, not ratchetKeyPair
95
+ const tx = await executor.respondToHandshake(inResponseTo, hexlify(tagKeyPair.publicKey), toUtf8Bytes(payload));
96
+ return {
97
+ tx,
98
+ salt,
99
+ tag: inResponseTo,
100
+ // Return ratchet keys for session initialization
101
+ responderEphemeralSecret: ratchetKeyPair.secretKey,
102
+ responderEphemeralPublic: ratchetKeyPair.publicKey,
103
+ kemSharedSecret,
104
+ };
105
+ }
@@ -1,28 +1,34 @@
1
1
  import { Signer } from "ethers";
2
- import { IdentityProof } from "./types.js";
3
- interface IdentityKeyPair {
4
- publicKey: Uint8Array;
5
- secretKey: Uint8Array;
6
- signingPublicKey: Uint8Array;
7
- signingSecretKey: Uint8Array;
2
+ import { IdentityContext, IdentityKeyPair, IdentityProof } from "./types.js";
3
+ export interface DerivedIdentityKeys {
4
+ /** VerbEth identity key pair (X25519 + Ed25519) */
5
+ keyPair: IdentityKeyPair;
6
+ /** Hex-encoded secp256k1 private key for session signer */
7
+ sessionPrivateKey: string;
8
+ /** Ethereum address of the session signer */
9
+ sessionAddress: string;
10
+ /** Public key hex strings for binding message */
11
+ pkX25519Hex: string;
12
+ pkEd25519Hex: string;
13
+ }
14
+ export interface DerivedIdentityWithProof extends DerivedIdentityKeys {
15
+ identityProof: IdentityProof;
8
16
  }
9
17
  /**
10
- * HKDF (RFC 5869) identity key derivation.
11
- * Returns a proof binding the derived keypair to the wallet address.
18
+ * Derive all identity keys and session key from a single seed signature.
12
19
  */
13
- export declare function deriveIdentityKeyPairWithProof(signer: any, address: string): Promise<{
14
- keyPair: IdentityKeyPair;
15
- identityProof: {
16
- message: string;
17
- signature: string;
18
- messageRawHex?: `0x${string}`;
19
- };
20
- }>;
21
- export declare function deriveIdentityWithUnifiedKeys(signer: Signer, address: string): Promise<{
20
+ export declare function deriveIdentityKeys(signer: any, address: string): Promise<DerivedIdentityKeys>;
21
+ /**
22
+ * Create the binding proof that ties the derived keys to the Safe address.
23
+ */
24
+ export declare function createBindingProof(signer: any, address: string, derivedKeys: DerivedIdentityKeys, executorSafeAddress: string, ctx?: IdentityContext): Promise<IdentityProof>;
25
+ export declare function deriveIdentityKeyPairWithProof(signer: any, address: string, executorSafeAddress?: string, ctx?: IdentityContext): Promise<DerivedIdentityWithProof>;
26
+ export declare function deriveIdentityWithUnifiedKeys(signer: Signer, address: string, executorSafeAddress?: string, ctx?: IdentityContext): Promise<{
22
27
  identityProof: IdentityProof;
23
28
  identityPubKey: Uint8Array;
24
29
  signingPubKey: Uint8Array;
25
30
  unifiedPubKeys: Uint8Array;
31
+ sessionPrivateKey: string;
32
+ sessionAddress: string;
26
33
  }>;
27
- export {};
28
34
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../src/identity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,UAAU,eAAe;IAEvB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;IAEtB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,gBAAgB,EAAE,UAAU,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IACT,OAAO,EAAE,eAAe,CAAC;IACzB,aAAa,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;KAC/B,CAAC;CACH,CAAC,CAyDD;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IACT,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;CAC5B,CAAC,CAcD"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../src/identity.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAqC,MAAM,QAAQ,CAAC;AAGnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwE7E,MAAM,WAAW,mBAAmB;IAClC,mDAAmD;IACnD,OAAO,EAAE,eAAe,CAAC;IACzB,2DAA2D;IAC3D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,aAAa,EAAE,aAAa,CAAC;CAC9B;AAMD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CA8D9B;AAMD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,mBAAmB,EAChC,mBAAmB,EAAE,MAAM,EAC3B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC,aAAa,CAAC,CAsBxB;AAID,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,mBAAmB,CAAC,EAAE,MAAM,EAC5B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC,wBAAwB,CAAC,CAcnC;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,mBAAmB,CAAC,EAAE,MAAM,EAC5B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC;IACT,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAqBD"}
@@ -1,22 +1,87 @@
1
+ // packages/sdk/src/utils/identity.ts
1
2
  import { sha256 } from "@noble/hashes/sha2";
2
3
  import { hkdf } from "@noble/hashes/hkdf";
3
- import { concat, hexlify } from "ethers";
4
+ import { Wallet, concat, hexlify, getBytes } from "ethers";
4
5
  import nacl from "tweetnacl";
5
6
  import { encodeUnifiedPubKeys } from "./payload.js";
7
+ const SECP256K1_N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
8
+ const SECP256K1_HALF_N = SECP256K1_N >> 1n;
9
+ function bytesToBigIntBE(bytes) {
10
+ let hex = "";
11
+ for (const b of bytes)
12
+ hex += b.toString(16).padStart(2, "0");
13
+ return hex.length ? BigInt("0x" + hex) : 0n;
14
+ }
15
+ function bigIntTo32BytesBE(x) {
16
+ let hex = x.toString(16);
17
+ if (hex.length > 64)
18
+ hex = hex.slice(hex.length - 64);
19
+ hex = hex.padStart(64, "0");
20
+ const out = new Uint8Array(32);
21
+ for (let i = 0; i < 32; i++) {
22
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
23
+ }
24
+ return out;
25
+ }
6
26
  /**
7
- * HKDF (RFC 5869) identity key derivation.
8
- * Returns a proof binding the derived keypair to the wallet address.
27
+ * Canonicalize an Ethereum ECDSA signature (65 bytes) to low-s form (only used as KDF input)
9
28
  */
10
- export async function deriveIdentityKeyPairWithProof(signer, address) {
11
- // 1) Local secret seed (32B CSPRNG), domain-separated by address
12
- const r = nacl.randomBytes(32);
29
+ function canonicalizeEcdsaSig65(sig) {
30
+ if (sig.length !== 65)
31
+ return sig;
32
+ const r = sig.slice(0, 32);
33
+ const s = sig.slice(32, 64);
34
+ const v = sig[64];
35
+ const sBig = bytesToBigIntBE(s);
36
+ if (sBig <= SECP256K1_HALF_N)
37
+ return sig;
38
+ const sLow = SECP256K1_N - sBig;
39
+ const out = new Uint8Array(65);
40
+ out.set(r, 0);
41
+ out.set(bigIntTo32BytesBE(sLow), 32);
42
+ out[64] = v;
43
+ return out;
44
+ }
45
+ function buildSeedMessage(addrLower) {
46
+ const lines = [
47
+ "VerbEth Identity Seed v1",
48
+ `Address: ${addrLower}`,
49
+ "Context: verbeth",
50
+ ];
51
+ return lines.join("\n");
52
+ }
53
+ function buildBindingMessage(addrLower, pkEd25519Hex, pkX25519Hex, executorSafeAddress, ctx) {
54
+ const lines = [
55
+ "VerbEth Key Binding v1",
56
+ `Address: ${addrLower}`,
57
+ `PkEd25519: ${pkEd25519Hex}`,
58
+ `PkX25519: ${pkX25519Hex}`,
59
+ `ExecutorSafeAddress: ${executorSafeAddress ?? ""}`,
60
+ ];
61
+ if (typeof ctx?.chainId === "number")
62
+ lines.push(`ChainId: ${ctx.chainId}`);
63
+ if (ctx?.rpId)
64
+ lines.push(`RpId: ${ctx.rpId}`);
65
+ return lines.join("\n");
66
+ }
67
+ // ============================================================================
68
+ // Derive all keys from seed signature
69
+ // ============================================================================
70
+ /**
71
+ * Derive all identity keys and session key from a single seed signature.
72
+ */
73
+ export async function deriveIdentityKeys(signer, address) {
13
74
  const enc = new TextEncoder();
14
75
  const addrLower = address.toLowerCase();
15
- // IKM = HKDF(r || "verbeth/addr:" || address_lower)
16
- // salt/info are public domain labels
17
- const seedSalt = enc.encode("verbeth/seed-v1");
76
+ const seedMessage = buildSeedMessage(addrLower);
77
+ let seedSignature = await signer.signMessage(seedMessage);
78
+ const seedSigBytes = canonicalizeEcdsaSig65(getBytes(seedSignature));
79
+ seedSignature = "";
80
+ // IKM = HKDF( canonicalSig || H(seedMessage) || "verbeth/addr:" || address_lower )
81
+ const seedSalt = enc.encode("verbeth/seed-sig-v1");
18
82
  const seedInfo = enc.encode("verbeth/ikm");
19
- const ikmInput = concat([r, enc.encode("verbeth/addr:" + addrLower)]);
83
+ const seedMsgHash = sha256(enc.encode(seedMessage));
84
+ const ikmInput = getBytes(concat([seedSigBytes, seedMsgHash, enc.encode("verbeth/addr:" + addrLower)]));
20
85
  const ikm = hkdf(sha256, ikmInput, seedSalt, seedInfo, 32);
21
86
  // Derive X25519 (encryption)
22
87
  const info_x25519 = enc.encode("verbeth-x25519-v1");
@@ -26,6 +91,22 @@ export async function deriveIdentityKeyPairWithProof(signer, address) {
26
91
  const info_ed25519 = enc.encode("verbeth-ed25519-v1");
27
92
  const ed25519_seed = hkdf(sha256, ikm, new Uint8Array(0), info_ed25519, 32);
28
93
  const signKeyPair = nacl.sign.keyPair.fromSeed(ed25519_seed);
94
+ // Derive secp256k1 session key for txs via Safe module
95
+ const info_session = enc.encode("verbeth-session-secp256k1-v1");
96
+ const sessionSeed = hkdf(sha256, ikm, new Uint8Array(0), info_session, 32);
97
+ const sessionPrivateKey = hexlify(sessionSeed);
98
+ const sessionWallet = new Wallet(sessionPrivateKey);
99
+ const sessionAddress = sessionWallet.address;
100
+ try {
101
+ seedSigBytes.fill(0);
102
+ seedMsgHash.fill(0);
103
+ ikmInput.fill(0);
104
+ ikm.fill(0);
105
+ ed25519_seed.fill(0);
106
+ x25519_sk.fill(0);
107
+ sessionSeed.fill(0);
108
+ }
109
+ catch { }
29
110
  const pkX25519Hex = hexlify(boxKeyPair.publicKey);
30
111
  const pkEd25519Hex = hexlify(signKeyPair.publicKey);
31
112
  const keyPair = {
@@ -34,37 +115,51 @@ export async function deriveIdentityKeyPairWithProof(signer, address) {
34
115
  signingPublicKey: signKeyPair.publicKey,
35
116
  signingSecretKey: signKeyPair.secretKey,
36
117
  };
37
- // 2) Single signature binding both public keys
38
- const bindingMsgLines = [
39
- "VerbEth Key Binding v1",
40
- `Address: ${addrLower}`,
41
- `PkEd25519: ${pkEd25519Hex}`,
42
- `PkX25519: ${pkX25519Hex}`,
43
- `Context: verbeth`,
44
- `Version: 1`,
45
- ];
46
- const message = bindingMsgLines.join("\n");
118
+ return {
119
+ keyPair,
120
+ sessionPrivateKey,
121
+ sessionAddress,
122
+ pkX25519Hex,
123
+ pkEd25519Hex,
124
+ };
125
+ }
126
+ // ============================================================================
127
+ // Create binding proof with Safe address
128
+ // ============================================================================
129
+ /**
130
+ * Create the binding proof that ties the derived keys to the Safe address.
131
+ */
132
+ export async function createBindingProof(signer, address, derivedKeys, executorSafeAddress, ctx) {
133
+ const addrLower = address.toLowerCase();
134
+ const executorSafeAddressLower = executorSafeAddress.toLowerCase();
135
+ const message = buildBindingMessage(addrLower, derivedKeys.pkEd25519Hex, derivedKeys.pkX25519Hex, executorSafeAddressLower, ctx);
47
136
  const signature = await signer.signMessage(message);
48
137
  const messageRawHex = ("0x" +
49
138
  Buffer.from(message, "utf-8").toString("hex"));
50
139
  return {
51
- keyPair,
52
- identityProof: {
53
- message,
54
- signature,
55
- messageRawHex,
56
- },
140
+ message,
141
+ signature,
142
+ messageRawHex,
143
+ };
144
+ }
145
+ // this is when the Safe address is known upfront
146
+ export async function deriveIdentityKeyPairWithProof(signer, address, executorSafeAddress, ctx) {
147
+ const derivedKeys = await deriveIdentityKeys(signer, address);
148
+ const identityProof = await createBindingProof(signer, address, derivedKeys, executorSafeAddress ?? "", ctx);
149
+ return {
150
+ ...derivedKeys,
151
+ identityProof,
57
152
  };
58
153
  }
59
- export async function deriveIdentityWithUnifiedKeys(signer, address) {
60
- const result = await deriveIdentityKeyPairWithProof(signer, address);
61
- const unifiedPubKeys = encodeUnifiedPubKeys(result.keyPair.publicKey, // X25519
62
- result.keyPair.signingPublicKey // Ed25519
63
- );
154
+ export async function deriveIdentityWithUnifiedKeys(signer, address, executorSafeAddress, ctx) {
155
+ const result = await deriveIdentityKeyPairWithProof(signer, address, executorSafeAddress, ctx);
156
+ const unifiedPubKeys = encodeUnifiedPubKeys(result.keyPair.publicKey, result.keyPair.signingPublicKey);
64
157
  return {
65
158
  identityProof: result.identityProof,
66
159
  identityPubKey: result.keyPair.publicKey,
67
160
  signingPubKey: result.keyPair.signingPublicKey,
68
161
  unifiedPubKeys,
162
+ sessionPrivateKey: result.sessionPrivateKey,
163
+ sessionAddress: result.sessionAddress,
69
164
  };
70
165
  }
@@ -1,18 +1,21 @@
1
1
  export * from './crypto.js';
2
2
  export * from './payload.js';
3
- export * from './send.js';
3
+ export * from './handshake.js';
4
4
  export * from './verify.js';
5
5
  export * from './types.js';
6
6
  export * from './utils.js';
7
7
  export * from './identity.js';
8
8
  export * from './executor.js';
9
- export { decryptMessage as decryptLog } from './crypto.js';
10
- export { getNextNonce } from './utils/nonce.js';
11
- export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakePayload, createHandshakeResponseContent, extractKeysFromHandshakePayload, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
12
- export { decryptAndExtractHandshakeKeys, decryptMessage, decryptHandshakeResponse } from './crypto.js';
9
+ export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakeResponseContent, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
10
+ export { decryptAndExtractHandshakeKeys, decryptHandshakeResponse } from './crypto.js';
13
11
  export { verifyIdentityProof, verifyAndExtractHandshakeKeys, verifyAndExtractHandshakeResponseKeys } from './verify.js';
14
12
  export { deriveIdentityKeyPairWithProof, deriveIdentityWithUnifiedKeys } from './identity.js';
15
13
  export { IExecutor, EOAExecutor, UserOpExecutor, DirectEntryPointExecutor, ExecutorFactory } from './executor.js';
16
- export { VerbethClient } from './client/index.js';
17
- export type { VerbethClientConfig, HandshakeResult, HandshakeResponseResult } from './client/index.js';
14
+ export { SafeSessionSigner } from "./utils/safeSessionSigner.js";
15
+ export type { SafeSessionSignerOptions } from "./utils/safeSessionSigner.js";
16
+ export { VerbethClient, VerbethClientBuilder, createVerbethClient, matchHsrToContact, } from './client/index.js';
17
+ export type { VerbethClientConfig, VerbethClientCallbacks, TopicRatchetEvent, MessageDecryptedEvent, HandshakeResult, HandshakeResponseResult, SessionStore, PendingStore, PreparedMessage, DecryptedMessage, PendingMessage, PendingStatus, SendResult, ConfirmResult, SerializedSessionInfo, HsrEventData, CreateInitiatorSessionFromHsrParams, CreateVerbethClientOptions, PendingContactEntry, } from './client/index.js';
18
+ export * from './ratchet/index.js';
19
+ export { dh, deriveTopic, hybridInitialSecret } from './ratchet/kdf.js';
20
+ export { kem } from './pq/kem.js';
18
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAE9B,OAAO,EAAE,cAAc,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,8BAA8B,EAC9B,+BAA+B,EAC/B,gCAAgC,EAChC,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,8BAA8B,EAC9B,cAAc,EACd,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,qCAAqC,EACtC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC9B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACxB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAE9B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,8BAA8B,EAC9B,gCAAgC,EAChC,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,8BAA8B,EAC9B,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,qCAAqC,EACtC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC9B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE7E,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EAEvB,YAAY,EACZ,YAAY,EAEZ,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEb,UAAU,EACV,aAAa,EAEb,qBAAqB,EACrB,YAAY,EACZ,mCAAmC,EACnC,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC"}
@@ -1,17 +1,19 @@
1
+ // packages/sdk/src/index.ts
1
2
  export * from './crypto.js';
2
3
  export * from './payload.js';
3
- export * from './send.js';
4
+ export * from './handshake.js';
4
5
  export * from './verify.js';
5
6
  export * from './types.js';
6
7
  export * from './utils.js';
7
8
  export * from './identity.js';
8
9
  export * from './executor.js';
9
- export { decryptMessage as decryptLog } from './crypto.js';
10
- export { getNextNonce } from './utils/nonce.js';
11
- export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakePayload, createHandshakeResponseContent, extractKeysFromHandshakePayload, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
12
- export { decryptAndExtractHandshakeKeys, decryptMessage, decryptHandshakeResponse } from './crypto.js';
10
+ export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakeResponseContent, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
11
+ export { decryptAndExtractHandshakeKeys, decryptHandshakeResponse } from './crypto.js';
13
12
  export { verifyIdentityProof, verifyAndExtractHandshakeKeys, verifyAndExtractHandshakeResponseKeys } from './verify.js';
14
13
  export { deriveIdentityKeyPairWithProof, deriveIdentityWithUnifiedKeys } from './identity.js';
15
14
  export { EOAExecutor, UserOpExecutor, DirectEntryPointExecutor, ExecutorFactory } from './executor.js';
16
- // high-level client API
17
- export { VerbethClient } from './client/index.js';
15
+ export { SafeSessionSigner } from "./utils/safeSessionSigner.js";
16
+ export { VerbethClient, VerbethClientBuilder, createVerbethClient, matchHsrToContact, } from './client/index.js';
17
+ export * from './ratchet/index.js';
18
+ export { dh, deriveTopic, hybridInitialSecret } from './ratchet/kdf.js';
19
+ export { kem } from './pq/kem.js';
@@ -1,4 +1,4 @@
1
- import { IdentityProof, TopicInfoWire } from './types.js';
1
+ import { IdentityProof } from './types.js';
2
2
  export interface EncryptedPayload {
3
3
  v: number;
4
4
  epk: string;
@@ -6,14 +6,6 @@ export interface EncryptedPayload {
6
6
  ct: string;
7
7
  sig?: string;
8
8
  }
9
- export interface MessagePayload {
10
- content: string;
11
- timestamp?: number;
12
- messageType?: 'text' | 'file' | 'media';
13
- metadata?: Record<string, any>;
14
- }
15
- export interface HandshakeResponsePayload extends EncryptedPayload {
16
- }
17
9
  export interface HandshakeContent {
18
10
  plaintextPayload: string;
19
11
  identityProof: IdentityProof;
@@ -50,30 +42,11 @@ export interface HandshakePayload {
50
42
  export interface HandshakeResponseContent {
51
43
  unifiedPubKeys: Uint8Array;
52
44
  ephemeralPubKey: Uint8Array;
45
+ kemCiphertext?: Uint8Array;
53
46
  note?: string;
54
47
  identityProof: IdentityProof;
55
- topicInfo?: TopicInfoWire;
56
48
  }
57
- export declare function encodeHandshakePayload(payload: HandshakePayload): Uint8Array;
58
- export declare function decodeHandshakePayload(encoded: Uint8Array): HandshakePayload;
59
- export declare function encodeHandshakeResponseContent(content: HandshakeResponseContent): Uint8Array;
60
- export declare function decodeHandshakeResponseContent(encoded: Uint8Array): HandshakeResponseContent;
61
- /**
62
- * Creates HandshakePayload from separate identity keys
63
- */
64
- export declare function createHandshakePayload(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, plaintextPayload: string): HandshakePayload;
65
- /**
66
- * Creates HandshakeResponseContent from separate identity keys
67
- */
68
- export declare function createHandshakeResponseContent(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, note?: string, identityProof?: IdentityProof, topicInfo?: TopicInfoWire): HandshakeResponseContent;
69
- /**
70
- * Extracts individual keys from HandshakePayload
71
- */
72
- export declare function extractKeysFromHandshakePayload(payload: HandshakePayload): {
73
- identityPubKey: Uint8Array;
74
- signingPubKey: Uint8Array;
75
- ephemeralPubKey: Uint8Array;
76
- } | null;
49
+ export declare function createHandshakeResponseContent(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, note?: string, identityProof?: IdentityProof, kemCiphertext?: Uint8Array): HandshakeResponseContent;
77
50
  /**
78
51
  * Extracts individual keys from HandshakeResponseContent
79
52
  */
@@ -1 +1 @@
1
- {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/payload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/payload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,CAUhF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE3E;AAED,wBAAgB,aAAa,CAAC,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,MAAM,CAS9H;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAC3C,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB,CAyBA;AAID,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,UAAU,CAQjE;AAGD,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GACzB,CAAC,CAGH;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,UAAU,EAAG,oBAAoB;AACjD,aAAa,EAAE,UAAU,GACxB,UAAU,CAOZ;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;CAC3B,GAAG,IAAI,CAkBP;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,UAAU,CAAC;IAC3B,eAAe,EAAE,UAAU,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,UAAU,CAAC;IAC3B,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,UAAU,EAC1B,aAAa,EAAE,UAAU,EACzB,eAAe,EAAE,UAAU,EAC3B,IAAI,CAAC,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,aAAa,EAC7B,aAAa,CAAC,EAAE,UAAU,GACzB,wBAAwB,CAY1B;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,wBAAwB,GAAG;IACnF,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,eAAe,EAAE,UAAU,CAAC;CAC7B,GAAG,IAAI,CASP;AAGD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAC9D,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;CAC3B,GAAG,IAAI,CAkBP"}
@@ -1,3 +1,4 @@
1
+ // packages/sdk/src/payload.ts
1
2
  export function parseHandshakePayload(plaintextPayload) {
2
3
  try {
3
4
  const parsed = JSON.parse(plaintextPayload);
@@ -80,7 +81,6 @@ signingPubKey // Ed25519 - 32 bytes
80
81
  */
81
82
  export function decodeUnifiedPubKeys(pubKeys) {
82
83
  if (pubKeys.length === 64) {
83
- // Legacy
84
84
  return {
85
85
  version: 0,
86
86
  identityPubKey: pubKeys.slice(0, 32),
@@ -88,7 +88,6 @@ export function decodeUnifiedPubKeys(pubKeys) {
88
88
  };
89
89
  }
90
90
  if (pubKeys.length === 65 && pubKeys[0] === 0x01) {
91
- // V1: with versioning
92
91
  return {
93
92
  version: 1,
94
93
  identityPubKey: pubKeys.slice(1, 33),
@@ -97,89 +96,16 @@ export function decodeUnifiedPubKeys(pubKeys) {
97
96
  }
98
97
  return null;
99
98
  }
100
- export function encodeHandshakePayload(payload) {
101
- return new TextEncoder().encode(JSON.stringify({
102
- unifiedPubKeys: Buffer.from(payload.unifiedPubKeys).toString('base64'),
103
- ephemeralPubKey: Buffer.from(payload.ephemeralPubKey).toString('base64'),
104
- plaintextPayload: payload.plaintextPayload
105
- }));
106
- }
107
- export function decodeHandshakePayload(encoded) {
108
- const json = new TextDecoder().decode(encoded);
109
- const parsed = JSON.parse(json);
110
- return {
111
- unifiedPubKeys: Uint8Array.from(Buffer.from(parsed.unifiedPubKeys, 'base64')),
112
- ephemeralPubKey: Uint8Array.from(Buffer.from(parsed.ephemeralPubKey, 'base64')),
113
- plaintextPayload: parsed.plaintextPayload
114
- };
115
- }
116
- export function encodeHandshakeResponseContent(content) {
117
- return new TextEncoder().encode(JSON.stringify({
118
- unifiedPubKeys: Buffer.from(content.unifiedPubKeys).toString('base64'),
119
- ephemeralPubKey: Buffer.from(content.ephemeralPubKey).toString('base64'),
120
- note: content.note,
121
- identityProof: content.identityProof,
122
- topicInfo: content.topicInfo ? {
123
- out: content.topicInfo.out,
124
- in: content.topicInfo.in,
125
- chk: content.topicInfo.chk
126
- } : undefined
127
- }));
128
- }
129
- export function decodeHandshakeResponseContent(encoded) {
130
- const json = new TextDecoder().decode(encoded);
131
- const obj = JSON.parse(json);
132
- if (!obj.identityProof) {
133
- throw new Error("Invalid handshake response: missing identityProof");
134
- }
135
- return {
136
- unifiedPubKeys: Uint8Array.from(Buffer.from(obj.unifiedPubKeys, 'base64')),
137
- ephemeralPubKey: Uint8Array.from(Buffer.from(obj.ephemeralPubKey, 'base64')),
138
- note: obj.note,
139
- identityProof: obj.identityProof,
140
- topicInfo: obj.topicInfo ? {
141
- out: obj.topicInfo.out,
142
- in: obj.topicInfo.in,
143
- chk: obj.topicInfo.chk
144
- } : undefined
145
- };
146
- }
147
- /**
148
- * Creates HandshakePayload from separate identity keys
149
- */
150
- export function createHandshakePayload(identityPubKey, signingPubKey, ephemeralPubKey, plaintextPayload) {
151
- return {
152
- unifiedPubKeys: encodeUnifiedPubKeys(identityPubKey, signingPubKey),
153
- ephemeralPubKey,
154
- plaintextPayload
155
- };
156
- }
157
- /**
158
- * Creates HandshakeResponseContent from separate identity keys
159
- */
160
- export function createHandshakeResponseContent(identityPubKey, signingPubKey, ephemeralPubKey, note, identityProof, topicInfo) {
99
+ export function createHandshakeResponseContent(identityPubKey, signingPubKey, ephemeralPubKey, note, identityProof, kemCiphertext) {
161
100
  if (!identityProof) {
162
101
  throw new Error("Identity proof is now mandatory for handshake responses");
163
102
  }
164
103
  return {
165
104
  unifiedPubKeys: encodeUnifiedPubKeys(identityPubKey, signingPubKey),
166
105
  ephemeralPubKey,
106
+ ...(kemCiphertext && { kemCiphertext }),
167
107
  note,
168
108
  identityProof,
169
- topicInfo
170
- };
171
- }
172
- /**
173
- * Extracts individual keys from HandshakePayload
174
- */
175
- export function extractKeysFromHandshakePayload(payload) {
176
- const decoded = decodeUnifiedPubKeys(payload.unifiedPubKeys);
177
- if (!decoded)
178
- return null;
179
- return {
180
- identityPubKey: decoded.identityPubKey,
181
- signingPubKey: decoded.signingPubKey,
182
- ephemeralPubKey: payload.ephemeralPubKey
183
109
  };
184
110
  }
185
111
  /**
@@ -0,0 +1,33 @@
1
+ export declare const kem: {
2
+ publicKeyBytes: number;
3
+ ciphertextBytes: number;
4
+ sharedSecretBytes: number;
5
+ /**
6
+ * Generate a new ML-KEM-768 keypair.
7
+ *
8
+ * @returns Object containing publicKey and secretKey
9
+ */
10
+ generateKeyPair(): {
11
+ publicKey: Uint8Array;
12
+ secretKey: Uint8Array;
13
+ };
14
+ /**
15
+ * Encapsulate a shared secret using the recipient's public key.
16
+ *
17
+ * @param publicKey - Recipient's ML-KEM-768 public key
18
+ * @returns Object containing ciphertext and sharedSecret
19
+ */
20
+ encapsulate(publicKey: Uint8Array): {
21
+ ciphertext: Uint8Array;
22
+ sharedSecret: Uint8Array;
23
+ };
24
+ /**
25
+ * Decapsulate a ciphertext using the secret key to recover the shared secret.
26
+ *
27
+ * @param ciphertext - KEM ciphertext
28
+ * @param secretKey - Recipient's ML-KEM-768 secret key
29
+ * @returns Shared secret
30
+ */
31
+ decapsulate(ciphertext: Uint8Array, secretKey: Uint8Array): Uint8Array;
32
+ };
33
+ //# sourceMappingURL=kem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kem.d.ts","sourceRoot":"","sources":["../../../../src/pq/kem.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,GAAG;;;;IAKd;;;;OAIG;uBACgB;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE;IAInE;;;;;OAKG;2BACoB,UAAU,GAAG;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,YAAY,EAAE,UAAU,CAAA;KAAE;IAKxF;;;;;;OAMG;4BACqB,UAAU,aAAa,UAAU,GAAG,UAAU;CAGvE,CAAC"}