@@ -130,16 +130,37 @@ async function decryptWeb(
130130}
131131
132132// ---------------------------------------------------------------------------
133- // Node.js implementation
133+ // Node.js implementation — use static imports resolved at load time.
134+ // Dynamic `await import("node:crypto")` hangs in some extension loaders
135+ // (e.g. OpenClaw gateway), so we resolve the modules eagerly when isNode.
134136// ---------------------------------------------------------------------------
135137
138+ // Node.js crypto modules — loaded eagerly.
139+ // We use a global cache keyed by "__e2e_crypto" to avoid re-importing
140+ // in environments where the module may be loaded multiple times.
141+ let _nodeCrypto : typeof import ( "node:crypto" ) | null = null ;
142+ let _nodeUtil : typeof import ( "node:util" ) | null = null ;
143+
144+ const _g = globalThis as Record < string , unknown > ;
145+ if ( isNode && _g . __e2e_nodeCrypto ) {
146+ _nodeCrypto = _g . __e2e_nodeCrypto as typeof import ( "node:crypto" ) ;
147+ _nodeUtil = _g . __e2e_nodeUtil as typeof import ( "node:util" ) ;
148+ }
149+
150+ async function ensureNodeModules ( ) : Promise < void > {
151+ if ( _nodeCrypto && _nodeUtil ) return ;
152+ _nodeCrypto = await import ( "node:crypto" ) ;
153+ _nodeUtil = await import ( "node:util" ) ;
154+ _g . __e2e_nodeCrypto = _nodeCrypto ;
155+ _g . __e2e_nodeUtil = _nodeUtil ;
156+ }
157+
136158async function deriveKeyNode (
137159 password : string ,
138160 userId : string ,
139161) : Promise < Uint8Array > {
140- const { pbkdf2 } = await import ( "node:crypto" ) ;
141- const { promisify } = await import ( "node:util" ) ;
142- const pbkdf2Async = promisify ( pbkdf2 ) ;
162+ await ensureNodeModules ( ) ;
163+ const pbkdf2Async = _nodeUtil ! . promisify ( _nodeCrypto ! . pbkdf2 ) ;
143164 const salt = SALT_PREFIX + userId ;
144165 const buf = await pbkdf2Async ( password , salt , PBKDF2_ITERATIONS , KEY_LENGTH , "sha256" ) ;
145166 return new Uint8Array ( buf ) ;
@@ -149,12 +170,12 @@ async function hkdfNonceNode(
149170 key : Uint8Array ,
150171 contextId : string ,
151172) : Promise < Uint8Array > {
152- const { createHmac } = await import ( "node:crypto" ) ;
173+ await ensureNodeModules ( ) ;
153174 const info = utf8Encode ( "nonce-" + contextId ) ;
154175 const input = new Uint8Array ( info . length + 1 ) ;
155176 input . set ( info ) ;
156177 input [ info . length ] = 0x01 ;
157- const hmac = createHmac ( "sha256" , Buffer . from ( key ) ) ;
178+ const hmac = _nodeCrypto ! . createHmac ( "sha256" , Buffer . from ( key ) ) ;
158179 hmac . update ( Buffer . from ( input ) ) ;
159180 const full = hmac . digest ( ) ;
160181 return new Uint8Array ( full . buffer , full . byteOffset , NONCE_LENGTH ) ;
@@ -165,9 +186,9 @@ async function encryptNode(
165186 plaintext : Uint8Array ,
166187 contextId : string ,
167188) : Promise < Uint8Array > {
168- const { createCipheriv } = await import ( "node:crypto" ) ;
189+ await ensureNodeModules ( ) ;
169190 const iv = await hkdfNonceNode ( key , contextId ) ;
170- const cipher = createCipheriv (
191+ const cipher = _nodeCrypto ! . createCipheriv (
171192 "aes-256-ctr" ,
172193 Buffer . from ( key ) ,
173194 Buffer . from ( iv ) ,
@@ -181,9 +202,9 @@ async function decryptNode(
181202 ciphertext : Uint8Array ,
182203 contextId : string ,
183204) : Promise < Uint8Array > {
184- const { createDecipheriv } = await import ( "node:crypto" ) ;
205+ await ensureNodeModules ( ) ;
185206 const iv = await hkdfNonceNode ( key , contextId ) ;
186- const decipher = createDecipheriv (
207+ const decipher = _nodeCrypto ! . createDecipheriv (
187208 "aes-256-ctr" ,
188209 Buffer . from ( key ) ,
189210 Buffer . from ( iv ) ,
0 commit comments