github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }