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  }