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

     1  package systests
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/keybase/client/go/client"
    10  	"github.com/keybase/client/go/libkb"
    11  	"github.com/keybase/client/go/logger"
    12  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    13  	"github.com/keybase/client/go/service"
    14  	"github.com/keybase/clockwork"
    15  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    16  	"github.com/stretchr/testify/require"
    17  	context "golang.org/x/net/context"
    18  )
    19  
    20  // deviceWrapper wraps a mock "device", meaning an independent running service and
    21  // some connected clients.
    22  type deviceWrapper struct {
    23  	tctx               *libkb.TestContext
    24  	clones, usedClones []*libkb.TestContext
    25  	stopCh             chan error
    26  	service            *service.Service
    27  	rekeyUI            *testRekeyUI
    28  	deviceKey          keybase1.PublicKey
    29  	rekeyClient        keybase1.RekeyClient
    30  	userClient         keybase1.UserClient
    31  	accountClient      keybase1.AccountClient
    32  	gregorClient       keybase1.GregorClient
    33  }
    34  
    35  func (d *deviceWrapper) KID() keybase1.KID {
    36  	return d.deviceKey.KID
    37  }
    38  
    39  func (d *deviceWrapper) start(numClones int) {
    40  	for i := 0; i < numClones; i++ {
    41  		d.clones = append(d.clones, cloneContext(d.tctx))
    42  	}
    43  	d.stopCh = make(chan error)
    44  	svc := service.NewService(d.tctx.G, false)
    45  	d.service = svc
    46  	startCh := svc.GetStartChannel()
    47  	go func() {
    48  		d.stopCh <- svc.Run()
    49  	}()
    50  	<-startCh
    51  }
    52  
    53  func (d *deviceWrapper) stop() error {
    54  	return <-d.stopCh
    55  }
    56  
    57  func (d *deviceWrapper) popClone() *libkb.TestContext {
    58  	if len(d.clones) == 0 {
    59  		panic("ran out of cloned environments")
    60  	}
    61  	ret := d.clones[0]
    62  	// Hold a reference to this clone for cleanup
    63  	d.usedClones = append(d.usedClones, ret)
    64  	d.clones = d.clones[1:]
    65  	return ret
    66  }
    67  
    68  func (d *deviceWrapper) loadEncryptionKIDs() (devices []keybase1.KID, backups []backupKey) {
    69  	keyMap := make(map[keybase1.KID]keybase1.PublicKey)
    70  
    71  	t := d.tctx.T
    72  	require.NotNil(t, d.userClient)
    73  	require.NotNil(t, d.userClient.Cli)
    74  	keys, err := d.userClient.LoadMyPublicKeys(context.TODO(), 0)
    75  	require.NoError(t, err)
    76  	for _, key := range keys {
    77  		keyMap[key.KID] = key
    78  	}
    79  
    80  	for _, key := range keys {
    81  		if key.IsSibkey {
    82  			continue
    83  		}
    84  		parent, found := keyMap[keybase1.KID(key.ParentID)]
    85  		if !found {
    86  			continue
    87  		}
    88  
    89  		switch parent.DeviceType {
    90  		case keybase1.DeviceTypeV2_PAPER:
    91  			backups = append(backups, backupKey{KID: key.KID, deviceID: parent.DeviceID})
    92  		case keybase1.DeviceTypeV2_DESKTOP:
    93  			devices = append(devices, key.KID)
    94  		default:
    95  		}
    96  	}
    97  	return devices, backups
    98  }
    99  
   100  func (rkt *rekeyTester) getFakeTLF() *fakeTLF {
   101  	if rkt.fakeTLF == nil {
   102  		rkt.fakeTLF = newFakeTLF()
   103  	}
   104  	return rkt.fakeTLF
   105  }
   106  
   107  func (tlf *fakeTLF) nextRevision() int {
   108  	tlf.revision++
   109  	return tlf.revision
   110  }
   111  
   112  type rekeyTester struct {
   113  	t          libkb.TestingTB
   114  	log        logger.Logger
   115  	devices    []*deviceWrapper
   116  	fakeClock  clockwork.FakeClock
   117  	backupKeys []backupKey
   118  	fakeTLF    *fakeTLF
   119  	username   string
   120  }
   121  
   122  func newRekeyTester(t libkb.TestingTB) *rekeyTester {
   123  	return &rekeyTester{
   124  		t: t,
   125  	}
   126  }
   127  
   128  func (rkt *rekeyTester) setup(nm string) *deviceWrapper {
   129  	rkt.fakeClock = clockwork.NewFakeClockAt(time.Now())
   130  	newDevice := rkt.setupDevice(nm)
   131  	rkt.log = newDevice.tctx.G.Log
   132  	return newDevice
   133  }
   134  
   135  func (rkt *rekeyTester) setupDevice(nm string) *deviceWrapper {
   136  	tctx := setupTest(rkt.t, nm)
   137  	tctx.G.SetClock(rkt.fakeClock)
   138  	tctx.G.Env.Test.UseTimeClockForNISTs = true
   139  	ret := &deviceWrapper{tctx: tctx}
   140  	rkt.devices = append(rkt.devices, ret)
   141  	return ret
   142  }
   143  
   144  func (rkt *rekeyTester) primaryDevice() *deviceWrapper {
   145  	return rkt.devices[0]
   146  }
   147  
   148  func (rkt *rekeyTester) primaryContext() *libkb.GlobalContext {
   149  	return rkt.primaryDevice().tctx.G
   150  }
   151  
   152  func (rkt *rekeyTester) cleanup() {
   153  	for _, od := range rkt.devices {
   154  		od.tctx.Cleanup()
   155  		if od.service != nil {
   156  			od.service.Stop(0)
   157  			err := od.stop()
   158  			require.NoError(od.tctx.T, err)
   159  		}
   160  		for _, cl := range od.clones {
   161  			cl.Cleanup()
   162  		}
   163  		for _, cl := range od.usedClones {
   164  			cl.Cleanup()
   165  		}
   166  	}
   167  }
   168  
   169  type testRekeyUI struct {
   170  	sessionID int
   171  	refreshes chan keybase1.RefreshArg
   172  	events    chan keybase1.RekeyEvent
   173  }
   174  
   175  func (ui *testRekeyUI) DelegateRekeyUI(_ context.Context) (int, error) {
   176  	ui.sessionID++
   177  	ret := ui.sessionID
   178  	return ret, nil
   179  }
   180  
   181  func (ui *testRekeyUI) Refresh(_ context.Context, arg keybase1.RefreshArg) error {
   182  	ui.refreshes <- arg
   183  	return nil
   184  }
   185  
   186  func (ui *testRekeyUI) RekeySendEvent(_ context.Context, arg keybase1.RekeySendEventArg) error {
   187  	ui.events <- arg.Event
   188  	return nil
   189  }
   190  
   191  func newTestRekeyUI() *testRekeyUI {
   192  	return &testRekeyUI{
   193  		sessionID: 0,
   194  		refreshes: make(chan keybase1.RefreshArg, 1000),
   195  		events:    make(chan keybase1.RekeyEvent, 1000),
   196  	}
   197  }
   198  
   199  func (rkt *rekeyTester) loadEncryptionKIDs() (devices []keybase1.KID, backups []backupKey) {
   200  	return rkt.primaryDevice().loadEncryptionKIDs()
   201  }
   202  
   203  func (rkt *rekeyTester) signupUser(dw *deviceWrapper) {
   204  	userInfo := randomUser("rekey")
   205  	tctx := dw.popClone()
   206  	g := tctx.G
   207  	signupUI := signupUI{
   208  		info:         userInfo,
   209  		Contextified: libkb.NewContextified(g),
   210  	}
   211  	g.SetUI(&signupUI)
   212  	signup := client.NewCmdSignupRunner(g)
   213  	signup.SetTest()
   214  	if err := signup.Run(); err != nil {
   215  		rkt.t.Fatal(err)
   216  	}
   217  	rkt.t.Logf("signed up %s", userInfo.username)
   218  	rkt.username = userInfo.username
   219  	var backupKey backupKey
   220  	devices, backups := rkt.loadEncryptionKIDs()
   221  	if len(devices) != 1 {
   222  		rkt.t.Fatalf("Expected 1 device back; got %d", len(devices))
   223  	}
   224  	if len(backups) != 1 {
   225  		rkt.t.Fatalf("Expected 1 backup back; got %d", len(backups))
   226  	}
   227  	dw.deviceKey.KID = devices[0]
   228  	backupKey = backups[0]
   229  	backupKey.secret = signupUI.info.displayedPaperKey
   230  	rkt.backupKeys = append(rkt.backupKeys, backupKey)
   231  }
   232  
   233  func (rkt *rekeyTester) startUIsAndClients(dw *deviceWrapper) {
   234  	ui := newTestRekeyUI()
   235  	dw.rekeyUI = ui
   236  	tctx := dw.popClone()
   237  	g := tctx.G
   238  
   239  	launch := func() error {
   240  		cli, xp, err := client.GetRPCClientWithContext(g)
   241  		if err != nil {
   242  			return err
   243  		}
   244  		srv := rpc.NewServer(xp, nil)
   245  		if err = srv.Register(keybase1.RekeyUIProtocol(ui)); err != nil {
   246  			return err
   247  		}
   248  		ncli := keybase1.DelegateUiCtlClient{Cli: cli}
   249  		if err = ncli.RegisterRekeyUI(context.TODO()); err != nil {
   250  			return err
   251  		}
   252  		dw.rekeyClient = keybase1.RekeyClient{Cli: cli}
   253  		dw.userClient = keybase1.UserClient{Cli: cli}
   254  		dw.gregorClient = keybase1.GregorClient{Cli: cli}
   255  		dw.accountClient = keybase1.AccountClient{Cli: cli}
   256  		return nil
   257  	}
   258  
   259  	if err := launch(); err != nil {
   260  		rkt.t.Fatalf("Failed to launch rekey UI: %s", err)
   261  	}
   262  }
   263  
   264  func (rkt *rekeyTester) confirmNoRekeyUIActivity(dw *deviceWrapper, hours int, force bool) {
   265  	assertNoActivity := func(hour int) {
   266  		for {
   267  			select {
   268  			case ev := <-dw.rekeyUI.events:
   269  				rkt.log.Debug("Hour %d: got rekey event: %+v", hour, ev)
   270  			case <-dw.rekeyUI.refreshes:
   271  				rkt.t.Fatalf("Didn't expect any rekeys; got one at hour %d\n", hour)
   272  			default:
   273  				return
   274  			}
   275  		}
   276  	}
   277  
   278  	for i := 0; i < hours; i++ {
   279  		assertNoActivity(i)
   280  		rkt.fakeClock.Advance(time.Hour)
   281  	}
   282  	err := dw.rekeyClient.RekeySync(context.TODO(), keybase1.RekeySyncArg{SessionID: 0, Force: force})
   283  	if err != nil {
   284  		rkt.t.Errorf("Error syncing rekey: %s", err)
   285  	}
   286  	assertNoActivity(hours + 1)
   287  }
   288  
   289  func (rkt *rekeyTester) makeFullyKeyedHomeTLF() {
   290  	kids := []keybase1.KID{}
   291  	for _, dev := range rkt.devices {
   292  		kids = append(kids, dev.deviceKey.KID)
   293  	}
   294  	for _, bkp := range rkt.backupKeys {
   295  		kids = append(kids, bkp.KID)
   296  	}
   297  	rkt.changeKeysOnHomeTLF(kids)
   298  }
   299  
   300  func (rkt *rekeyTester) changeKeysOnHomeTLF(kids []keybase1.KID) {
   301  
   302  	rkt.log.Debug("+ changeKeysOnHomeTLF(%v)", kids)
   303  	defer rkt.log.Debug("- changeKeysOnHomeTLF")
   304  
   305  	var kidStrings []string
   306  
   307  	for _, kid := range kids {
   308  		kidStrings = append(kidStrings, string(kid))
   309  	}
   310  
   311  	// Use the global context from the service for making API calls
   312  	// to the API server.
   313  	g := rkt.primaryContext()
   314  	fakeTLF := rkt.getFakeTLF()
   315  	mctx := libkb.NewMetaContextBackground(g)
   316  	apiArg := libkb.APIArg{
   317  		Args: libkb.HTTPArgs{
   318  			"tlfid":          libkb.S{Val: string(fakeTLF.id)},
   319  			"kids":           libkb.S{Val: strings.Join(kidStrings, ",")},
   320  			"folderRevision": libkb.I{Val: fakeTLF.nextRevision()},
   321  		},
   322  		Endpoint:    "test/fake_home_tlf",
   323  		SessionType: libkb.APISessionTypeREQUIRED,
   324  	}
   325  	_, err := g.API.Post(mctx, apiArg)
   326  	if err != nil {
   327  		rkt.t.Fatalf("Failed to post fake TLF: %s", err)
   328  	}
   329  }
   330  
   331  func (rkt *rekeyTester) bumpTLF(kid keybase1.KID) {
   332  
   333  	rkt.log.Debug("+ bumpTLF(%s)", kid)
   334  	defer rkt.log.Debug("- bumpTLF")
   335  
   336  	// Use the global context from the service for making API calls
   337  	// to the API server.
   338  	g := rkt.primaryContext()
   339  
   340  	apiArg := libkb.APIArg{
   341  		Args: libkb.HTTPArgs{
   342  			"kid": libkb.S{Val: string(kid)},
   343  		},
   344  		Endpoint:    "kbfs/bump_rekey",
   345  		SessionType: libkb.APISessionTypeREQUIRED,
   346  	}
   347  
   348  	mctx := libkb.NewMetaContextBackground(g)
   349  	_, err := g.API.Post(mctx, apiArg)
   350  	if err != nil {
   351  		rkt.t.Fatalf("Failed to bump rekey to front of line: %s", err)
   352  	}
   353  }
   354  
   355  func (rkt *rekeyTester) kickRekeyd() {
   356  
   357  	// Use the global context from the service for making API calls
   358  	// to the API server.
   359  	g := rkt.primaryContext()
   360  
   361  	apiArg := libkb.APIArg{
   362  		Endpoint:    "test/accelerate_rekeyd",
   363  		Args:        libkb.HTTPArgs{},
   364  		SessionType: libkb.APISessionTypeREQUIRED,
   365  	}
   366  
   367  	mctx := libkb.NewMetaContextBackground(g)
   368  	_, err := g.API.Post(mctx, apiArg)
   369  	if err != nil {
   370  		rkt.t.Errorf("Failed to accelerate rekeyd: %s", err)
   371  	}
   372  }
   373  
   374  func (rkt *rekeyTester) assertRekeyWindowPushed(dw *deviceWrapper) {
   375  	rkt.log.Debug("+ assertRekeyWindowPushed")
   376  	select {
   377  	case <-dw.rekeyUI.refreshes:
   378  	case <-time.After(30 * time.Second):
   379  		rkt.t.Fatalf("no gregor came in after 30s; something is broken")
   380  	}
   381  	rkt.log.Debug("- assertRekeyWindowPushed")
   382  }
   383  
   384  func (rkt *rekeyTester) consumeAllRekeyRefreshes(dw *deviceWrapper) int {
   385  	i := 0
   386  	rkt.log.Debug("consumeAllRekeyRefreshes")
   387  	for {
   388  		rkt.log.Debug("consumeAllRekeyRefreshes iter %d", i)
   389  		select {
   390  		case <-dw.rekeyUI.refreshes:
   391  			i++
   392  		default:
   393  			return i
   394  		}
   395  	}
   396  }
   397  
   398  func (rkt *rekeyTester) clearAllEvents(dw *deviceWrapper) {
   399  	loop := true
   400  	rkt.log.Debug("+ clearing all events")
   401  	for loop {
   402  		select {
   403  		case ev := <-dw.rekeyUI.events:
   404  			rkt.log.Debug("| Got rekey event: %+v", ev)
   405  		case r := <-dw.rekeyUI.refreshes:
   406  			rkt.log.Debug("| Got refresh: %+v", r)
   407  		default:
   408  			loop = false
   409  		}
   410  	}
   411  	rkt.log.Debug("- cleared all events")
   412  }
   413  
   414  func (rkt *rekeyTester) snoozeRekeyWindow(dw *deviceWrapper) {
   415  	rkt.log.Debug("+ -------- snoozeRekeyWindow ---------")
   416  	defer rkt.log.Debug("- -------- snoozeRekeyWindow ---------")
   417  
   418  	_, err := dw.rekeyClient.RekeyStatusFinish(context.TODO(), 0)
   419  	if err != nil {
   420  		rkt.t.Fatalf("Failed to finish rekey: %s\n", err)
   421  	}
   422  
   423  	// There might be a few stragglers --- that's OK, just clear
   424  	// them out, but no more once we advance the clock!
   425  	err = dw.rekeyClient.RekeySync(context.TODO(), keybase1.RekeySyncArg{SessionID: 0, Force: false})
   426  	if err != nil {
   427  		rkt.t.Fatalf("Failed to sync: %s", err)
   428  	}
   429  	rkt.clearAllEvents(dw)
   430  
   431  	// Our snooze should be 23 hours long, and should be resistant
   432  	// to interrupts.
   433  	rkt.log.Debug("+ confirming no rekey activity (1)")
   434  	rkt.confirmNoRekeyUIActivity(dw, 14, false)
   435  	rkt.log.Debug("- confirmed / disconfirmed")
   436  }
   437  
   438  func (rkt *rekeyTester) confirmSnoozeContiues(dw *deviceWrapper) {
   439  
   440  	rkt.log.Debug("+ -------- confirmSnoozeContiues ---------")
   441  	defer rkt.log.Debug("- -------- confirmSnoozeContiues ---------")
   442  
   443  	rkt.log.Debug("+ confirming no rekey activity (2)")
   444  	rkt.confirmNoRekeyUIActivity(dw, 9, false)
   445  	rkt.log.Debug("- confirmed / disconfirmed")
   446  }
   447  
   448  func (rkt *rekeyTester) confirmRekeyWakesUp(dw *deviceWrapper) {
   449  
   450  	rkt.log.Debug("+ -------- confirmRekeyWakesUp ---------")
   451  	defer rkt.log.Debug("- -------- confirmRekeyWakesUp ---------")
   452  
   453  	// In 2 more hours, we should get rereminded
   454  	rkt.fakeClock.Advance(2 * time.Hour)
   455  
   456  	// Now sync so that we're sure we get a full run through the loop.
   457  	err := dw.rekeyClient.RekeySync(context.TODO(), keybase1.RekeySyncArg{SessionID: 0, Force: false})
   458  	if err != nil {
   459  		rkt.t.Fatalf("Error syncing rekey: %s", err)
   460  	}
   461  
   462  	if numRefreshes := rkt.consumeAllRekeyRefreshes(dw); numRefreshes == 0 {
   463  		rkt.t.Fatal("snoozed rekey window never came back")
   464  	} else {
   465  		rkt.log.Debug("Got %d refreshes", numRefreshes)
   466  	}
   467  
   468  	rkt.clearAllEvents(dw)
   469  }
   470  
   471  type rekeyBackupKeyUI struct {
   472  	baseNullUI
   473  	secret string
   474  }
   475  
   476  var _ libkb.LoginUI = (*rekeyBackupKeyUI)(nil)
   477  
   478  func (u *rekeyBackupKeyUI) DisplayPaperKeyPhrase(_ context.Context, arg keybase1.DisplayPaperKeyPhraseArg) error {
   479  	u.secret = arg.Phrase
   480  	return nil
   481  }
   482  func (u *rekeyBackupKeyUI) DisplayPrimaryPaperKey(context.Context, keybase1.DisplayPrimaryPaperKeyArg) error {
   483  	return nil
   484  }
   485  func (u *rekeyBackupKeyUI) PromptRevokePaperKeys(context.Context, keybase1.PromptRevokePaperKeysArg) (bool, error) {
   486  	return false, nil
   487  }
   488  func (u *rekeyBackupKeyUI) GetEmailOrUsername(context.Context, int) (string, error) {
   489  	return "", nil
   490  }
   491  
   492  func (u *rekeyBackupKeyUI) GetLoginUI() libkb.LoginUI {
   493  	return u
   494  }
   495  
   496  func (u *rekeyBackupKeyUI) GetSecretUI() libkb.SecretUI {
   497  	return u
   498  }
   499  
   500  func (u *rekeyBackupKeyUI) GetPassphrase(p keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (res keybase1.GetPassphraseRes, err error) {
   501  	return res, err
   502  }
   503  
   504  func (u *rekeyBackupKeyUI) PromptResetAccount(_ context.Context, arg keybase1.PromptResetAccountArg) (keybase1.ResetPromptResponse, error) {
   505  	return keybase1.ResetPromptResponse_NOTHING, nil
   506  }
   507  
   508  func (u *rekeyBackupKeyUI) DisplayResetProgress(_ context.Context, arg keybase1.DisplayResetProgressArg) error {
   509  	return nil
   510  }
   511  
   512  func (u *rekeyBackupKeyUI) ExplainDeviceRecovery(_ context.Context, arg keybase1.ExplainDeviceRecoveryArg) error {
   513  	return nil
   514  }
   515  
   516  func (u *rekeyBackupKeyUI) PromptPassphraseRecovery(_ context.Context, arg keybase1.PromptPassphraseRecoveryArg) (bool, error) {
   517  	return false, nil
   518  }
   519  
   520  func (u *rekeyBackupKeyUI) ChooseDeviceToRecoverWith(_ context.Context, arg keybase1.ChooseDeviceToRecoverWithArg) (keybase1.DeviceID, error) {
   521  	return "", nil
   522  }
   523  
   524  func (u *rekeyBackupKeyUI) DisplayResetMessage(_ context.Context, arg keybase1.DisplayResetMessageArg) error {
   525  	return nil
   526  }
   527  
   528  func (rkt *rekeyTester) findNewBackupKey(newList []backupKey) (ret backupKey, found bool) {
   529  	for _, newBackup := range newList {
   530  		tmpFound := false
   531  		for _, existingBackup := range rkt.backupKeys {
   532  			if newBackup.KID.Equal(existingBackup.KID) {
   533  				tmpFound = true
   534  				break
   535  			}
   536  		}
   537  		if !tmpFound {
   538  			return newBackup, true
   539  		}
   540  	}
   541  	return ret, false
   542  }
   543  
   544  func (rkt *rekeyTester) findNewDeviceKey(newList []keybase1.KID) (ret keybase1.KID, found bool) {
   545  	for _, newKID := range newList {
   546  		tmpFound := false
   547  		for _, device := range rkt.devices {
   548  			if !device.KID().IsNil() && newKID.Equal(device.KID()) {
   549  				tmpFound = true
   550  				break
   551  			}
   552  		}
   553  		if !tmpFound {
   554  			return newKID, true
   555  		}
   556  	}
   557  	return ret, false
   558  }
   559  
   560  func (rkt *rekeyTester) generateNewBackupKey(dw *deviceWrapper) {
   561  	rkt.log.Debug("+ ----- generateNewBackupKey ---------")
   562  	defer rkt.log.Debug("- ----- generateNewBackupKey ---------")
   563  	tctx := dw.popClone()
   564  	g := tctx.G
   565  	ui := rekeyBackupKeyUI{}
   566  	g.SetUI(&ui)
   567  	paperGen := client.NewCmdPaperKeyRunner(g)
   568  	if err := paperGen.Run(); err != nil {
   569  		rkt.t.Fatal(err)
   570  	}
   571  	_, backups := rkt.loadEncryptionKIDs()
   572  	backupKey, found := rkt.findNewBackupKey(backups)
   573  	if !found {
   574  		rkt.t.Fatalf("didn't find a new backup key!")
   575  	}
   576  	backupKey.secret = ui.secret
   577  	g.Log.Debug("New backup key is: %s", backupKey.KID)
   578  
   579  	rkt.backupKeys = append(rkt.backupKeys, backupKey)
   580  	rkt.bumpTLF(backupKey.KID)
   581  	rkt.kickRekeyd()
   582  }
   583  
   584  func (rkt *rekeyTester) expectAlreadyKeyedNoop(dw *deviceWrapper) {
   585  
   586  	rkt.log.Debug("+ ----------- expectAlreadyKeyedNoop ------------")
   587  	defer rkt.log.Debug("- ----------- expectAlreadyKeyedNoop ------------")
   588  
   589  	var done bool
   590  	for !done {
   591  		select {
   592  		case ev := <-dw.rekeyUI.events:
   593  			switch ev.EventType {
   594  			case keybase1.RekeyEventType_CURRENT_DEVICE_CAN_REKEY:
   595  				done = true
   596  			case keybase1.RekeyEventType_NO_GREGOR_MESSAGES, keybase1.RekeyEventType_NO_PROBLEMS:
   597  				rkt.log.Debug("| In waiting for 'CURRENT_DEVICE_CAN_REKEY': %+v", ev)
   598  			default:
   599  				rkt.t.Fatalf("Got wrong event type: %+v", ev)
   600  				done = true
   601  			}
   602  		case <-time.After(30 * time.Second):
   603  			rkt.t.Fatal("Didn't get an event before 30s timeout")
   604  		}
   605  	}
   606  	rkt.confirmNoRekeyUIActivity(dw, 28, false)
   607  }
   608  
   609  type rekeyProvisionUI struct {
   610  	baseNullUI
   611  	username  string
   612  	backupKey backupKey
   613  }
   614  
   615  var _ libkb.LoginUI = (*rekeyProvisionUI)(nil)
   616  
   617  func (r *rekeyProvisionUI) GetEmailOrUsername(context.Context, int) (string, error) {
   618  	return r.username, nil
   619  }
   620  func (r *rekeyProvisionUI) PromptRevokePaperKeys(context.Context, keybase1.PromptRevokePaperKeysArg) (ret bool, err error) {
   621  	return false, nil
   622  }
   623  func (r *rekeyProvisionUI) DisplayPaperKeyPhrase(context.Context, keybase1.DisplayPaperKeyPhraseArg) error {
   624  	return nil
   625  }
   626  func (r *rekeyProvisionUI) DisplayPrimaryPaperKey(context.Context, keybase1.DisplayPrimaryPaperKeyArg) error {
   627  	return nil
   628  }
   629  func (r *rekeyProvisionUI) ChooseProvisioningMethod(context.Context, keybase1.ChooseProvisioningMethodArg) (ret keybase1.ProvisionMethod, err error) {
   630  	return ret, nil
   631  }
   632  func (r *rekeyProvisionUI) ChooseGPGMethod(context.Context, keybase1.ChooseGPGMethodArg) (ret keybase1.GPGMethod, err error) {
   633  	return ret, nil
   634  }
   635  func (r *rekeyProvisionUI) SwitchToGPGSignOK(context.Context, keybase1.SwitchToGPGSignOKArg) (ret bool, err error) {
   636  	return ret, nil
   637  }
   638  func (r *rekeyProvisionUI) ChooseDeviceType(context.Context, keybase1.ChooseDeviceTypeArg) (ret keybase1.DeviceType, err error) {
   639  	return ret, nil
   640  }
   641  func (r *rekeyProvisionUI) DisplayAndPromptSecret(context.Context, keybase1.DisplayAndPromptSecretArg) (ret keybase1.SecretResponse, err error) {
   642  	return ret, nil
   643  }
   644  func (r *rekeyProvisionUI) DisplaySecretExchanged(context.Context, int) error {
   645  	return nil
   646  }
   647  func (r *rekeyProvisionUI) PromptNewDeviceName(context.Context, keybase1.PromptNewDeviceNameArg) (ret string, err error) {
   648  	return "taco tsar", nil
   649  }
   650  func (r *rekeyProvisionUI) ProvisioneeSuccess(context.Context, keybase1.ProvisioneeSuccessArg) error {
   651  	return nil
   652  }
   653  func (r *rekeyProvisionUI) ProvisionerSuccess(context.Context, keybase1.ProvisionerSuccessArg) error {
   654  	return nil
   655  }
   656  func (r *rekeyProvisionUI) ChooseDevice(context.Context, keybase1.ChooseDeviceArg) (ret keybase1.DeviceID, err error) {
   657  	return r.backupKey.deviceID, nil
   658  }
   659  func (r *rekeyProvisionUI) GetPassphrase(context.Context, keybase1.GetPassphraseArg) (ret keybase1.GetPassphraseRes, err error) {
   660  	ret.Passphrase = r.backupKey.secret
   661  	return ret, nil
   662  }
   663  func (r *rekeyProvisionUI) PromptResetAccount(_ context.Context, arg keybase1.PromptResetAccountArg) (keybase1.ResetPromptResponse, error) {
   664  	return keybase1.ResetPromptResponse_NOTHING, nil
   665  }
   666  func (r *rekeyProvisionUI) DisplayResetProgress(_ context.Context, arg keybase1.DisplayResetProgressArg) error {
   667  	return nil
   668  }
   669  func (r *rekeyProvisionUI) ExplainDeviceRecovery(_ context.Context, arg keybase1.ExplainDeviceRecoveryArg) error {
   670  	return nil
   671  }
   672  func (r *rekeyProvisionUI) PromptPassphraseRecovery(_ context.Context, arg keybase1.PromptPassphraseRecoveryArg) (bool, error) {
   673  	return false, nil
   674  }
   675  func (r *rekeyProvisionUI) ChooseDeviceToRecoverWith(_ context.Context, arg keybase1.ChooseDeviceToRecoverWithArg) (keybase1.DeviceID, error) {
   676  	return "", nil
   677  }
   678  func (r *rekeyProvisionUI) DisplayResetMessage(_ context.Context, arg keybase1.DisplayResetMessageArg) error {
   679  	return nil
   680  }
   681  
   682  func (rkt *rekeyTester) provisionNewDevice() *deviceWrapper {
   683  	rkt.log.Debug("+ ---------- provisionNewDevice ----------")
   684  	defer rkt.log.Debug("- ---------- provisionNewDevice ----------")
   685  
   686  	dev2 := rkt.setupDevice("rkd2")
   687  	dev2.start(1)
   688  	tctx := dev2.popClone()
   689  	g := tctx.G
   690  	var loginClient keybase1.LoginClient
   691  	ui := &rekeyProvisionUI{username: rkt.username, backupKey: rkt.backupKeys[0]}
   692  	rekeyUI := newTestRekeyUI()
   693  	dev2.rekeyUI = rekeyUI
   694  
   695  	launch := func() error {
   696  		cli, xp, err := client.GetRPCClientWithContext(g)
   697  		if err != nil {
   698  			return err
   699  		}
   700  		srv := rpc.NewServer(xp, nil)
   701  		protocols := []rpc.Protocol{
   702  			keybase1.LoginUiProtocol(ui),
   703  			keybase1.SecretUiProtocol(ui),
   704  			keybase1.ProvisionUiProtocol(ui),
   705  			keybase1.RekeyUIProtocol(rekeyUI),
   706  		}
   707  		for _, prot := range protocols {
   708  			if err = srv.Register(prot); err != nil {
   709  				return err
   710  			}
   711  		}
   712  		ncli := keybase1.DelegateUiCtlClient{Cli: cli}
   713  		if err = ncli.RegisterRekeyUI(context.TODO()); err != nil {
   714  			return err
   715  		}
   716  		loginClient = keybase1.LoginClient{Cli: cli}
   717  		_ = loginClient
   718  		dev2.rekeyClient = keybase1.RekeyClient{Cli: cli}
   719  		return nil
   720  	}
   721  
   722  	if err := launch(); err != nil {
   723  		rkt.t.Fatalf("Failed to login rekey UI: %s", err)
   724  	}
   725  	cmd := client.NewCmdLoginRunner(g)
   726  	if err := cmd.Run(); err != nil {
   727  		rkt.t.Fatalf("Login failed: %s\n", err)
   728  	}
   729  
   730  	var found bool
   731  	devices, _ := rkt.loadEncryptionKIDs()
   732  	dev2.deviceKey.KID, found = rkt.findNewDeviceKey(devices)
   733  	if !found {
   734  		rkt.t.Fatalf("Failed to failed device kid for new device")
   735  	}
   736  	rkt.log.Debug("new device KID: %s", dev2.deviceKey.KID)
   737  
   738  	// Clear the paper key because we don't want it hanging around to
   739  	// solve the problems we're trying to induce.
   740  	m := libkb.NewMetaContextBackground(dev2.tctx.G)
   741  	m.ActiveDevice().ClearProvisioningKey(m)
   742  
   743  	return dev2
   744  }
   745  
   746  func (rkt *rekeyTester) bumpTLFAndAssertRekeyWindowPushed(dw *deviceWrapper) {
   747  
   748  	rkt.log.Debug("+ -------- bumpTLFAndAssertRekeyWindowPushed ------------")
   749  	defer rkt.log.Debug("- -------- bumpTLFAndAssertRekeyWindowPushed ------------")
   750  
   751  	// We shouldn't get a rekey UI until we bump the TLF forward (and therefore get a gregor
   752  	// message). We're cheating here and mixing fast-forwardable time (via fake clock),
   753  	// and real time (on the API server).
   754  	//
   755  	// NOTE(maxtaco): 2016-12-08
   756  	//
   757  	// Disable the following since it was breaking CI on a windows node with a clock
   758  	// skew:
   759  	//
   760  	// rkt.confirmNoRekeyUIActivity(dw, 3, false)
   761  
   762  	// But now we're bumping this device forward in the queue. This should trigger the gregor
   763  	// notification and also the push of the real rekey window.
   764  	rkt.bumpTLF(dw.deviceKey.KID)
   765  
   766  	rkt.kickRekeyd()
   767  	rkt.assertRekeyWindowPushed(dw)
   768  }
   769  
   770  func (rkt *rekeyTester) confirmRekeyDismiss(dw *deviceWrapper) {
   771  	err := dw.rekeyClient.RekeySync(context.TODO(), keybase1.RekeySyncArg{SessionID: 0, Force: false})
   772  	if err != nil {
   773  		rkt.t.Fatalf("failed to sync: %s", err)
   774  	}
   775  	found := false
   776  	done := false
   777  	for !found && !done {
   778  		select {
   779  		case rf := <-dw.rekeyUI.refreshes:
   780  			n := len(rf.ProblemSetDevices.ProblemSet.Tlfs)
   781  			if n == 0 {
   782  				found = true
   783  			} else {
   784  				rkt.log.Debug("found a refresh with tlfs=%d: %v", n, rf)
   785  			}
   786  		default:
   787  			done = true
   788  		}
   789  	}
   790  	if !found {
   791  		rkt.t.Fatalf("failed to find a refresh UI dismissal")
   792  	}
   793  }
   794  
   795  func (rkt *rekeyTester) isGregorStateEmpty() (ret bool) {
   796  	rkt.log.Debug("+ isGregorStateEmpty")
   797  	defer func() { rkt.log.Debug(fmt.Sprintf("- isGregorStateEmpty -> %v", ret)) }()
   798  	state, err := rkt.primaryDevice().gregorClient.GetState(context.TODO())
   799  	if err != nil {
   800  		rkt.log.Warning("failed to query gregor state: %s", err)
   801  		return false
   802  	}
   803  
   804  	for _, item := range state.Items_ {
   805  		if item.Item_ != nil && string(item.Item_.Category_) == service.TLFRekeyGregorCategory {
   806  			rkt.log.Debug("Found an unexpected Gregor Item: %v", item)
   807  			return false
   808  		}
   809  	}
   810  	return true
   811  }
   812  
   813  func (rkt *rekeyTester) confirmGregorStateIsClean() {
   814  	rkt.log.Debug("+ confirmGregorStateIsClean")
   815  	defer rkt.log.Debug("- confirmGregorStateIsClean")
   816  
   817  	start := time.Now()
   818  	last := start.Add(3 * time.Second * libkb.CITimeMultiplier(rkt.primaryContext()))
   819  	i := 0
   820  	var delay time.Duration
   821  
   822  	for time.Now().Before(last) {
   823  		if rkt.isGregorStateEmpty() {
   824  			return
   825  		}
   826  		if delay < time.Second {
   827  			delay += 10 * time.Millisecond
   828  		}
   829  		rkt.log.Debug("came back dirty; trying again in %s (attempt %d)", delay, i)
   830  		time.Sleep(delay)
   831  		i++
   832  	}
   833  	rkt.t.Fatal("Failed to find a clean gregor state")
   834  }
   835  
   836  func (rkt *rekeyTester) fullyRekeyAndAssertCleared(dw *deviceWrapper) {
   837  	rkt.log.Debug("+ fullyRekeyAndAssertCleared")
   838  	defer rkt.log.Debug("- fullyRekeyAndAssertCleared")
   839  
   840  	rkt.makeFullyKeyedHomeTLF()
   841  	rkt.confirmNoRekeyUIActivity(dw, 14, false)
   842  	rkt.confirmGregorStateIsClean()
   843  	rkt.confirmNoRekeyUIActivity(dw, 14, false)
   844  }
   845  
   846  func testRekeyOnce(t libkb.TestingTB) {
   847  	rkt := newRekeyTester(t)
   848  	primaryDevice := rkt.setup("rekey")
   849  	defer rkt.cleanup()
   850  
   851  	// 0. Start up the primary device; Set numClones=4, meaning we're going to clone
   852  	// the context 4 times (one for each client that will connect).
   853  	primaryDevice.start(4)
   854  	rkt.startUIsAndClients(primaryDevice)
   855  
   856  	// 1. Sign up a fake user with a device and paper key
   857  	rkt.signupUser(primaryDevice)
   858  
   859  	// 2. Make a private home TLF keyed only for the device key (not the paper)
   860  	rkt.makeFullyKeyedHomeTLF()
   861  
   862  	// 3. Assert no rekey activity
   863  	rkt.confirmNoRekeyUIActivity(primaryDevice, 28, false)
   864  
   865  	// 4. Now delegate to a new paper key
   866  	rkt.generateNewBackupKey(primaryDevice)
   867  
   868  	// 5. Now assert that we weren't notified of something being up
   869  	// because our device is already properly keyed. And then expect
   870  	// no rekey activity thereafter
   871  	rkt.expectAlreadyKeyedNoop(primaryDevice)
   872  
   873  	// 5.5 Now rekey fully and make sure that the gregor state is clean.
   874  	rkt.fullyRekeyAndAssertCleared(primaryDevice)
   875  
   876  	// 6. Provision a new device.
   877  	secondaryDevice := rkt.provisionNewDevice()
   878  
   879  	// 7. wait for an incoming gregor notification for the new TLF,
   880  	// since it's in a broken rekey state.
   881  	rkt.bumpTLFAndAssertRekeyWindowPushed(secondaryDevice)
   882  
   883  	// 8. But nothing should happen to the existing (primary) device.
   884  	rkt.expectAlreadyKeyedNoop(primaryDevice)
   885  
   886  	// 9. Dismiss the window and assert it doesn't show up again for
   887  	// another 24 hours.
   888  	rkt.snoozeRekeyWindow(secondaryDevice)
   889  
   890  	// 10. Generate a new backup key, but make sure the snooze still holds.
   891  	// For some reason, this doesn't work on the secondary device.
   892  	// Merg. But shouldn't really matter.
   893  	rkt.generateNewBackupKey(primaryDevice)
   894  
   895  	// 11. Confirm that the snooze still continues after the above new key.
   896  	rkt.confirmSnoozeContiues(secondaryDevice)
   897  
   898  	// 12. After about 2 hours of sleeping, we should wake up since
   899  	// our 24 hours is over.
   900  	rkt.confirmRekeyWakesUp(secondaryDevice)
   901  
   902  	// 13. no go about resolving all of the broken TLFs
   903  	rkt.makeFullyKeyedHomeTLF()
   904  
   905  	// 14. Assert that the rekey UI on the secondary device is dismissed.
   906  	rkt.confirmRekeyDismiss(secondaryDevice)
   907  
   908  	// 15. Now confirm that nothing is doing...
   909  	rkt.confirmNoRekeyUIActivity(secondaryDevice, 30, true)
   910  
   911  	// 16. Confirm that gregor is clean
   912  	rkt.confirmGregorStateIsClean()
   913  }
   914  
   915  func TestRekey(t *testing.T) {
   916  	retryFlakeyTestOnlyUseIfPermitted(t, 5, testRekeyOnce)
   917  }