1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"reflect"
8	"sync"
9	"testing"
10	"time"
11
12	"github.com/keybase/client/go/libkb"
13	keybase1 "github.com/keybase/client/go/protocol/keybase1"
14	"github.com/stretchr/testify/require"
15)
16
17func runIdentify(tc *libkb.TestContext, username string) (idUI *FakeIdentifyUI, res *keybase1.Identify2ResUPK2, err error) {
18	idUI = &FakeIdentifyUI{}
19	arg := keybase1.Identify2Arg{
20		UserAssertion:    username,
21		AlwaysBlock:      true,
22		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
23	}
24
25	uis := libkb.UIs{
26		LogUI:      tc.G.UI.GetLogUI(),
27		IdentifyUI: idUI,
28	}
29
30	eng := NewResolveThenIdentify2(tc.G, &arg)
31	m := NewMetaContextForTest(*tc).WithUIs(uis)
32	err = RunEngine2(m, eng)
33	if err != nil {
34		return idUI, nil, err
35	}
36	res, err = eng.Result(m)
37	if err != nil {
38		return idUI, nil, err
39	}
40	return idUI, res, nil
41}
42
43func checkAliceProofs(tb libkb.TestingTB, idUI *FakeIdentifyUI, user *keybase1.UserPlusKeysV2) {
44	checkKeyedProfile(tb, idUI, user, "alice", map[string]string{
45		"github":  "kbtester2",
46		"twitter": "tacovontaco",
47	})
48}
49
50func checkBobProofs(tb libkb.TestingTB, idUI *FakeIdentifyUI, user *keybase1.UserPlusKeysV2) {
51	checkKeyedProfile(tb, idUI, user, "bob", map[string]string{
52		"github":  "kbtester1",
53		"twitter": "kbtester1",
54	})
55}
56
57func checkCharlieProofs(tb libkb.TestingTB, idUI *FakeIdentifyUI, user *keybase1.UserPlusKeysV2) {
58	checkKeyedProfile(tb, idUI, user, "charlie", map[string]string{
59		"github":  "tacoplusplus",
60		"twitter": "tacovontaco",
61	})
62}
63
64func checkDougProofs(tb libkb.TestingTB, idUI *FakeIdentifyUI, user *keybase1.UserPlusKeysV2) {
65	checkKeyedProfile(tb, idUI, user, "doug", nil)
66}
67
68func checkKeyedProfile(tb libkb.TestingTB, idUI *FakeIdentifyUI, them *keybase1.UserPlusKeysV2, name string, expectedProofs map[string]string) {
69	if them == nil {
70		tb.Fatal("nil 'them' user")
71	}
72	exported := &keybase1.User{
73		Uid:      them.GetUID(),
74		Username: them.GetName(),
75	}
76	if !reflect.DeepEqual(idUI.User, exported) {
77		tb.Fatal("LaunchNetworkChecks User not equal to result user.", idUI.User, exported)
78	}
79
80	if !reflect.DeepEqual(expectedProofs, idUI.Proofs) {
81		tb.Fatal("Wrong proofs.", expectedProofs, idUI.Proofs)
82	}
83}
84
85func checkDisplayKeys(t *testing.T, idUI *FakeIdentifyUI, callCount, keyCount int) {
86	if idUI.DisplayKeyCalls != callCount {
87		t.Errorf("DisplayKey calls: %d.  expected %d.", idUI.DisplayKeyCalls, callCount)
88	}
89
90	if len(idUI.Keys) != keyCount {
91		t.Errorf("keys: %d, expected %d.", len(idUI.Keys), keyCount)
92		for k, v := range idUI.Keys {
93			t.Logf("key: %+v, %+v", k, v)
94		}
95	}
96}
97
98func TestIdAlice(t *testing.T) {
99	tc := SetupEngineTest(t, "id")
100	defer tc.Cleanup()
101	idUI, result, err := runIdentify(&tc, "t_alice")
102	require.NoError(t, err)
103	checkAliceProofs(t, idUI, &result.Upk.Current)
104	checkDisplayKeys(t, idUI, 1, 1)
105}
106
107func TestIdBob(t *testing.T) {
108	tc := SetupEngineTest(t, "id")
109	defer tc.Cleanup()
110	idUI, result, err := runIdentify(&tc, "t_bob")
111	require.NoError(t, err)
112	checkBobProofs(t, idUI, &result.Upk.Current)
113	checkDisplayKeys(t, idUI, 1, 1)
114}
115
116func TestIdCharlie(t *testing.T) {
117	tc := SetupEngineTest(t, "id")
118	defer tc.Cleanup()
119	idUI, result, err := runIdentify(&tc, "t_charlie")
120	require.NoError(t, err)
121	checkCharlieProofs(t, idUI, &result.Upk.Current)
122	checkDisplayKeys(t, idUI, 1, 1)
123}
124
125func TestIdDoug(t *testing.T) {
126	tc := SetupEngineTest(t, "id")
127	defer tc.Cleanup()
128	idUI, result, err := runIdentify(&tc, "t_doug")
129	require.NoError(t, err)
130	checkDougProofs(t, idUI, &result.Upk.Current)
131	checkDisplayKeys(t, idUI, 1, 1)
132}
133
134func TestIdEllen(t *testing.T) {
135	tc := SetupEngineTest(t, "id")
136	defer tc.Cleanup()
137	idUI, _, err := runIdentify(&tc, "t_ellen")
138	require.NoError(t, err)
139	checkDisplayKeys(t, idUI, 0, 0)
140}
141
142// TestIdPGPNotEldest creates a user with a pgp key that isn't
143// eldest key, then runs identify to make sure the pgp key is
144// still displayed.
145func TestIdPGPNotEldest(t *testing.T) {
146	tc := SetupEngineTest(t, "id")
147	defer tc.Cleanup()
148
149	// create new user, then add pgp key
150	u := CreateAndSignupFakeUser(tc, "login")
151	uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: u.NewSecretUI()}
152	_, _, key := genPGPKeyAndArmor(t, tc, u.Email)
153	eng, err := NewPGPKeyImportEngineFromBytes(tc.G, []byte(key), true)
154	require.NoError(t, err)
155
156	m := NewMetaContextForTest(tc).WithUIs(uis)
157	err = RunEngine2(m, eng)
158	require.NoError(t, err)
159
160	Logout(tc)
161
162	idUI, _, err := runIdentify(&tc, u.Username)
163	require.NoError(t, err)
164
165	checkDisplayKeys(t, idUI, 1, 1)
166}
167
168func TestIdGenericSocialProof(t *testing.T) {
169	tc := SetupEngineTest(t, "id")
170	defer tc.Cleanup()
171
172	// create new user and have them prove a gubble.social account
173	fu := CreateAndSignupFakeUser(tc, "login")
174	proveGubbleSocial(tc, fu, libkb.KeybaseSignatureV2)
175	proveGubbleCloud(tc, fu, libkb.KeybaseSignatureV2)
176	Logout(tc)
177
178	fu2 := CreateAndSignupFakeUser(tc, "login")
179	fu2.LoginOrBust(tc)
180
181	idUI, result, err := runIdentify(&tc, fu.Username)
182	require.NoError(t, err)
183
184	expectedResult := keybase1.ProofResult{
185		State:  keybase1.ProofState_OK,
186		Status: keybase1.ProofStatus_OK,
187		Desc:   "",
188	}
189
190	require.Equal(t, expectedResult, idUI.ProofResults["gubble.social"].ProofResult)
191	require.Equal(t, expectedResult, idUI.ProofResults["gubble.cloud"].ProofResult)
192
193	checkKeyedProfile(t, idUI, &result.Upk.Current, fu.Username, map[string]string{
194		"gubble.social": fu.Username,
195		"gubble.cloud":  fu.Username,
196	})
197}
198
199type FakeIdentifyUI struct {
200	Proofs          map[string]string
201	ProofResults    map[string]keybase1.LinkCheckResult
202	User            *keybase1.User
203	Keys            map[libkb.PGPFingerprint]*keybase1.TrackDiff
204	DisplayKeyCalls int
205	DisplayKeyDiffs []*keybase1.TrackDiff
206	Outcome         *keybase1.IdentifyOutcome
207	StartCount      int
208	Token           keybase1.TrackToken
209	BrokenTracking  bool
210	DisplayTLFArg   keybase1.DisplayTLFCreateWithInviteArg
211	DisplayTLFCount int
212	FakeConfirm     bool
213	LastTrack       *keybase1.TrackSummary
214	sync.Mutex
215}
216
217func (ui *FakeIdentifyUI) FinishWebProofCheck(_ libkb.MetaContext, proof keybase1.RemoteProof, result keybase1.LinkCheckResult) error {
218	ui.Lock()
219	defer ui.Unlock()
220	if ui.Proofs == nil {
221		ui.Proofs = make(map[string]string)
222	}
223	ui.Proofs[proof.Key] = proof.Value
224
225	if ui.ProofResults == nil {
226		ui.ProofResults = make(map[string]keybase1.LinkCheckResult)
227	}
228	ui.ProofResults[proof.Key] = result
229	if result.BreaksTracking {
230		ui.BrokenTracking = true
231	}
232	return nil
233}
234
235func (ui *FakeIdentifyUI) FinishSocialProofCheck(_ libkb.MetaContext, proof keybase1.RemoteProof, result keybase1.LinkCheckResult) error {
236	ui.Lock()
237	defer ui.Unlock()
238	if ui.Proofs == nil {
239		ui.Proofs = make(map[string]string)
240	}
241	ui.Proofs[proof.Key] = proof.Value
242	if ui.ProofResults == nil {
243		ui.ProofResults = make(map[string]keybase1.LinkCheckResult)
244	}
245	ui.ProofResults[proof.Key] = result
246	if result.BreaksTracking {
247		ui.BrokenTracking = true
248	}
249	return nil
250}
251
252func (ui *FakeIdentifyUI) Confirm(_ libkb.MetaContext, outcome *keybase1.IdentifyOutcome) (result keybase1.ConfirmResult, err error) {
253	ui.Lock()
254	defer ui.Unlock()
255
256	// Do a short sleep. This helps trigger bugs when other code is racing
257	// against the UI here. (Note from Jack: In the bug I initially added this
258	// for, 10ms was just enough to trigger it. I'm adding in an extra factor
259	// of 10.)
260	time.Sleep(100 * time.Millisecond)
261
262	ui.Outcome = outcome
263	bypass := ui.FakeConfirm || outcome.TrackOptions.BypassConfirm
264	result.IdentityConfirmed = bypass
265	result.RemoteConfirmed = bypass && !outcome.TrackOptions.ExpiringLocal
266	return
267}
268
269func (ui *FakeIdentifyUI) DisplayCryptocurrency(libkb.MetaContext, keybase1.Cryptocurrency) error {
270	return nil
271}
272
273func (ui *FakeIdentifyUI) DisplayStellarAccount(libkb.MetaContext, keybase1.StellarAccount) error {
274	return nil
275}
276
277func (ui *FakeIdentifyUI) DisplayKey(_ libkb.MetaContext, ik keybase1.IdentifyKey) error {
278	ui.Lock()
279	defer ui.Unlock()
280	if ui.Keys == nil {
281		ui.Keys = make(map[libkb.PGPFingerprint]*keybase1.TrackDiff)
282	}
283
284	fp := libkb.ImportPGPFingerprintSlice(ik.PGPFingerprint)
285	if fp != nil {
286		ui.Keys[*fp] = ik.TrackDiff
287	}
288
289	if ik.TrackDiff != nil {
290		ui.DisplayKeyDiffs = append(ui.DisplayKeyDiffs, ik.TrackDiff)
291	}
292
293	ui.DisplayKeyCalls++
294	return nil
295}
296func (ui *FakeIdentifyUI) ReportLastTrack(_ libkb.MetaContext, summary *keybase1.TrackSummary) error {
297	ui.LastTrack = summary
298	return nil
299}
300
301func (ui *FakeIdentifyUI) Start(_ libkb.MetaContext, username string, _ keybase1.IdentifyReason, _ bool) error {
302	ui.Lock()
303	defer ui.Unlock()
304	ui.StartCount++
305	return nil
306}
307
308func (ui *FakeIdentifyUI) Cancel(libkb.MetaContext) error {
309	return nil
310}
311
312func (ui *FakeIdentifyUI) Finish(libkb.MetaContext) error {
313	return nil
314}
315
316func (ui *FakeIdentifyUI) Dismiss(_ libkb.MetaContext, _ string, _ keybase1.DismissReason) error {
317	return nil
318}
319
320func (ui *FakeIdentifyUI) LaunchNetworkChecks(_ libkb.MetaContext, id *keybase1.Identity, user *keybase1.User) error {
321	ui.Lock()
322	defer ui.Unlock()
323	ui.User = user
324	return nil
325}
326
327func (ui *FakeIdentifyUI) DisplayTrackStatement(libkb.MetaContext, string) error {
328	return nil
329}
330
331func (ui *FakeIdentifyUI) DisplayUserCard(libkb.MetaContext, keybase1.UserCard) error {
332	return nil
333}
334
335func (ui *FakeIdentifyUI) ReportTrackToken(_ libkb.MetaContext, tok keybase1.TrackToken) error {
336	ui.Token = tok
337	return nil
338}
339
340func (ui *FakeIdentifyUI) SetStrict(b bool) {
341}
342
343func (ui *FakeIdentifyUI) DisplayTLFCreateWithInvite(_ libkb.MetaContext, arg keybase1.DisplayTLFCreateWithInviteArg) error {
344	ui.DisplayTLFCount++
345	ui.DisplayTLFArg = arg
346	return nil
347}
348