@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
@@ -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"}
@@ -0,0 +1,137 @@
1
+ // packages/sdk/src/crypto.ts
2
+ import nacl from 'tweetnacl';
3
+ import { keccak256, toUtf8Bytes, dataSlice } from 'ethers';
4
+ import { sha256 } from '@noble/hashes/sha2';
5
+ import { hkdf } from '@noble/hashes/hkdf';
6
+ import { encodePayload, decodePayload, encodeStructuredContent, decodeStructuredContent, extractKeysFromHandshakeResponse } from './payload.js';
7
+ /**
8
+ * Encrypts a structured payload (JSON-serializable objects)
9
+ */
10
+ export function encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey) {
11
+ // encode payload as binary JSON
12
+ const plaintext = encodeStructuredContent(payload);
13
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
14
+ const box = nacl.box(plaintext, nonce, recipientPublicKey, ephemeralSecretKey);
15
+ let sig;
16
+ if (staticSigningSecretKey && staticSigningPublicKey) {
17
+ const dataToSign = Buffer.concat([ephemeralPublicKey, nonce, box]);
18
+ sig = nacl.sign.detached(dataToSign, staticSigningSecretKey);
19
+ }
20
+ return encodePayload(ephemeralPublicKey, nonce, box, sig);
21
+ }
22
+ /**
23
+ * Decrypts a structured payload with converter function
24
+ */
25
+ export function decryptStructuredPayload(payloadJson, recipientSecretKey, converter, staticSigningPublicKey) {
26
+ const { epk, nonce, ciphertext, sig } = decodePayload(payloadJson);
27
+ if (sig && staticSigningPublicKey) {
28
+ const dataToVerify = Buffer.concat([epk, nonce, ciphertext]);
29
+ const valid = nacl.sign.detached.verify(dataToVerify, sig, staticSigningPublicKey);
30
+ if (!valid)
31
+ return null;
32
+ }
33
+ const box = nacl.box.open(ciphertext, nonce, epk, recipientSecretKey);
34
+ if (!box)
35
+ return null;
36
+ return decodeStructuredContent(box, converter);
37
+ }
38
+ // wrappers for encrypting and decrypting messages
39
+ export function encryptMessage(message, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey) {
40
+ const payload = { content: message };
41
+ return encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey);
42
+ }
43
+ export function decryptMessage(payloadJson, recipientSecretKey, staticSigningPublicKey) {
44
+ const result = decryptStructuredPayload(payloadJson, recipientSecretKey, (obj) => obj, staticSigningPublicKey);
45
+ return result ? result.content : null;
46
+ }
47
+ /**
48
+ * Decrypts handshake response and extracts individual keys from unified format
49
+ */
50
+ export function decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey) {
51
+ return decryptStructuredPayload(payloadJson, initiatorEphemeralSecretKey, (obj) => {
52
+ if (!obj.identityProof) {
53
+ throw new Error("Invalid handshake response: missing identityProof");
54
+ }
55
+ return {
56
+ unifiedPubKeys: Uint8Array.from(Buffer.from(obj.unifiedPubKeys, 'base64')),
57
+ ephemeralPubKey: Uint8Array.from(Buffer.from(obj.ephemeralPubKey, 'base64')),
58
+ note: obj.note,
59
+ identityProof: obj.identityProof
60
+ };
61
+ });
62
+ }
63
+ /**
64
+ * helper to decrypt handshake response and extract individual keys
65
+ */
66
+ export function decryptAndExtractHandshakeKeys(payloadJson, initiatorEphemeralSecretKey) {
67
+ const decrypted = decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey);
68
+ if (!decrypted)
69
+ return null;
70
+ const extracted = extractKeysFromHandshakeResponse(decrypted);
71
+ if (!extracted)
72
+ return null;
73
+ return {
74
+ identityPubKey: extracted.identityPubKey,
75
+ signingPubKey: extracted.signingPubKey,
76
+ ephemeralPubKey: extracted.ephemeralPubKey,
77
+ note: decrypted.note,
78
+ identityProof: decrypted.identityProof
79
+ };
80
+ }
81
+ /**
82
+ * HKDF(sha256) on shared secret, info="verbeth:hsr", then Keccak-256 -> bytes32 (0x...)
83
+ */
84
+ function finalizeHsrTag(shared) {
85
+ const okm = hkdf(sha256, shared, new Uint8Array(0), toUtf8Bytes("verbeth:hsr"), 32);
86
+ return keccak256(okm);
87
+ }
88
+ /**
89
+ * Responder: tag = H( KDF( ECDH(r, viewPubA), "verbeth:hsr"))
90
+ */
91
+ export function computeTagFromResponder(rSecretKey, viewPubA) {
92
+ const shared = nacl.scalarMult(rSecretKey, viewPubA);
93
+ return finalizeHsrTag(shared);
94
+ }
95
+ /**
96
+ * Initiator: tag = H( KDF( ECDH(viewPrivA, R), "verbeth:hsr"))
97
+ */
98
+ export function computeTagFromInitiator(viewPrivA, R) {
99
+ const shared = nacl.scalarMult(viewPrivA, R);
100
+ return finalizeHsrTag(shared);
101
+ }
102
+ /**
103
+ * Derives a bytes32 topic from the shared secret via HKDF(SHA256) + Keccak-256.
104
+ * - info: domain separation (e.g., "verbeth:topic-out:v1")
105
+ * - salt: recommended to use a tag as salt (stable and shareable)
106
+ */
107
+ function deriveTopic(shared, info, salt) {
108
+ const okm = hkdf(sha256, shared, salt ?? new Uint8Array(0), new TextEncoder().encode(info), 32);
109
+ return keccak256(okm);
110
+ }
111
+ export function deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey) {
112
+ return nacl.scalarMult(myIdentitySecretKey, theirIdentityPublicKey);
113
+ }
114
+ /**
115
+ * Directional duplex topics (Initiator-Responder, Responder-Initiator).
116
+ * Recommended salt: tag (bytes)
117
+ */
118
+ export function deriveDuplexTopics(myIdentitySecretKey, theirIdentityPublicKey, salt) {
119
+ const shared = deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey);
120
+ const topicOut = deriveTopic(shared, "verbeth:topic-out:v1", salt);
121
+ const topicIn = deriveTopic(shared, "verbeth:topic-in:v1", salt);
122
+ const chkFull = keccak256(Buffer.concat([
123
+ toUtf8Bytes("verbeth:topic-chk:v1"),
124
+ Buffer.from(topicOut.slice(2), 'hex'),
125
+ Buffer.from(topicIn.slice(2), 'hex'),
126
+ ]));
127
+ const checksum = dataSlice(chkFull, 8);
128
+ return { topicOut, topicIn, checksum };
129
+ }
130
+ export function verifyDuplexTopicsChecksum(topicOut, topicIn, checksum) {
131
+ const chkFull = keccak256(Buffer.concat([
132
+ toUtf8Bytes("verbeth:topic-chk:v1"),
133
+ Buffer.from(topicOut.slice(2), 'hex'),
134
+ Buffer.from(topicIn.slice(2), 'hex'),
135
+ ]));
136
+ return dataSlice(chkFull, 8) === checksum;
137
+ }
@@ -0,0 +1,73 @@
1
+ import { Signer, Contract, BaseContract } from "ethers";
2
+ import type { LogChainV1 } from "@verbeth/contracts/typechain-types";
3
+ export declare function split128x128(word: bigint): readonly [bigint, bigint];
4
+ export interface IExecutor {
5
+ sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
6
+ initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
7
+ respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
8
+ }
9
+ export declare class EOAExecutor implements IExecutor {
10
+ private contract;
11
+ constructor(contract: LogChainV1);
12
+ sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
13
+ initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
14
+ respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
15
+ }
16
+ export declare class BaseSmartAccountExecutor implements IExecutor {
17
+ private baseAccountProvider;
18
+ private logChainAddress;
19
+ private paymasterServiceUrl?;
20
+ private subAccountAddress?;
21
+ private logChainInterface;
22
+ private chainId;
23
+ constructor(baseAccountProvider: any, logChainAddress: string, chainId?: number, // Base mainnet by default
24
+ paymasterServiceUrl?: string | undefined, subAccountAddress?: string | undefined);
25
+ sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
26
+ initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
27
+ respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
28
+ private executeCalls;
29
+ }
30
+ export declare class UserOpExecutor implements IExecutor {
31
+ private smartAccountAddress;
32
+ private logChainAddress;
33
+ private bundlerClient;
34
+ private smartAccountClient;
35
+ private logChainInterface;
36
+ private smartAccountInterface;
37
+ constructor(smartAccountAddress: string, logChainAddress: string, bundlerClient: any, smartAccountClient: any);
38
+ sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
39
+ initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
40
+ respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
41
+ private executeUserOp;
42
+ }
43
+ export declare class DirectEntryPointExecutor implements IExecutor {
44
+ private smartAccountAddress;
45
+ private logChainAddress;
46
+ private smartAccountClient;
47
+ private signer;
48
+ private logChainInterface;
49
+ private smartAccountInterface;
50
+ private entryPointContract;
51
+ private spec;
52
+ constructor(smartAccountAddress: string, entryPointContract: Contract | BaseContract, logChainAddress: string, smartAccountClient: any, signer: Signer);
53
+ sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
54
+ initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
55
+ respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
56
+ private executeDirectUserOp;
57
+ }
58
+ export declare class ExecutorFactory {
59
+ static createEOA(contract: LogChainV1): IExecutor;
60
+ static createBaseSmartAccount(baseAccountProvider: any, logChainAddress: string, chainId?: number, paymasterServiceUrl?: string, subAccountAddress?: string): IExecutor;
61
+ static createUserOp(smartAccountAddress: string, _entryPointAddress: string, logChainAddress: string, bundlerClient: any, smartAccountClient: any): IExecutor;
62
+ static createDirectEntryPoint(smartAccountAddress: string, entryPointContract: Contract | BaseContract, logChainAddress: string, smartAccountClient: any, signer: Signer): IExecutor;
63
+ static createAuto(signerOrAccount: any, contract: LogChainV1, options?: {
64
+ entryPointAddress?: string;
65
+ entryPointContract?: Contract | BaseContract;
66
+ logChainAddress?: string;
67
+ bundlerClient?: any;
68
+ baseAccountProvider?: any;
69
+ chainId?: number;
70
+ isTestEnvironment?: boolean;
71
+ }): Promise<IExecutor>;
72
+ }
73
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/executor.ts"],"names":[],"mappings":"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"}