1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4// There are two test files by this name. One in libkb, one in engine.
5
6package engine
7
8import (
9	"sync"
10	"testing"
11	"time"
12
13	"github.com/keybase/client/go/libkb"
14	keybase1 "github.com/keybase/client/go/protocol/keybase1"
15	"github.com/keybase/clockwork"
16	"github.com/stretchr/testify/require"
17	"golang.org/x/net/context"
18)
19
20func TestLoadDeviceKeyNew(t *testing.T) {
21	tc := SetupEngineTest(t, "clu")
22	defer tc.Cleanup()
23
24	t.Logf("create new user")
25	fu := NewFakeUserOrBust(t, "paper")
26	arg := MakeTestSignupEngineRunArg(fu)
27	arg.SkipPaper = false
28	loginUI := &paperLoginUI{Username: fu.Username}
29	uis := libkb.UIs{
30		LogUI:    tc.G.UI.GetLogUI(),
31		GPGUI:    &gpgtestui{},
32		SecretUI: fu.NewSecretUI(),
33		LoginUI:  loginUI,
34	}
35	s := NewSignupEngine(tc.G, &arg)
36	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
37	if err != nil {
38		tc.T.Fatal(err)
39	}
40	t.Logf("using username:%+v", fu.Username)
41	loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional()
42	user, err := libkb.LoadUser(loadArg)
43	if err != nil {
44		tc.T.Fatal(err)
45	}
46	t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID())
47
48	assertNumDevicesAndKeys(tc, fu, 2, 4)
49
50	devices, _ := getActiveDevicesAndKeys(tc, fu)
51	var device1 *libkb.Device
52	for _, device := range devices {
53		if device.Type != keybase1.DeviceTypeV2_PAPER {
54			device1 = device.Device
55		}
56	}
57	require.NotNil(t, device1, "device1 should be non-nil")
58	t.Logf("using device1:%+v", device1.ID)
59
60	t.Logf("load existing device key")
61	upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device1.ID)
62	require.NoError(t, err)
63	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
64	require.Equal(t, device1.ID, deviceKey.DeviceID, "deviceID must match")
65	require.Equal(t, *device1.Description, deviceKey.DeviceDescription, "device name must match")
66	require.Nil(t, revoked, "device not revoked")
67	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
68		dev, err := u.GetDevice(device1.ID)
69		require.NoError(t, err)
70		require.NotNil(t, dev)
71		return nil
72	})
73	require.NoError(t, err)
74
75	Logout(tc)
76
77	if len(loginUI.PaperPhrase) == 0 {
78		t.Fatal("login ui has no paper key phrase")
79	}
80
81	t.Logf("create new device")
82	// redo SetupEngineTest to get a new home directory...should look like a new device.
83	tc2 := SetupEngineTest(t, "login")
84	defer tc2.Cleanup()
85
86	secUI := fu.NewSecretUI()
87	provUI := newTestProvisionUIPaper()
88	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
89	uis = libkb.UIs{
90		ProvisionUI: provUI,
91		LogUI:       tc2.G.UI.GetLogUI(),
92		SecretUI:    secUI,
93		LoginUI:     provLoginUI,
94		GPGUI:       &gpgtestui{},
95	}
96
97	eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase)
98	m := NewMetaContextForTest(tc2).WithUIs(uis)
99	if err := RunEngine2(m, eng); err != nil {
100		t.Fatal(err)
101	}
102	t.Logf("d2 provisioned (1)")
103
104	testUserHasDeviceKey(tc2)
105	require.NoError(t, AssertProvisioned(tc2))
106	t.Logf("d2 provisioned (2)")
107
108	devices, _ = getActiveDevicesAndKeys(tc, fu)
109	var device2 *libkb.Device
110	for _, device := range devices {
111		if device.Type != keybase1.DeviceTypeV2_PAPER && device.ID != device1.ID {
112			device2 = device.Device
113		}
114	}
115	require.NotNil(t, device2, "device2 should be non-nil")
116	t.Logf("using device2:%+v", device2.ID)
117
118	t.Logf("load brand new device (while old is cached)")
119	upk, deviceKey, revoked, err = tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device2.ID)
120	require.NoError(t, err)
121	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
122	require.Equal(t, device2.ID, deviceKey.DeviceID, "deviceID must match")
123	require.Equal(t, *device2.Description, deviceKey.DeviceDescription, "device name must match")
124	require.Nil(t, revoked, "device not revoked")
125	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
126		dev, err := u.GetDevice(deviceKey.DeviceID)
127		require.NoError(t, err)
128		require.NotNil(t, dev)
129		return nil
130	})
131	require.NoError(t, err)
132}
133
134func TestLoadDeviceKeyRevoked(t *testing.T) {
135	tc := SetupEngineTest(t, "clu")
136	defer tc.Cleanup()
137
138	fu := CreateAndSignupFakeUserPaper(tc, "rev")
139	t.Logf("using username:%+v", fu.Username)
140	loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional()
141	user, err := libkb.LoadUser(loadArg)
142	if err != nil {
143		tc.T.Fatal(err)
144	}
145	t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID())
146
147	assertNumDevicesAndKeys(tc, fu, 2, 4)
148
149	devices, _ := getActiveDevicesAndKeys(tc, fu)
150	var thisDevice *libkb.Device
151	for _, device := range devices {
152		if device.Type != keybase1.DeviceTypeV2_PAPER {
153			thisDevice = device.Device
154		}
155	}
156
157	// Revoke the current device with --force
158	err = doRevokeDevice(tc, fu, thisDevice.ID, true, false)
159	if err != nil {
160		tc.T.Fatal(err)
161	}
162
163	assertNumDevicesAndKeys(tc, fu, 1, 2)
164
165	t.Logf("load revoked device")
166	upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), thisDevice.ID)
167	require.NoError(t, err)
168	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
169	require.Equal(t, thisDevice.ID, deviceKey.DeviceID, "deviceID must match")
170	require.Equal(t, *thisDevice.Description, deviceKey.DeviceDescription, "device name must match")
171	require.NotNil(t, revoked, "device should be revoked")
172	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
173		dev, err := u.GetDevice(deviceKey.DeviceID)
174		require.NoError(t, err)
175		require.NotNil(t, dev)
176		require.False(t, dev.IsActive())
177		dev, err = u.GetDevice(thisDevice.ID)
178		require.NoError(t, err)
179		require.NotNil(t, dev)
180		return nil
181	})
182	require.NoError(t, err)
183}
184
185func TestFullSelfCacherFlushSingleMachine(t *testing.T) {
186	tc := SetupEngineTest(t, "fsc")
187	defer tc.Cleanup()
188	sigVersion := libkb.GetDefaultSigVersion(tc.G)
189
190	fu := CreateAndSignupFakeUser(tc, "fsc")
191
192	var scv keybase1.Seqno
193	err := tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
194		require.NotNil(t, u)
195		scv = u.GetSigChainLastKnownSeqno()
196		return nil
197	})
198	require.NoError(t, err)
199	trackAlice(tc, fu, sigVersion)
200	defer untrackAlice(tc, fu, sigVersion)
201	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
202		require.NotNil(t, u)
203		require.True(t, u.GetSigChainLastKnownSeqno() > scv)
204		return nil
205	})
206	require.NoError(t, err)
207}
208
209func TestFullSelfCacherFlushTwoMachines(t *testing.T) {
210	tc := SetupEngineTest(t, "fsc")
211	defer tc.Cleanup()
212	fakeClock := clockwork.NewFakeClockAt(time.Now())
213	tc.G.SetClock(fakeClock)
214
215	t.Logf("create new user")
216	fu := NewFakeUserOrBust(t, "paper")
217	arg := MakeTestSignupEngineRunArg(fu)
218	arg.SkipPaper = false
219	loginUI := &paperLoginUI{Username: fu.Username}
220	uis := libkb.UIs{
221		LogUI:    tc.G.UI.GetLogUI(),
222		GPGUI:    &gpgtestui{},
223		SecretUI: fu.NewSecretUI(),
224		LoginUI:  loginUI,
225	}
226	s := NewSignupEngine(tc.G, &arg)
227	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
228	if err != nil {
229		tc.T.Fatal(err)
230	}
231	t.Logf("using username:%+v", fu.Username)
232
233	var scv keybase1.Seqno
234	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
235		require.NotNil(t, u)
236		scv = u.GetSigChainLastKnownSeqno()
237		return nil
238	})
239	require.NoError(t, err)
240
241	if len(loginUI.PaperPhrase) == 0 {
242		t.Fatal("login ui has no paper key phrase")
243	}
244
245	t.Logf("create new device")
246	// redo SetupEngineTest to get a new home directory...should look like a new device.
247	tc2 := SetupEngineTest(t, "login")
248	defer tc2.Cleanup()
249
250	secUI := fu.NewSecretUI()
251	provUI := newTestProvisionUIPaper()
252	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
253	uis = libkb.UIs{
254		ProvisionUI: provUI,
255		LogUI:       tc2.G.UI.GetLogUI(),
256		SecretUI:    secUI,
257		LoginUI:     provLoginUI,
258		GPGUI:       &gpgtestui{},
259	}
260
261	eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase)
262	m := NewMetaContextForTest(tc2).WithUIs(uis)
263	if err := RunEngine2(m, eng); err != nil {
264		t.Fatal(err)
265	}
266	t.Logf("d2 provisioned (1)")
267
268	// Without pubsub (not available on engine tests), we don't get any
269	// invalidation of the user on the first machine (tc). So this
270	// user's sigchain should stay the same.
271	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
272		require.NotNil(t, u)
273		require.True(t, u.GetSigChainLastKnownSeqno() == scv)
274		return nil
275	})
276	require.NoError(t, err)
277
278	// After the CachedUserTimeout, the FullSelfer ought to repoll.
279	// Check that the sigchain is updated after the repoll, which reflects
280	// the new device having been added.
281	fakeClock.Advance(libkb.CachedUserTimeout + time.Second)
282	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
283		require.NotNil(t, u)
284		require.True(t, u.GetSigChainLastKnownSeqno() > scv)
285		return nil
286	})
287	require.NoError(t, err)
288}
289
290func TestUPAKDeadlock(t *testing.T) {
291	tc := SetupEngineTest(t, "upak")
292	defer tc.Cleanup()
293	fu := CreateAndSignupFakeUserPaper(tc, "upak")
294
295	// First clear the cache
296	tc.G.KeyfamilyChanged(context.TODO(), fu.UID())
297
298	var wg sync.WaitGroup
299
300	ch := make(chan struct{})
301
302	tc.G.GetFullSelfer().(*libkb.CachedFullSelf).TestDeadlocker = func() {
303		<-ch
304	}
305
306	tc.G.GetUPAKLoader().(*libkb.CachedUPAKLoader).TestDeadlocker = func() {
307		ch <- struct{}{}
308	}
309
310	wg.Add(1)
311	go func() {
312		_ = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
313			require.Equal(t, u.GetUID(), fu.UID(), "right UID")
314			return nil
315		})
316		wg.Done()
317	}()
318
319	wg.Add(1)
320	go func() {
321		un, err := tc.G.GetUPAKLoader().LookupUsername(context.TODO(), fu.UID())
322		require.NoError(t, err)
323		if un.String() != fu.Username {
324			t.Errorf("username mismatch: %s != %s", un, fu.Username)
325		}
326		wg.Done()
327	}()
328
329	doneCh := make(chan struct{})
330	go func() {
331		wg.Wait()
332		doneCh <- struct{}{}
333	}()
334
335	select {
336	case <-doneCh:
337		break
338	case <-time.After(20 * time.Second):
339		t.Fatal("deadlocked!")
340	}
341}
342
343func TestLoadAfterAcctReset1(t *testing.T) {
344	// One context for user that will be doing LoadUser, and another
345	// for user that will sign up and reset itself.
346	tc := SetupEngineTest(t, "clu")
347	defer tc.Cleanup()
348
349	resetUserTC := SetupEngineTest(t, "clu2")
350	defer resetUserTC.Cleanup()
351
352	t.Logf("create new user")
353	fu := CreateAndSignupFakeUser(resetUserTC, "res")
354
355	fakeClock := clockwork.NewFakeClockAt(time.Now())
356	tc.G.SetClock(fakeClock)
357
358	loadUpak := func() error {
359		t.Logf("loadUpak: using username:%+v", fu.Username)
360		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithNetContext(context.TODO()).WithStaleOK(false).WithForceMerkleServerPolling(true)
361
362		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
363		if err != nil {
364			return err
365		}
366
367		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
368		return nil
369	}
370
371	err := loadUpak()
372	if err != nil {
373		t.Fatalf("Failed to load user: %+v", err)
374	}
375
376	ResetAccount(resetUserTC, fu)
377
378	loadUpakExpectFailure := func() {
379		err := loadUpak()
380		if err == nil {
381			t.Fatalf("Expected UPAKLoader.Load to fail on nuked account.")
382		} else if _, ok := err.(libkb.NoKeyError); !ok {
383			t.Fatalf("Expected UPAKLoader.Load to fail with NoKeyError, instead failed with: %+v", err)
384		}
385	}
386
387	// advance the clock past the cache timeout
388	fakeClock.Advance(libkb.CachedUserTimeout * 10)
389	loadUpakExpectFailure()
390
391	// Try again, see if still errors out (this time user should not
392	// be in cache at all).
393	fakeClock.Advance(libkb.CachedUserTimeout * 10)
394	loadUpakExpectFailure()
395}
396
397func TestLoadAfterAcctReset2(t *testing.T) {
398	// One context for user that will be doing LoadUser, and another
399	// for user that will sign up and reset itself.
400	tc := SetupEngineTest(t, "clu")
401	defer tc.Cleanup()
402
403	resetUserTC := SetupEngineTest(t, "clu2")
404	defer resetUserTC.Cleanup()
405
406	t.Logf("create new user")
407	fu := CreateAndSignupFakeUser(resetUserTC, "res")
408
409	fakeClock := clockwork.NewFakeClockAt(time.Now())
410	tc.G.SetClock(fakeClock)
411
412	loadUpak := func() (*keybase1.UserPlusAllKeys, error) {
413		t.Logf("loadUpak: using username:%+v", fu.Username)
414		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false)
415		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
416		if err != nil {
417			return nil, err
418		}
419
420		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
421		return upak, nil
422	}
423
424	upak1, err := loadUpak()
425	if err != nil {
426		t.Fatalf("Failed to load user: %+v", err)
427	}
428
429	// Reset account and then login again to establish new eldest and
430	// add new device keys.
431	ResetAccount(resetUserTC, fu)
432	tcp := SetupEngineTest(t, "login")
433	defer tcp.Cleanup()
434
435	fu.LoginOrBust(tcp)
436	if err := AssertProvisioned(tcp); err != nil {
437		t.Fatal(err)
438	}
439
440	fakeClock.Advance(libkb.CachedUserTimeout * 10)
441	upak2, err := loadUpak()
442	if err != nil {
443		t.Fatalf("Failed to load user after reset+login with: %+v", err)
444	}
445
446	if upak1.Base.DeviceKeys[0].KID == upak2.Base.DeviceKeys[0].KID {
447		t.Fatal("Found old device key after LoadUser.")
448	}
449}
450
451// Test the bug in CORE-6943: after a reset, if we did two
452// logins in a row, right on top of each other, previous subchains
453// would be dropped from the self UPAK.
454func TestLoadAfterAcctResetCORE6943(t *testing.T) {
455	tc := SetupEngineTest(t, "clu")
456	defer tc.Cleanup()
457	sigVersion := libkb.GetDefaultSigVersion(tc.G)
458
459	t.Logf("create new user")
460	fu := CreateAndSignupFakeUser(tc, "res")
461
462	trackAlice(tc, fu, sigVersion)
463
464	loadUpak := func() (*keybase1.UserPlusAllKeys, error) {
465		t.Logf("loadUpak: using username:%+v", fu.Username)
466		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false)
467		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
468		if err != nil {
469			return nil, err
470		}
471
472		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
473		return upak, nil
474	}
475
476	upak1, err := loadUpak()
477	if err != nil {
478		t.Fatalf("Failed to load user: %+v", err)
479	}
480
481	// Reset account and then login again to establish new eldest and
482	// add new device keys.
483	ResetAccount(tc, fu)
484
485	tc.G.GetUPAKLoader().Invalidate(context.TODO(), fu.UID())
486
487	fu.LoginOrBust(tc)
488	if err := AssertProvisioned(tc); err != nil {
489		t.Fatal(err)
490	}
491	// login a second time to force the bug.
492	fu.LoginOrBust(tc)
493
494	// Make sure that we can load the eldest key from the previous subchain
495	_, _, _, err = tc.G.GetUPAKLoader().LoadKeyV2(context.TODO(), fu.UID(), upak1.Base.DeviceKeys[0].KID)
496
497	if err != nil {
498		t.Fatal("Failed to load a UID/KID combo from first incarnation")
499	}
500
501	_, err = loadUpak()
502	if err != nil {
503		t.Fatalf("Failed to load user: %+v", err)
504	}
505}
506
507func TestUPAKUnstub(t *testing.T) {
508	tc := SetupEngineTest(t, "login")
509	defer tc.Cleanup()
510
511	u1 := CreateAndSignupFakeUser(tc, "first")
512	Logout(tc)
513	u2 := CreateAndSignupFakeUser(tc, "secon")
514
515	testTrack(t, tc, libkb.KeybaseSignatureV2, "t_alice")
516	testTrack(t, tc, libkb.KeybaseSignatureV2, u1.Username)
517
518	// The last link is always unstubbed, so this is a throw-away so that we have some links that
519	// are stubbed (the two just above).
520	testTrack(t, tc, libkb.KeybaseSignatureV2, "t_bob")
521
522	Logout(tc)
523	t.Logf("first logging back in")
524	u1.LoginOrBust(tc)
525
526	upl := tc.G.GetUPAKLoader()
527	mctx := NewMetaContextForTest(tc)
528
529	// wipe out all the caches
530	_, err := tc.G.LocalDb.Nuke()
531	require.NoError(t, err)
532	upl.Invalidate(mctx.Ctx(), u2.UID())
533
534	assertStubbed := func() {
535		arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID())
536		upak, _, err := upl.LoadV2(arg)
537		require.NoError(t, err)
538		require.Equal(t, 1, len(upak.Current.RemoteTracks))
539		require.Equal(t, "t_bob", upak.Current.RemoteTracks[keybase1.UID("afb5eda3154bc13c1df0189ce93ba119")].Username)
540		require.False(t, upak.Current.Unstubbed)
541	}
542
543	assertStubbed()
544
545	Logout(tc)
546	t.Logf("second logging back in")
547	u2.LoginOrBust(tc)
548
549	assertStubbed()
550
551	assertAllLinks := func(stubMode libkb.StubMode) {
552		arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID()).WithStubMode(stubMode)
553		upak, _, err := upl.LoadV2(arg)
554		require.NoError(t, err)
555		require.Equal(t, 3, len(upak.Current.RemoteTracks))
556		require.Equal(t, u1.Username, upak.Current.RemoteTracks[u1.UID()].Username)
557		require.True(t, upak.Current.Unstubbed)
558	}
559
560	assertAllLinks(libkb.StubModeUnstubbed)
561
562	Logout(tc)
563	t.Logf("first logging back in")
564	u1.LoginOrBust(tc)
565
566	assertAllLinks(libkb.StubModeUnstubbed)
567	assertAllLinks(libkb.StubModeStubbed)
568}
569
570func TestInvalidation(t *testing.T) {
571	tc := SetupEngineTest(t, "login")
572	defer tc.Cleanup()
573	u := CreateAndSignupFakeUser(tc, "first")
574	upl := tc.G.GetUPAKLoader()
575	mctx := NewMetaContextForTest(tc)
576	arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID())
577	upak, _, err := upl.LoadV2(arg)
578	require.NoError(t, err)
579	require.NotNil(t, upak)
580	upl.Invalidate(mctx.Ctx(), u.UID())
581	arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true)
582	_, _, err = upl.LoadV2(arg)
583	require.Error(t, err)
584	require.IsType(t, libkb.UserNotFoundError{}, err)
585	require.Contains(t, err.Error(), "cached user found, but it was stale, and cached only")
586	arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true).WithStaleOK(true)
587	upak, _, err = upl.LoadV2(arg)
588	require.NoError(t, err)
589	require.NotNil(t, upak)
590}
591