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\n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\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}`