@verbeth/sdk 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +202 -0
  2. package/dist/esm/src/client/VerbethClient.d.ts +134 -0
  3. package/dist/esm/src/client/VerbethClient.d.ts.map +1 -0
  4. package/dist/esm/src/client/VerbethClient.js +191 -0
  5. package/dist/esm/src/client/index.d.ts +3 -0
  6. package/dist/esm/src/client/index.d.ts.map +1 -0
  7. package/dist/esm/src/client/index.js +2 -0
  8. package/dist/esm/src/client/types.d.ts +30 -0
  9. package/dist/esm/src/client/types.d.ts.map +1 -0
  10. package/dist/esm/src/client/types.js +2 -0
  11. package/dist/esm/src/crypto.d.ts +46 -0
  12. package/dist/esm/src/crypto.d.ts.map +1 -0
  13. package/dist/esm/src/crypto.js +137 -0
  14. package/dist/esm/src/executor.d.ts +73 -0
  15. package/dist/esm/src/executor.d.ts.map +1 -0
  16. package/dist/esm/src/executor.js +353 -0
  17. package/dist/esm/src/identity.d.ts +28 -0
  18. package/dist/esm/src/identity.d.ts.map +1 -0
  19. package/dist/esm/src/identity.js +70 -0
  20. package/dist/esm/src/index.d.ts +18 -0
  21. package/dist/esm/src/index.d.ts.map +1 -0
  22. package/dist/esm/src/index.js +17 -0
  23. package/dist/esm/src/payload.d.ts +94 -0
  24. package/dist/esm/src/payload.d.ts.map +1 -0
  25. package/dist/esm/src/payload.js +216 -0
  26. package/dist/esm/src/send.d.ts +50 -0
  27. package/dist/esm/src/send.d.ts.map +1 -0
  28. package/dist/esm/src/send.js +75 -0
  29. package/dist/esm/src/types.d.ts +73 -0
  30. package/dist/esm/src/types.d.ts.map +1 -0
  31. package/dist/esm/src/types.js +2 -0
  32. package/dist/esm/src/utils/nonce.d.ts +2 -0
  33. package/dist/esm/src/utils/nonce.d.ts.map +1 -0
  34. package/dist/esm/src/utils/nonce.js +6 -0
  35. package/dist/esm/src/utils/x25519.d.ts +6 -0
  36. package/dist/esm/src/utils/x25519.d.ts.map +1 -0
  37. package/dist/esm/src/utils/x25519.js +12 -0
  38. package/dist/esm/src/utils.d.ts +29 -0
  39. package/dist/esm/src/utils.d.ts.map +1 -0
  40. package/dist/esm/src/utils.js +123 -0
  41. package/dist/esm/src/verify.d.ts +54 -0
  42. package/dist/esm/src/verify.d.ts.map +1 -0
  43. package/dist/esm/src/verify.js +186 -0
  44. package/dist/src/client/VerbethClient.d.ts +134 -0
  45. package/dist/src/client/VerbethClient.d.ts.map +1 -0
  46. package/dist/src/client/VerbethClient.js +191 -0
  47. package/dist/src/client/index.d.ts +3 -0
  48. package/dist/src/client/index.d.ts.map +1 -0
  49. package/dist/src/client/index.js +2 -0
  50. package/dist/src/client/types.d.ts +30 -0
  51. package/dist/src/client/types.d.ts.map +1 -0
  52. package/dist/src/client/types.js +2 -0
  53. package/dist/src/crypto.d.ts +46 -0
  54. package/dist/src/crypto.d.ts.map +1 -0
  55. package/dist/src/crypto.js +137 -0
  56. package/dist/src/executor.d.ts +73 -0
  57. package/dist/src/executor.d.ts.map +1 -0
  58. package/dist/src/executor.js +353 -0
  59. package/dist/src/identity.d.ts +28 -0
  60. package/dist/src/identity.d.ts.map +1 -0
  61. package/dist/src/identity.js +70 -0
  62. package/dist/src/index.d.ts +18 -0
  63. package/dist/src/index.d.ts.map +1 -0
  64. package/dist/src/index.js +17 -0
  65. package/dist/src/payload.d.ts +94 -0
  66. package/dist/src/payload.d.ts.map +1 -0
  67. package/dist/src/payload.js +216 -0
  68. package/dist/src/send.d.ts +50 -0
  69. package/dist/src/send.d.ts.map +1 -0
  70. package/dist/src/send.js +75 -0
  71. package/dist/src/types.d.ts +73 -0
  72. package/dist/src/types.d.ts.map +1 -0
  73. package/dist/src/types.js +2 -0
  74. package/dist/src/utils/nonce.d.ts +2 -0
  75. package/dist/src/utils/nonce.d.ts.map +1 -0
  76. package/dist/src/utils/nonce.js +6 -0
  77. package/dist/src/utils/x25519.d.ts +6 -0
  78. package/dist/src/utils/x25519.d.ts.map +1 -0
  79. package/dist/src/utils/x25519.js +12 -0
  80. package/dist/src/utils.d.ts +29 -0
  81. package/dist/src/utils.d.ts.map +1 -0
  82. package/dist/src/utils.js +123 -0
  83. package/dist/src/verify.d.ts +54 -0
  84. package/dist/src/verify.d.ts.map +1 -0
  85. package/dist/src/verify.js +186 -0
  86. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # @verbeth/sdk
