github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/ephemeral/kex2_test.go (about)

     1  package ephemeral
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"sync"
     8  	"testing"
     9  
    10  	"github.com/keybase/client/go/engine"
    11  	"github.com/keybase/client/go/kbtest"
    12  	"github.com/keybase/client/go/kex2"
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestKex2Provision(t *testing.T) {
    19  	subTestKex2Provision(t, false /* upgradePerUserKey */)
    20  }
    21  
    22  func TestKex2ProvisionPUK(t *testing.T) {
    23  	subTestKex2Provision(t, true /* upgradePerUserKey */)
    24  }
    25  
    26  func fakeSalt() []byte {
    27  	return []byte("fakeSALTfakeSALT")
    28  }
    29  
    30  func subTestKex2Provision(t *testing.T, upgradePerUserKey bool) {
    31  	// device X (provisioner) context:
    32  	tcX := libkb.SetupTest(t, "kex2provision", 2)
    33  	defer tcX.Cleanup()
    34  	tcX.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
    35  	mctxX := libkb.NewMetaContextForTest(tcX)
    36  	NewEphemeralStorageAndInstall(mctxX)
    37  
    38  	// provisioner needs to be logged in
    39  	userX, err := kbtest.CreateAndSignupFakeUser("X", tcX.G)
    40  	require.NoError(t, err)
    41  	err = userX.Login(tcX.G)
    42  	require.NoError(t, err)
    43  
    44  	// If we don't have a PUK, we can't make any EKs.
    45  	if upgradePerUserKey {
    46  		// The test user has a PUK, but it's not automatically loaded. We have to
    47  		// explicitly sync it.
    48  		keyring, err := tcX.G.GetPerUserKeyring(context.Background())
    49  		require.NoError(t, err)
    50  		err = keyring.Sync(mctxX)
    51  		require.NoError(t, err)
    52  
    53  		ekLib := tcX.G.GetEKLib()
    54  		err = ekLib.KeygenIfNeeded(mctxX)
    55  		require.NoError(t, err)
    56  	}
    57  
    58  	// After the provision, Y should have access to this userEK generation
    59  	userEKBoxStorageX := tcX.G.GetUserEKBoxStorage()
    60  	userEKGenX, err := userEKBoxStorageX.MaxGeneration(mctxX, false)
    61  	require.NoError(t, err)
    62  
    63  	var userEKX keybase1.UserEk
    64  	if upgradePerUserKey {
    65  		require.True(t, userEKGenX > 0)
    66  		userEKX, err = userEKBoxStorageX.Get(mctxX, userEKGenX, nil)
    67  		require.NoError(t, err)
    68  	} else {
    69  		require.EqualValues(t, userEKGenX, -1)
    70  	}
    71  
    72  	// device Y (provisionee) context:
    73  	tcY := libkb.SetupTest(t, "kex2provision", 2)
    74  	defer tcY.Cleanup()
    75  	tcY.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
    76  	mctxY := libkb.NewMetaContextForTest(tcY)
    77  	NewEphemeralStorageAndInstall(mctxY)
    78  
    79  	var secretX kex2.Secret
    80  	_, err = rand.Read(secretX[:])
    81  	require.NoError(t, err)
    82  
    83  	var secretY kex2.Secret
    84  	_, err = rand.Read(secretY[:])
    85  	require.NoError(t, err)
    86  
    87  	var wg sync.WaitGroup
    88  
    89  	// start provisionee
    90  	wg.Add(1)
    91  	go func() {
    92  		defer wg.Done()
    93  
    94  		err := (func() error {
    95  			uis := libkb.UIs{
    96  				ProvisionUI: &kbtest.TestProvisionUI{SecretCh: make(chan kex2.Secret, 1)},
    97  			}
    98  			deviceID, err := libkb.NewDeviceID()
    99  			if err != nil {
   100  				return err
   101  			}
   102  			suffix, err := libkb.RandBytes(5)
   103  			if err != nil {
   104  				return err
   105  			}
   106  			dname := fmt.Sprintf("device_%x", suffix)
   107  			device := &libkb.Device{
   108  				ID:          deviceID,
   109  				Description: &dname,
   110  				Type:        keybase1.DeviceTypeV2_DESKTOP,
   111  			}
   112  			provisionee := engine.NewKex2Provisionee(tcY.G, device, secretY, userX.GetUID(), fakeSalt())
   113  			mctxY = mctxY.WithUIs(uis).WithNewProvisionalLoginContext()
   114  			return engine.RunEngine2(mctxY, provisionee)
   115  		})()
   116  		require.NoError(t, err, "provisionee")
   117  	}()
   118  
   119  	// start provisioner
   120  	wg.Add(1)
   121  	go func() {
   122  		defer wg.Done()
   123  		uis := libkb.UIs{
   124  			SecretUI:    userX.NewSecretUI(),
   125  			ProvisionUI: &kbtest.TestProvisionUI{},
   126  		}
   127  		provisioner := engine.NewKex2Provisioner(tcX.G, secretX, nil)
   128  		go provisioner.AddSecret(secretY)
   129  		mctxX = mctxX.WithUIs(uis)
   130  		if err := engine.RunEngine2(mctxX, provisioner); err != nil {
   131  			t.Errorf("provisioner error: %s", err)
   132  			return
   133  		}
   134  	}()
   135  
   136  	wg.Wait()
   137  
   138  	deviceEKStorageY := tcY.G.GetDeviceEKStorage()
   139  	maxDeviceEKGenerationY, err := deviceEKStorageY.MaxGeneration(mctxY, false)
   140  	require.NoError(t, err)
   141  	if upgradePerUserKey {
   142  		// Confirm that Y has a deviceEK.
   143  		require.True(t, maxDeviceEKGenerationY > 0)
   144  		deviceEKY, err := deviceEKStorageY.Get(mctxY, maxDeviceEKGenerationY)
   145  		require.NoError(t, err)
   146  		// Clear out DeviceCtime since it won't be present in fetched data,
   147  		// it's only known locally.
   148  		require.NotEqual(t, 0, deviceEKY.Metadata.DeviceCtime)
   149  		deviceEKY.Metadata.DeviceCtime = 0
   150  
   151  		// Make sure the server knows about our device_ek
   152  		merkleRootPtr, err := tcY.G.GetMerkleClient().FetchRootFromServer(mctxY, libkb.EphemeralKeyMerkleFreshness)
   153  		require.NoError(t, err)
   154  
   155  		fetchedDevices, err := allActiveDeviceEKMetadata(mctxY, *merkleRootPtr)
   156  		require.NoError(t, err)
   157  
   158  		deviceEKMetatdata, ok := fetchedDevices[mctxY.ActiveDevice().DeviceID()]
   159  		require.True(t, ok)
   160  		require.Equal(t, deviceEKY.Metadata, deviceEKMetatdata)
   161  
   162  	} else {
   163  		require.EqualValues(t, -1, maxDeviceEKGenerationY)
   164  	}
   165  	// Confirm Y has a userEK at the same generation as X. If we didn't have a
   166  	// PUK this generation will be -1.
   167  	userEKBoxStorageY := tcY.G.GetUserEKBoxStorage()
   168  	userEKGenY, err := userEKBoxStorageY.MaxGeneration(mctxY, false)
   169  	require.NoError(t, err)
   170  	require.EqualValues(t, userEKGenX, userEKGenY)
   171  
   172  	if upgradePerUserKey {
   173  		userEKY, err := userEKBoxStorageY.Get(mctxY, userEKGenY, nil)
   174  		require.NoError(t, err)
   175  		require.Equal(t, userEKX, userEKY)
   176  
   177  		// Now clear local store and make sure the server has reboxed userEK.
   178  		rawUserEKBoxStorage := NewUserEKBoxStorage()
   179  		err = rawUserEKBoxStorage.Delete(mctxY, userEKGenY)
   180  		require.NoError(t, err)
   181  		userEKBoxStorageY.ClearCache()
   182  
   183  		userEKYFetched, err := userEKBoxStorageY.Get(mctxY, userEKGenY, nil)
   184  		require.NoError(t, err)
   185  		require.Equal(t, userEKX, userEKYFetched)
   186  	}
   187  }