github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/identify3/identify3_test.go (about)

     1  package identify3
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/keybase/client/go/engine"
    10  	"github.com/keybase/client/go/externalstest"
    11  	"github.com/keybase/client/go/kbtest"
    12  	"github.com/keybase/client/go/libkb"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	insecureTriplesec "github.com/keybase/go-triplesec-insecure"
    15  	"github.com/stretchr/testify/require"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  func SetupTest(tb libkb.TestingTB, name string) libkb.TestContext {
    20  	tc := externalstest.SetupTest(tb, name, 2)
    21  	// use an insecure triplesec in tests
    22  	tc.G.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) {
    23  		warner := func() { tc.G.Log.Warning("Installing insecure Triplesec with weak stretch parameters") }
    24  		isProduction := func() bool {
    25  			return tc.G.Env.GetRunMode() == libkb.ProductionRunMode
    26  		}
    27  		return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction)
    28  	}
    29  	return tc
    30  }
    31  
    32  type id3results struct {
    33  	resultType       keybase1.Identify3ResultType
    34  	rows             []keybase1.Identify3Row
    35  	cards            []keybase1.UserCard
    36  	timedOut         bool
    37  	userWasReset     bool
    38  	numProofsToCheck int
    39  }
    40  
    41  func (r *id3results) pushRow(row keybase1.Identify3Row) {
    42  	r.rows = append(r.rows, row)
    43  }
    44  
    45  func (r *id3results) pushUserCard(card keybase1.UserCard) {
    46  	r.cards = append(r.cards, card)
    47  }
    48  
    49  func (r *id3results) hitTimeout() {
    50  	r.timedOut = true
    51  }
    52  
    53  func (r *id3results) hitUserReset() {
    54  	r.userWasReset = true
    55  }
    56  
    57  type fakeUI3 struct {
    58  	sync.Mutex
    59  	resultCh   chan<- keybase1.Identify3ResultType
    60  	id3results id3results
    61  }
    62  
    63  func (f *fakeUI3) Identify3ShowTracker(context.Context, keybase1.Identify3ShowTrackerArg) error {
    64  	return nil
    65  }
    66  func (f *fakeUI3) Identify3UpdateRow(_ context.Context, row keybase1.Identify3Row) error {
    67  	f.Lock()
    68  	defer f.Unlock()
    69  	f.id3results.pushRow(row)
    70  	return nil
    71  }
    72  func (f *fakeUI3) Identify3UserReset(context.Context, keybase1.Identify3GUIID) error {
    73  	f.Lock()
    74  	defer f.Unlock()
    75  	f.id3results.hitUserReset()
    76  	return nil
    77  }
    78  func (f *fakeUI3) Identify3UpdateUserCard(_ context.Context, card keybase1.Identify3UpdateUserCardArg) error {
    79  	f.Lock()
    80  	defer f.Unlock()
    81  	f.id3results.pushUserCard(card.Card)
    82  	return nil
    83  }
    84  func (f *fakeUI3) Identify3TrackerTimedOut(context.Context, keybase1.Identify3GUIID) error {
    85  	f.Lock()
    86  	defer f.Unlock()
    87  	f.id3results.hitTimeout()
    88  	return nil
    89  }
    90  func (f *fakeUI3) Identify3Result(_ context.Context, res keybase1.Identify3ResultArg) error {
    91  	f.Lock()
    92  	f.id3results.resultType = res.Result
    93  	f.Unlock()
    94  	f.resultCh <- res.Result
    95  	return nil
    96  }
    97  func (f *fakeUI3) Identify3Summary(_ context.Context, arg keybase1.Identify3Summary) error {
    98  	f.Lock()
    99  	f.id3results.numProofsToCheck = arg.NumProofsToCheck
   100  	f.Unlock()
   101  	return nil
   102  }
   103  
   104  func (f *fakeUI3) results() id3results {
   105  	f.Lock()
   106  	defer f.Unlock()
   107  	return f.id3results
   108  }
   109  
   110  func findRows(t *testing.T, haystack []keybase1.Identify3Row, needles []keybase1.Identify3Row) {
   111  	i := 0
   112  	for _, h := range haystack {
   113  		needle := needles[i]
   114  		if h.Key != needle.Key || h.Value != needle.Value {
   115  			continue
   116  		}
   117  		if needle.SiteURL != "" {
   118  			require.Equal(t, h.SiteURL, needle.SiteURL)
   119  		}
   120  		if needle.ProofURL != "" {
   121  			require.Equal(t, h.ProofURL, needle.ProofURL)
   122  		}
   123  		if needle.State != keybase1.Identify3RowState(0) {
   124  			require.Equal(t, h.State, needle.State)
   125  		}
   126  		if needle.Color != keybase1.Identify3RowColor(0) {
   127  			require.Equal(t, h.Color, needle.Color)
   128  		}
   129  		if needle.Metas != nil {
   130  			require.Equal(t, h.Metas, needle.Metas)
   131  		}
   132  		i++
   133  		if i == len(needles) {
   134  			return
   135  		}
   136  	}
   137  	require.Fail(t, "didn't find all wanted rows")
   138  }
   139  
   140  func addBTCAddr(tc libkb.TestContext, u *kbtest.FakeUser, addr string) {
   141  	t := tc.T
   142  	uis := libkb.UIs{
   143  		LogUI:    tc.G.UI.GetLogUI(),
   144  		SecretUI: u.NewSecretUI(),
   145  	}
   146  	e := engine.NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: addr})
   147  	m := libkb.NewMetaContextForTest(tc).WithUIs(uis)
   148  	err := engine.RunEngine2(m, e)
   149  	require.NoError(t, err)
   150  }
   151  
   152  func TestCryptocurrency(t *testing.T) {
   153  	tc := SetupTest(t, "id3")
   154  	defer tc.Cleanup()
   155  	alice, err := kbtest.CreateAndSignupFakeUser("alice", tc.G)
   156  
   157  	// The keybase Bitcoin pin address.
   158  	addr := "1HUCBSJeHnkhzrVKVjaVmWg2QtZS1mdfaz"
   159  	addBTCAddr(tc, alice, addr)
   160  	require.NoError(t, err)
   161  	bob, err := kbtest.CreateAndSignupFakeUser("bob", tc.G)
   162  	require.NoError(t, err)
   163  
   164  	assertTrackResult := func(res id3results, green bool) {
   165  		require.False(t, res.userWasReset)
   166  
   167  		// We get one row of results, just the cryptocurrency row.
   168  		require.Equal(t, 1, len(res.rows))
   169  		require.Equal(t, "btc", res.rows[0].Key)
   170  		require.Equal(t, addr, res.rows[0].Value)
   171  		if green {
   172  			require.Equal(t, keybase1.Identify3RowColor_GREEN, res.rows[0].Color)
   173  		} else {
   174  			require.Equal(t, keybase1.Identify3RowColor_BLUE, res.rows[0].Color)
   175  		}
   176  		require.Equal(t, keybase1.Identify3RowState_VALID, res.rows[0].State)
   177  	}
   178  
   179  	mctx := libkb.NewMetaContextForTest(tc)
   180  	res := runID3(t, mctx, alice.Username, true)
   181  	// Row color should be blue because we are not tracking.
   182  	assertTrackResult(res, false /* green */)
   183  
   184  	_, err = kbtest.RunTrack(tc, bob, alice.Username)
   185  	require.NoError(t, err)
   186  
   187  	res = runID3(t, mctx, alice.Username, true)
   188  	assertTrackResult(res, true /* green */)
   189  	require.Equal(t, res.numProofsToCheck, 0)
   190  }
   191  
   192  func TestFollowUnfollowTracy(t *testing.T) {
   193  	tc := SetupTest(t, "id3")
   194  	defer tc.Cleanup()
   195  	_, err := kbtest.CreateAndSignupFakeUser("id3", tc.G)
   196  	require.NoError(t, err)
   197  
   198  	mctx := libkb.NewMetaContextForTest(tc)
   199  	res := runID3(t, mctx, "t_tracy", true /* follow */)
   200  	require.Equal(t, res.resultType, keybase1.Identify3ResultType_OK)
   201  	require.Equal(t, len(res.rows), 9)
   202  	require.Equal(t, len(res.cards), 1)
   203  	require.Equal(t, res.numProofsToCheck, 4)
   204  
   205  	findRows(t, res.rows, []keybase1.Identify3Row{
   206  		{
   207  			Key:   "twitter",
   208  			Value: "tacovontaco",
   209  			State: keybase1.Identify3RowState_CHECKING,
   210  			Color: keybase1.Identify3RowColor_GRAY,
   211  		},
   212  		{
   213  			Key:   "twitter",
   214  			Value: "tacovontaco",
   215  			State: keybase1.Identify3RowState_VALID,
   216  			Color: keybase1.Identify3RowColor_BLUE,
   217  		},
   218  	})
   219  	findRows(t, res.rows, []keybase1.Identify3Row{
   220  		{
   221  			Key:   "https",
   222  			Value: "keybase.io",
   223  			State: keybase1.Identify3RowState_CHECKING,
   224  			Color: keybase1.Identify3RowColor_GRAY,
   225  		},
   226  		{
   227  			Key:   "https",
   228  			Value: "keybase.io",
   229  			State: keybase1.Identify3RowState_WARNING,
   230  			Color: keybase1.Identify3RowColor_ORANGE,
   231  			Metas: []keybase1.Identify3RowMeta{{Color: keybase1.Identify3RowColor_ORANGE, Label: "unreachable"}},
   232  		},
   233  	})
   234  
   235  	res = runID3(t, mctx, "t_tracy", false /* follow */)
   236  	require.Equal(t, res.resultType, keybase1.Identify3ResultType_OK)
   237  	require.Equal(t, len(res.rows), 9)
   238  	require.Equal(t, len(res.cards), 1)
   239  
   240  	findRows(t, res.rows, []keybase1.Identify3Row{
   241  		{
   242  			Key:   "twitter",
   243  			Value: "tacovontaco",
   244  			State: keybase1.Identify3RowState_CHECKING,
   245  			Color: keybase1.Identify3RowColor_GRAY,
   246  		},
   247  		{
   248  			Key:   "twitter",
   249  			Value: "tacovontaco",
   250  			State: keybase1.Identify3RowState_VALID,
   251  			Color: keybase1.Identify3RowColor_GREEN,
   252  		},
   253  	})
   254  	findRows(t, res.rows, []keybase1.Identify3Row{
   255  		{
   256  			Key:   "https",
   257  			Value: "keybase.io",
   258  			State: keybase1.Identify3RowState_CHECKING,
   259  			Color: keybase1.Identify3RowColor_GRAY,
   260  		},
   261  		{
   262  			Key:   "https",
   263  			Value: "keybase.io",
   264  			State: keybase1.Identify3RowState_WARNING,
   265  			Color: keybase1.Identify3RowColor_GREEN,
   266  			Metas: []keybase1.Identify3RowMeta{{Color: keybase1.Identify3RowColor_GREEN, Label: "ignored"}},
   267  		},
   268  	})
   269  }
   270  
   271  func runID3(t *testing.T, mctx libkb.MetaContext, user string, follow bool) id3results {
   272  	guiid, err := libkb.NewIdentify3GUIID()
   273  	require.NoError(t, err)
   274  	resultCh := make(chan keybase1.Identify3ResultType)
   275  	fakeUI3 := fakeUI3{resultCh: resultCh}
   276  	err = Identify3(mctx, &fakeUI3, keybase1.Identify3Arg{
   277  		Assertion: keybase1.Identify3Assertion(user),
   278  		GuiID:     guiid,
   279  	})
   280  	require.NoError(t, err)
   281  	<-resultCh
   282  	err = FollowUser(mctx, keybase1.Identify3FollowUserArg{
   283  		GuiID:  guiid,
   284  		Follow: follow,
   285  	})
   286  	require.NoError(t, err)
   287  	res := fakeUI3.results()
   288  	for _, row := range res.rows {
   289  		checkIcon(t, row.Key, row.SiteIcon)
   290  		checkIcon(t, row.Key, row.SiteIconDarkmode)
   291  		checkIcon(t, row.Key, row.SiteIconFull)
   292  		checkIcon(t, row.Key, row.SiteIconFullDarkmode)
   293  		if row.Priority == 0 || row.Priority == 9999999 {
   294  			t.Fatalf("unexpected priority %v %v", row.Key, row.Priority)
   295  		}
   296  	}
   297  	return res
   298  }
   299  
   300  func TestFollowResetFollow(t *testing.T) {
   301  
   302  	tc := SetupTest(t, "id3")
   303  	defer tc.Cleanup()
   304  	alice, err := kbtest.CreateAndSignupFakeUser("id3a", tc.G)
   305  	require.NoError(t, err)
   306  	bob, err := kbtest.CreateAndSignupFakeUser("id3b", tc.G)
   307  	require.NoError(t, err)
   308  	mctx := libkb.NewMetaContextForTest(tc)
   309  	res := runID3(t, mctx, alice.Username, true)
   310  	require.False(t, res.userWasReset)
   311  
   312  	kbtest.Logout(tc)
   313  	kbtest.ResetAccount(tc, alice)
   314  	err = alice.Login(tc.G)
   315  	require.NoError(t, err)
   316  	kbtest.Logout(tc)
   317  
   318  	err = bob.Login(tc.G)
   319  	require.NoError(t, err)
   320  	res = runID3(t, mctx, alice.Username, true)
   321  	require.True(t, res.userWasReset)
   322  	res = runID3(t, mctx, alice.Username, true)
   323  	require.False(t, res.userWasReset)
   324  }
   325  
   326  func checkIcon(t testing.TB, service string, icon []keybase1.SizedImage) {
   327  	if service == "theqrl.org" {
   328  		// Skip checking for logos for this one.
   329  		return
   330  	}
   331  	require.Len(t, icon, 2, "%v", service)
   332  	for _, icon := range icon {
   333  		if icon.Width < 2 {
   334  			t.Fatalf("unreasonable icon size")
   335  		}
   336  		if kbtest.SkipIconRemoteTest() {
   337  			t.Logf("Skipping icon remote test")
   338  			require.True(t, len(icon.Path) > 8)
   339  		} else {
   340  			resp, err := http.Get(icon.Path)
   341  			require.NoError(t, err, "%v", service)
   342  			require.Equal(t, 200, resp.StatusCode, "icon file should be reachable")
   343  			require.NoError(t, err)
   344  			body, err := io.ReadAll(resp.Body)
   345  			require.NoError(t, err)
   346  			if len(body) < 150 {
   347  				t.Fatalf("unreasonable icon payload size")
   348  			}
   349  		}
   350  	}
   351  }