2
+
3
+ Verbeth enables secure, E2EE messaging using Ethereum event logs as the only transport layer. No servers, no relays—just the blockchain.
4
+
5
+ ## Features
6
+
7
+ - **End-to-end encryption** using NaCl Box (X25519 + XSalsa20-Poly1305)
8
+ - **Forward secrecy** with ephemeral keys per message
9
+ - **Handshake protocol** for secure key exchange
10
+ - **Privacy-focused** with minimal metadata via `recipientHash`
11
+ - **EOA & Smart Account support** (ERC-1271/6492 compatible)
12
+ - **Fully on-chain** - no centralized infrastructure
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @verbeth/sdk ethers tweetnacl
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### 1. Initialize with VerbethClient (Recommended)
23
+
24
+ ```typescript
25
+ import { VerbethClient, ExecutorFactory, deriveIdentityKeyPairWithProof } from '@verbeth/sdk';
26
+ import { Contract, BrowserProvider } from 'ethers';
27
+ import { LogChainV1__factory } from '@verbeth/contracts/typechain-types';
28
+
29
+ // Setup
30
+ const provider = new BrowserProvider(window.ethereum);
31
+ const signer = await provider.getSigner();
32
+ const address = await signer.getAddress();
33
+
34
+ // Create contract instance
35
+ const contract = LogChainV1__factory.connect(LOGCHAIN_ADDRESS, signer);
36
+
37
+ // Derive identity keys (done once, then stored)
38
+ const { identityKeyPair, identityProof } = await deriveIdentityKeyPairWithProof(signer);
39
+
40
+ // Create executor (handles transaction submission)
41
+ const executor = ExecutorFactory.createEOA(contract);
42
+
43
+ // Initialize client
44
+ const client = new VerbethClient({
45
+ executor,
46
+ identityKeyPair,
47
+ identityProof,
48
+ signer,
49
+ address
50
+ });
51
+
52
+ // Send a handshake to start chatting
53
+ const { tx, ephemeralKeyPair } = await client.sendHandshake(
54
+ '0xRecipientAddress...',
55
+ 'Hello! Want to chat?'
56
+ );
57
+
58
+ // Store ephemeralKeyPair. you'll just need it to decrypt the handshake response!
59
+
60
+ // Accept a handshake
61
+ const { tx, duplexTopics } = await client.acceptHandshake(
62
+ handshakeEvent.ephemeralPubKey,
63
+ handshakeEvent.identityPubKey,
64
+ 'Sure, lets chat!'
65
+ );
66
+
67
+ // Send encrypted messages
68
+ await client.sendMessage(
69
+ duplexTopics.topicOut,
70
+ recipientIdentityPubKey,
71
+ 'This message is encrypted!'
72
+ );
73
+
74
+ // Decrypt received messages
75
+ const decrypted = await client.decryptMessage(
76
+ messageEvent.ciphertext,
77
+ senderIdentityPubKey
78
+ );
79
+ ```
80
+
81
+ ### 2. Low-level API
82
+
83
+ For more control, use the low-level functions:
84
+
85
+ ```typescript
86
+ import {
87
+ initiateHandshake,
88
+ respondToHandshake,
89
+ sendEncryptedMessage,
90
+ decryptMessage,
91
+ deriveIdentityKeyPairWithProof
92
+ } from '@verbeth/sdk';
93
+
94
+ // Generate identity keys
95
+ const { identityKeyPair, identityProof } = await deriveIdentityKeyPairWithProof(signer);
96
+
97
+ // Initiate handshake
98
+ const ephemeralKeyPair = nacl.box.keyPair();
99
+ const tx = await initiateHandshake({
100
+ executor,
101
+ recipientAddress: '0xBob...',
102
+ ephemeralPubKey: ephemeralKeyPair.publicKey,
103
+ identityKeyPair,
104
+ identityProof,
105
+ plaintextPayload: 'Hi Bob!'
106
+ });
107
+
108
+ // Send encrypted message
109
+ await sendEncryptedMessage({
110
+ executor,
111
+ topic: derivedTopic,
112
+ message: 'Secret message',
113
+ recipientPubKey: bobsIdentityKey,
114
+ senderAddress: myAddress,
115
+ senderSignKeyPair: identityKeyPair,
116
+ timestamp: Date.now()
117
+ });
118
+
119
+ // Decrypt message
120
+ const plaintext = decryptMessage(
121
+ ciphertext,
122
+ senderIdentityPubKey,
123
+ myIdentityKeyPair.secretKey
124
+ );
125
+ ```
126
+
127
+ ## Smart Account Support
128
+
129
+ Verbeth works with ERC-4337 smart accounts:
130
+
131
+ ```typescript
132
+ import { ExecutorFactory } from '@verbeth/sdk';
133
+
134
+ // For UserOp-based execution
135
+ const executor = ExecutorFactory.createUserOp(
136
+ contract,
137
+ bundler,
138
+ smartAccount,
139
+ signer
140
+ );
141
+
142
+ // For direct EntryPoint execution
143
+ const executor = ExecutorFactory.createDirectEntryPoint(
144
+ contract,
145
+ entryPoint,
146
+ smartAccountAddress,
147
+ signer
148
+ );
149
+ ```
150
+
151
+ ## Contract Addresses
152
+
153
+ **LogChainV1 Singleton:** `0x41a3eaC0d858028E9228d1E2092e6178fc81c4f0`
154
+
155
+ **ERC1967Proxy:** `0x62720f39d5Ec6501508bDe4D152c1E13Fd2F6707`
156
+
157
+ ## How It Works
158
+
159
+ 1. **Identity Keys**: Each account derives long-term X25519 (encryption) + Ed25519 (signing) keys bound to their address via signature
160
+ 2. **Handshake**: Alice sends her ephemeral key + identity proof to Bob via a `Handshake` event
161
+ 3. **Response**: Bob verifies Alice's identity and responds with his keys + duplex topics
162
+ 4. **Messaging**: Both parties derive shared topics and exchange encrypted messages via `MessageSent` events
163
+ 5. **Decryption**: Recipients monitor their inbound topic and decrypt with their identity key
164
+
165
+
166
+ ## Security Considerations
167
+
168
+ - **Forward Secrecy**: Fresh ephemeral keys per message provide sender-side forward secrecy
169
+ - **Identity Binding**: Addresses are cryptographically bound to long-term keys via signature
170
+ - **Non-Repudiation**: Optional Ed25519 signatures prove message origin
171
+ - **Privacy**: RecipientHash hides recipient identity; duplex topics separate communication channels
172
+
173
+ ⚠️ **Note**: Current design provides sender-side forward secrecy. Recipient-side FS requires ephemeral↔ephemeral or session ratcheting (e.g., Double Ratchet).
174
+
175
+ ## Built With
176
+
177
+ - [TweetNaCl](https://tweetnacl.js.org/) - Encryption primitives
178
+ - [Ethers v6](https://docs.ethers.org/v6/) - Ethereum interactions
179
+ - [Viem](https://viem.sh/) - EIP-1271/6492 verification
180
+ - [Noble Curves](https://github.com/paulmillr/noble-curves) - Elliptic curve operations
181
+
182
+ ## Examples
183
+
184
+ Check out the [demo application](https://github.com/okrame/verbeth-sdk/tree/main/apps/demo) for a complete implementation.
185
+
186
+ ## Documentation
187
+
188
+ For detailed protocol documentation, security analysis, and improvement proposals, see the [main repository](https://github.com/okrame/verbeth-sdk).
189
+
190
+ ## License
191
+
192
+ MPL-2.0
193
+
194
+ ## Links
195
+
196
+ - [GitHub Repository](https://github.com/okrame/verbeth-sdk)
197
+ - [Demo App](https://verbeth-demo.vercel.app/)
198
+ - [Contract Source](https://github.com/okrame/verbeth-sdk/tree/main/packages/contracts)
199
+
200
+ ---
201
+
202
+ **Questions or feedback?** Open an issue on [GitHub](https://github.com/okrame/verbeth-sdk/issues).
@@ -0,0 +1,134 @@
1
+ import type { VerbethClientConfig, HandshakeResult, HandshakeResponseResult } from './types.js';
2
+ import type { IExecutor } from '../executor.js';
3
+ import type { IdentityKeyPair } from '../types.js';
4
+ import * as crypto from '../crypto.js';
5
+ import * as payload from '../payload.js';
6
+ import * as verify from '../verify.js';
7
+ import * as utils from '../utils.js';
8
+ import * as identity from '../identity.js';
9
+ /**
10
+ * High-level client for Verbeth E2EE messaging
11
+ *
12
+ * VerbethClient provides a simplified API for common operations while
13
+ * maintaining access to all low-level functions.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const client = new VerbethClient({
18
+ * executor,
19
+ * identityKeyPair,
20
+ * identityProof,
21
+ * signer,
22
+ * address: '0x...'
23
+ * });
24
+ *
25
+ * // Send a handshake
26
+ * const { tx, ephemeralKeyPair } = await client.sendHandshake(
27
+ * '0xBob...',
28
+ * 'Hello Bob!'
29
+ * );
30
+ *
31
+ * // Send a message
32
+ * await client.sendMessage(
33
+ * contact.topicOutbound,
34
+ * contact.identityPubKey,
35
+ * 'Hello again!'
36
+ * );
37
+ * ```
38
+ */
39
+ export declare class VerbethClient {
40
+ private readonly executor;
41
+ private readonly identityKeyPair;
42
+ private readonly identityProof;
43
+ private readonly signer;
44
+ private readonly address;
45
+ /**
46
+ * creates a new VerbethClient instance
47
+ *
48
+ * @param config - Client configuration with session-level parameters
49
+ */
50
+ constructor(config: VerbethClientConfig);
51
+ /**
52
+ * Initiates a handshake with a recipient
53
+ *
54
+ * generates an ephemeral keypair for this handshake.
55
+ * the ephemeralKeyPair must be stored to decrypt the response later.
56
+ *
57
+ * @param recipientAddress - Blockchain address of the recipient
58
+ * @param message - Plaintext message to include in the handshake
59
+ * @returns Transaction response and the ephemeral keypair (must be stored!)
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const { tx, ephemeralKeyPair } = await client.sendHandshake(
64
+ * '0xBob...',
65
+ * 'Hi Bob!'
66
+ * );
67
+ *
68
+ * // Store ephemeralKeyPair.secretKey to decrypt Bob's response
69
+ * await storage.saveContact({
70
+ * address: '0xBob...',
71
+ * ephemeralKey: ephemeralKeyPair.secretKey,
72
+ * // ...
73
+ * });
74
+ * ```
75
+ */
76
+ sendHandshake(recipientAddress: string, message: string): Promise<HandshakeResult>;
77
+ /**
78
+ * Accepts a handshake from an initiator
79
+ *
80
+ * derives duplex topics for the conversation and returns them.
81
+ *
82
+ * @param initiatorEphemeralPubKey - initiator's ephemeral public key from handshake event
83
+ * @param initiatorIdentityPubKey - initiator's long-term X25519 identity key
84
+ * @param note - response message to send back
85
+ * @returns transaction, derived duplex topics, and response tag
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const { tx, duplexTopics } = await client.acceptHandshake(
90
+ * handshake.ephemeralPubKey,
91
+ * handshake.identityPubKey,
92
+ * 'Hello Alice!'
93
+ * );
94
+ *
95
+ * // Store the topics for future messaging
96
+ * await storage.saveContact({
97
+ * address: handshake.sender,
98
+ * topicOutbound: duplexTopics.topicIn, // Responder writes to topicIn
99
+ * topicInbound: duplexTopics.topicOut, // Responder reads from topicOut
100
+ * // ...
101
+ * });
102
+ * ```
103
+ */
104
+ acceptHandshake(initiatorEphemeralPubKey: Uint8Array, initiatorIdentityPubKey: Uint8Array, note: string): Promise<HandshakeResponseResult>;
105
+ /**
106
+ * Sends an encrypted message to a contact
107
+ *
108
+ * handles timestamp, signing keys, and sender address.
109
+ *
110
+ * @param topicOutbound - The outbound topic for this conversation
111
+ * @param recipientPubKey - Recipient's X25519 public key (from handshake)
112
+ * @param message - Plaintext message to encrypt and send
113
+ * @returns Transaction response
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * await client.sendMessage(
118
+ * contact.topicOutbound,
119
+ * contact.identityPubKey,
120
+ * 'Hello again!'
121
+ * );
122
+ * ```
123
+ */
124
+ sendMessage(topicOutbound: string, recipientPubKey: Uint8Array, message: string): Promise<any>;
125
+ get crypto(): typeof crypto;
126
+ get payload(): typeof payload;
127
+ get verify(): typeof verify;
128
+ get utils(): typeof utils;
129
+ get identity(): typeof identity;
130
+ get executorInstance(): IExecutor;
131
+ get identityKeyPairInstance(): IdentityKeyPair;
132
+ get userAddress(): string;
133
+ }
134
+ //# sourceMappingURL=VerbethClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VerbethClient.d.ts","sourceRoot":"","sources":["../../../../src/client/VerbethClient.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAChG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,aAAa,CAAC;AAGlE,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,OAAO,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC;;;;OAIG;gBACS,MAAM,EAAE,mBAAmB;IAQvC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,CAAC;IAgB3B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,eAAe,CACnB,wBAAwB,EAAE,UAAU,EACpC,uBAAuB,EAAE,UAAU,EACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uBAAuB,CAAC;IAoBnC;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,CACf,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,UAAU,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,GAAG,CAAC;IAqBf,IAAI,MAAM,kBAET;IAED,IAAI,OAAO,mBAEV;IAED,IAAI,MAAM,kBAET;IAED,IAAI,KAAK,iBAER;IAED,IAAI,QAAQ,oBAEX;IAED,IAAI,gBAAgB,IAAI,SAAS,CAEhC;IAGD,IAAI,uBAAuB,IAAI,eAAe,CAE7C;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;CACF"}
@@ -0,0 +1,191 @@
1
+ // packages/sdk/src/client/VerbethClient.ts
2
+ import nacl from 'tweetnacl';
3
+ import { initiateHandshake, respondToHandshake, sendEncryptedMessage } from '../send.js';
4
+ import { deriveDuplexTopics } from '../crypto.js';
5
+ import * as crypto from '../crypto.js';
6
+ import * as payload from '../payload.js';
7
+ import * as verify from '../verify.js';
8
+ import * as utils from '../utils.js';
9
+ import * as identity from '../identity.js';
10
+ /**
11
+ * High-level client for Verbeth E2EE messaging
12
+ *
13
+ * VerbethClient provides a simplified API for common operations while
14
+ * maintaining access to all low-level functions.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const client = new VerbethClient({
19
+ * executor,
20
+ * identityKeyPair,
21
+ * identityProof,
22
+ * signer,
23
+ * address: '0x...'
24
+ * });
25
+ *
26
+ * // Send a handshake
27
+ * const { tx, ephemeralKeyPair } = await client.sendHandshake(
28
+ * '0xBob...',
29
+ * 'Hello Bob!'
30
+ * );
31
+ *
32
+ * // Send a message
33
+ * await client.sendMessage(
34
+ * contact.topicOutbound,
35
+ * contact.identityPubKey,
36
+ * 'Hello again!'
37
+ * );
38
+ * ```
39
+ */
40
+ export class VerbethClient {
41
+ /**
42
+ * creates a new VerbethClient instance
43
+ *
44
+ * @param config - Client configuration with session-level parameters
45
+ */
46
+ constructor(config) {
47
+ this.executor = config.executor;
48
+ this.identityKeyPair = config.identityKeyPair;
49
+ this.identityProof = config.identityProof;
50
+ this.signer = config.signer;
51
+ this.address = config.address;
52
+ }
53
+ /**
54
+ * Initiates a handshake with a recipient
55
+ *
56
+ * generates an ephemeral keypair for this handshake.
57
+ * the ephemeralKeyPair must be stored to decrypt the response later.
58
+ *
59
+ * @param recipientAddress - Blockchain address of the recipient
60
+ * @param message - Plaintext message to include in the handshake
61
+ * @returns Transaction response and the ephemeral keypair (must be stored!)
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const { tx, ephemeralKeyPair } = await client.sendHandshake(
66
+ * '0xBob...',
67
+ * 'Hi Bob!'
68
+ * );
69
+ *
70
+ * // Store ephemeralKeyPair.secretKey to decrypt Bob's response
71
+ * await storage.saveContact({
72
+ * address: '0xBob...',
73
+ * ephemeralKey: ephemeralKeyPair.secretKey,
74
+ * // ...
75
+ * });
76
+ * ```
77
+ */
78
+ async sendHandshake(recipientAddress, message) {
79
+ const ephemeralKeyPair = nacl.box.keyPair();
80
+ const tx = await initiateHandshake({
81
+ executor: this.executor,
82
+ recipientAddress,
83
+ identityKeyPair: this.identityKeyPair,
84
+ ephemeralPubKey: ephemeralKeyPair.publicKey,
85
+ plaintextPayload: message,
86
+ identityProof: this.identityProof,
87
+ signer: this.signer,
88
+ });
89
+ return { tx, ephemeralKeyPair };
90
+ }
91
+ /**
92
+ * Accepts a handshake from an initiator
93
+ *
94
+ * derives duplex topics for the conversation and returns them.
95
+ *
96
+ * @param initiatorEphemeralPubKey - initiator's ephemeral public key from handshake event
97
+ * @param initiatorIdentityPubKey - initiator's long-term X25519 identity key
98
+ * @param note - response message to send back
99
+ * @returns transaction, derived duplex topics, and response tag
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const { tx, duplexTopics } = await client.acceptHandshake(
104
+ * handshake.ephemeralPubKey,
105
+ * handshake.identityPubKey,
106
+ * 'Hello Alice!'
107
+ * );
108
+ *
109
+ * // Store the topics for future messaging
110
+ * await storage.saveContact({
111
+ * address: handshake.sender,
112
+ * topicOutbound: duplexTopics.topicIn, // Responder writes to topicIn
113
+ * topicInbound: duplexTopics.topicOut, // Responder reads from topicOut
114
+ * // ...
115
+ * });
116
+ * ```
117
+ */
118
+ async acceptHandshake(initiatorEphemeralPubKey, initiatorIdentityPubKey, note) {
119
+ const { tx, salt, tag } = await respondToHandshake({
120
+ executor: this.executor,
121
+ initiatorPubKey: initiatorEphemeralPubKey,
122
+ responderIdentityKeyPair: this.identityKeyPair,
123
+ note,
124
+ identityProof: this.identityProof,
125
+ signer: this.signer,
126
+ initiatorIdentityPubKey,
127
+ });
128
+ const duplexTopics = deriveDuplexTopics(this.identityKeyPair.secretKey, initiatorIdentityPubKey, salt);
129
+ return { tx, duplexTopics, tag };
130
+ }
131
+ /**
132
+ * Sends an encrypted message to a contact
133
+ *
134
+ * handles timestamp, signing keys, and sender address.
135
+ *
136
+ * @param topicOutbound - The outbound topic for this conversation
137
+ * @param recipientPubKey - Recipient's X25519 public key (from handshake)
138
+ * @param message - Plaintext message to encrypt and send
139
+ * @returns Transaction response
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * await client.sendMessage(
144
+ * contact.topicOutbound,
145
+ * contact.identityPubKey,
146
+ * 'Hello again!'
147
+ * );
148
+ * ```
149
+ */
150
+ async sendMessage(topicOutbound, recipientPubKey, message) {
151
+ const signingKeyPair = {
152
+ publicKey: this.identityKeyPair.signingPublicKey,
153
+ secretKey: this.identityKeyPair.signingSecretKey,
154
+ };
155
+ const timestamp = Math.floor(Date.now() / 1000);
156
+ return sendEncryptedMessage({
157
+ executor: this.executor,
158
+ topic: topicOutbound,
159
+ message,
160
+ recipientPubKey,
161
+ senderAddress: this.address,
162
+ senderSignKeyPair: signingKeyPair,
163
+ timestamp,
164
+ });
165
+ }
166
+ // ========== low-level API ==========
167
+ get crypto() {
168
+ return crypto;
169
+ }
170
+ get payload() {
171
+ return payload;
172
+ }
173
+ get verify() {
174
+ return verify;
175
+ }
176
+ get utils() {
177
+ return utils;
178
+ }
179
+ get identity() {
180
+ return identity;
181
+ }
182
+ get executorInstance() {
183
+ return this.executor;
184
+ }
185
+ get identityKeyPairInstance() {
186
+ return this.identityKeyPair;
187
+ }
188
+ get userAddress() {
189
+ return this.address;
190
+ }
191
+ }
@@ -0,0 +1,3 @@
1
+ export { VerbethClient } from './VerbethClient.js';
2
+ export type { VerbethClientConfig, HandshakeResult, HandshakeResponseResult } from './types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACxB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ // packages/sdk/src/client/index.ts
2
+ export { VerbethClient } from './VerbethClient.js';
@@ -0,0 +1,30 @@
1
+ import type { Signer } from 'ethers';
2
+ import type { IExecutor } from '../executor.js';
3
+ import type { IdentityKeyPair, IdentityProof, DuplexTopics } from '../types.js';
4
+ import type nacl from 'tweetnacl';
5
+ /**
6
+ * Configuration for creating a VerbethClient instance
7
+ */
8
+ export interface VerbethClientConfig {
9
+ executor: IExecutor;
10
+ identityKeyPair: IdentityKeyPair;
11
+ identityProof: IdentityProof;
12
+ signer: Signer;
13
+ address: string;
14
+ }
15
+ /**
16
+ * Result from initiating a handshake
17
+ */
18
+ export interface HandshakeResult {
19
+ tx: any;
20
+ ephemeralKeyPair: nacl.BoxKeyPair;
21
+ }
22
+ /**
23
+ * Result from accepting a handshake
24
+ */
25
+ export interface HandshakeResponseResult {
26
+ tx: any;
27
+ duplexTopics: DuplexTopics;
28
+ tag: string;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/client/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,SAAS,CAAC;IACpB,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,GAAG,CAAC;IACR,gBAAgB,EAAE,IAAI,CAAC,UAAU,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,GAAG,CAAC;IACR,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb"}
@@ -0,0 +1,2 @@
1
+ // packages/sdk/src/client/types.ts
2
+ export {};
@@ -0,0 +1,46 @@
1
+ import { HandshakeResponseContent } from './payload.js';
2
+ import { IdentityProof } from './types.js';
3
+ /**
4
+ * Encrypts a structured payload (JSON-serializable objects)
5
+ */
6
+ export declare function encryptStructuredPayload<T>(payload: T, recipientPublicKey: Uint8Array, ephemeralSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, staticSigningSecretKey?: Uint8Array, staticSigningPublicKey?: Uint8Array): string;
7
+ /**
8
+ * Decrypts a structured payload with converter function
9
+ */
10
+ export declare function decryptStructuredPayload<T>(payloadJson: string, recipientSecretKey: Uint8Array, converter: (obj: any) => T, staticSigningPublicKey?: Uint8Array): T | null;
11
+ export declare function encryptMessage(message: string, recipientPublicKey: Uint8Array, ephemeralSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, staticSigningSecretKey?: Uint8Array, staticSigningPublicKey?: Uint8Array): string;
12
+ export declare function decryptMessage(payloadJson: string, recipientSecretKey: Uint8Array, staticSigningPublicKey?: Uint8Array): string | null;
13
+ /**
14
+ * Decrypts handshake response and extracts individual keys from unified format
15
+ */
16
+ export declare function decryptHandshakeResponse(payloadJson: string, initiatorEphemeralSecretKey: Uint8Array): HandshakeResponseContent | null;
17
+ /**
18
+ * helper to decrypt handshake response and extract individual keys
19
+ */
20
+ export declare function decryptAndExtractHandshakeKeys(payloadJson: string, initiatorEphemeralSecretKey: Uint8Array): {
21
+ identityPubKey: Uint8Array;
22
+ signingPubKey: Uint8Array;
23
+ ephemeralPubKey: Uint8Array;
24
+ note?: string;
25
+ identityProof: IdentityProof;
26
+ } | null;
27
+ /**
28
+ * Responder: tag = H( KDF( ECDH(r, viewPubA), "verbeth:hsr"))
29
+ */
30
+ export declare function computeTagFromResponder(rSecretKey: Uint8Array, viewPubA: Uint8Array): `0x${string}`;
31
+ /**
32
+ * Initiator: tag = H( KDF( ECDH(viewPrivA, R), "verbeth:hsr"))
33
+ */
34
+ export declare function computeTagFromInitiator(viewPrivA: Uint8Array, R: Uint8Array): `0x${string}`;
35
+ export declare function deriveLongTermShared(myIdentitySecretKey: Uint8Array, theirIdentityPublicKey: Uint8Array): Uint8Array;
36
+ /**
37
+ * Directional duplex topics (Initiator-Responder, Responder-Initiator).
38
+ * Recommended salt: tag (bytes)
39
+ */
40
+ export declare function deriveDuplexTopics(myIdentitySecretKey: Uint8Array, theirIdentityPublicKey: Uint8Array, salt?: Uint8Array): {
41
+ topicOut: `0x${string}`;
42
+ topicIn: `0x${string}`;
43
+ checksum: `0x${string}`;
44
+ };
45
+ export declare function verifyDuplexTopicsChecksum(topicOut: `0x${string}`, topicIn: `0x${string}`, checksum: `0x${string}`): boolean;
46
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/crypto.ts"],"names":[],"mappings":"AAMA,OAAO,EAML,wBAAwB,EAEzB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,CAAC,EACV,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,sBAAsB,CAAC,EAAE,UAAU,EACnC,sBAAsB,CAAC,EAAE,UAAU,GAClC,MAAM,CAcR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,UAAU,EAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAC1B,sBAAsB,CAAC,EAAE,UAAU,GAClC,CAAC,GAAG,IAAI,CAaV;AAGD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,sBAAsB,CAAC,EAAE,UAAU,EACnC,sBAAsB,CAAC,EAAE,UAAU,GAClC,MAAM,CAUR;AAED,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,UAAU,EAC9B,sBAAsB,CAAC,EAAE,UAAU,GAClC,MAAM,GAAG,IAAI,CAQf;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,2BAA2B,EAAE,UAAU,GACtC,wBAAwB,GAAG,IAAI,CAgBjC;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,EACnB,2BAA2B,EAAE,UAAU,GACtC;IACD,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,eAAe,EAAE,UAAU,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;CAC9B,GAAG,IAAI,CAcP;AAWD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,UAAU,GACnB,KAAK,MAAM,EAAE,CAGf;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,UAAU,EACrB,CAAC,EAAE,UAAU,GACZ,KAAK,MAAM,EAAE,CAGf;AAkBD,wBAAgB,oBAAoB,CAClC,mBAAmB,EAAE,UAAU,EAC/B,sBAAsB,EAAE,UAAU,GACjC,UAAU,CAEZ;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,mBAAmB,EAAE,UAAU,EAC/B,sBAAsB,EAAE,UAAU,EAClC,IAAI,CAAC,EAAE,UAAU,GAChB;IAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAA;CAAE,CAW9E;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,KAAK,MAAM,EAAE,EACvB,OAAO,EAAE,KAAK,MAAM,EAAE,EACtB,QAAQ,EAAE,KAAK,MAAM,EAAE,GACtB,OAAO,CAOT"}