@@ -2504,6 +2504,155 @@ describe('V2 Wallets:', function () {
25042504 } ) ;
25052505 } ) ;
25062506
2507+ it ( 'should include webauthnInfo in request when provided (ECDH branch)' , async ( ) => {
2508+ const fromUserPrv = Math . random ( ) ;
2509+ const walletPassphrase = 'bitgo1234' ;
2510+ const webauthnPassphrase = 'prf-derived-secret' ;
2511+ const shareId = '66a229dbdccdcfb95b44fc2745a60bd4' ;
2512+ const keychainTest : OptionalKeychainEncryptedKey = {
2513+ encryptedPrv : bitgo . encrypt ( { input : fromUserPrv . toString ( ) , password : walletPassphrase } ) ,
2514+ } ;
2515+ const userPrv = decryptKeychainPrivateKey ( bitgo , keychainTest , walletPassphrase ) ;
2516+ if ( ! userPrv ) {
2517+ throw new Error ( 'Unable to decrypt user keychain' ) ;
2518+ }
2519+
2520+ const toKeychain = utxoLib . bip32 . fromSeed ( Buffer . from ( 'deadbeef02deadbeef02deadbeef02deadbeef02' , 'hex' ) ) ;
2521+ const path = 'm/999999/1/1' ;
2522+ const pubkey = toKeychain . derivePath ( path ) . publicKey . toString ( 'hex' ) ;
2523+
2524+ const eckey = makeRandomKey ( ) ;
2525+ const secret = getSharedSecret ( eckey , Buffer . from ( pubkey , 'hex' ) ) . toString ( 'hex' ) ;
2526+ const newEncryptedPrv = bitgo . encrypt ( { password : secret , input : userPrv } ) ;
2527+
2528+ let capturedBody : any ;
2529+ nock ( bgUrl )
2530+ . get ( '/api/v2/walletshares' )
2531+ . reply ( 200 , {
2532+ incoming : [
2533+ {
2534+ id : shareId ,
2535+ isUMSInitiated : true ,
2536+ keychain : {
2537+ path : path ,
2538+ fromPubKey : eckey . publicKey . toString ( 'hex' ) ,
2539+ encryptedPrv : newEncryptedPrv ,
2540+ toPubKey : pubkey ,
2541+ pub : pubkey ,
2542+ } ,
2543+ } ,
2544+ ] ,
2545+ } ) ;
2546+ nock ( bgUrl )
2547+ . put ( '/api/v2/walletshares/accept' , ( body ) => {
2548+ capturedBody = body ;
2549+ return true ;
2550+ } )
2551+ . reply ( 200 , {
2552+ acceptedWalletShares : [ { walletShareId : shareId } ] ,
2553+ } ) ;
2554+
2555+ const myEcdhKeychain = await bitgo . keychains ( ) . create ( ) ;
2556+ sinon . stub ( bitgo , 'getECDHKeychain' ) . resolves ( {
2557+ encryptedXprv : bitgo . encrypt ( { input : myEcdhKeychain . xprv , password : walletPassphrase } ) ,
2558+ } ) ;
2559+
2560+ const prvKey = bitgo . decrypt ( {
2561+ password : walletPassphrase ,
2562+ input : bitgo . encrypt ( { input : myEcdhKeychain . xprv , password : walletPassphrase } ) ,
2563+ } ) ;
2564+ sinon . stub ( bitgo , 'decrypt' ) . returns ( prvKey ) ;
2565+ sinon . stub ( moduleBitgo , 'getSharedSecret' ) . resolves ( 'fakeSharedSecret' ) ;
2566+
2567+ await wallets . bulkAcceptShare ( {
2568+ walletShareIds : [ shareId ] ,
2569+ userLoginPassword : walletPassphrase ,
2570+ webauthnInfo : {
2571+ otpDeviceId : 'device-001' ,
2572+ prfSalt : 'salt-abc' ,
2573+ passphrase : webauthnPassphrase ,
2574+ } ,
2575+ } ) ;
2576+
2577+ const sentEntries = capturedBody . keysForWalletShares ;
2578+ sentEntries . should . have . length ( 1 ) ;
2579+ sentEntries [ 0 ] . should . have . property ( 'encryptedPrv' ) ;
2580+ sentEntries [ 0 ] . should . have . property ( 'webauthnInfo' ) ;
2581+ sentEntries [ 0 ] . webauthnInfo . should . have . property ( 'otpDeviceId' , 'device-001' ) ;
2582+ sentEntries [ 0 ] . webauthnInfo . should . have . property ( 'prfSalt' , 'salt-abc' ) ;
2583+ sentEntries [ 0 ] . webauthnInfo . should . have . property ( 'encryptedPrv' ) ;
2584+ sentEntries [ 0 ] . webauthnInfo . should . not . have . property ( 'passphrase' ) ;
2585+ } ) ;
2586+
2587+ it ( 'should NOT include webauthnInfo when not provided (backward compat)' , async ( ) => {
2588+ const fromUserPrv = Math . random ( ) ;
2589+ const walletPassphrase = 'bitgo1234' ;
2590+ const shareId = '66a229dbdccdcfb95b44fc2745a60bd4' ;
2591+ const keychainTest : OptionalKeychainEncryptedKey = {
2592+ encryptedPrv : bitgo . encrypt ( { input : fromUserPrv . toString ( ) , password : walletPassphrase } ) ,
2593+ } ;
2594+ const userPrv = decryptKeychainPrivateKey ( bitgo , keychainTest , walletPassphrase ) ;
2595+ if ( ! userPrv ) {
2596+ throw new Error ( 'Unable to decrypt user keychain' ) ;
2597+ }
2598+
2599+ const toKeychain = utxoLib . bip32 . fromSeed ( Buffer . from ( 'deadbeef02deadbeef02deadbeef02deadbeef02' , 'hex' ) ) ;
2600+ const path = 'm/999999/1/1' ;
2601+ const pubkey = toKeychain . derivePath ( path ) . publicKey . toString ( 'hex' ) ;
2602+
2603+ const eckey = makeRandomKey ( ) ;
2604+ const secret = getSharedSecret ( eckey , Buffer . from ( pubkey , 'hex' ) ) . toString ( 'hex' ) ;
2605+ const newEncryptedPrv = bitgo . encrypt ( { password : secret , input : userPrv } ) ;
2606+
2607+ let capturedBody : any ;
2608+ nock ( bgUrl )
2609+ . get ( '/api/v2/walletshares' )
2610+ . reply ( 200 , {
2611+ incoming : [
2612+ {
2613+ id : shareId ,
2614+ isUMSInitiated : true ,
2615+ keychain : {
2616+ path : path ,
2617+ fromPubKey : eckey . publicKey . toString ( 'hex' ) ,
2618+ encryptedPrv : newEncryptedPrv ,
2619+ toPubKey : pubkey ,
2620+ pub : pubkey ,
2621+ } ,
2622+ } ,
2623+ ] ,
2624+ } ) ;
2625+ nock ( bgUrl )
2626+ . put ( '/api/v2/walletshares/accept' , ( body ) => {
2627+ capturedBody = body ;
2628+ return true ;
2629+ } )
2630+ . reply ( 200 , {
2631+ acceptedWalletShares : [ { walletShareId : shareId } ] ,
2632+ } ) ;
2633+
2634+ const myEcdhKeychain = await bitgo . keychains ( ) . create ( ) ;
2635+ sinon . stub ( bitgo , 'getECDHKeychain' ) . resolves ( {
2636+ encryptedXprv : bitgo . encrypt ( { input : myEcdhKeychain . xprv , password : walletPassphrase } ) ,
2637+ } ) ;
2638+ const prvKey = bitgo . decrypt ( {
2639+ password : walletPassphrase ,
2640+ input : bitgo . encrypt ( { input : myEcdhKeychain . xprv , password : walletPassphrase } ) ,
2641+ } ) ;
2642+ sinon . stub ( bitgo , 'decrypt' ) . returns ( prvKey ) ;
2643+ sinon . stub ( moduleBitgo , 'getSharedSecret' ) . resolves ( 'fakeSharedSecret' ) ;
2644+
2645+ await wallets . bulkAcceptShare ( {
2646+ walletShareIds : [ shareId ] ,
2647+ userLoginPassword : walletPassphrase ,
2648+ } ) ;
2649+
2650+ const sentEntries = capturedBody . keysForWalletShares ;
2651+ sentEntries . should . have . length ( 1 ) ;
2652+ sentEntries [ 0 ] . should . have . property ( 'encryptedPrv' ) ;
2653+ sentEntries [ 0 ] . should . not . have . property ( 'webauthnInfo' ) ;
2654+ } ) ;
2655+
25072656 it ( 'should handle 413 payload too large error with smart retry' , async ( ) => {
25082657 const walletPassphrase = 'bitgo1234' ;
25092658 const fromUserPrv = Math . random ( ) ;
0 commit comments