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

     1  package engine
     2  
     3  import (
     4  	"crypto/rand"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/keybase/go-crypto/ed25519"
    13  
    14  	libkb "github.com/keybase/client/go/libkb"
    15  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    16  	clockwork "github.com/keybase/clockwork"
    17  	jsonw "github.com/keybase/go-jsonw"
    18  	require "github.com/stretchr/testify/require"
    19  	"golang.org/x/net/context"
    20  )
    21  
    22  func importTrackingLink(t *testing.T, g *libkb.GlobalContext) *libkb.TrackChainLink {
    23  	cl, err := libkb.ImportLinkFromServer(libkb.NewMetaContextBackground(g), nil, []byte(trackingServerReply), trackingUID)
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	gl := libkb.GenericChainLink{ChainLink: cl}
    28  	tcl, err := libkb.ParseTrackChainLink(gl)
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	return tcl
    33  }
    34  
    35  func TestIdentify2WithUIDImportTrackingLink(t *testing.T) {
    36  	tc := libkb.SetupTest(t, "TestIdentify2WithUIDImportTrackingLink", 0)
    37  	defer tc.Cleanup()
    38  	link := importTrackingLink(t, tc.G)
    39  	if link == nil {
    40  		t.Fatalf("link import failed")
    41  	}
    42  }
    43  
    44  type cacheStats struct {
    45  	hit     int
    46  	timeout int
    47  	miss    int
    48  	notime  int
    49  	breaks  int
    50  }
    51  
    52  type identify2testCache map[keybase1.UID](*keybase1.Identify2ResUPK2)
    53  
    54  func (c cacheStats) eq(h, t, m, n, b int) bool {
    55  	return h == c.hit && t == c.timeout && m == c.miss && n == c.notime && b == c.breaks
    56  }
    57  
    58  type Identify2WithUIDTester struct {
    59  	libkb.Contextified
    60  	libkb.BaseServiceType
    61  	sync.Mutex
    62  	finishCh        chan struct{}
    63  	startCh         chan struct{}
    64  	checkStatusHook func(libkb.SigHint, libkb.ProofCheckerMode) libkb.ProofError
    65  	cache           identify2testCache
    66  	slowStats       cacheStats
    67  	fastStats       cacheStats
    68  	now             time.Time
    69  	card            keybase1.UserCard
    70  	userLoads       map[keybase1.UID]int
    71  	noDiskCache     bool
    72  }
    73  
    74  func newIdentify2WithUIDTester(g *libkb.GlobalContext) *Identify2WithUIDTester {
    75  	return &Identify2WithUIDTester{
    76  		Contextified: libkb.NewContextified(g),
    77  		finishCh:     make(chan struct{}),
    78  		startCh:      make(chan struct{}, 1),
    79  		cache:        make(identify2testCache),
    80  		now:          time.Now(),
    81  		userLoads:    make(map[keybase1.UID]int),
    82  	}
    83  }
    84  
    85  func (i *Identify2WithUIDTester) ListProofCheckers(libkb.MetaContext) []string { return nil }
    86  func (i *Identify2WithUIDTester) ListServicesThatAcceptNewProofs(libkb.MetaContext) []string {
    87  	return nil
    88  }
    89  func (i *Identify2WithUIDTester) ListDisplayConfigs(libkb.MetaContext) []keybase1.ServiceDisplayConfig {
    90  	return nil
    91  }
    92  func (i *Identify2WithUIDTester) SuggestionFoldPriority(libkb.MetaContext) int { return 0 }
    93  func (i *Identify2WithUIDTester) Key() string                                  { return i.GetTypeName() }
    94  func (i *Identify2WithUIDTester) CheckProofText(text string, id keybase1.SigID, sig string) error {
    95  	return nil
    96  }
    97  func (i *Identify2WithUIDTester) DisplayName() string  { return "Identify2WithUIDTester" }
    98  func (i *Identify2WithUIDTester) GetPrompt() string    { return "" }
    99  func (i *Identify2WithUIDTester) GetProofType() string { return "" }
   100  func (i *Identify2WithUIDTester) GetTypeName() string  { return "" }
   101  func (i *Identify2WithUIDTester) NormalizeRemoteName(_ libkb.MetaContext, name string) (string, error) {
   102  	return name, nil
   103  }
   104  func (i *Identify2WithUIDTester) NormalizeUsername(name string) (string, error)    { return name, nil }
   105  func (i *Identify2WithUIDTester) PostInstructions(remotename string) *libkb.Markup { return nil }
   106  func (i *Identify2WithUIDTester) RecheckProofPosting(tryNumber int, status keybase1.ProofStatus, remotename string) (*libkb.Markup, error) {
   107  	return nil, nil
   108  }
   109  func (i *Identify2WithUIDTester) ToServiceJSON(remotename string) *jsonw.Wrapper { return nil }
   110  
   111  func (i *Identify2WithUIDTester) MakeProofChecker(_ libkb.RemoteProofChainLink) libkb.ProofChecker {
   112  	return i
   113  }
   114  func (i *Identify2WithUIDTester) GetServiceType(context.Context, string) libkb.ServiceType { return i }
   115  func (i *Identify2WithUIDTester) PickerSubtext() string                                    { return "" }
   116  
   117  func (i *Identify2WithUIDTester) CheckStatus(m libkb.MetaContext, h libkb.SigHint,
   118  	pcm libkb.ProofCheckerMode, _ keybase1.MerkleStoreEntry) (*libkb.SigHint, libkb.ProofError) {
   119  	if i.checkStatusHook != nil {
   120  		return nil, i.checkStatusHook(h, pcm)
   121  	}
   122  	m.Debug("Check status rubber stamp: %+v", h)
   123  	return nil, nil
   124  }
   125  
   126  func (i *Identify2WithUIDTester) GetTorError() libkb.ProofError {
   127  	return nil
   128  }
   129  
   130  func (i *Identify2WithUIDTester) FinishSocialProofCheck(libkb.MetaContext, keybase1.RemoteProof, keybase1.LinkCheckResult) error {
   131  	return nil
   132  }
   133  func (i *Identify2WithUIDTester) Confirm(libkb.MetaContext, *keybase1.IdentifyOutcome) (res keybase1.ConfirmResult, err error) {
   134  	return
   135  }
   136  func (i *Identify2WithUIDTester) FinishWebProofCheck(libkb.MetaContext, keybase1.RemoteProof, keybase1.LinkCheckResult) error {
   137  	return nil
   138  }
   139  func (i *Identify2WithUIDTester) DisplayCryptocurrency(libkb.MetaContext, keybase1.Cryptocurrency) error {
   140  	return nil
   141  }
   142  func (i *Identify2WithUIDTester) DisplayStellarAccount(libkb.MetaContext, keybase1.StellarAccount) error {
   143  	return nil
   144  }
   145  func (i *Identify2WithUIDTester) DisplayKey(libkb.MetaContext, keybase1.IdentifyKey) error {
   146  	return nil
   147  }
   148  func (i *Identify2WithUIDTester) ReportLastTrack(libkb.MetaContext, *keybase1.TrackSummary) error {
   149  	return nil
   150  }
   151  func (i *Identify2WithUIDTester) LaunchNetworkChecks(libkb.MetaContext, *keybase1.Identity, *keybase1.User) error {
   152  	return nil
   153  }
   154  func (i *Identify2WithUIDTester) DisplayTrackStatement(libkb.MetaContext, string) error {
   155  	return nil
   156  }
   157  func (i *Identify2WithUIDTester) ReportTrackToken(libkb.MetaContext, keybase1.TrackToken) (err error) {
   158  	return nil
   159  }
   160  func (i *Identify2WithUIDTester) SetStrict(b bool) error {
   161  	return nil
   162  }
   163  func (i *Identify2WithUIDTester) DisplayUserCard(_ libkb.MetaContext, card keybase1.UserCard) error {
   164  	i.Lock()
   165  	defer i.Unlock()
   166  	i.card = card
   167  	return nil
   168  }
   169  
   170  func (i *Identify2WithUIDTester) DisplayTLFCreateWithInvite(libkb.MetaContext, keybase1.DisplayTLFCreateWithInviteArg) error {
   171  	return nil
   172  }
   173  
   174  func (i *Identify2WithUIDTester) Cancel(libkb.MetaContext) error {
   175  	return nil
   176  }
   177  
   178  func (i *Identify2WithUIDTester) Finish(libkb.MetaContext) error {
   179  	i.finishCh <- struct{}{}
   180  	return nil
   181  }
   182  
   183  func (i *Identify2WithUIDTester) Dismiss(_ libkb.MetaContext, _ string, _ keybase1.DismissReason) error {
   184  	return nil
   185  }
   186  
   187  func (i *Identify2WithUIDTester) Start(libkb.MetaContext, string, keybase1.IdentifyReason, bool) error {
   188  	i.startCh <- struct{}{}
   189  	return nil
   190  }
   191  
   192  func (i *Identify2WithUIDTester) Get(uid keybase1.UID, gctf libkb.GetCheckTimeFunc, gcdf libkb.GetCacheDurationFunc, breaksOK bool) (*keybase1.Identify2ResUPK2, error) {
   193  	i.Lock()
   194  	defer i.Unlock()
   195  	res := i.cache[uid]
   196  	stats := &i.slowStats
   197  
   198  	// Please excuse this horrible hack, but use the `GetCacheDurationFunc` to see if we're dealing
   199  	// with a fast cache duration
   200  	if gcdf(keybase1.Identify2ResUPK2{}) == libkb.Identify2CacheShortTimeout {
   201  		stats = &i.fastStats
   202  	}
   203  
   204  	if res == nil {
   205  		stats.miss++
   206  		return nil, nil
   207  	}
   208  	if gctf != nil {
   209  		then := gctf(*res)
   210  		if then == 0 {
   211  			stats.notime++
   212  			return nil, libkb.TimeoutError{}
   213  		}
   214  		if res.TrackBreaks != nil && !breaksOK {
   215  			stats.breaks++
   216  			return nil, libkb.TrackBrokenError{}
   217  		}
   218  		timeout := gcdf(*res)
   219  		thenTime := keybase1.FromTime(then)
   220  		if i.now.Sub(thenTime) > timeout {
   221  			stats.timeout++
   222  			return nil, libkb.TimeoutError{}
   223  		}
   224  	}
   225  	stats.hit++
   226  	return res, nil
   227  }
   228  
   229  func (i *Identify2WithUIDTester) Insert(up *keybase1.Identify2ResUPK2) error {
   230  	i.Lock()
   231  	defer i.Unlock()
   232  	tmp := *up
   233  	copy := &tmp
   234  	copy.Upk.Uvv.CachedAt = keybase1.ToTime(i.now)
   235  	i.cache[up.Upk.GetUID()] = copy
   236  	return nil
   237  }
   238  func (i *Identify2WithUIDTester) DidFullUserLoad(uid keybase1.UID) {
   239  	i.Lock()
   240  	defer i.Unlock()
   241  	i.userLoads[uid]++
   242  }
   243  func (i *Identify2WithUIDTester) UseDiskCache() bool {
   244  	i.Lock()
   245  	defer i.Unlock()
   246  	return !i.noDiskCache
   247  }
   248  
   249  func (i *Identify2WithUIDTester) Delete(uid keybase1.UID) error {
   250  	i.Lock()
   251  	defer i.Unlock()
   252  	delete(i.cache, uid)
   253  	return nil
   254  }
   255  
   256  func (i *Identify2WithUIDTester) Shutdown() {}
   257  
   258  var _ libkb.Identify2Cacher = (*Identify2WithUIDTester)(nil)
   259  
   260  func identify2MetaContext(tc libkb.TestContext, i libkb.IdentifyUI) libkb.MetaContext {
   261  	return NewMetaContextForTest(tc).WithUIs(libkb.UIs{IdentifyUI: i})
   262  }
   263  
   264  func TestIdentify2WithUIDWithoutTrack(t *testing.T) {
   265  	tc := SetupEngineTest(t, "Identify2WithUIDWithoutTrack")
   266  	defer tc.Cleanup()
   267  	i := newIdentify2WithUIDTester(tc.G)
   268  	tc.G.SetProofServices(i)
   269  	arg := &keybase1.Identify2Arg{
   270  		Uid:              tracyUID,
   271  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   272  	}
   273  	eng := NewIdentify2WithUID(tc.G, arg)
   274  	err := eng.Run(identify2MetaContext(tc, i))
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	<-i.finishCh
   279  }
   280  
   281  func launchWaiter(t *testing.T, ch chan struct{}) func() {
   282  	waitCh := make(chan error)
   283  	go func() {
   284  		select {
   285  		case <-ch:
   286  			waitCh <- nil
   287  		case <-time.After(10 * time.Second):
   288  			waitCh <- errors.New("failed to get a finish after timeout")
   289  		}
   290  	}()
   291  	return func() {
   292  		err := <-waitCh
   293  		if err != nil {
   294  			t.Fatal(err)
   295  		}
   296  	}
   297  }
   298  
   299  func TestIdentify2WithUIDWithTrack(t *testing.T) {
   300  	tc := SetupEngineTest(t, "Identify2WithUIDWithTrack")
   301  	defer tc.Cleanup()
   302  	i := newIdentify2WithUIDTester(tc.G)
   303  	tc.G.SetProofServices(i)
   304  	arg := &keybase1.Identify2Arg{
   305  		Uid:              tracyUID,
   306  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   307  	}
   308  	eng := NewIdentify2WithUID(tc.G, arg)
   309  
   310  	eng.testArgs = &Identify2WithUIDTestArgs{
   311  		noMe: true,
   312  		tcl:  importTrackingLink(t, tc.G),
   313  	}
   314  
   315  	waiter := launchWaiter(t, i.finishCh)
   316  	err := eng.Run(identify2MetaContext(tc, i))
   317  	if err != nil {
   318  		t.Fatal(err)
   319  	}
   320  
   321  	waiter()
   322  }
   323  
   324  func TestIdentify2WithUIDWithTrackAndSuppress(t *testing.T) {
   325  	tc := SetupEngineTest(t, "Identify2WithUIDWithTrackAndSuppress")
   326  	defer tc.Cleanup()
   327  	i := newIdentify2WithUIDTester(tc.G)
   328  	tc.G.SetProofServices(i)
   329  	arg := &keybase1.Identify2Arg{
   330  		Uid:              tracyUID,
   331  		CanSuppressUI:    true,
   332  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   333  	}
   334  	eng := NewIdentify2WithUID(tc.G, arg)
   335  
   336  	eng.testArgs = &Identify2WithUIDTestArgs{
   337  		noMe: true,
   338  		tcl:  importTrackingLink(t, tc.G),
   339  	}
   340  
   341  	err := eng.Run(identify2MetaContext(tc, i))
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  
   346  	select {
   347  	case <-i.startCh:
   348  		t.Fatalf("did not expect the identify to start")
   349  	default:
   350  	}
   351  
   352  	select {
   353  	case <-i.finishCh:
   354  		t.Fatalf("did not expect the identify to end")
   355  	default:
   356  	}
   357  }
   358  
   359  func identify2WithUIDWithBrokenTrackMakeEngine(t *testing.T, arg *keybase1.Identify2Arg) (func(), error) {
   360  	tc := SetupEngineTest(t, "testIdentify2WithUIDWithBrokenTrack")
   361  	defer tc.Cleanup()
   362  	i := newIdentify2WithUIDTester(tc.G)
   363  	tc.G.SetProofServices(i)
   364  	eng := NewIdentify2WithUID(tc.G, arg)
   365  
   366  	eng.testArgs = &Identify2WithUIDTestArgs{
   367  		noMe:  true,
   368  		cache: i,
   369  		tcl:   importTrackingLink(t, tc.G),
   370  	}
   371  	i.checkStatusHook = func(l libkb.SigHint, _ libkb.ProofCheckerMode) libkb.ProofError {
   372  		if strings.Contains(l.GetHumanURL(), "twitter") {
   373  			tc.G.Log.Debug("failing twitter proof %s", l.GetHumanURL())
   374  			return libkb.NewProofError(keybase1.ProofStatus_DELETED, "gone!")
   375  		}
   376  		return nil
   377  	}
   378  	waiter := launchWaiter(t, i.finishCh)
   379  	err := eng.Run(identify2MetaContext(tc, i))
   380  	return waiter, err
   381  }
   382  
   383  func testIdentify2WithUIDWithBrokenTrack(t *testing.T, suppress bool) {
   384  	arg := &keybase1.Identify2Arg{
   385  		Uid:              tracyUID,
   386  		CanSuppressUI:    suppress,
   387  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   388  	}
   389  	waiter, err := identify2WithUIDWithBrokenTrackMakeEngine(t, arg)
   390  
   391  	if err == nil {
   392  		t.Fatal("expected an ID2 error since twitter proof failed")
   393  	}
   394  	waiter()
   395  }
   396  
   397  func TestIdentify2WithUIDWithBrokenTrack(t *testing.T) {
   398  	testIdentify2WithUIDWithBrokenTrack(t, false)
   399  }
   400  
   401  func TestIdentify2WithUIDWithBrokenTrackWithSuppressUI(t *testing.T) {
   402  	testIdentify2WithUIDWithBrokenTrack(t, true)
   403  }
   404  
   405  func TestIdentify2WithUIDWithUntrackedFastPath(t *testing.T) {
   406  	tc := SetupEngineTest(t, "TestIdentify2WithUIDWithUntrackedFastPath")
   407  	defer tc.Cleanup()
   408  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   409  
   410  	fu := CreateAndSignupFakeUser(tc, "track")
   411  
   412  	runID2 := func(expectFastPath bool) {
   413  
   414  		tester := newIdentify2WithUIDTester(tc.G)
   415  		tester.noDiskCache = true
   416  
   417  		eng := NewIdentify2WithUID(tc.G, &keybase1.Identify2Arg{Uid: aliceUID, IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_GUI})
   418  		eng.testArgs = &Identify2WithUIDTestArgs{
   419  			cache:                  tester,
   420  			allowUntrackedFastPath: true,
   421  		}
   422  		err := eng.Run(identify2MetaContext(tc, tester))
   423  		require.NoError(t, err)
   424  		require.Equal(t, expectFastPath, (eng.testArgs.stats.untrackedFastPaths == 1), "right number of untracked fast paths")
   425  	}
   426  
   427  	runID2(true)
   428  	trackAlice(tc, fu, sigVersion)
   429  	defer untrackAlice(tc, fu, sigVersion)
   430  	runID2(false)
   431  }
   432  
   433  func TestIdentify2WithUIDWithBrokenTrackFromChatGUI(t *testing.T) {
   434  
   435  	tc := SetupEngineTest(t, "TestIdentify2WithUIDWithBrokenTrackFromChatGUI")
   436  	defer tc.Cleanup()
   437  	tester := newIdentify2WithUIDTester(tc.G)
   438  	tc.G.SetProofServices(tester)
   439  	tester.checkStatusHook = func(l libkb.SigHint, _ libkb.ProofCheckerMode) libkb.ProofError {
   440  		if strings.Contains(l.GetHumanURL(), "twitter") {
   441  			tc.G.Log.Debug("failing twitter proof %s", l.GetHumanURL())
   442  			return libkb.NewProofError(keybase1.ProofStatus_DELETED, "gone!")
   443  		}
   444  		return nil
   445  	}
   446  
   447  	origUI := tester
   448  
   449  	checkBrokenRes := func(res *keybase1.Identify2ResUPK2) {
   450  		if !res.Upk.GetUID().Equal(tracyUID) {
   451  			t.Fatal("bad UID for t_tracy")
   452  		}
   453  		if res.Upk.GetName() != "t_tracy" {
   454  			t.Fatal("bad username for t_tracy")
   455  		}
   456  		if len(res.Upk.Current.DeviceKeys) != 4 {
   457  			t.Fatal("wrong # of device keys for tracy")
   458  		}
   459  		if res.TrackBreaks == nil || len(res.TrackBreaks.Proofs) != 1 {
   460  			t.Fatal("Expected to get back 1 broken proof")
   461  		}
   462  		if res.TrackBreaks.Proofs[0].RemoteProof.Key != "twitter" {
   463  			t.Fatal("Expected a twitter proof type")
   464  		}
   465  		if res.TrackBreaks.Proofs[0].Lcr.RemoteDiff.Type != keybase1.TrackDiffType_REMOTE_FAIL {
   466  			t.Fatal("wrong remote failure type")
   467  		}
   468  	}
   469  
   470  	runChatGUI := func() {
   471  		// Now run the engine again, but in gui mode, and check that we don't hit
   472  		// the cached broken guy.
   473  		eng := NewIdentify2WithUID(tc.G, &keybase1.Identify2Arg{Uid: tracyUID, IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_GUI})
   474  
   475  		eng.testArgs = &Identify2WithUIDTestArgs{
   476  			noMe:                   true,
   477  			cache:                  tester,
   478  			tcl:                    importTrackingLink(t, tc.G),
   479  			allowUntrackedFastPath: true,
   480  		}
   481  
   482  		waiter := launchWaiter(t, tester.finishCh)
   483  		m := identify2MetaContext(tc, tester)
   484  		err := eng.Run(m)
   485  		// Since we threw away the test UI, we have to manually complete the UI here,
   486  		// otherwise the waiter() will block indefinitely.
   487  		_ = origUI.Finish(m)
   488  		waiter()
   489  		if err != nil {
   490  			t.Fatalf("expected no ID2 error; got %v", err)
   491  		}
   492  		res, err := eng.Result(m)
   493  		if err != nil {
   494  			t.Fatalf("unexpected export error: %s", err)
   495  		}
   496  		checkBrokenRes(res)
   497  		if n := eng.testArgs.stats.untrackedFastPaths; n > 0 {
   498  			t.Fatalf("Didn't expect any untracked fast paths, but got %d", n)
   499  		}
   500  	}
   501  
   502  	runStandard := func() {
   503  		// Now run the engine again, but in normal mode, and check that we don't hit
   504  		// the cached broken guy.
   505  		eng := NewIdentify2WithUID(tc.G, &keybase1.Identify2Arg{Uid: tracyUID, IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI})
   506  
   507  		eng.testArgs = &Identify2WithUIDTestArgs{
   508  			noMe:  true,
   509  			cache: tester,
   510  			tcl:   importTrackingLink(t, tc.G),
   511  		}
   512  
   513  		waiter := launchWaiter(t, tester.finishCh)
   514  		err := eng.Run(identify2MetaContext(tc, tester))
   515  		waiter()
   516  		if err == nil {
   517  			t.Fatalf("Expected a break with running ID2 in standard mode")
   518  		}
   519  	}
   520  
   521  	runChatGUI()
   522  
   523  	// First time through, we should miss both caches
   524  	if !tester.fastStats.eq(0, 0, 1, 0, 0) || !tester.slowStats.eq(0, 0, 1, 0, 0) {
   525  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   526  	}
   527  
   528  	runStandard()
   529  
   530  	// If we run without the chat GUI, we should hit the cache, but have it be
   531  	// disqualified because the cached copy has broken tracker statements.
   532  	if !tester.fastStats.eq(0, 0, 1, 0, 1) || !tester.slowStats.eq(0, 0, 1, 0, 1) {
   533  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   534  	}
   535  
   536  	runChatGUI()
   537  
   538  	// The next time we run with the chat GUI, we won't hit the slow or fast
   539  	// cache, since the failure in standard mode cleared out the cache for this
   540  	// user.
   541  	if !tester.fastStats.eq(0, 0, 2, 0, 1) || !tester.slowStats.eq(0, 0, 2, 0, 1) {
   542  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   543  	}
   544  
   545  	tester.incNow(time.Second)
   546  	runChatGUI()
   547  
   548  	// Now we should get a fast cache hit
   549  	if !tester.fastStats.eq(1, 0, 2, 0, 1) || !tester.slowStats.eq(0, 0, 2, 0, 1) {
   550  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   551  	}
   552  
   553  	tester.incNow(time.Second + libkb.Identify2CacheShortTimeout)
   554  	runChatGUI()
   555  
   556  	// A fast cache timeout and a slow cache hit!
   557  	if !tester.fastStats.eq(1, 1, 2, 0, 1) || !tester.slowStats.eq(1, 0, 2, 0, 1) {
   558  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   559  	}
   560  
   561  	// The fast cached should have been primed with the slow cache, so we expected
   562  	// a fast cache hit
   563  	runChatGUI()
   564  	if !tester.fastStats.eq(2, 1, 2, 0, 1) || !tester.slowStats.eq(1, 0, 2, 0, 1) {
   565  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   566  	}
   567  
   568  	tester.incNow(time.Second + libkb.Identify2CacheBrokenTimeout)
   569  	runChatGUI()
   570  
   571  	// After the broken timeout passes, we should get timeouts on both caches
   572  	if !tester.fastStats.eq(2, 2, 2, 0, 1) || !tester.slowStats.eq(1, 1, 2, 0, 1) {
   573  		t.Fatalf("bad cache stats: %+v, %+v", tester.fastStats, tester.slowStats)
   574  	}
   575  }
   576  
   577  func TestIdentify2WithUIDWithAssertion(t *testing.T) {
   578  	tc := SetupEngineTest(t, "Identify2WithUIDWithAssertion")
   579  	defer tc.Cleanup()
   580  	i := newIdentify2WithUIDTester(tc.G)
   581  	tc.G.SetProofServices(i)
   582  	arg := &keybase1.Identify2Arg{
   583  		Uid:              tracyUID,
   584  		UserAssertion:    "tacovontaco@twitter",
   585  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   586  	}
   587  	eng := NewIdentify2WithUID(tc.G, arg)
   588  
   589  	eng.testArgs = &Identify2WithUIDTestArgs{
   590  		noMe: true,
   591  	}
   592  
   593  	err := eng.Run(identify2MetaContext(tc, i))
   594  	if err != nil {
   595  		t.Fatal(err)
   596  	}
   597  
   598  	<-i.finishCh
   599  }
   600  
   601  func TestIdentify2WithUIDWithAssertions(t *testing.T) {
   602  	tc := SetupEngineTest(t, "Identify2WithUIDWithAssertion")
   603  	defer tc.Cleanup()
   604  	i := newIdentify2WithUIDTester(tc.G)
   605  	tc.G.SetProofServices(i)
   606  	arg := &keybase1.Identify2Arg{
   607  		Uid:              tracyUID,
   608  		UserAssertion:    "tacovontaco@twitter+t_tracy@rooter",
   609  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   610  	}
   611  	eng := NewIdentify2WithUID(tc.G, arg)
   612  
   613  	eng.testArgs = &Identify2WithUIDTestArgs{
   614  		noMe: true,
   615  	}
   616  
   617  	err := eng.Run(identify2MetaContext(tc, i))
   618  	if err != nil {
   619  		t.Fatal(err)
   620  	}
   621  
   622  	<-i.finishCh
   623  }
   624  
   625  func TestIdentify2WithUIDWithNonExistentAssertion(t *testing.T) {
   626  	tc := SetupEngineTest(t, "Identify2WithUIDWithNonExistentAssertion")
   627  	defer tc.Cleanup()
   628  	i := newIdentify2WithUIDTester(tc.G)
   629  	tc.G.SetProofServices(i)
   630  	arg := &keybase1.Identify2Arg{
   631  		Uid:              tracyUID,
   632  		UserAssertion:    "beyonce@twitter",
   633  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   634  	}
   635  	eng := NewIdentify2WithUID(tc.G, arg)
   636  
   637  	eng.testArgs = &Identify2WithUIDTestArgs{
   638  		noMe: true,
   639  	}
   640  
   641  	done := make(chan bool)
   642  	starts := 0
   643  	go func() {
   644  		select {
   645  		case <-i.startCh:
   646  			starts++
   647  		case <-done:
   648  			return
   649  		}
   650  	}()
   651  
   652  	err := eng.Run(identify2MetaContext(tc, i))
   653  	if err == nil {
   654  		t.Fatal(err)
   655  	}
   656  	if _, ok := err.(libkb.UnmetAssertionError); !ok {
   657  		t.Fatalf("Wanted an error of type %T; got %T", libkb.UnmetAssertionError{}, err)
   658  	}
   659  	if starts > 0 {
   660  		t.Fatalf("Didn't expect the identify UI to start in this case")
   661  	}
   662  
   663  	done <- true
   664  }
   665  
   666  func TestIdentify2WithUIDWithFailedAssertion(t *testing.T) {
   667  	tc := SetupEngineTest(t, "TestIdentify2WithUIDWithFailedAssertion")
   668  	defer tc.Cleanup()
   669  	i := newIdentify2WithUIDTester(tc.G)
   670  	tc.G.SetProofServices(i)
   671  	arg := &keybase1.Identify2Arg{
   672  		Uid:              tracyUID,
   673  		UserAssertion:    "tacovontaco@twitter",
   674  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   675  	}
   676  	eng := NewIdentify2WithUID(tc.G, arg)
   677  
   678  	eng.testArgs = &Identify2WithUIDTestArgs{
   679  		noMe: true,
   680  	}
   681  
   682  	starts := 0
   683  	var wg sync.WaitGroup
   684  	wg.Add(1)
   685  	go func() {
   686  		tc.G.Log.Debug("In BG: waiting for UI notification on startCh")
   687  		<-i.startCh
   688  		starts++
   689  		tc.G.Log.Debug("In BG: waited for UI notification on startCh")
   690  		wg.Done()
   691  	}()
   692  
   693  	i.checkStatusHook = func(l libkb.SigHint, _ libkb.ProofCheckerMode) libkb.ProofError {
   694  		if strings.Contains(l.GetHumanURL(), "twitter") {
   695  			tc.G.Log.Debug("failing twitter proof %s", l.GetHumanURL())
   696  			return libkb.NewProofError(keybase1.ProofStatus_DELETED, "gone!")
   697  		}
   698  		return nil
   699  	}
   700  
   701  	err := eng.Run(identify2MetaContext(tc, i))
   702  
   703  	if err == nil {
   704  		t.Fatal(err)
   705  	}
   706  	if _, ok := err.(libkb.ProofError); !ok {
   707  		t.Fatalf("Wanted an error of type libkb.ProofError; got %T", err)
   708  	}
   709  	wg.Wait()
   710  	if starts != 1 {
   711  		t.Fatalf("Expected the UI to have started")
   712  	}
   713  	<-i.finishCh
   714  }
   715  
   716  func TestIdentify2WithUIDWithFailedAncillaryAssertion(t *testing.T) {
   717  	tc := SetupEngineTest(t, "TestIdentify2WithUIDWithFailedAncillaryAssertion")
   718  	defer tc.Cleanup()
   719  	i := newIdentify2WithUIDTester(tc.G)
   720  	tc.G.SetProofServices(i)
   721  	arg := &keybase1.Identify2Arg{
   722  		Uid:              tracyUID,
   723  		UserAssertion:    "tacoplusplus@github+t_tracy@rooter",
   724  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   725  	}
   726  	eng := NewIdentify2WithUID(tc.G, arg)
   727  
   728  	eng.testArgs = &Identify2WithUIDTestArgs{
   729  		noMe: true,
   730  	}
   731  
   732  	var wg sync.WaitGroup
   733  	wg.Add(1)
   734  
   735  	i.checkStatusHook = func(l libkb.SigHint, _ libkb.ProofCheckerMode) libkb.ProofError {
   736  		switch {
   737  		case strings.Contains(l.GetHumanURL(), "twitter"):
   738  			wg.Done()
   739  			tc.G.Log.Debug("failing twitter proof %s", l.GetHumanURL())
   740  			return libkb.NewProofError(keybase1.ProofStatus_DELETED, "gone!")
   741  		case strings.Contains(l.GetHumanURL(), "github"):
   742  			wg.Wait()
   743  			return nil
   744  		case strings.Contains(l.GetHumanURL(), "rooter"):
   745  			wg.Wait()
   746  			return nil
   747  		default:
   748  			return nil
   749  		}
   750  	}
   751  
   752  	err := eng.Run(identify2MetaContext(tc, i))
   753  
   754  	if err != nil {
   755  		t.Fatal(err)
   756  	}
   757  	<-i.startCh
   758  	<-i.finishCh
   759  }
   760  
   761  func (i *Identify2WithUIDTester) incNow(d time.Duration) {
   762  	i.Lock()
   763  	defer i.Unlock()
   764  	i.now = i.now.Add(d)
   765  }
   766  
   767  func TestIdentify2WithUIDCache(t *testing.T) {
   768  	tc := SetupEngineTest(t, "Identify2WithUIDWithoutTrack")
   769  	defer tc.Cleanup()
   770  	i := newIdentify2WithUIDTester(tc.G)
   771  	tc.G.SetProofServices(i)
   772  	arg := &keybase1.Identify2Arg{
   773  		Uid:              tracyUID,
   774  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   775  	}
   776  	run := func() {
   777  		eng := NewIdentify2WithUID(tc.G, arg)
   778  		eng.testArgs = &Identify2WithUIDTestArgs{
   779  			cache: i,
   780  			clock: func() time.Time { return i.now },
   781  		}
   782  		err := eng.Run(identify2MetaContext(tc, i))
   783  		if err != nil {
   784  			t.Fatal(err)
   785  		}
   786  	}
   787  
   788  	// First time we'll cause an ID, so we need to finish
   789  	run()
   790  	<-i.startCh
   791  	<-i.finishCh
   792  
   793  	if !i.fastStats.eq(0, 0, 1, 0, 0) || !i.slowStats.eq(0, 0, 1, 0, 0) {
   794  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   795  	}
   796  
   797  	i.incNow(time.Second)
   798  	run()
   799  
   800  	// A new fast-path hit
   801  	if !i.fastStats.eq(1, 0, 1, 0, 0) || !i.slowStats.eq(0, 0, 1, 0, 0) {
   802  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   803  	}
   804  
   805  	i.incNow(time.Second + libkb.Identify2CacheShortTimeout)
   806  	run()
   807  
   808  	// A new fast-path timeout and a new slow-path hit
   809  	if !i.fastStats.eq(1, 1, 1, 0, 0) || !i.slowStats.eq(1, 0, 1, 0, 0) {
   810  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   811  	}
   812  
   813  	i.incNow(time.Second + libkb.Identify2CacheLongTimeout)
   814  	run()
   815  	<-i.startCh
   816  	<-i.finishCh
   817  
   818  	// A new fast-path timeout and a new slow-path timeout
   819  	if !i.fastStats.eq(1, 2, 1, 0, 0) || !i.slowStats.eq(1, 1, 1, 0, 0) {
   820  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   821  	}
   822  
   823  	i.incNow(time.Second)
   824  	run()
   825  	// A new fast-path hit
   826  	if !i.fastStats.eq(2, 2, 1, 0, 0) || !i.slowStats.eq(1, 1, 1, 0, 0) {
   827  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   828  	}
   829  
   830  	arg.UserAssertion = "tacovontaco@twitter"
   831  	i.incNow(time.Second)
   832  	run()
   833  	// A new slow-path hit; we have to use the slow path with assertions
   834  	if !i.fastStats.eq(2, 2, 1, 0, 0) || !i.slowStats.eq(2, 1, 1, 0, 0) {
   835  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   836  	}
   837  }
   838  
   839  func TestIdentify2WithUIDLocalAssertions(t *testing.T) {
   840  	tc := SetupEngineTest(t, "TestIdentify2WithUIDLocalAssertions")
   841  	defer tc.Cleanup()
   842  	i := newIdentify2WithUIDTester(tc.G)
   843  	tc.G.SetProofServices(i)
   844  	arg := &keybase1.Identify2Arg{
   845  		Uid:              tracyUID,
   846  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   847  	}
   848  
   849  	run := func() {
   850  		testArgs := &Identify2WithUIDTestArgs{
   851  			cache: i,
   852  			clock: func() time.Time { return i.now },
   853  		}
   854  		eng := NewIdentify2WithUID(tc.G, arg)
   855  		eng.testArgs = testArgs
   856  		err := eng.Run(identify2MetaContext(tc, i))
   857  		if err != nil {
   858  			t.Fatal(err)
   859  		}
   860  	}
   861  
   862  	numTracyLoads := func() int {
   863  		tracyUID := keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")
   864  		return i.userLoads[tracyUID]
   865  	}
   866  
   867  	// First time we'll cause an ID, so we need to start & finish
   868  	arg.UserAssertion = "4ff50d580914427227bb14c821029e2c7cf0d488@" + libkb.PGPAssertionKey
   869  	run()
   870  	if n := numTracyLoads(); n != 1 {
   871  		t.Fatalf("expected 1 full user load; got %d", n)
   872  	}
   873  	<-i.startCh
   874  	<-i.finishCh
   875  
   876  	// Don't attempt to hit fast cache, since we're using local assertions.
   877  	if !i.fastStats.eq(0, 0, 0, 0, 0) || !i.slowStats.eq(0, 0, 1, 0, 0) {
   878  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   879  	}
   880  
   881  	i.incNow(time.Second)
   882  	run()
   883  	// A new slow-path hit
   884  	if !i.fastStats.eq(0, 0, 0, 0, 0) || !i.slowStats.eq(1, 0, 1, 0, 0) {
   885  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   886  	}
   887  	if n := numTracyLoads(); n != 1 {
   888  		t.Fatalf("expected 1 full user load; got %d", n)
   889  	}
   890  	arg.UserAssertion += "+tacovontaco@twitter"
   891  	i.incNow(time.Second)
   892  	run()
   893  	// A new slow-path hit
   894  	if !i.fastStats.eq(0, 0, 0, 0, 0) || !i.slowStats.eq(2, 0, 1, 0, 0) {
   895  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   896  	}
   897  	if n := numTracyLoads(); n != 2 {
   898  		t.Fatalf("expected 2 full user load; got %d", n)
   899  	}
   900  
   901  	i.incNow(libkb.Identify2CacheLongTimeout)
   902  	run()
   903  	<-i.startCh
   904  	<-i.finishCh
   905  	// A new slow-path timeout
   906  	if !i.fastStats.eq(0, 0, 0, 0, 0) || !i.slowStats.eq(2, 1, 1, 0, 0) {
   907  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   908  	}
   909  
   910  	i.incNow(time.Second)
   911  	run()
   912  	// A new slow-path hit
   913  	if !i.fastStats.eq(0, 0, 0, 0, 0) || !i.slowStats.eq(3, 1, 1, 0, 0) {
   914  		t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats)
   915  	}
   916  }
   917  
   918  func TestResolveAndIdentify2WithUIDWithAssertions(t *testing.T) {
   919  	tc := SetupEngineTest(t, "Identify2WithUIDWithAssertion")
   920  	defer tc.Cleanup()
   921  	i := newIdentify2WithUIDTester(tc.G)
   922  	tc.G.SetProofServices(i)
   923  	arg := &keybase1.Identify2Arg{
   924  		UserAssertion:    "tacovontaco@twitter+t_tracy@rooter",
   925  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   926  	}
   927  	eng := NewResolveThenIdentify2(tc.G, arg)
   928  	eng.testArgs = &Identify2WithUIDTestArgs{
   929  		noMe: true,
   930  	}
   931  	err := eng.Run(identify2MetaContext(tc, i))
   932  	if err != nil {
   933  		t.Fatal(err)
   934  	}
   935  	<-i.startCh
   936  	<-i.finishCh
   937  }
   938  
   939  func TestIdentify2NoSigchain(t *testing.T) {
   940  	tc := SetupEngineTest(t, "Identify2NoSigchain")
   941  	defer tc.Cleanup()
   942  
   943  	u, _ := createFakeUserWithNoKeys(tc)
   944  	Logout(tc)
   945  
   946  	i := newIdentify2WithUIDTester(tc.G)
   947  	tc.G.SetProofServices(i)
   948  	arg := &keybase1.Identify2Arg{
   949  		UserAssertion:    u,
   950  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   951  	}
   952  	eng := NewResolveThenIdentify2(tc.G, arg)
   953  	m := identify2MetaContext(tc, i)
   954  	err := eng.Run(m)
   955  	if err != nil {
   956  		t.Fatalf("identify2 failed on user with no keys: %s", err)
   957  	}
   958  
   959  	// kbfs would like to have some info about the user
   960  	result, err := eng.Result(m)
   961  	if err != nil {
   962  		t.Fatalf("unexpeted export error: %s", err)
   963  	}
   964  	if result == nil {
   965  		t.Fatal("no result on id2 w/ no sigchain")
   966  	}
   967  	if result.Upk.GetName() != u {
   968  		t.Errorf("result username: %q, expected %q", result.Upk.GetName(), u)
   969  	}
   970  }
   971  
   972  // See CORE-4310
   973  func TestIdentifyAfterDbNuke(t *testing.T) {
   974  	tc := SetupEngineTest(t, "track")
   975  	defer tc.Cleanup()
   976  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   977  	fu := CreateAndSignupFakeUser(tc, "track")
   978  
   979  	trackAlice(tc, fu, sigVersion)
   980  	defer untrackAlice(tc, fu, sigVersion)
   981  
   982  	runIDAlice := func() {
   983  
   984  		i := newIdentify2WithUIDTester(tc.G)
   985  		arg := &keybase1.Identify2Arg{
   986  			Uid:              aliceUID,
   987  			IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   988  		}
   989  		eng := NewIdentify2WithUID(tc.G, arg)
   990  		eng.testArgs = &Identify2WithUIDTestArgs{
   991  			noCache: true,
   992  		}
   993  		waiter := launchWaiter(t, i.finishCh)
   994  		if err := eng.Run(identify2MetaContext(tc, i)); err != nil {
   995  			t.Fatal(err)
   996  		}
   997  		waiter()
   998  	}
   999  
  1000  	tc.G.Log.Debug("------------ ID Alice Iteration 0 ---------------")
  1001  	runIDAlice()
  1002  	if _, err := tc.G.LocalDb.Nuke(); err != nil {
  1003  		t.Fatal(err)
  1004  	}
  1005  	if err := tc.G.ConfigureCaches(); err != nil {
  1006  		t.Fatal(err)
  1007  	}
  1008  	tc.G.Log.Debug("------------ ID Alice Iteration 1 ---------------")
  1009  	runIDAlice()
  1010  }
  1011  
  1012  func TestNoSelfHostedIdentifyInPassiveMode(t *testing.T) {
  1013  	tc := SetupEngineTest(t, "id")
  1014  	defer tc.Cleanup()
  1015  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
  1016  
  1017  	eve := CreateAndSignupFakeUser(tc, "e")
  1018  	_, _, err := proveRooter(tc.G, eve, sigVersion)
  1019  	tc.G.ProofCache.DisableDisk()
  1020  	require.NoError(t, err)
  1021  	Logout(tc)
  1022  
  1023  	alice := CreateAndSignupFakeUser(tc, "a")
  1024  
  1025  	runTest := func(identifyBehavior keybase1.TLFIdentifyBehavior, returnUnchecked bool, shouldCheck bool, wantedMode libkb.ProofCheckerMode) {
  1026  
  1027  		i := newIdentify2WithUIDTester(tc.G)
  1028  		checked := false
  1029  		i.checkStatusHook = func(l libkb.SigHint, pcm libkb.ProofCheckerMode) libkb.ProofError {
  1030  			checked = true
  1031  			if strings.Contains(l.GetHumanURL(), "rooter") {
  1032  				if !shouldCheck {
  1033  					t.Fatalf("should not have gotten a check; should have hit cache")
  1034  				}
  1035  				require.Equal(t, pcm, wantedMode, "we get a passive ID in GUI mode")
  1036  				if returnUnchecked {
  1037  					return libkb.ProofErrorUnchecked
  1038  				}
  1039  			}
  1040  			tc.G.Log.Debug("proof rubber-stamped: %s", l.GetHumanURL())
  1041  			return nil
  1042  		}
  1043  
  1044  		tc.G.SetProofServices(i)
  1045  		arg := &keybase1.Identify2Arg{
  1046  			Uid:              eve.UID(),
  1047  			IdentifyBehavior: identifyBehavior,
  1048  			NeedProofSet:     true,
  1049  		}
  1050  		eng := NewIdentify2WithUID(tc.G, arg)
  1051  		eng.testArgs = &Identify2WithUIDTestArgs{
  1052  			noMe: false,
  1053  		}
  1054  		var waiter func()
  1055  		if !identifyBehavior.ShouldSuppressTrackerPopups() {
  1056  			waiter = launchWaiter(t, i.finishCh)
  1057  		}
  1058  		err := eng.Run(identify2MetaContext(tc, i))
  1059  		require.NoError(t, err)
  1060  		require.Equal(t, checked, shouldCheck)
  1061  		if waiter != nil {
  1062  			waiter()
  1063  		}
  1064  	}
  1065  
  1066  	// Alice ID's Eve, in chat mode, without a track. Assert that we get a
  1067  	// PASSIVE proof checker mode for rooter.
  1068  	runTest(keybase1.TLFIdentifyBehavior_CHAT_GUI, true, true, libkb.ProofCheckerModePassive)
  1069  
  1070  	// Alice ID's Eve, in standard ID mode, without a track. Assert that we get a
  1071  	// ACTIVE proof checker mode for the rooter
  1072  	runTest(keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, false, true, libkb.ProofCheckerModeActive)
  1073  
  1074  	// Alice ID's Eve in chat mode, without a track. But she should hit the proof cache
  1075  	// from right above.
  1076  	runTest(keybase1.TLFIdentifyBehavior_CHAT_GUI, false, false, libkb.ProofCheckerModePassive)
  1077  
  1078  	trackUser(tc, alice, eve.NormalizedUsername(), sigVersion)
  1079  
  1080  	err = tc.G.ProofCache.Reset()
  1081  	require.NoError(t, err)
  1082  
  1083  	// Alice ID's Eve, in chat mode, with a track. Assert that we get an
  1084  	// Active proof checker mode for rooter.
  1085  	runTest(keybase1.TLFIdentifyBehavior_CHAT_GUI, true, true, libkb.ProofCheckerModeActive)
  1086  }
  1087  
  1088  func TestSkipExternalChecks(t *testing.T) {
  1089  	arg := &keybase1.Identify2Arg{
  1090  		Uid:           tracyUID,
  1091  		CanSuppressUI: true,
  1092  	}
  1093  	arg.IdentifyBehavior = keybase1.TLFIdentifyBehavior_KBFS_REKEY
  1094  	_, err := identify2WithUIDWithBrokenTrackMakeEngine(t, arg)
  1095  	require.NoError(t, err)
  1096  
  1097  	arg.IdentifyBehavior = keybase1.TLFIdentifyBehavior_KBFS_QR
  1098  	_, err = identify2WithUIDWithBrokenTrackMakeEngine(t, arg)
  1099  	require.NoError(t, err)
  1100  
  1101  	arg.IdentifyBehavior = keybase1.TLFIdentifyBehavior_CHAT_CLI
  1102  	_, err = identify2WithUIDWithBrokenTrackMakeEngine(t, arg)
  1103  	require.Error(t, err)
  1104  }
  1105  
  1106  type evilResolver struct {
  1107  	*libkb.ResolverImpl
  1108  	badPrefix string
  1109  	badUID    keybase1.UID
  1110  }
  1111  
  1112  func (e *evilResolver) ResolveFullExpressionWithBody(m libkb.MetaContext, s string) libkb.ResolveResult {
  1113  	ret := e.ResolverImpl.ResolveFullExpressionWithBody(m, s)
  1114  	if strings.HasPrefix(s, e.badPrefix) {
  1115  		ret.SetUIDForTesting(e.badUID)
  1116  	}
  1117  	return ret
  1118  }
  1119  
  1120  var _ libkb.Resolver = (*evilResolver)(nil)
  1121  
  1122  func TestResolveAndCheck(t *testing.T) {
  1123  	tc := SetupEngineTest(t, "id")
  1124  	defer tc.Cleanup()
  1125  	m := NewMetaContextForTest(tc)
  1126  	goodResolver := tc.G.Resolver.(*libkb.ResolverImpl)
  1127  	evilResolver := evilResolver{goodResolver, "t_alice", tracyUID}
  1128  
  1129  	var tests = []struct {
  1130  		s       string
  1131  		e       error
  1132  		useEvil bool
  1133  	}{
  1134  		{"tacovontaco@twitter+t_tracy@rooter", nil, false},
  1135  		{"tacovontaco@twitter+t_tracy@rooter+t_tracy", nil, false},
  1136  		{"t_tracy", nil, false},
  1137  		{"t_tracy+" + string(tracyUID) + "@uid", nil, false},
  1138  		{"tacovontaco@twitter+t_tracy@rooter+foobunny@github", libkb.UnmetAssertionError{}, false},
  1139  		{"foobunny@github", libkb.ResolutionError{}, false},
  1140  		{"foobunny", libkb.NotFoundError{}, false},
  1141  		{"foobunny+foobunny@github", libkb.NotFoundError{}, false},
  1142  		{"t_alice", libkb.UIDMismatchError{}, true},
  1143  		{"t_alice+t_tracy@rooter", libkb.UnmetAssertionError{}, true},
  1144  		{"t_alice+" + string(aliceUID) + "@uid", libkb.UnmetAssertionError{}, true},
  1145  		{"foobunny@gubble.social", libkb.ResolutionError{}, false},
  1146  	}
  1147  	for _, test := range tests {
  1148  		tc.G.Resolver = goodResolver
  1149  		if test.useEvil {
  1150  			tc.G.Resolver = &evilResolver
  1151  		}
  1152  		upk, err := ResolveAndCheck(m, test.s, true /*useTracking*/)
  1153  		require.IsType(t, test.e, err)
  1154  		if err == nil {
  1155  			require.True(t, upk.GetUID().Equal(tracyUID))
  1156  			require.Equal(t, upk.GetName(), "t_tracy")
  1157  		}
  1158  	}
  1159  
  1160  	// Test happy path for gubble social assertion
  1161  	fu := CreateAndSignupFakeUser(tc, "track")
  1162  	proveGubbleSocial(tc, fu, libkb.KeybaseSignatureV2)
  1163  	assertion := fmt.Sprintf("%s@gubble.social", fu.Username)
  1164  	upk, err := ResolveAndCheck(m, assertion, true /* useTracking */)
  1165  	require.NoError(t, err)
  1166  	require.True(t, upk.GetUID().Equal(fu.UID()))
  1167  	require.Equal(t, upk.GetName(), fu.Username)
  1168  }
  1169  
  1170  // TestTrackThenRevokeWithDifferentChatModes is described in CORE-9372. The scenario
  1171  // is that: (1) bob proves rooter; (2) alice follows bob; (3) bob revokes rooter;
  1172  // (4) alice ID's bob with CHAT_GUI, and that should work; (5)
  1173  // alice ID's bob with CHAT_GUI_STRICT, and that should fail
  1174  func TestTrackThenRevokeThenIdentifyWithDifferentChatModes(t *testing.T) {
  1175  	tc := SetupEngineTest(t, "id")
  1176  	defer tc.Cleanup()
  1177  
  1178  	fakeClock := clockwork.NewFakeClockAt(time.Now())
  1179  	tc.G.SetClock(fakeClock)
  1180  
  1181  	bob := CreateAndSignupFakeUser(tc, "b")
  1182  	_, sigID, err := proveRooter(tc.G, bob, 2)
  1183  	require.NoError(t, err)
  1184  	alice := CreateAndSignupFakeUser(tc, "a")
  1185  	trackUser(tc, alice, bob.NormalizedUsername(), 2)
  1186  	Logout(tc)
  1187  	err = bob.Login(tc.G)
  1188  	require.NoError(t, err)
  1189  	err = doRevokeSig(tc, bob, sigID)
  1190  	require.NoError(t, err)
  1191  	Logout(tc)
  1192  	err = alice.Login(tc.G)
  1193  	require.NoError(t, err)
  1194  
  1195  	// Blast through the cache
  1196  	fakeClock.Advance(libkb.Identify2CacheLongTimeout + time.Minute)
  1197  
  1198  	runIdentify := func(idb keybase1.TLFIdentifyBehavior) (err error) {
  1199  		idUI := &FakeIdentifyUI{}
  1200  		arg := keybase1.Identify2Arg{
  1201  			UserAssertion:    bob.Username,
  1202  			UseDelegateUI:    false,
  1203  			CanSuppressUI:    true,
  1204  			IdentifyBehavior: idb,
  1205  		}
  1206  
  1207  		uis := libkb.UIs{
  1208  			LogUI:      tc.G.UI.GetLogUI(),
  1209  			IdentifyUI: idUI,
  1210  		}
  1211  		eng := NewResolveThenIdentify2(tc.G, &arg)
  1212  		m := NewMetaContextForTest(tc).WithUIs(uis)
  1213  		err = RunEngine2(m, eng)
  1214  		return err
  1215  	}
  1216  
  1217  	err = runIdentify(keybase1.TLFIdentifyBehavior_CHAT_GUI)
  1218  	require.NoError(t, err)
  1219  }
  1220  
  1221  // Alice signs up using key X, Bob signs up, Bob tracks Alice,
  1222  // Alice resets and provisions using the same key X, Bob ids Alice
  1223  func TestTrackResetReuseKey(t *testing.T) {
  1224  	// Prepare key X
  1225  	var keyX [ed25519.SeedSize]byte
  1226  	_, err := rand.Read(keyX[:])
  1227  	require.NoError(t, err)
  1228  
  1229  	// Alice signs up using key X
  1230  	tcX := SetupEngineTest(t, "ida")
  1231  	defer tcX.Cleanup()
  1232  	fuX := NewFakeUserOrBust(t, "ida")
  1233  	suArg := MakeTestSignupEngineRunArg(fuX)
  1234  	pairX, err := libkb.GenerateNaclSigningKeyPairFromSeed(keyX)
  1235  	require.NoError(t, err)
  1236  	suArg.naclSigningKeyPair = pairX
  1237  	fuX.DeviceName = suArg.DeviceName
  1238  	SignupFakeUserWithArg(tcX, fuX, suArg)
  1239  	require.NoError(t, AssertProvisioned(tcX))
  1240  
  1241  	// Bob signs up using whatever key
  1242  	tcY := SetupEngineTest(t, "idb")
  1243  	defer tcY.Cleanup()
  1244  	fuY := CreateAndSignupFakeUser(tcY, "idb")
  1245  	require.NoError(t, AssertProvisioned(tcY))
  1246  
  1247  	// Bob should be able to ID Alice without any issues
  1248  	idUI := &FakeIdentifyUI{}
  1249  	require.NoError(t, RunEngine2(
  1250  		NewMetaContextForTest(tcY).WithUIs(libkb.UIs{
  1251  			LogUI:      tcY.G.UI.GetLogUI(),
  1252  			IdentifyUI: &FakeIdentifyUI{},
  1253  		}),
  1254  		NewResolveThenIdentify2(tcY.G, &keybase1.Identify2Arg{
  1255  			UserAssertion:    fuX.Username,
  1256  			ForceDisplay:     true,
  1257  			IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
  1258  		})),
  1259  	)
  1260  	require.False(t, idUI.BrokenTracking)
  1261  	require.Empty(t, idUI.DisplayKeyDiffs)
  1262  
  1263  	// Bob tracks Alice
  1264  	trackUser(tcY, fuY, fuX.NormalizedUsername(), libkb.GetDefaultSigVersion(tcX.G))
  1265  	assertTracking(tcY, fuX.Username)
  1266  
  1267  	// Alice gets reset and logs out
  1268  	ResetAccount(tcX, fuX)
  1269  
  1270  	// Alice logs in (and provisions) again
  1271  	loginEng := NewLogin(tcX.G, keybase1.DeviceTypeV2_DESKTOP, fuX.Username, keybase1.ClientType_CLI)
  1272  	loginEng.naclSigningKeyPair = pairX
  1273  	require.NoError(t,
  1274  		RunEngine2(
  1275  			NewMetaContextForTest(tcX).WithUIs(libkb.UIs{
  1276  				ProvisionUI: newTestProvisionUI(),
  1277  				LoginUI:     &libkb.TestLoginUI{},
  1278  				LogUI:       tcX.G.UI.GetLogUI(),
  1279  				SecretUI:    fuX.NewSecretUI(),
  1280  				GPGUI:       &gpgtestui{},
  1281  			}),
  1282  			loginEng,
  1283  		),
  1284  	)
  1285  	require.NoError(t, AssertProvisioned(tcX))
  1286  
  1287  	// Manually get rid of the id2 cache
  1288  	require.NoError(t, tcY.G.Identify2Cache().Delete(fuX.UID()))
  1289  
  1290  	// Bob should see that Alice reset even though the eldest kid is the same
  1291  	idUI = &FakeIdentifyUI{}
  1292  	err = RunEngine2(
  1293  		NewMetaContextForTest(tcY).WithUIs(libkb.UIs{
  1294  			LogUI:      tcY.G.UI.GetLogUI(),
  1295  			IdentifyUI: idUI,
  1296  		}),
  1297  		NewResolveThenIdentify2(tcY.G, &keybase1.Identify2Arg{
  1298  			UserAssertion:    fuX.Username,
  1299  			ForceDisplay:     true,
  1300  			IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
  1301  		}),
  1302  	)
  1303  	require.Error(t, err)
  1304  	require.Equal(t, "1 followed proof failed", err.(libkb.IdentifySummaryError).Problems()[0])
  1305  	require.Len(t, idUI.DisplayKeyDiffs, 1, "key diffs count")
  1306  	require.Equal(t, keybase1.TrackDiffType_NEW_ELDEST, idUI.DisplayKeyDiffs[0].Type, "key diff new eldest")
  1307  	require.False(t, idUI.BrokenTracking) // tracking is not "broken" for this user - it's a key change
  1308  
  1309  	// He should be able to retrack
  1310  	trackUser(tcY, fuY, fuX.NormalizedUsername(), libkb.GetDefaultSigVersion(tcX.G))
  1311  	assertTracking(tcY, fuX.Username)
  1312  
  1313  	// Which should fix the identification
  1314  	idUI = &FakeIdentifyUI{}
  1315  	require.NoError(t, RunEngine2(
  1316  		NewMetaContextForTest(tcY).WithUIs(libkb.UIs{
  1317  			LogUI:      tcY.G.UI.GetLogUI(),
  1318  			IdentifyUI: idUI,
  1319  		}),
  1320  		NewResolveThenIdentify2(tcY.G, &keybase1.Identify2Arg{
  1321  			UserAssertion:    fuX.Username,
  1322  			ForceDisplay:     true,
  1323  			IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
  1324  		})),
  1325  	)
  1326  	require.False(t, idUI.BrokenTracking)
  1327  	require.Empty(t, idUI.DisplayKeyDiffs)
  1328  }
  1329  
  1330  var aliceUID = keybase1.UID("295a7eea607af32040647123732bc819")
  1331  var tracyUID = keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")
  1332  var trackingUID = keybase1.UID("92b3b3dbe457059f28c9f74e8e6b9419")
  1333  var trackingServerReply = `{"seqno":3,"payload_hash":"c3ffe390e9c9dabdd5f7253b81e0a38fad2c17589a9c7fcd967958418055140a","sig_id":"4ec10665ad163d0aa419ce4eab8ff661429c9a3a32cd4978fdb8c6b5c6d047620f","sig_id_short":"TsEGZa0WPQqkGc5Oq4_2YUKcmjoyzUl4_bjG","kid":"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a","sig":"-----BEGIN PGP MESSAGE-----\nVersion: Keybase OpenPGP v2.0.49\nComment: https://keybase.io/crypto\n\nyMWEAnictVZriF3VFZ7xVR1aDCqV0CbqqaDFG7vfj9FENJChFsUKWszD69p7rz1z\nk8ncm3vPZBxMQCytQjQEtQXBX42gJUgQ9U+1Jo2hVeID3wqKggoqiIiUtkLVtW+u\nyUxmwArpj8M97LPv2t/6vrW+vQ7+6MShkeEb7zjnd3fEcTt86NmvpoeuxzfOu6UK\n7TRbjd5SxckWTtXlbQo2YzVabcLZAD28uNU+dwZDtb1RVsp3nEzYq5ubWol2Mc54\nlkFkhi76xDPzPgWjIkRpTDTgI09gJD1CcWFppzHAtIGYQRonVUYGVaPKralx7Ha6\nrQKiAue8dhZ5RBuSxRRQIgaH2eXksg2SRwUi0x8n2r16Htyqj7TZh7fI/uOMe7of\nzosggySUSlumfRYUNFuFDk3wivuysYfdAbV1F+JsM3eJ8cQLs2VhU+GWUmjFXnlr\npeZW7PZa7alqlKtGNQnEOS3GCSCiyprymisr3PzQzX7wEvQwAcGKrAhQSmiU8KiT\ndYxRXsii7wMbyFo4my/CHLIE8yEJFFoLSY+PWZE81hoLyRlnk9OCMc0dpGiV9jox\n+q4Zimwh2MAYkUWYOuOdJh1EGa5b7ESVs2ZJO+YpPWEF8R64ik5wRtBFtDGzpJyb\nJyOi8YFrQ+k5zjxlrJVLHnyywlja7iWlC8pwlcEqGUs1QTQENhiTUkG2oVF1cXO7\nxman227nw/hi3dp8hGnhFGtUcbrbpWOpWqJTMULKznELCF4pLYyRDj2oyKSKnghJ\nybigQQpP2LPxyTsmPUuEHwfBvVbMuX7wThe3llpiPhsAowMjAqPWTmiu6SwlAQPP\nXliPKHgKXuuYs2QeqPAM1SA1DC9FOcilENzPp9/gExg3NRPU0NzYK1V1pNPrmVZd\nY/eYGoXY3tqeKj994UqYZj3boW+iUfVqqAt6+tLDLVPtalTTW2v8cNcZS12A3vKo\nA+XGSTXLLXWZswKcZEoQXuF0AOctuMxFihKpTShJdCyw0qcl2uC87Y0FYjh5RAyq\nORfRE+NJyCQMo7oTXvlSgRaJLlJJobXScO8KuTGgRWGTSTkoIxeKUYIPxDgOSn8/\nMcZb9cR0WKhFZ3K6V55jxZCLiWHmiBEyWgNCk3ExZciUiIlCEI8pceuQrI8JA+QR\nTlvqFG8jFyKAQgXUtvm7xfDFnwZiIOiAyC21pUXqV5dyslwJTvZB7ZZJWxCQlZXk\nqpF6NtPJSeooFSMwsECMfvCBGMdB6e8nBu1Y2BhHHXauDpy4YnwxMewcMVhUZEko\nBbWDDmSgAGRZ3GDwAI5rGXIGzY0MmllvqK6CTZpFUgKsVfw7xVBkxkfECDlJZQQI\n7iVZny/yZ8mRYquY6UKMhWNmHBNWJgzFw60DKoasY8j6GDE86wf/1qYAHe0zkUVD\n/evpjjCSgHtaY14oncvF58nNQYGMjMqCzDgEKjxFFNj/XYxywy8YSqo+/XU7tidp\nfaKuO73RxTRZTBE/RxEvEBHo6vY+Bs09pWmNjtzSIpmViOiCMyFApDmCRgO60ulC\nYZln8gKxiFdt6B/TrKE1WcB3YHayDak5Ab2J4yPJ/yeJ7WUM6acwmEYa1dH5g77N\nrzLryO/x5k6ri81W2aEtQe7TPSgPAmyNMeSPHBgNCHQ9SZc1GRF6lxwNS0w6IchM\nLWc2QPI8Z2rT5ERyKvmjiZLD1TBOISndKainu1htP7B//UlDwyNDp5x8Qhljh0ZO\nW/LtcHvjsz/4+jd3PvjW9b+c+Wu165rHX/z37/+w4qevrZr5hbj5oZ3m6i0zn709\n8vzaM+4be3THexufO/PzsRde+scPmxe8vORPo8s/vPhn+9/574p7bl9+w8lX7xz7\nci3fduDLu8ccDC9j7YevW7vi/aeePvE/ay7a9sm6Fz9fddnLl+45eOU/7ZZDa/bx\n19bA7jdXXfHzXR//Zf/eH7ceuOndjVvOXr2zXrbjzKVXfbB6RG5bsu+SB3cv3XvW\n5c+sfHfpXedf+/wrH+35+/1P7LhmaO/GJ9m9n35xq37zJ39++I0PP75t9SPPPXXP\nnuFTGvJVue+Zs/617PW/XX76JbuvHb7w16ctP9h97I+/XXn/Vzt/tf7Ahl3r3Ekr\nHzt0Q3P9OxPLmqeOPX3RVSPpG2YdtWQ=\n=h5Bq\n-----END PGP MESSAGE-----","payload_json":"{\"body\":{\"client\":{\"name\":\"keybase.io web\"},\"key\":{\"eldest_kid\":\"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a\",\"fingerprint\":\"a889587e1ce7bd7edbe3eeb8ef8fd8f7b31c4a2f\",\"host\":\"keybase.io\",\"key_id\":\"ef8fd8f7b31c4a2f\",\"kid\":\"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a\",\"uid\":\"92b3b3dbe457059f28c9f74e8e6b9419\",\"username\":\"tracy_friend1\"},\"track\":{\"basics\":{\"id_version\":14,\"last_id_change\":1449514728,\"username\":\"t_tracy\"},\"id\":\"eb72f49f2dde6429e5d78003dae0c919\",\"key\":{\"key_fingerprint\":\"\",\"kid\":\"01209bd2e255235529cf45877767ad8687d85200518adc74595d058750e2f7ab7b000a\"},\"pgp_keys\":[{\"key_fingerprint\":\"4ff50d580914427227bb14c821029e2c7cf0d488\",\"kid\":\"0101ee69b1566428109eb7548d9a9d7267d48933daa4614fa743cedbeac618ab66dd0a\"}],\"remote_proofs\":[{\"ctime\":1449512840,\"curr\":\"f09c84ccadf8817aea944526638e9a4c034c9200dd68b5a3292c7f69d980390d\",\"etime\":1954088840,\"prev\":\"909f6aa65b050ec5582515cad43aeb1f9279ee21db955cff309abe4692b7e11a\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"twitter\",\"username\":\"tacovontaco\"},\"proof_type\":2,\"state\":1},\"seqno\":5,\"sig_id\":\"67570e971c5b8881cf07179d1872a83042be4285ba897a8f12dc3e419cade80b0f\",\"sig_type\":2},{\"ctime\":1449512883,\"curr\":\"8ad8ce94c9d23d260750294905877ef92adf4e7736198909fcbe7e27d6dfb463\",\"etime\":1954088883,\"prev\":\"f09c84ccadf8817aea944526638e9a4c034c9200dd68b5a3292c7f69d980390d\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"github\",\"username\":\"tacoplusplus\"},\"proof_type\":3,\"state\":1},\"seqno\":6,\"sig_id\":\"bfe76a25acf046f7477350291cdd178e1f0026a49f85733d97c122ba4e4a000f0f\",\"sig_type\":2},{\"ctime\":1449512914,\"curr\":\"ea5bee1701e7ec7c8dfd71421bd2ab6fb0fa2af473412c664fa49d35c34078ea\",\"etime\":1954088914,\"prev\":\"8ad8ce94c9d23d260750294905877ef92adf4e7736198909fcbe7e27d6dfb463\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"rooter\",\"username\":\"t_tracy\"},\"proof_type\":100001,\"state\":1},\"seqno\":7,\"sig_id\":\"0c467de321795b777aa10916eb9aa8153bffa5163b5079600db7d50ca00a77410f\",\"sig_type\":2},{\"ctime\":1449514687,\"curr\":\"bfd3462a2193fa7946f7f31e5074cfc4ac95400680273deb520078a6a4f5cbf5\",\"etime\":1954090687,\"prev\":\"9ae84f56c0c62dc91206363b9f5609245f94199d58a4a3c0bee7d4bb91c47de7\",\"remote_key_proof\":{\"check_data_json\":{\"hostname\":\"keybase.io\",\"protocol\":\"https:\"},\"proof_type\":1000,\"state\":1},\"seqno\":9,\"sig_id\":\"92eeea3db99cb519409765c17ea32a82ce8b86bbacd8f366e8e8930f1faea20b0f\",\"sig_type\":2}],\"seq_tail\":{\"payload_hash\":\"bfd3462a2193fa7946f7f31e5074cfc4ac95400680273deb520078a6a4f5cbf5\",\"seqno\":9,\"sig_id\":\"92eeea3db99cb519409765c17ea32a82ce8b86bbacd8f366e8e8930f1faea20b0f\"}},\"type\":\"track\",\"version\":1},\"ctime\":1449514785,\"expire_in\":157680000,\"prev\":\"a4f76660341a087d69238f5a25e98d8b3d038224457107bad91ffdfbd82d84d9\",\"seqno\":3,\"tag\":\"signature\"}","sig_type":3,"ctime":1449514785,"etime":1607194785,"rtime":null,"sig_status":0,"prev":"a4f76660341a087d69238f5a25e98d8b3d038224457107bad91ffdfbd82d84d9","proof_id":null,"proof_type":null,"proof_text_check":null,"proof_text_full":null,"check_data_json":null,"remote_id":null,"api_url":null,"human_url":null,"proof_state":null,"proof_status":null,"retry_count":null,"hard_fail_count":null,"last_check":null,"last_success":null,"version":null,"fingerprint":"a889587e1ce7bd7edbe3eeb8ef8fd8f7b31c4a2f","sig_version":1}`