@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.
- package/README.md +202 -0
- package/dist/esm/src/client/VerbethClient.d.ts +134 -0
- package/dist/esm/src/client/VerbethClient.d.ts.map +1 -0
- package/dist/esm/src/client/VerbethClient.js +191 -0
- package/dist/esm/src/client/index.d.ts +3 -0
- package/dist/esm/src/client/index.d.ts.map +1 -0
- package/dist/esm/src/client/index.js +2 -0
- package/dist/esm/src/client/types.d.ts +30 -0
- package/dist/esm/src/client/types.d.ts.map +1 -0
- package/dist/esm/src/client/types.js +2 -0
- package/dist/esm/src/crypto.d.ts +46 -0
- package/dist/esm/src/crypto.d.ts.map +1 -0
- package/dist/esm/src/crypto.js +137 -0
- package/dist/esm/src/executor.d.ts +73 -0
- package/dist/esm/src/executor.d.ts.map +1 -0
- package/dist/esm/src/executor.js +353 -0
- package/dist/esm/src/identity.d.ts +28 -0
- package/dist/esm/src/identity.d.ts.map +1 -0
- package/dist/esm/src/identity.js +70 -0
- package/dist/esm/src/index.d.ts +18 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/index.js +17 -0
- package/dist/esm/src/payload.d.ts +94 -0
- package/dist/esm/src/payload.d.ts.map +1 -0
- package/dist/esm/src/payload.js +216 -0
- package/dist/esm/src/send.d.ts +50 -0
- package/dist/esm/src/send.d.ts.map +1 -0
- package/dist/esm/src/send.js +75 -0
- package/dist/esm/src/types.d.ts +73 -0
- package/dist/esm/src/types.d.ts.map +1 -0
- package/dist/esm/src/types.js +2 -0
- package/dist/esm/src/utils/nonce.d.ts +2 -0
- package/dist/esm/src/utils/nonce.d.ts.map +1 -0
- package/dist/esm/src/utils/nonce.js +6 -0
- package/dist/esm/src/utils/x25519.d.ts +6 -0
- package/dist/esm/src/utils/x25519.d.ts.map +1 -0
- package/dist/esm/src/utils/x25519.js +12 -0
- package/dist/esm/src/utils.d.ts +29 -0
- package/dist/esm/src/utils.d.ts.map +1 -0
- package/dist/esm/src/utils.js +123 -0
- package/dist/esm/src/verify.d.ts +54 -0
- package/dist/esm/src/verify.d.ts.map +1 -0
- package/dist/esm/src/verify.js +186 -0
- package/dist/src/client/VerbethClient.d.ts +134 -0
- package/dist/src/client/VerbethClient.d.ts.map +1 -0
- package/dist/src/client/VerbethClient.js +191 -0
- package/dist/src/client/index.d.ts +3 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +2 -0
- package/dist/src/client/types.d.ts +30 -0
- package/dist/src/client/types.d.ts.map +1 -0
- package/dist/src/client/types.js +2 -0
- package/dist/src/crypto.d.ts +46 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +137 -0
- package/dist/src/executor.d.ts +73 -0
- package/dist/src/executor.d.ts.map +1 -0
- package/dist/src/executor.js +353 -0
- package/dist/src/identity.d.ts +28 -0
- package/dist/src/identity.d.ts.map +1 -0
- package/dist/src/identity.js +70 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +17 -0
- package/dist/src/payload.d.ts +94 -0
- package/dist/src/payload.d.ts.map +1 -0
- package/dist/src/payload.js +216 -0
- package/dist/src/send.d.ts +50 -0
- package/dist/src/send.d.ts.map +1 -0
- package/dist/src/send.js +75 -0
- package/dist/src/types.d.ts +73 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils/nonce.d.ts +2 -0
- package/dist/src/utils/nonce.d.ts.map +1 -0
- package/dist/src/utils/nonce.js +6 -0
- package/dist/src/utils/x25519.d.ts +6 -0
- package/dist/src/utils/x25519.d.ts.map +1 -0
- package/dist/src/utils/x25519.js +12 -0
- package/dist/src/utils.d.ts +29 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +123 -0
- package/dist/src/verify.d.ts +54 -0
- package/dist/src/verify.d.ts.map +1 -0
- package/dist/src/verify.js +186 -0
- 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 @@
|
|
|
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,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,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":"AAEA,OAAO,EACL,MAAM,EACN,QAAQ,EAER,YAAY,EAGb,MAAM,QAAQ,CAAC;AAOhB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAOrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAGpE;AA0BD,MAAM,WAAW,SAAS;IACxB,WAAW,CACT,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC,CAAC;IAEhB,iBAAiB,CACf,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,GAAG,CAAC,CAAC;IAEhB,kBAAkB,CAChB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC,CAAC;CACjB;AAGD,qBAAa,WAAY,YAAW,SAAS;IAC/B,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,UAAU;IAElC,WAAW,CACf,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC;IAIT,iBAAiB,CACrB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,GAAG,CAAC;IAST,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC;CAGhB;AAGD,qBAAa,wBAAyB,YAAW,SAAS;IAKtD,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,eAAe;IAEvB,OAAO,CAAC,mBAAmB,CAAC;IAC5B,OAAO,CAAC,iBAAiB,CAAC;IAR5B,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,OAAO,CAAS;gBAGd,mBAAmB,EAAE,GAAG,EACxB,eAAe,EAAE,MAAM,EAC/B,OAAO,SAAO,EAAE,0BAA0B;IAClC,mBAAmB,CAAC,EAAE,MAAM,YAAA,EAC5B,iBAAiB,CAAC,EAAE,MAAM,YAAA;IAiB9B,WAAW,CACf,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC;IAiBT,iBAAiB,CACrB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,GAAG,CAAC;IAeT,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC;YAeD,YAAY;CAkD3B;AAGD,qBAAa,cAAe,YAAW,SAAS;IAK5C,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,kBAAkB;IAP5B,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,qBAAqB,CAAY;gBAG/B,mBAAmB,EAAE,MAAM,EAC3B,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,GAAG,EAClB,kBAAkB,EAAE,GAAG;IAc3B,WAAW,CACf,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC;IAkBT,iBAAiB,CACrB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,GAAG,CAAC;IAkBT,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC;YAkBD,aAAa;CA8B5B;AAGD,qBAAa,wBAAyB,YAAW,SAAS;IAOtD,OAAO,CAAC,mBAAmB;IAE3B,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,MAAM;IAVhB,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,qBAAqB,CAAY;IACzC,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,IAAI,CAAgB;gBAGlB,mBAAmB,EAAE,MAAM,EACnC,kBAAkB,EAAE,QAAQ,GAAG,YAAY,EACnC,eAAe,EAAE,MAAM,EACvB,kBAAkB,EAAE,GAAG,EACvB,MAAM,EAAE,MAAM;IAiBlB,WAAW,CACf,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC;IAkBT,iBAAiB,CACrB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,GAAG,CAAC;IAkBT,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC;YAkBD,mBAAmB;CAoDlC;AAED,qBAAa,eAAe;IAC1B,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS;IAIjD,MAAM,CAAC,sBAAsB,CAC3B,mBAAmB,EAAE,GAAG,EACxB,eAAe,EAAE,MAAM,EACvB,OAAO,SAAO,EACd,mBAAmB,CAAC,EAAE,MAAM,EAC5B,iBAAiB,CAAC,EAAE,MAAM,GACzB,SAAS;IAUZ,MAAM,CAAC,YAAY,CACjB,mBAAmB,EAAE,MAAM,EAC3B,kBAAkB,EAAE,MAAM,EAC1B,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,GAAG,EAClB,kBAAkB,EAAE,GAAG,GACtB,SAAS;IASZ,MAAM,CAAC,sBAAsB,CAC3B,mBAAmB,EAAE,MAAM,EAC3B,kBAAkB,EAAE,QAAQ,GAAG,YAAY,EAC3C,eAAe,EAAE,MAAM,EACvB,kBAAkB,EAAE,GAAG,EACvB,MAAM,EAAE,MAAM,GACb,SAAS;WAWC,UAAU,CACrB,eAAe,EAAE,GAAG,EACpB,QAAQ,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE;QACR,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,kBAAkB,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;QAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,GAAG,CAAC;QACpB,mBAAmB,CAAC,EAAE,GAAG,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,GACA,OAAO,CAAC,SAAS,CAAC;CAkEtB"}
|