github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/signup_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 "fmt" 8 "os" 9 "path/filepath" 10 "runtime" 11 "testing" 12 13 "encoding/base64" 14 "github.com/keybase/client/go/bot" 15 "github.com/keybase/client/go/libkb" 16 keybase1 "github.com/keybase/client/go/protocol/keybase1" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func AssertDeviceID(g *libkb.GlobalContext) (err error) { 21 if g.Env.GetDeviceID().IsNil() { 22 err = fmt.Errorf("Device ID should not have been reset!") 23 } 24 return 25 } 26 27 func TestSignupEngine(t *testing.T) { 28 subTestSignupEngine(t, false) 29 } 30 31 func subTestSignupEngine(t *testing.T, upgradePerUserKey bool) { 32 tc := SetupEngineTest(t, "signup") 33 defer tc.Cleanup() 34 var err error 35 36 tc.Tp.DisableUpgradePerUserKey = !upgradePerUserKey 37 38 fu := CreateAndSignupFakeUser(tc, "se") 39 40 if err = AssertLoggedIn(tc); err != nil { 41 t.Fatal(err) 42 } 43 44 if err = AssertDeviceID(tc.G); err != nil { 45 t.Fatal(err) 46 } 47 48 me, err := libkb.LoadMe(libkb.NewLoadUserArg(tc.G)) 49 if err != nil { 50 t.Fatal(err) 51 } 52 if me.GetEldestKID().IsNil() { 53 t.Fatal("after signup, eldest kid is nil") 54 } 55 56 // Now try to logout and log back in 57 Logout(tc) 58 59 if err = AssertDeviceID(tc.G); err != nil { 60 t.Fatal(err) 61 } 62 63 if err := AssertLoggedOut(tc); err != nil { 64 t.Fatal(err) 65 } 66 67 fu.LoginOrBust(tc) 68 69 if err = AssertDeviceID(tc.G); err != nil { 70 t.Fatal(err) 71 } 72 73 if err = AssertLoggedIn(tc); err != nil { 74 t.Fatal(err) 75 } 76 77 if err = AssertDeviceID(tc.G); err != nil { 78 t.Fatal(err) 79 } 80 81 // Now try to logout and log back in 82 Logout(tc) 83 84 if err := AssertLoggedOut(tc); err != nil { 85 t.Fatal(err) 86 } 87 88 secretUI := fu.NewSecretUI() 89 err = fu.LoginWithSecretUI(secretUI, tc.G) 90 if err != nil { 91 t.Fatal(err) 92 } 93 94 if !secretUI.CalledGetPassphrase { 95 t.Errorf("secretUI.GetKeybasePassphrase() not called") 96 } 97 98 if err = AssertDeviceID(tc.G); err != nil { 99 t.Fatal(err) 100 } 101 102 if err = AssertLoggedIn(tc); err != nil { 103 t.Fatal(err) 104 } 105 106 // Now try to logout to make sure we logged out OK 107 Logout(tc) 108 109 if err = AssertDeviceID(tc.G); err != nil { 110 t.Fatal(err) 111 } 112 113 if err = AssertLoggedOut(tc); err != nil { 114 t.Fatal(err) 115 } 116 } 117 118 // Test that after signing up the used User object has their first per-user-key 119 // locall delegated. 120 func TestSignupLocalDelegatePerUserKey(t *testing.T) { 121 tc := SetupEngineTest(t, "signup") 122 defer tc.Cleanup() 123 124 _, signupEngine := CreateAndSignupFakeUser2(tc, "se") 125 126 u := signupEngine.GetMe() 127 require.NotNil(t, u, "no user from signup engine") 128 puk := u.GetComputedKeyFamily().GetLatestPerUserKey() 129 require.NotNil(t, puk, "no local per-user-key") 130 require.Equal(t, 1, puk.Gen) 131 } 132 133 func TestSignupWithGPG(t *testing.T) { 134 tc := SetupEngineTest(t, "signupWithGPG") 135 defer tc.Cleanup() 136 137 fu := NewFakeUserOrBust(t, "se") 138 if err := tc.GenerateGPGKeyring(fu.Email); err != nil { 139 t.Fatal(err) 140 } 141 arg := MakeTestSignupEngineRunArg(fu) 142 arg.SkipGPG = false 143 s := NewSignupEngine(tc.G, &arg) 144 uis := libkb.UIs{ 145 LogUI: tc.G.UI.GetLogUI(), 146 GPGUI: &gpgtestui{}, 147 SecretUI: fu.NewSecretUI(), 148 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 149 } 150 if err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s); err != nil { 151 t.Fatal(err) 152 } 153 } 154 155 func TestLocalKeySecurity(t *testing.T) { 156 tc := SetupEngineTest(t, "signup") 157 defer tc.Cleanup() 158 fu := NewFakeUserOrBust(t, "se") 159 arg := MakeTestSignupEngineRunArg(fu) 160 s := NewSignupEngine(tc.G, &arg) 161 uis := libkb.UIs{ 162 LogUI: tc.G.UI.GetLogUI(), 163 GPGUI: &gpgtestui{}, 164 SecretUI: fu.NewSecretUI(), 165 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 166 } 167 if err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s); err != nil { 168 t.Fatal(err) 169 } 170 171 m := NewMetaContextForTest(tc) 172 lks := libkb.NewLKSec(s.ppStream, s.uid) 173 if err := lks.Load(m); err != nil { 174 t.Fatal(err) 175 } 176 177 text := "the people on the bus go up and down, up and down, up and down" 178 enc, err := lks.Encrypt(m, []byte(text)) 179 if err != nil { 180 t.Fatal(err) 181 } 182 183 dec, _, _, err := lks.Decrypt(m, enc) 184 if err != nil { 185 t.Fatal(err) 186 } 187 if string(dec) != text { 188 t.Errorf("decrypt: %q, expected %q", string(dec), text) 189 } 190 } 191 192 // Test that the signup engine stores the secret correctly when 193 // StoreSecret is set. 194 func TestLocalKeySecurityStoreSecret(t *testing.T) { 195 tc := SetupEngineTest(t, "signup") 196 defer tc.Cleanup() 197 fu := NewFakeUserOrBust(t, "se") 198 mctx := tc.MetaContext() 199 200 secretStore := libkb.NewSecretStore(mctx, fu.NormalizedUsername()) 201 if secretStore == nil { 202 t.Skip("No SecretStore on this platform") 203 } 204 205 _, err := secretStore.RetrieveSecret(NewMetaContextForTest(tc)) 206 if err == nil { 207 t.Fatal("User unexpectedly has secret") 208 } 209 210 arg := MakeTestSignupEngineRunArg(fu) 211 arg.StoreSecret = true 212 s := SignupFakeUserWithArg(tc, fu, arg) 213 214 secret, err := s.lks.GetSecret(mctx) 215 if err != nil { 216 t.Fatal(err) 217 } 218 219 storedSecret, err := secretStore.RetrieveSecret(NewMetaContextForTest(tc)) 220 if err != nil { 221 t.Error(err) 222 } 223 224 if !secret.Equal(storedSecret) { 225 t.Errorf("Expected %v, got %v", secret, storedSecret) 226 } 227 228 err = tc.G.SecretStore().ClearSecret(NewMetaContextForTest(tc), fu.NormalizedUsername()) 229 if err != nil { 230 t.Error(err) 231 } 232 } 233 234 func TestIssue280(t *testing.T) { 235 tc := SetupEngineTest(t, "login") 236 defer tc.Cleanup() 237 238 // Initialize state with user U1 239 u1 := CreateAndSignupFakeUser(tc, "login") 240 Logout(tc) 241 u1.LoginOrBust(tc) 242 Logout(tc) 243 244 // Now try to sign in as user U2, and do something 245 // that needs access to a locked local secret key. 246 // Delegating to a new PGP key seems good enough. 247 u2 := CreateAndSignupFakeUser(tc, "login") 248 249 secui := u2.NewSecretUI() 250 arg := PGPKeyImportEngineArg{ 251 Gen: &libkb.PGPGenArg{ 252 PrimaryBits: 768, 253 SubkeyBits: 768, 254 }, 255 } 256 err := arg.Gen.MakeAllIds(tc.G) 257 require.NoError(t, err) 258 uis := libkb.UIs{ 259 LogUI: tc.G.UI.GetLogUI(), 260 SecretUI: secui, 261 } 262 eng := NewPGPKeyImportEngine(tc.G, arg) 263 m := NewMetaContextForTest(tc).WithUIs(uis) 264 err = RunEngine2(m, eng) 265 if err != nil { 266 t.Fatal(err) 267 } 268 } 269 270 func TestSignupGeneratesPaperKey(t *testing.T) { 271 tc := SetupEngineTest(t, "signup") 272 defer tc.Cleanup() 273 274 fu := CreateAndSignupFakeUserPaper(tc, "se") 275 hasOnePaperDev(tc, fu) 276 } 277 278 func TestSignupPassphrases(t *testing.T) { 279 tc := SetupEngineTest(t, "signup") 280 defer tc.Cleanup() 281 CreateAndSignupFakeUserWithPassphrase(tc, "pass", "123456789012") 282 CreateAndSignupFakeUserWithPassphrase(tc, "pass", "12345678") 283 } 284 285 func TestSignupShortPassphrase(t *testing.T) { 286 tc := SetupEngineTest(t, "signup") 287 defer tc.Cleanup() 288 289 fu := NewFakeUserOrBust(t, "sup") 290 fu.Passphrase = "1234567" 291 uis := libkb.UIs{ 292 LogUI: tc.G.UI.GetLogUI(), 293 GPGUI: &gpgtestui{}, 294 SecretUI: fu.NewSecretUI(), 295 LoginUI: &libkb.TestLoginUI{Username: fu.Username}, 296 } 297 arg := MakeTestSignupEngineRunArg(fu) 298 t.Logf("signup arg: %+v", arg) 299 s := NewSignupEngine(tc.G, &arg) 300 err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s) 301 if err == nil { 302 t.Fatal("signup worked with short passphrase") 303 } 304 if _, ok := err.(libkb.PassphraseError); !ok { 305 t.Fatalf("error type: %T, expected libkb.PassphraseError", err) 306 } 307 } 308 309 func TestSignupNonAsciiDeviceName(t *testing.T) { 310 tc := SetupEngineTest(t, "signup") 311 defer tc.Cleanup() 312 313 testValues := []struct { 314 deviceName string 315 err error 316 }{ 317 {"perfectly-reasonable", nil}, 318 {"definitely🙃not🐉ascii", libkb.DeviceBadNameError{}}, 319 } 320 321 for _, testVal := range testValues { 322 fu, _ := NewFakeUser("sup") 323 arg := MakeTestSignupEngineRunArg(fu) 324 arg.DeviceName = testVal.deviceName 325 _, err := CreateAndSignupFakeUserSafeWithArg(tc.G, fu, arg) 326 require.IsType(t, err, testVal.err) 327 } 328 } 329 330 func TestSignupNOPWBadParams(t *testing.T) { 331 tc := SetupEngineTest(t, "signup_nopw") 332 defer tc.Cleanup() 333 334 fu, _ := NewFakeUser("sup") 335 arg := MakeTestSignupEngineRunArg(fu) 336 arg.StoreSecret = false 337 arg.GenerateRandomPassphrase = true 338 arg.Passphrase = "" 339 _, err := CreateAndSignupFakeUserSafeWithArg(tc.G, fu, arg) 340 require.Error(t, err) 341 342 // Make sure user has not signed up - the engine should fail before running 343 // signup_join. 344 loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional() 345 _, err = libkb.LoadUser(loadArg) 346 require.Error(t, err) 347 require.IsType(t, libkb.NotFoundError{}, err) 348 } 349 350 func TestSignupWithoutSecretStore(t *testing.T) { 351 tc := SetupEngineTest(t, "signup_nopw") 352 defer tc.Cleanup() 353 354 // Setup memory-only secret store. 355 libkb.ReplaceSecretStoreForTests(tc, "" /* dataDir */) 356 357 fu, _ := NewFakeUser("sup") 358 arg := MakeTestSignupEngineRunArg(fu) 359 arg.StoreSecret = true 360 arg.GenerateRandomPassphrase = true 361 arg.Passphrase = "" 362 _, err := CreateAndSignupFakeUserSafeWithArg(tc.G, fu, arg) 363 require.Error(t, err) 364 require.Contains(t, err.Error(), "persistent secret store is required") 365 366 // Make sure user has not signed up - the engine should fail before running 367 // signup_join. 368 loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional() 369 _, err = libkb.LoadUser(loadArg) 370 require.Error(t, err) 371 require.IsType(t, libkb.NotFoundError{}, err) 372 } 373 374 func TestSignupWithBadSecretStore(t *testing.T) { 375 if runtime.GOOS == "windows" { 376 t.Skip("this test uses chmod, skipping on Windows") 377 } 378 379 tc := SetupEngineTest(t, "signup_nopw") 380 defer tc.Cleanup() 381 tc.G.Env.Test.SecretStorePrimingDisabled = false 382 383 // Create a secret store that's read only - even though 384 // secret store exists, secrets cannot be stored. 385 td, cleanup := libkb.CreateReadOnlySecretStoreDir(tc) 386 defer cleanup() 387 libkb.ReplaceSecretStoreForTests(tc, td) 388 389 fu, _ := NewFakeUser("sup") 390 arg := MakeTestSignupEngineRunArg(fu) 391 arg.StoreSecret = true 392 arg.GenerateRandomPassphrase = true 393 arg.Passphrase = "" 394 _, err := CreateAndSignupFakeUserSafeWithArg(tc.G, fu, arg) 395 require.Error(t, err) 396 require.IsType(t, SecretStoreNotFunctionalError{}, err) 397 require.Contains(t, err.Error(), "permission denied") 398 399 // Make sure user has not signed up - the engine should fail before running 400 // signup_join. 401 loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional() 402 _, err = libkb.LoadUser(loadArg) 403 require.Error(t, err) 404 require.IsType(t, libkb.NotFoundError{}, err) 405 } 406 407 func assertNoFiles(t *testing.T, dir string, files []string) { 408 err := filepath.Walk(dir, 409 func(path string, info os.FileInfo, err error) error { 410 if err != nil { 411 return err 412 } 413 for _, f := range files { 414 require.NotEqual(t, f, filepath.Base(path)) 415 416 } 417 return nil 418 }, 419 ) 420 require.NoError(t, err) 421 } 422 423 func TestBotSignup(t *testing.T) { 424 tc := SetupEngineTest(t, "signup_bot") 425 defer tc.Cleanup() 426 _ = CreateAndSignupFakeUser(tc, "own") 427 428 mctx := NewMetaContextForTest(tc) 429 botToken, err := bot.CreateToken(mctx) 430 require.NoError(t, err) 431 432 fuBot, err := NewFakeUser("bot") 433 require.NoError(t, err) 434 botName := fuBot.Username 435 436 // Signup tc2 in Bot mode 437 tc2 := SetupEngineTest(t, "signup_bot") 438 defer tc2.Cleanup() 439 440 twiddle := func(tok keybase1.BotToken) keybase1.BotToken { 441 b, err := base64.URLEncoding.DecodeString(string(tok)) 442 require.NoError(t, err) 443 b[0] ^= 0x1 444 return keybase1.BotToken(base64.URLEncoding.EncodeToString(b)) 445 } 446 447 arg := SignupEngineRunArg{ 448 Username: botName, 449 InviteCode: libkb.TestInvitationCode, 450 StoreSecret: false, 451 GenerateRandomPassphrase: true, 452 SkipGPG: true, 453 SkipMail: true, 454 SkipPaper: true, 455 BotToken: twiddle(botToken), 456 } 457 458 uis := libkb.UIs{ 459 LogUI: tc.G.UI.GetLogUI(), 460 } 461 462 // First fail the signup since we put up a bad bot Token 463 signupEng := NewSignupEngine(tc2.G, &arg) 464 m := NewMetaContextForTest(tc2).WithUIs(uis) 465 err = RunEngine2(m, signupEng) 466 require.Error(t, err) 467 appErr, ok := err.(SignupJoinEngineRunRes).Err.(libkb.AppStatusError) 468 require.True(t, ok) 469 require.Equal(t, appErr.Code, int(keybase1.StatusCode_SCBotSignupTokenNotFound)) 470 471 // Next success since we have a good bot token 472 arg.BotToken = botToken 473 signupEng = NewSignupEngine(tc2.G, &arg) 474 err = RunEngine2(m, signupEng) 475 require.NoError(tc2.T, err) 476 pk := signupEng.PaperKey() 477 478 // Check that it worked to sign in 479 testSign(t, tc2) 480 trackAlice(tc2, fuBot, 2) 481 err = m.LogoutAndDeprovisionIfRevoked() 482 require.NoError(t, err) 483 484 // Check that we didn't write a config.json or anything 485 assertNoDurableFiles := func() { 486 assertNoFiles(t, tc2.G.Env.GetConfigDir(), 487 []string{ 488 "config.json", 489 filepath.Base(tc2.G.SKBFilenameForUser(libkb.NewNormalizedUsername(botName))), 490 }) 491 } 492 assertNoDurableFiles() 493 494 Logout(tc2) 495 496 // Now check that we can log back in via oneshot 497 oneshotEng := NewLoginOneshot(tc2.G, keybase1.LoginOneshotArg{ 498 Username: botName, 499 PaperKey: pk.String(), 500 }) 501 m = NewMetaContextForTest(tc2) 502 err = RunEngine2(m, oneshotEng) 503 require.NoError(t, err) 504 err = AssertProvisioned(tc2) 505 require.NoError(t, err) 506 testSign(t, tc2) 507 untrackAlice(tc2, fuBot, 2) 508 err = m.LogoutAndDeprovisionIfRevoked() 509 require.NoError(t, err) 510 assertNoDurableFiles() 511 }