github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/upak_loader_test.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 // There are two test files by this name. One in libkb, one in engine. 5 6 package engine 7 8 import ( 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/keybase/client/go/libkb" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 "github.com/keybase/clockwork" 16 "github.com/stretchr/testify/require" 17 "golang.org/x/net/context" 18 ) 19 20 func TestLoadDeviceKeyNew(t *testing.T) { 21 tc := SetupEngineTest(t, "clu") 22 defer tc.Cleanup() 23 24 t.Logf("create new user") 25 fu := NewFakeUserOrBust(t, "paper") 26 arg := MakeTestSignupEngineRunArg(fu) 27 arg.SkipPaper = false 28 loginUI := &paperLoginUI{Username: fu.Username} 29 uis := libkb.UIs{ 30 LogUI: tc.G.UI.GetLogUI(), 31 GPGUI: &gpgtestui{}, 32 SecretUI: fu.NewSecretUI(), 33 LoginUI: loginUI, 34 } 35 s := NewSignupEngine(tc.G, &arg) 36 err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s) 37 if err != nil { 38 tc.T.Fatal(err) 39 } 40 t.Logf("using username:%+v", fu.Username) 41 loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional() 42 user, err := libkb.LoadUser(loadArg) 43 if err != nil { 44 tc.T.Fatal(err) 45 } 46 t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID()) 47 48 assertNumDevicesAndKeys(tc, fu, 2, 4) 49 50 devices, _ := getActiveDevicesAndKeys(tc, fu) 51 var device1 *libkb.Device 52 for _, device := range devices { 53 if device.Type != keybase1.DeviceTypeV2_PAPER { 54 device1 = device.Device 55 } 56 } 57 require.NotNil(t, device1, "device1 should be non-nil") 58 t.Logf("using device1:%+v", device1.ID) 59 60 t.Logf("load existing device key") 61 upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device1.ID) 62 require.NoError(t, err) 63 require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match") 64 require.Equal(t, device1.ID, deviceKey.DeviceID, "deviceID must match") 65 require.Equal(t, *device1.Description, deviceKey.DeviceDescription, "device name must match") 66 require.Nil(t, revoked, "device not revoked") 67 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 68 dev, err := u.GetDevice(device1.ID) 69 require.NoError(t, err) 70 require.NotNil(t, dev) 71 return nil 72 }) 73 require.NoError(t, err) 74 75 Logout(tc) 76 77 if len(loginUI.PaperPhrase) == 0 { 78 t.Fatal("login ui has no paper key phrase") 79 } 80 81 t.Logf("create new device") 82 // redo SetupEngineTest to get a new home directory...should look like a new device. 83 tc2 := SetupEngineTest(t, "login") 84 defer tc2.Cleanup() 85 86 secUI := fu.NewSecretUI() 87 provUI := newTestProvisionUIPaper() 88 provLoginUI := &libkb.TestLoginUI{Username: fu.Username} 89 uis = libkb.UIs{ 90 ProvisionUI: provUI, 91 LogUI: tc2.G.UI.GetLogUI(), 92 SecretUI: secUI, 93 LoginUI: provLoginUI, 94 GPGUI: &gpgtestui{}, 95 } 96 97 eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase) 98 m := NewMetaContextForTest(tc2).WithUIs(uis) 99 if err := RunEngine2(m, eng); err != nil { 100 t.Fatal(err) 101 } 102 t.Logf("d2 provisioned (1)") 103 104 testUserHasDeviceKey(tc2) 105 require.NoError(t, AssertProvisioned(tc2)) 106 t.Logf("d2 provisioned (2)") 107 108 devices, _ = getActiveDevicesAndKeys(tc, fu) 109 var device2 *libkb.Device 110 for _, device := range devices { 111 if device.Type != keybase1.DeviceTypeV2_PAPER && device.ID != device1.ID { 112 device2 = device.Device 113 } 114 } 115 require.NotNil(t, device2, "device2 should be non-nil") 116 t.Logf("using device2:%+v", device2.ID) 117 118 t.Logf("load brand new device (while old is cached)") 119 upk, deviceKey, revoked, err = tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device2.ID) 120 require.NoError(t, err) 121 require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match") 122 require.Equal(t, device2.ID, deviceKey.DeviceID, "deviceID must match") 123 require.Equal(t, *device2.Description, deviceKey.DeviceDescription, "device name must match") 124 require.Nil(t, revoked, "device not revoked") 125 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 126 dev, err := u.GetDevice(deviceKey.DeviceID) 127 require.NoError(t, err) 128 require.NotNil(t, dev) 129 return nil 130 }) 131 require.NoError(t, err) 132 } 133 134 func TestLoadDeviceKeyRevoked(t *testing.T) { 135 tc := SetupEngineTest(t, "clu") 136 defer tc.Cleanup() 137 138 fu := CreateAndSignupFakeUserPaper(tc, "rev") 139 t.Logf("using username:%+v", fu.Username) 140 loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional() 141 user, err := libkb.LoadUser(loadArg) 142 if err != nil { 143 tc.T.Fatal(err) 144 } 145 t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID()) 146 147 assertNumDevicesAndKeys(tc, fu, 2, 4) 148 149 devices, _ := getActiveDevicesAndKeys(tc, fu) 150 var thisDevice *libkb.Device 151 for _, device := range devices { 152 if device.Type != keybase1.DeviceTypeV2_PAPER { 153 thisDevice = device.Device 154 } 155 } 156 157 // Revoke the current device with --force 158 err = doRevokeDevice(tc, fu, thisDevice.ID, true, false) 159 if err != nil { 160 tc.T.Fatal(err) 161 } 162 163 assertNumDevicesAndKeys(tc, fu, 1, 2) 164 165 t.Logf("load revoked device") 166 upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), thisDevice.ID) 167 require.NoError(t, err) 168 require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match") 169 require.Equal(t, thisDevice.ID, deviceKey.DeviceID, "deviceID must match") 170 require.Equal(t, *thisDevice.Description, deviceKey.DeviceDescription, "device name must match") 171 require.NotNil(t, revoked, "device should be revoked") 172 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 173 dev, err := u.GetDevice(deviceKey.DeviceID) 174 require.NoError(t, err) 175 require.NotNil(t, dev) 176 require.False(t, dev.IsActive()) 177 dev, err = u.GetDevice(thisDevice.ID) 178 require.NoError(t, err) 179 require.NotNil(t, dev) 180 return nil 181 }) 182 require.NoError(t, err) 183 } 184 185 func TestFullSelfCacherFlushSingleMachine(t *testing.T) { 186 tc := SetupEngineTest(t, "fsc") 187 defer tc.Cleanup() 188 sigVersion := libkb.GetDefaultSigVersion(tc.G) 189 190 fu := CreateAndSignupFakeUser(tc, "fsc") 191 192 var scv keybase1.Seqno 193 err := tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 194 require.NotNil(t, u) 195 scv = u.GetSigChainLastKnownSeqno() 196 return nil 197 }) 198 require.NoError(t, err) 199 trackAlice(tc, fu, sigVersion) 200 defer untrackAlice(tc, fu, sigVersion) 201 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 202 require.NotNil(t, u) 203 require.True(t, u.GetSigChainLastKnownSeqno() > scv) 204 return nil 205 }) 206 require.NoError(t, err) 207 } 208 209 func TestFullSelfCacherFlushTwoMachines(t *testing.T) { 210 tc := SetupEngineTest(t, "fsc") 211 defer tc.Cleanup() 212 fakeClock := clockwork.NewFakeClockAt(time.Now()) 213 tc.G.SetClock(fakeClock) 214 215 t.Logf("create new user") 216 fu := NewFakeUserOrBust(t, "paper") 217 arg := MakeTestSignupEngineRunArg(fu) 218 arg.SkipPaper = false 219 loginUI := &paperLoginUI{Username: fu.Username} 220 uis := libkb.UIs{ 221 LogUI: tc.G.UI.GetLogUI(), 222 GPGUI: &gpgtestui{}, 223 SecretUI: fu.NewSecretUI(), 224 LoginUI: loginUI, 225 } 226 s := NewSignupEngine(tc.G, &arg) 227 err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s) 228 if err != nil { 229 tc.T.Fatal(err) 230 } 231 t.Logf("using username:%+v", fu.Username) 232 233 var scv keybase1.Seqno 234 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 235 require.NotNil(t, u) 236 scv = u.GetSigChainLastKnownSeqno() 237 return nil 238 }) 239 require.NoError(t, err) 240 241 if len(loginUI.PaperPhrase) == 0 { 242 t.Fatal("login ui has no paper key phrase") 243 } 244 245 t.Logf("create new device") 246 // redo SetupEngineTest to get a new home directory...should look like a new device. 247 tc2 := SetupEngineTest(t, "login") 248 defer tc2.Cleanup() 249 250 secUI := fu.NewSecretUI() 251 provUI := newTestProvisionUIPaper() 252 provLoginUI := &libkb.TestLoginUI{Username: fu.Username} 253 uis = libkb.UIs{ 254 ProvisionUI: provUI, 255 LogUI: tc2.G.UI.GetLogUI(), 256 SecretUI: secUI, 257 LoginUI: provLoginUI, 258 GPGUI: &gpgtestui{}, 259 } 260 261 eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase) 262 m := NewMetaContextForTest(tc2).WithUIs(uis) 263 if err := RunEngine2(m, eng); err != nil { 264 t.Fatal(err) 265 } 266 t.Logf("d2 provisioned (1)") 267 268 // Without pubsub (not available on engine tests), we don't get any 269 // invalidation of the user on the first machine (tc). So this 270 // user's sigchain should stay the same. 271 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 272 require.NotNil(t, u) 273 require.True(t, u.GetSigChainLastKnownSeqno() == scv) 274 return nil 275 }) 276 require.NoError(t, err) 277 278 // After the CachedUserTimeout, the FullSelfer ought to repoll. 279 // Check that the sigchain is updated after the repoll, which reflects 280 // the new device having been added. 281 fakeClock.Advance(libkb.CachedUserTimeout + time.Second) 282 err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 283 require.NotNil(t, u) 284 require.True(t, u.GetSigChainLastKnownSeqno() > scv) 285 return nil 286 }) 287 require.NoError(t, err) 288 } 289 290 func TestUPAKDeadlock(t *testing.T) { 291 tc := SetupEngineTest(t, "upak") 292 defer tc.Cleanup() 293 fu := CreateAndSignupFakeUserPaper(tc, "upak") 294 295 // First clear the cache 296 tc.G.KeyfamilyChanged(context.TODO(), fu.UID()) 297 298 var wg sync.WaitGroup 299 300 ch := make(chan struct{}) 301 302 tc.G.GetFullSelfer().(*libkb.CachedFullSelf).TestDeadlocker = func() { 303 <-ch 304 } 305 306 tc.G.GetUPAKLoader().(*libkb.CachedUPAKLoader).TestDeadlocker = func() { 307 ch <- struct{}{} 308 } 309 310 wg.Add(1) 311 go func() { 312 _ = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error { 313 require.Equal(t, u.GetUID(), fu.UID(), "right UID") 314 return nil 315 }) 316 wg.Done() 317 }() 318 319 wg.Add(1) 320 go func() { 321 un, err := tc.G.GetUPAKLoader().LookupUsername(context.TODO(), fu.UID()) 322 require.NoError(t, err) 323 if un.String() != fu.Username { 324 t.Errorf("username mismatch: %s != %s", un, fu.Username) 325 } 326 wg.Done() 327 }() 328 329 doneCh := make(chan struct{}) 330 go func() { 331 wg.Wait() 332 doneCh <- struct{}{} 333 }() 334 335 select { 336 case <-doneCh: 337 break 338 case <-time.After(20 * time.Second): 339 t.Fatal("deadlocked!") 340 } 341 } 342 343 func TestLoadAfterAcctReset1(t *testing.T) { 344 // One context for user that will be doing LoadUser, and another 345 // for user that will sign up and reset itself. 346 tc := SetupEngineTest(t, "clu") 347 defer tc.Cleanup() 348 349 resetUserTC := SetupEngineTest(t, "clu2") 350 defer resetUserTC.Cleanup() 351 352 t.Logf("create new user") 353 fu := CreateAndSignupFakeUser(resetUserTC, "res") 354 355 fakeClock := clockwork.NewFakeClockAt(time.Now()) 356 tc.G.SetClock(fakeClock) 357 358 loadUpak := func() error { 359 t.Logf("loadUpak: using username:%+v", fu.Username) 360 loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithNetContext(context.TODO()).WithStaleOK(false).WithForceMerkleServerPolling(true) 361 362 upak, _, err := tc.G.GetUPAKLoader().Load(loadArg) 363 if err != nil { 364 return err 365 } 366 367 t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys)) 368 return nil 369 } 370 371 err := loadUpak() 372 if err != nil { 373 t.Fatalf("Failed to load user: %+v", err) 374 } 375 376 ResetAccount(resetUserTC, fu) 377 378 loadUpakExpectFailure := func() { 379 err := loadUpak() 380 if err == nil { 381 t.Fatalf("Expected UPAKLoader.Load to fail on nuked account.") 382 } else if _, ok := err.(libkb.NoKeyError); !ok { 383 t.Fatalf("Expected UPAKLoader.Load to fail with NoKeyError, instead failed with: %+v", err) 384 } 385 } 386 387 // advance the clock past the cache timeout 388 fakeClock.Advance(libkb.CachedUserTimeout * 10) 389 loadUpakExpectFailure() 390 391 // Try again, see if still errors out (this time user should not 392 // be in cache at all). 393 fakeClock.Advance(libkb.CachedUserTimeout * 10) 394 loadUpakExpectFailure() 395 } 396 397 func TestLoadAfterAcctReset2(t *testing.T) { 398 // One context for user that will be doing LoadUser, and another 399 // for user that will sign up and reset itself. 400 tc := SetupEngineTest(t, "clu") 401 defer tc.Cleanup() 402 403 resetUserTC := SetupEngineTest(t, "clu2") 404 defer resetUserTC.Cleanup() 405 406 t.Logf("create new user") 407 fu := CreateAndSignupFakeUser(resetUserTC, "res") 408 409 fakeClock := clockwork.NewFakeClockAt(time.Now()) 410 tc.G.SetClock(fakeClock) 411 412 loadUpak := func() (*keybase1.UserPlusAllKeys, error) { 413 t.Logf("loadUpak: using username:%+v", fu.Username) 414 loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false) 415 upak, _, err := tc.G.GetUPAKLoader().Load(loadArg) 416 if err != nil { 417 return nil, err 418 } 419 420 t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys)) 421 return upak, nil 422 } 423 424 upak1, err := loadUpak() 425 if err != nil { 426 t.Fatalf("Failed to load user: %+v", err) 427 } 428 429 // Reset account and then login again to establish new eldest and 430 // add new device keys. 431 ResetAccount(resetUserTC, fu) 432 tcp := SetupEngineTest(t, "login") 433 defer tcp.Cleanup() 434 435 fu.LoginOrBust(tcp) 436 if err := AssertProvisioned(tcp); err != nil { 437 t.Fatal(err) 438 } 439 440 fakeClock.Advance(libkb.CachedUserTimeout * 10) 441 upak2, err := loadUpak() 442 if err != nil { 443 t.Fatalf("Failed to load user after reset+login with: %+v", err) 444 } 445 446 if upak1.Base.DeviceKeys[0].KID == upak2.Base.DeviceKeys[0].KID { 447 t.Fatal("Found old device key after LoadUser.") 448 } 449 } 450 451 // Test the bug in CORE-6943: after a reset, if we did two 452 // logins in a row, right on top of each other, previous subchains 453 // would be dropped from the self UPAK. 454 func TestLoadAfterAcctResetCORE6943(t *testing.T) { 455 tc := SetupEngineTest(t, "clu") 456 defer tc.Cleanup() 457 sigVersion := libkb.GetDefaultSigVersion(tc.G) 458 459 t.Logf("create new user") 460 fu := CreateAndSignupFakeUser(tc, "res") 461 462 trackAlice(tc, fu, sigVersion) 463 464 loadUpak := func() (*keybase1.UserPlusAllKeys, error) { 465 t.Logf("loadUpak: using username:%+v", fu.Username) 466 loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false) 467 upak, _, err := tc.G.GetUPAKLoader().Load(loadArg) 468 if err != nil { 469 return nil, err 470 } 471 472 t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys)) 473 return upak, nil 474 } 475 476 upak1, err := loadUpak() 477 if err != nil { 478 t.Fatalf("Failed to load user: %+v", err) 479 } 480 481 // Reset account and then login again to establish new eldest and 482 // add new device keys. 483 ResetAccount(tc, fu) 484 485 tc.G.GetUPAKLoader().Invalidate(context.TODO(), fu.UID()) 486 487 fu.LoginOrBust(tc) 488 if err := AssertProvisioned(tc); err != nil { 489 t.Fatal(err) 490 } 491 // login a second time to force the bug. 492 fu.LoginOrBust(tc) 493 494 // Make sure that we can load the eldest key from the previous subchain 495 _, _, _, err = tc.G.GetUPAKLoader().LoadKeyV2(context.TODO(), fu.UID(), upak1.Base.DeviceKeys[0].KID) 496 497 if err != nil { 498 t.Fatal("Failed to load a UID/KID combo from first incarnation") 499 } 500 501 _, err = loadUpak() 502 if err != nil { 503 t.Fatalf("Failed to load user: %+v", err) 504 } 505 } 506 507 func TestUPAKUnstub(t *testing.T) { 508 tc := SetupEngineTest(t, "login") 509 defer tc.Cleanup() 510 511 u1 := CreateAndSignupFakeUser(tc, "first") 512 Logout(tc) 513 u2 := CreateAndSignupFakeUser(tc, "secon") 514 515 testTrack(t, tc, libkb.KeybaseSignatureV2, "t_alice") 516 testTrack(t, tc, libkb.KeybaseSignatureV2, u1.Username) 517 518 // The last link is always unstubbed, so this is a throw-away so that we have some links that 519 // are stubbed (the two just above). 520 testTrack(t, tc, libkb.KeybaseSignatureV2, "t_bob") 521 522 Logout(tc) 523 t.Logf("first logging back in") 524 u1.LoginOrBust(tc) 525 526 upl := tc.G.GetUPAKLoader() 527 mctx := NewMetaContextForTest(tc) 528 529 // wipe out all the caches 530 _, err := tc.G.LocalDb.Nuke() 531 require.NoError(t, err) 532 upl.Invalidate(mctx.Ctx(), u2.UID()) 533 534 assertStubbed := func() { 535 arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID()) 536 upak, _, err := upl.LoadV2(arg) 537 require.NoError(t, err) 538 require.Equal(t, 1, len(upak.Current.RemoteTracks)) 539 require.Equal(t, "t_bob", upak.Current.RemoteTracks[keybase1.UID("afb5eda3154bc13c1df0189ce93ba119")].Username) 540 require.False(t, upak.Current.Unstubbed) 541 } 542 543 assertStubbed() 544 545 Logout(tc) 546 t.Logf("second logging back in") 547 u2.LoginOrBust(tc) 548 549 assertStubbed() 550 551 assertAllLinks := func(stubMode libkb.StubMode) { 552 arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID()).WithStubMode(stubMode) 553 upak, _, err := upl.LoadV2(arg) 554 require.NoError(t, err) 555 require.Equal(t, 3, len(upak.Current.RemoteTracks)) 556 require.Equal(t, u1.Username, upak.Current.RemoteTracks[u1.UID()].Username) 557 require.True(t, upak.Current.Unstubbed) 558 } 559 560 assertAllLinks(libkb.StubModeUnstubbed) 561 562 Logout(tc) 563 t.Logf("first logging back in") 564 u1.LoginOrBust(tc) 565 566 assertAllLinks(libkb.StubModeUnstubbed) 567 assertAllLinks(libkb.StubModeStubbed) 568 } 569 570 func TestInvalidation(t *testing.T) { 571 tc := SetupEngineTest(t, "login") 572 defer tc.Cleanup() 573 u := CreateAndSignupFakeUser(tc, "first") 574 upl := tc.G.GetUPAKLoader() 575 mctx := NewMetaContextForTest(tc) 576 arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()) 577 upak, _, err := upl.LoadV2(arg) 578 require.NoError(t, err) 579 require.NotNil(t, upak) 580 upl.Invalidate(mctx.Ctx(), u.UID()) 581 arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true) 582 _, _, err = upl.LoadV2(arg) 583 require.Error(t, err) 584 require.IsType(t, libkb.UserNotFoundError{}, err) 585 require.Contains(t, err.Error(), "cached user found, but it was stale, and cached only") 586 arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true).WithStaleOK(true) 587 upak, _, err = upl.LoadV2(arg) 588 require.NoError(t, err) 589 require.NotNil(t, upak) 590 }