github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/common_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 package engine 5 6 import ( 7 "crypto/rand" 8 "encoding/hex" 9 "errors" 10 "fmt" 11 "testing" 12 13 "github.com/keybase/client/go/externalstest" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/keybase1" 16 insecureTriplesec "github.com/keybase/go-triplesec-insecure" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func SetupEngineTest(tb libkb.TestingTB, name string) libkb.TestContext { 21 tc := externalstest.SetupTest(tb, name, 2) 22 23 // use an insecure triplesec in tests 24 tc.G.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) { 25 warner := func() { tc.G.Log.Warning("Installing insecure Triplesec with weak stretch parameters") } 26 isProduction := func() bool { 27 return tc.G.Env.GetRunMode() == libkb.ProductionRunMode 28 } 29 return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction) 30 } 31 32 return tc 33 } 34 35 func SetupEngineTestRealTriplesec(tb libkb.TestingTB, name string) libkb.TestContext { 36 tc := externalstest.SetupTest(tb, name, 2) 37 tc.G.NewTriplesec = libkb.NewSecureTriplesec 38 return tc 39 } 40 41 type FakeUser struct { 42 Username string 43 Email string 44 Passphrase string 45 User *libkb.User 46 EncryptionKey libkb.GenericKey 47 DeviceName string 48 } 49 50 func NewFakeUser(prefix string) (fu *FakeUser, err error) { 51 buf := make([]byte, 5) 52 if _, err = rand.Read(buf); err != nil { 53 return 54 } 55 username := fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(buf)) 56 email := fmt.Sprintf("%s@noemail.keybase.io", username) 57 buf = make([]byte, 12) 58 if _, err = rand.Read(buf); err != nil { 59 return 60 } 61 passphrase := hex.EncodeToString(buf) 62 fu = &FakeUser{Username: username, Email: email, Passphrase: passphrase} 63 return 64 } 65 66 func (fu FakeUser) NormalizedUsername() libkb.NormalizedUsername { 67 return libkb.NewNormalizedUsername(fu.Username) 68 } 69 70 func (fu *FakeUser) LoadUser(tc libkb.TestContext) error { 71 var err error 72 fu.User, err = libkb.LoadMe(libkb.NewLoadUserArg(tc.G)) 73 return err 74 } 75 76 func (fu FakeUser) UID() keybase1.UID { 77 // All new-style names will have a 1-to-1 mapping 78 return libkb.UsernameToUID(fu.Username) 79 } 80 81 func (fu FakeUser) UserVersion() keybase1.UserVersion { 82 return keybase1.UserVersion{Uid: fu.UID(), EldestSeqno: 1} 83 } 84 85 func NewFakeUserOrBust(tb libkb.TestingTB, prefix string) (fu *FakeUser) { 86 var err error 87 if fu, err = NewFakeUser(prefix); err != nil { 88 tb.Fatal(err) 89 } 90 return fu 91 } 92 93 const defaultDeviceName = "my device" 94 95 // MakeTestSignupEngineRunArg fills a SignupEngineRunArg with the most 96 // common parameters for testing and returns it. 97 func MakeTestSignupEngineRunArg(fu *FakeUser) SignupEngineRunArg { 98 return SignupEngineRunArg{ 99 Username: fu.Username, 100 Email: fu.Email, 101 InviteCode: libkb.TestInvitationCode, 102 Passphrase: fu.Passphrase, 103 StoreSecret: false, 104 DeviceName: defaultDeviceName, 105 SkipGPG: true, 106 SkipMail: true, 107 SkipPaper: true, 108 } 109 } 110 111 func SignupFakeUserWithArg(tc libkb.TestContext, fu *FakeUser, arg SignupEngineRunArg) *SignupEngine { 112 uis := libkb.UIs{ 113 LogUI: tc.G.UI.GetLogUI(), 114 GPGUI: &gpgtestui{}, 115 SecretUI: fu.NewSecretUI(), 116 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 117 } 118 s := NewSignupEngine(tc.G, &arg) 119 m := NewMetaContextForTest(tc).WithUIs(uis) 120 err := RunEngine2(m, s) 121 require.NoError(tc.T, err) 122 fu.EncryptionKey = s.encryptionKey 123 return s 124 } 125 126 func CreateAndSignupFakeUser(tc libkb.TestContext, prefix string) *FakeUser { 127 fu, _ := CreateAndSignupFakeUser2(tc, prefix) 128 return fu 129 } 130 131 func CreateAndSignupFakeUser2(tc libkb.TestContext, prefix string) (*FakeUser, *SignupEngine) { 132 fu := NewFakeUserOrBust(tc.T, prefix) 133 tc.G.Log.Debug("New test user: %s / %s", fu.Username, fu.Email) 134 arg := MakeTestSignupEngineRunArg(fu) 135 fu.DeviceName = arg.DeviceName 136 eng := SignupFakeUserWithArg(tc, fu, arg) 137 return fu, eng 138 } 139 140 func CreateAndSignupFakeUserPaper(tc libkb.TestContext, prefix string) *FakeUser { 141 fu := NewFakeUserOrBust(tc.T, prefix) 142 tc.G.Log.Debug("New test user: %s / %s", fu.Username, fu.Email) 143 arg := MakeTestSignupEngineRunArg(fu) 144 arg.SkipPaper = false 145 _ = SignupFakeUserWithArg(tc, fu, arg) 146 return fu 147 } 148 149 func CreateAndSignupFakeUserSafeWithArg(g *libkb.GlobalContext, fu *FakeUser, arg SignupEngineRunArg) (*FakeUser, error) { 150 uis := libkb.UIs{ 151 LogUI: g.UI.GetLogUI(), 152 GPGUI: &gpgtestui{}, 153 SecretUI: fu.NewSecretUI(), 154 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 155 } 156 s := NewSignupEngine(g, &arg) 157 err := RunEngine2(libkb.NewMetaContextTODO(g).WithUIs(uis), s) 158 if err != nil { 159 return nil, err 160 } 161 return fu, nil 162 } 163 164 func CreateAndSignupFakeUserSafe(g *libkb.GlobalContext, prefix string) (*FakeUser, error) { 165 fu, err := NewFakeUser(prefix) 166 if err != nil { 167 return nil, err 168 } 169 arg := MakeTestSignupEngineRunArg(fu) 170 171 return CreateAndSignupFakeUserSafeWithArg(g, fu, arg) 172 } 173 174 func CreateAndSignupFakeUserGPG(tc libkb.TestContext, prefix string) *FakeUser { 175 fu := NewFakeUserOrBust(tc.T, prefix) 176 if err := tc.GenerateGPGKeyring(fu.Email); err != nil { 177 tc.T.Fatal(err) 178 } 179 arg := MakeTestSignupEngineRunArg(fu) 180 arg.SkipGPG = false 181 uis := libkb.UIs{ 182 LogUI: tc.G.UI.GetLogUI(), 183 GPGUI: &gpgtestui{}, 184 SecretUI: fu.NewSecretUI(), 185 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 186 } 187 s := NewSignupEngine(tc.G, &arg) 188 err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s) 189 if err != nil { 190 tc.T.Fatal(err) 191 } 192 return fu 193 } 194 195 func SignupFakeUserStoreSecret(tc libkb.TestContext, prefix string) *FakeUser { 196 fu := NewFakeUserOrBust(tc.T, prefix) 197 tc.G.Log.Debug("New test user: %s / %s", fu.Username, fu.Email) 198 arg := MakeTestSignupEngineRunArg(fu) 199 arg.SkipPaper = false 200 arg.StoreSecret = true 201 _ = SignupFakeUserWithArg(tc, fu, arg) 202 return fu 203 } 204 205 func CreateAndSignupFakeUserCustomArg(tc libkb.TestContext, prefix string, fmod func(*SignupEngineRunArg)) (fu *FakeUser, signingKey libkb.GenericKey, encryptionKey libkb.NaclDHKeyPair) { 206 fu = NewFakeUserOrBust(tc.T, prefix) 207 arg := MakeTestSignupEngineRunArg(fu) 208 fmod(&arg) 209 uis := libkb.UIs{ 210 LogUI: tc.G.UI.GetLogUI(), 211 GPGUI: &gpgtestui{}, 212 SecretUI: fu.NewSecretUI(), 213 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 214 } 215 s := NewSignupEngine(tc.G, &arg) 216 err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s) 217 if err != nil { 218 tc.T.Fatal(err) 219 } 220 return fu, s.signingKey, s.encryptionKey 221 } 222 223 func CreateAndSignupFakeUserWithPassphrase(tc libkb.TestContext, prefix, passphrase string) *FakeUser { 224 fu := NewFakeUserOrBust(tc.T, prefix) 225 fu.Passphrase = passphrase 226 tc.G.Log.Debug("New test user: %s / %s", fu.Username, fu.Email) 227 arg := MakeTestSignupEngineRunArg(fu) 228 SignupFakeUserWithArg(tc, fu, arg) 229 return fu 230 } 231 232 func (fu *FakeUser) LoginWithSecretUI(secui libkb.SecretUI, g *libkb.GlobalContext) error { 233 uis := libkb.UIs{ 234 ProvisionUI: newTestProvisionUI(), 235 LogUI: g.UI.GetLogUI(), 236 GPGUI: &gpgtestui{}, 237 SecretUI: secui, 238 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 239 } 240 m := libkb.NewMetaContextTODO(g).WithUIs(uis) 241 li := NewLogin(g, keybase1.DeviceTypeV2_DESKTOP, fu.Username, keybase1.ClientType_CLI) 242 return RunEngine2(m, li) 243 } 244 245 func (fu *FakeUser) Login(g *libkb.GlobalContext) error { 246 s := fu.NewSecretUI() 247 return fu.LoginWithSecretUI(s, g) 248 } 249 250 type nullSecretUI struct{} 251 252 func (n nullSecretUI) GetPassphrase(pinentry keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (keybase1.GetPassphraseRes, error) { 253 return keybase1.GetPassphraseRes{}, errors.New("nullSecretUI should never be called") 254 } 255 256 func (fu *FakeUser) SwitchTo(g *libkb.GlobalContext, withPassword bool) error { 257 var secui libkb.SecretUI 258 if withPassword { 259 secui = fu.NewSecretUI() 260 } else { 261 secui = nullSecretUI{} 262 } 263 uis := libkb.UIs{ 264 ProvisionUI: newTestProvisionUI(), 265 LogUI: g.UI.GetLogUI(), 266 GPGUI: &gpgtestui{}, 267 SecretUI: secui, 268 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 269 } 270 m := libkb.NewMetaContextTODO(g).WithUIs(uis) 271 li := NewLoginWithUserSwitch(g, keybase1.DeviceTypeV2_DESKTOP, fu.Username, keybase1.ClientType_CLI, true) 272 return RunEngine2(m, li) 273 } 274 275 func (fu *FakeUser) LoginOrBust(tc libkb.TestContext) { 276 if err := fu.Login(tc.G); err != nil { 277 tc.T.Fatal(err) 278 } 279 } 280 281 func (fu *FakeUser) NewSecretUI() *libkb.TestSecretUI { 282 return &libkb.TestSecretUI{Passphrase: fu.Passphrase} 283 } 284 285 func (fu *FakeUser) NewCountSecretUI() *libkb.TestCountSecretUI { 286 return &libkb.TestCountSecretUI{Passphrase: fu.Passphrase} 287 } 288 289 func AssertProvisioned(tc libkb.TestContext) error { 290 m := NewMetaContextForTest(tc) 291 prov, err := isLoggedInWithError(m) 292 if err != nil { 293 return err 294 } 295 if !prov { 296 return libkb.LoginRequiredError{} 297 } 298 return nil 299 } 300 301 func AssertLoggedIn(tc libkb.TestContext) error { 302 if !LoggedIn(tc) { 303 return libkb.LoginRequiredError{} 304 } 305 return nil 306 } 307 308 func AssertLoggedOut(tc libkb.TestContext) error { 309 if LoggedIn(tc) { 310 return libkb.LogoutError{} 311 } 312 return nil 313 } 314 315 func LoggedIn(tc libkb.TestContext) bool { 316 return tc.G.ActiveDevice.Valid() 317 } 318 319 func Logout(tc libkb.TestContext) { 320 mctx := libkb.NewMetaContextForTest(tc) 321 if err := mctx.LogoutKillSecrets(); err != nil { 322 tc.T.Fatalf("logout error: %s", err) 323 } 324 } 325 326 // TODO: Add tests that use testEngineWithSecretStore for every engine 327 // that should work with the secret store. 328 329 // testEngineWithSecretStore takes a given engine-running function and 330 // makes sure that it works with the secret store, i.e. that it stores 331 // data into it when told to and reads data out from it. 332 func testEngineWithSecretStore( 333 t *testing.T, 334 runEngine func(libkb.TestContext, *FakeUser, libkb.SecretUI)) { 335 336 tc := SetupEngineTest(t, "wss") 337 defer tc.Cleanup() 338 339 fu := SignupFakeUserStoreSecret(tc, "wss") 340 simulateServiceRestart(t, tc, fu) 341 342 testSecretUI := libkb.TestSecretUI{ 343 Passphrase: fu.Passphrase, 344 StoreSecret: true, 345 } 346 runEngine(tc, fu, &testSecretUI) 347 348 if testSecretUI.CalledGetPassphrase { 349 t.Fatal("GetPassphrase() unexpectedly called") 350 } 351 } 352 353 func SetupTwoDevices(t *testing.T, nm string) (user *FakeUser, dev1 libkb.TestContext, dev2 libkb.TestContext, cleanup func()) { 354 return SetupTwoDevicesWithHook(t, nm, nil) 355 } 356 357 func SetupTwoDevicesWithHook(t *testing.T, nm string, hook func(tc *libkb.TestContext)) (user *FakeUser, dev1 libkb.TestContext, dev2 libkb.TestContext, cleanup func()) { 358 if len(nm) > 5 { 359 t.Fatalf("Sorry, test name must be fewer than 6 chars (got %q)", nm) 360 } 361 362 // device X (provisioner) context: 363 dev1 = SetupEngineTest(t, nm) 364 365 // device Y (provisionee) context: 366 dev2 = SetupEngineTest(t, nm) 367 if hook != nil { 368 hook(&dev2) 369 } 370 371 user = NewFakeUserOrBust(t, nm) 372 arg := MakeTestSignupEngineRunArg(user) 373 arg.SkipPaper = false 374 loginUI := &paperLoginUI{Username: user.Username} 375 uis := libkb.UIs{ 376 LogUI: dev1.G.UI.GetLogUI(), 377 GPGUI: &gpgtestui{}, 378 SecretUI: user.NewSecretUI(), 379 LoginUI: loginUI, 380 } 381 s := NewSignupEngine(dev1.G, &arg) 382 err := RunEngine2(NewMetaContextForTest(dev1).WithUIs(uis), s) 383 if err != nil { 384 t.Fatal(err) 385 } 386 387 assertNumDevicesAndKeys(dev1, user, 2, 4) 388 389 if len(loginUI.PaperPhrase) == 0 { 390 t.Fatal("login ui has no paper key phrase") 391 } 392 393 secUI := user.NewSecretUI() 394 secUI.Passphrase = loginUI.PaperPhrase 395 provUI := newTestProvisionUIPaper() 396 provLoginUI := &libkb.TestLoginUI{Username: user.Username} 397 uis = libkb.UIs{ 398 ProvisionUI: provUI, 399 LogUI: dev2.G.UI.GetLogUI(), 400 SecretUI: secUI, 401 LoginUI: provLoginUI, 402 GPGUI: &gpgtestui{}, 403 } 404 eng := NewLogin(dev2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI) 405 m2 := NewMetaContextForTest(dev2).WithUIs(uis) 406 if err := RunEngine2(m2, eng); err != nil { 407 t.Fatal(err) 408 } 409 410 testUserHasDeviceKey(dev2) 411 412 assertNumDevicesAndKeys(dev2, user, 3, 6) 413 414 if err := AssertProvisioned(dev2); err != nil { 415 t.Fatal(err) 416 } 417 418 cleanup = func() { 419 dev1.Cleanup() 420 dev2.Cleanup() 421 } 422 423 return user, dev1, dev2, cleanup 424 } 425 426 func NewMetaContextForTest(tc libkb.TestContext) libkb.MetaContext { 427 return libkb.NewMetaContextForTest(tc) 428 } 429 func NewMetaContextForTestWithLogUI(tc libkb.TestContext) libkb.MetaContext { 430 return libkb.NewMetaContextForTestWithLogUI(tc) 431 } 432 433 func ResetAccount(tc libkb.TestContext, u *FakeUser) { 434 ResetAccountNoLogout(tc, u) 435 Logout(tc) 436 } 437 438 func ResetAccountNoLogout(tc libkb.TestContext, u *FakeUser) { 439 m := NewMetaContextForTest(tc) 440 err := libkb.ResetAccount(m, u.NormalizedUsername(), u.Passphrase) 441 if err != nil { 442 tc.T.Fatalf("In account reset: %s", err) 443 } 444 tc.T.Logf("Account reset for user %s", u.Username) 445 } 446 447 func ForcePUK(tc libkb.TestContext) { 448 arg := &PerUserKeyUpgradeArgs{} 449 eng := NewPerUserKeyUpgrade(tc.G, arg) 450 uis := libkb.UIs{ 451 LogUI: tc.G.UI.GetLogUI(), 452 } 453 m := NewMetaContextForTest(tc).WithUIs(uis) 454 if err := RunEngine2(m, eng); err != nil { 455 tc.T.Fatal(err) 456 } 457 } 458 459 func getUserSeqno(tc *libkb.TestContext, uid keybase1.UID) keybase1.Seqno { 460 mctx := NewMetaContextForTest(*tc) 461 res, err := tc.G.API.Get(mctx, libkb.APIArg{ 462 Endpoint: "user/lookup", 463 Args: libkb.HTTPArgs{ 464 "uid": libkb.UIDArg(uid), 465 }, 466 }) 467 require.NoError(tc.T, err) 468 seqno, err := res.Body.AtKey("them").AtKey("sigs").AtKey("last").AtKey("seqno").GetInt() 469 require.NoError(tc.T, err) 470 return keybase1.Seqno(seqno) 471 } 472 473 func checkUserSeqno(tc *libkb.TestContext, uid keybase1.UID, expected keybase1.Seqno) { 474 require.Equal(tc.T, expected, getUserSeqno(tc, uid)) 475 } 476 477 func fakeSalt() []byte { 478 return []byte("fakeSALTfakeSALT") 479 } 480 481 func clearCaches(g *libkb.GlobalContext) { 482 g.ActiveDevice.ClearCaches() 483 }