@@ -364,22 +364,26 @@ function Home({ navigation }: Props) {
364364 '[Vault Signing] Received request for chain:' ,
365365 socketVaultSigningRequest . chain ,
366366 ) ;
367- // Defensively parse fields that may arrive as JSON strings
368- // (matching the refresh/action path parsing at handleRefresh)
369- const data = { ...socketVaultSigningRequest } ;
370- if ( typeof data . recipients === 'string' ) {
371- data . recipients = JSON . parse ( data . recipients ) ;
372- }
373- if ( typeof data . inputDetails === 'string' ) {
374- data . inputDetails = JSON . parse ( data . inputDetails ) ;
375- }
376- if ( typeof data . allSignerKeys === 'string' ) {
377- data . allSignerKeys = JSON . parse ( data . allSignerKeys ) ;
378- }
379- if ( typeof data . allSignerNonces === 'string' ) {
380- data . allSignerNonces = JSON . parse ( data . allSignerNonces ) ;
367+ try {
368+ // Defensively parse fields that may arrive as JSON strings
369+ // (matching the refresh/action path parsing at handleRefresh)
370+ const data = { ...socketVaultSigningRequest } ;
371+ if ( typeof data . recipients === 'string' ) {
372+ data . recipients = JSON . parse ( data . recipients ) ;
373+ }
374+ if ( typeof data . inputDetails === 'string' ) {
375+ data . inputDetails = JSON . parse ( data . inputDetails ) ;
376+ }
377+ if ( typeof data . allSignerKeys === 'string' ) {
378+ data . allSignerKeys = JSON . parse ( data . allSignerKeys ) ;
379+ }
380+ if ( typeof data . allSignerNonces === 'string' ) {
381+ data . allSignerNonces = JSON . parse ( data . allSignerNonces ) ;
382+ }
383+ setVaultSigningData ( data ) ;
384+ } catch {
385+ displayMessage ( 'error' , t ( 'home:err_invalid_request' ) , 5000 ) ;
381386 }
382- setVaultSigningData ( data ) ;
383387 }
384388 } , [ socketVaultSigningRequest ] ) ;
385389
@@ -1247,6 +1251,8 @@ function Home({ navigation }: Props) {
12471251 } ) ;
12481252 }
12491253 } ;
1254+ const nonceReplenishInProgressRef = useRef ( false ) ;
1255+
12501256 /**
12511257 * Check enterprise nonce pool and replenish if below threshold.
12521258 * Generates nonces locally, stores private parts in Keychain,
@@ -1258,6 +1264,8 @@ function Home({ navigation }: Props) {
12581264 replenishNeeded ?: boolean ,
12591265 ) => {
12601266 if ( ! sspWalletKeyInternalIdentity ) return ;
1267+ if ( nonceReplenishInProgressRef . current ) return ;
1268+ nonceReplenishInProgressRef . current = true ;
12611269 try {
12621270 const TARGET_COUNT = 50 ;
12631271
@@ -1337,6 +1345,8 @@ function Home({ navigation }: Props) {
13371345 } catch ( error ) {
13381346 // Non-critical — don't block Key functionality
13391347 console . log ( '[Enterprise Nonces] Key replenish error:' , error ) ;
1348+ } finally {
1349+ nonceReplenishInProgressRef . current = false ;
13401350 }
13411351 } ;
13421352
@@ -1670,6 +1680,11 @@ function Home({ navigation }: Props) {
16701680 const handleVaultXpubAction = async ( ) => {
16711681 if ( ! vaultXpubData ) return ;
16721682
1683+ // Hoist sensitive vars outside try so they can be cleared in catch
1684+ let pwForEncryption = '' ;
1685+ let mnemonicPhrase = '' ;
1686+ let xprivKeyDecrypted = '' ;
1687+
16731688 try {
16741689 // Get decryption keys from keychain
16751690 const encryptionKey = await Keychain . getGenericPassword ( {
@@ -1691,14 +1706,13 @@ function Home({ navigation }: Props) {
16911706 const passwordDecryptedString = passwordDecrypted . toString (
16921707 CryptoJS . enc . Utf8 ,
16931708 ) ;
1694- let pwForEncryption = encryptionKey . password + passwordDecryptedString ;
1709+ pwForEncryption = encryptionKey . password + passwordDecryptedString ;
16951710
16961711 // Decrypt mnemonic seed phrase
16971712 const mmm = CryptoJS . AES . decrypt ( seedPhrase , pwForEncryption ) ;
1698- let mnemonicPhrase = mmm . toString ( CryptoJS . enc . Utf8 ) ;
1713+ mnemonicPhrase = mmm . toString ( CryptoJS . enc . Utf8 ) ;
16991714
17001715 if ( ! mnemonicPhrase ) {
1701- pwForEncryption = '' ;
17021716 throw new Error ( 'Failed to decrypt mnemonic' ) ;
17031717 }
17041718
@@ -1725,7 +1739,7 @@ function Home({ navigation }: Props) {
17251739 throw new Error ( 'xprivKey not available' ) ;
17261740 }
17271741 const xprivDecrypted = CryptoJS . AES . decrypt ( idXprivKey , pwForEncryption ) ;
1728- let xprivKeyDecrypted = xprivDecrypted . toString ( CryptoJS . enc . Utf8 ) ;
1742+ xprivKeyDecrypted = xprivDecrypted . toString ( CryptoJS . enc . Utf8 ) ;
17291743 if ( ! xprivKeyDecrypted ) {
17301744 throw new Error ( 'Failed to decrypt xprivKey' ) ;
17311745 }
@@ -1735,8 +1749,6 @@ function Home({ navigation }: Props) {
17351749 0 ,
17361750 identityChain ,
17371751 ) ;
1738- // Clear decrypted xpriv — no longer needed
1739- xprivKeyDecrypted = '' ;
17401752 const xpubMessage = `SSP_VAULT_XPUB:key:${ vaultXpub } :${ vaultXpubData . chain } :${ String ( vaultXpubData . orgIndex ) } ` ;
17411753 const keyXpubSignature = signMessage (
17421754 xpubMessage ,
@@ -1746,6 +1758,7 @@ function Home({ navigation }: Props) {
17461758
17471759 // Clear sensitive key material
17481760 identityKeypair . privKey = '' ;
1761+ xprivKeyDecrypted = '' ;
17491762 mnemonicPhrase = '' ;
17501763 pwForEncryption = '' ;
17511764
@@ -1769,6 +1782,10 @@ function Home({ navigation }: Props) {
17691782
17701783 displayMessage ( 'success' , t ( 'home:vault_xpub_success' ) ) ;
17711784 } catch ( error ) {
1785+ // Clear sensitive key material on error path
1786+ xprivKeyDecrypted = '' ;
1787+ mnemonicPhrase = '' ;
1788+ pwForEncryption = '' ;
17721789 console . error ( '[Vault Xpub] Error:' , error ) ;
17731790 displayMessage ( 'error' , t ( 'home:err_vault_xpub_failed' ) ) ;
17741791 } finally {
@@ -1807,6 +1824,7 @@ function Home({ navigation }: Props) {
18071824 // Hoist sensitive vars outside try so they can be cleared in catch/finally
18081825 let vaultXpriv = '' ;
18091826 let pwForEncryption = '' ;
1827+ let mnemonicPhrase = '' ;
18101828
18111829 try {
18121830 // Get decryption keys from keychain
@@ -1833,7 +1851,7 @@ function Home({ navigation }: Props) {
18331851
18341852 // Decrypt mnemonic seed phrase
18351853 const mmm = CryptoJS . AES . decrypt ( seedPhrase , pwForEncryption ) ;
1836- let mnemonicPhrase = mmm . toString ( CryptoJS . enc . Utf8 ) ;
1854+ mnemonicPhrase = mmm . toString ( CryptoJS . enc . Utf8 ) ;
18371855
18381856 if ( ! mnemonicPhrase ) {
18391857 throw new Error ( 'Failed to decrypt mnemonic' ) ;
@@ -1973,6 +1991,9 @@ function Home({ navigation }: Props) {
19731991 vaultSigningData . sigOne ,
19741992 ) ;
19751993
1994+ // Clear EVM signing keypair private key
1995+ signingKeypair . privKey = '' ;
1996+
19761997 // Build response with signerContribution + challenge for wallet to forward
19771998 const responsePayload : Record < string , unknown > = {
19781999 signerContribution : vaultSchnorrResult . signerContribution ,
@@ -2079,6 +2100,9 @@ function Home({ navigation }: Props) {
20792100 amount ,
20802101 witnessScriptBuf ,
20812102 ) ;
2103+
2104+ // Clear per-input private key material
2105+ signingKeypair . privKey = '' ;
20822106 }
20832107
20842108 // Build with both wallet + key sigs (still incomplete if M>1)
@@ -2111,6 +2135,7 @@ function Home({ navigation }: Props) {
21112135 // Clear sensitive key material on error path
21122136 vaultXpriv = '' ;
21132137 pwForEncryption = '' ;
2138+ mnemonicPhrase = '' ;
21142139 console . error ( '[Vault Signing] Error:' , error ) ;
21152140 displayMessage ( 'error' , t ( 'home:err_vault_sign_failed' ) ) ;
21162141 } finally {
0 commit comments