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