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	"bytes"
8	"crypto/rand"
9	"errors"
10	"fmt"
11	"io/ioutil"
12	"os"
13	"path/filepath"
14	"strings"
15	"sync"
16	"testing"
17	"time"
18
19	"github.com/keybase/clockwork"
20	"github.com/stretchr/testify/require"
21	"golang.org/x/net/context"
22
23	"github.com/keybase/client/go/kex2"
24	"github.com/keybase/client/go/libkb"
25	gregor1 "github.com/keybase/client/go/protocol/gregor1"
26	keybase1 "github.com/keybase/client/go/protocol/keybase1"
27)
28
29func TestLoginLogoutLogin(t *testing.T) {
30	tc := SetupEngineTest(t, "login")
31	defer tc.Cleanup()
32
33	u1 := CreateAndSignupFakeUser(tc, "login")
34	Logout(tc)
35	u1.LoginOrBust(tc)
36}
37
38// Test login switching between two different users.
39func TestLoginAndSwitchWithLogout(t *testing.T) {
40	tc := SetupEngineTest(t, "login")
41	defer tc.Cleanup()
42
43	u1 := CreateAndSignupFakeUser(tc, "first")
44	Logout(tc)
45	u2 := CreateAndSignupFakeUser(tc, "secon")
46	Logout(tc)
47	t.Logf("first logging back in")
48	u1.LoginOrBust(tc)
49	Logout(tc)
50	t.Logf("second logging back in")
51	u2.LoginOrBust(tc)
52}
53
54func TestLoginTwiceLogoutOnce(t *testing.T) {
55	tc := SetupEngineTest(t, "login")
56	defer tc.Cleanup()
57	u1 := CreateAndSignupFakeUser(tc, "first")
58	Logout(tc)
59	t.Logf("Logout first, and signup  u2")
60	u2 := CreateAndSignupFakeUser(tc, "secon")
61	t.Logf("Login u1")
62	err := u1.SwitchTo(tc.G, true)
63	require.NoError(t, err)
64	t.Logf("Logged in u1")
65	err = u2.SwitchTo(tc.G, true)
66	require.NoError(t, err)
67	eng := NewLogout(libkb.LogoutOptions{})
68	mctx := NewMetaContextForTest(tc).WithUIs(libkb.UIs{
69		LoginUI:  &libkb.TestLoginUI{},
70		SecretUI: &nullSecretUI{},
71	})
72	err = RunEngine2(mctx, eng)
73	require.NoError(t, err)
74	// Log back into the first user, but shouldn't need a password
75	err = u1.SwitchTo(tc.G, true)
76	require.NoError(t, err)
77	require.True(t, tc.G.ActiveDevice.Valid())
78	require.Equal(t, tc.G.ActiveDevice.UID(), u1.UID())
79}
80
81// Test login switching between two different users.
82func TestLoginAndSwitchWithoutLogout(t *testing.T) {
83	tc := SetupEngineTest(t, "login")
84	defer tc.Cleanup()
85	u1 := CreateAndSignupFakeUser(tc, "first")
86	Logout(tc)
87	t.Logf("Logout first, and signup  u2")
88	u2 := CreateAndSignupFakeUser(tc, "secon")
89	t.Logf("Login u1")
90	err := u1.SwitchTo(tc.G, true)
91	require.NoError(t, err)
92	t.Logf("Logged in u1")
93	err = u2.SwitchTo(tc.G, true)
94	require.NoError(t, err)
95	t.Logf("Logged in u2")
96
97	swtch := func(u *FakeUser) {
98		err := u.SwitchTo(tc.G, false)
99		require.NoError(t, err)
100	}
101	for i := 0; i < 3; i++ {
102		swtch(u1)
103		swtch(u2)
104	}
105}
106
107func TestLoginUsernameWhitespace(t *testing.T) {
108	tc := SetupEngineTest(t, "login")
109	defer tc.Cleanup()
110
111	u1 := CreateAndSignupFakeUser(tc, "lg")
112	Logout(tc)
113	u1.Username = " " + u1.Username
114	u1.LoginOrBust(tc)
115}
116
117// Login should now unlock device keys at the end, no matter what.
118func TestLoginUnlocksDeviceKeys(t *testing.T) {
119	tc := SetupEngineTest(t, "login")
120	defer tc.Cleanup()
121
122	u1 := CreateAndSignupFakeUser(tc, "login")
123	Logout(tc)
124	u1.LoginOrBust(tc)
125
126	assertPassphraseStreamCache(tc)
127	assertDeviceKeysCached(tc)
128	assertSecretStored(tc, u1.Username)
129}
130
131func TestLoginActiveDevice(t *testing.T) {
132	tc := SetupEngineTest(t, "login")
133	defer tc.Cleanup()
134
135	u1 := CreateAndSignupFakeUser(tc, "login")
136	Logout(tc)
137	u1.LoginOrBust(tc)
138
139	assertDeviceKeysCached(tc)
140	require.Equal(t, tc.G.ActiveDevice.Name(), defaultDeviceName)
141
142	simulateServiceRestart(t, tc, u1)
143
144	assertDeviceKeysCached(tc)
145	require.Equal(t, tc.G.ActiveDevice.Name(), defaultDeviceName)
146}
147
148func TestCreateFakeUserNoKeys(t *testing.T) {
149	tc := SetupEngineTest(t, "login")
150	defer tc.Cleanup()
151
152	createFakeUserWithNoKeys(tc)
153
154	me, err := libkb.LoadMe(libkb.NewLoadUserPubOptionalArg(tc.G))
155	if err != nil {
156		t.Fatal(err)
157	}
158
159	kf := me.GetKeyFamily()
160	if kf == nil {
161		t.Fatal("user has a nil key family")
162	}
163	if me.GetEldestKID().Exists() {
164		t.Fatalf("user has an eldest key, they should have no keys: %s", me.GetEldestKID())
165	}
166
167	ckf := me.GetComputedKeyFamily()
168	if ckf.HasActiveKey() {
169		t.Errorf("user has an active key, but they should have no keys")
170	}
171}
172
173func testUserHasDeviceKey(tc libkb.TestContext) {
174	me, err := libkb.LoadMe(libkb.NewLoadUserPubOptionalArg(tc.G))
175	require.NoError(tc.T, err)
176
177	kf := me.GetKeyFamily()
178	require.NotNil(tc.T, kf)
179	require.False(tc.T, me.GetEldestKID().IsNil())
180
181	ckf := me.GetComputedKeyFamily()
182	require.NotNil(tc.T, ckf)
183
184	active := ckf.HasActiveKey()
185	require.True(tc.T, active)
186
187	subkey, err := me.GetDeviceSubkey()
188	require.NoError(tc.T, err)
189	require.NotNil(tc.T, subkey)
190}
191
192func TestUserEmails(t *testing.T) {
193	tc := SetupEngineTest(t, "login")
194	defer tc.Cleanup()
195
196	CreateAndSignupFakeUser(tc, "login")
197	emails, err := libkb.LoadUserEmails(NewMetaContextForTest(tc))
198	if err != nil {
199		t.Fatal(err)
200	}
201	if len(emails) == 0 {
202		t.Errorf("No emails for user")
203	}
204}
205
206func TestProvisionDesktopAfterSwitch(t *testing.T) {
207	testProvisionAfterSwitch(t, true)
208}
209
210func TestProvisionAfterSwitchWithWrongUser(t *testing.T) {
211	testProvisionAfterSwitch(t, false)
212}
213
214func testProvisionAfterSwitch(t *testing.T, shouldItWork bool) {
215	// device X (provisioner) context:
216	t.Logf("setup X")
217	tcX := SetupEngineTest(t, "kex2provision")
218	defer tcX.Cleanup()
219
220	// device Y (provisionee) context:
221	t.Logf("setup Y")
222	tcY := SetupEngineTest(t, "template")
223	defer tcY.Cleanup()
224
225	// provisioner needs to be logged in
226	t.Logf("provisioner login")
227	userX := CreateAndSignupFakeUserPaper(tcX, "login")
228	Logout(tcX)
229
230	var secretX kex2.Secret
231	_, err := rand.Read(secretX[:])
232	require.NoError(t, err)
233
234	// Now make a user Pam
235	userPam := CreateAndSignupFakeUserPaper(tcX, "login")
236
237	userProvisionAs := userX
238	if !shouldItWork {
239		userProvisionAs = userPam
240	}
241
242	// Now switch back to userX, which may or may not be userProvisionAs (above)
243	err = userX.SwitchTo(tcX.G, true)
244	require.NoError(t, err)
245
246	secretCh := make(chan kex2.Secret)
247
248	// provisionee calls login:
249	t.Logf("provisionee login")
250	uis := libkb.UIs{
251		ProvisionUI: newTestProvisionUISecretCh(secretCh),
252		LoginUI:     &libkb.TestLoginUI{Username: userProvisionAs.Username},
253		LogUI:       tcY.G.UI.GetLogUI(),
254		SecretUI:    &libkb.TestSecretUI{},
255		GPGUI:       &gpgtestui{},
256	}
257
258	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
259
260	var wg sync.WaitGroup
261
262	assertError := func(err error) {
263		if shouldItWork {
264			require.NoError(t, err)
265		} else {
266			require.Error(t, err)
267			require.True(t, strings.Contains(err.Error(), "is a different user than we wanted"))
268		}
269	}
270
271	// start provisionee
272	t.Logf("start provisionee")
273	wg.Add(1)
274	go func() {
275		defer wg.Done()
276		m := NewMetaContextForTest(tcY).WithUIs(uis)
277		err := RunEngine2(m, eng)
278		assertError(err)
279	}()
280
281	// start provisioner
282	t.Logf("start provisioner")
283	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
284	wg.Add(1)
285	go func() {
286		defer wg.Done()
287		uis := libkb.UIs{
288			SecretUI:    userProvisionAs.NewSecretUI(),
289			ProvisionUI: newTestProvisionUI(),
290		}
291		m := NewMetaContextForTest(tcX).WithUIs(uis)
292		err := RunEngine2(m, provisioner)
293		assertError(err)
294	}()
295
296	secretFromY := <-secretCh
297
298	provisioner.AddSecret(secretFromY)
299
300	t.Logf("wait")
301	wg.Wait()
302
303	require.False(t, t.Failed(), "prior failure in a goroutine")
304
305	if !shouldItWork {
306		return
307	}
308
309	t.Logf("asserts")
310	if err := AssertProvisioned(tcY); err != nil {
311		t.Fatal(err)
312	}
313
314	// after provisioning, the passphrase stream should be cached
315	// (note that this just checks the passphrase stream, not 3sec)
316	assertPassphraseStreamCache(tcY)
317
318	// after provisioning, the device keys should be cached
319	assertDeviceKeysCached(tcY)
320
321	// after provisioning, the secret should be stored
322	assertSecretStored(tcY, userX.Username)
323
324}
325
326func TestProvisionDesktop(t *testing.T) {
327	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
328		testProvisionDesktop(t, false, sigVersion)
329	})
330}
331
332func TestProvisionDesktopPUK(t *testing.T) {
333	testProvisionDesktop(t, true, libkb.KeybaseNullSigVersion)
334}
335
336func testProvisionDesktop(t *testing.T, upgradePerUserKey bool, sigVersion libkb.SigVersion) {
337	// device X (provisioner) context:
338	t.Logf("setup X")
339	tcX := SetupEngineTest(t, "kex2provision")
340	defer tcX.Cleanup()
341	tcX.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
342
343	// device Y (provisionee) context:
344	t.Logf("setup Y")
345	tcY := SetupEngineTest(t, "template")
346	defer tcY.Cleanup()
347	tcY.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
348
349	// provisioner needs to be logged in
350	t.Logf("provisioner login")
351	userX := CreateAndSignupFakeUserPaper(tcX, "login")
352	var secretX kex2.Secret
353	if _, err := rand.Read(secretX[:]); err != nil {
354		t.Fatal(err)
355	}
356
357	secretCh := make(chan kex2.Secret)
358
359	// provisionee calls login:
360	t.Logf("provisionee login")
361	uis := libkb.UIs{
362		ProvisionUI: newTestProvisionUISecretCh(secretCh),
363		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
364		LogUI:       tcY.G.UI.GetLogUI(),
365		SecretUI:    &libkb.TestSecretUI{},
366		GPGUI:       &gpgtestui{},
367	}
368
369	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
370
371	var wg sync.WaitGroup
372
373	// start provisionee
374	t.Logf("start provisionee")
375	wg.Add(1)
376	go func() {
377		defer wg.Done()
378		m := NewMetaContextForTest(tcY).WithUIs(uis)
379		if err := RunEngine2(m, eng); err != nil {
380			t.Errorf("login error: %s", err)
381			return
382		}
383	}()
384
385	// start provisioner
386	t.Logf("start provisioner")
387	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
388	wg.Add(1)
389	go func() {
390		defer wg.Done()
391
392		uis := libkb.UIs{
393			SecretUI:    userX.NewSecretUI(),
394			ProvisionUI: newTestProvisionUI(),
395		}
396		m := NewMetaContextForTest(tcX).WithUIs(uis)
397		if err := RunEngine2(m, provisioner); err != nil {
398			t.Errorf("provisioner error: %s", err)
399			return
400		}
401	}()
402
403	secretFromY := <-secretCh
404
405	provisioner.AddSecret(secretFromY)
406
407	t.Logf("wait")
408	wg.Wait()
409
410	require.False(t, t.Failed(), "prior failure in a goroutine")
411
412	t.Logf("asserts")
413	if err := AssertProvisioned(tcY); err != nil {
414		t.Fatal(err)
415	}
416
417	// after provisioning, the passphrase stream should be cached
418	// (note that this just checks the passphrase stream, not 3sec)
419	assertPassphraseStreamCache(tcY)
420
421	// after provisioning, the device keys should be cached
422	assertDeviceKeysCached(tcY)
423
424	// after provisioning, the secret should be stored
425	assertSecretStored(tcY, userX.Username)
426
427	t.Logf("test tracks")
428	testTrack(t, tcY, sigVersion, "t_alice")
429
430	// Make sure that we can still track without a passphrase
431	// after a simulated service restart.  In other words, that
432	// the full LKSec secret was written to the secret store.
433	simulateServiceRestart(t, tcY, userX)
434	testTrack(t, tcY, sigVersion, "t_bob")
435}
436
437func TestProvisionMobile(t *testing.T) {
438	// device X (provisioner) context:
439	tcX := SetupEngineTest(t, "kex2provision")
440	defer tcX.Cleanup()
441
442	// device Y (provisionee) context:
443	tcY := SetupEngineTest(t, "template")
444	defer tcY.Cleanup()
445
446	// provisioner needs to be logged in
447	userX := CreateAndSignupFakeUserPaper(tcX, "login")
448	var secretX kex2.Secret
449	if _, err := rand.Read(secretX[:]); err != nil {
450		t.Fatal(err)
451	}
452
453	secretCh := make(chan kex2.Secret)
454
455	// provisionee calls login:
456	uis := libkb.UIs{
457		ProvisionUI: newTestProvisionUISecretCh(secretCh),
458		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
459		LogUI:       tcY.G.UI.GetLogUI(),
460		SecretUI:    &libkb.TestSecretUI{},
461		GPGUI:       &gpgtestui{},
462	}
463	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_MOBILE, "", keybase1.ClientType_CLI)
464
465	var wg sync.WaitGroup
466
467	// start provisionee
468	wg.Add(1)
469	go func() {
470		defer wg.Done()
471		m := NewMetaContextForTest(tcY).WithUIs(uis)
472		if err := RunEngine2(m, eng); err != nil {
473			t.Errorf("login error: %s", err)
474			return
475		}
476	}()
477
478	// start provisioner
479	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
480	wg.Add(1)
481	go func() {
482		defer wg.Done()
483
484		uis := libkb.UIs{
485			SecretUI:    userX.NewSecretUI(),
486			ProvisionUI: newTestProvisionUI(),
487		}
488		m := NewMetaContextForTest(tcX).WithUIs(uis)
489		if err := RunEngine2(m, provisioner); err != nil {
490			t.Errorf("provisioner error: %s", err)
491			return
492		}
493	}()
494
495	secretFromY := <-secretCh
496
497	provisioner.AddSecret(secretFromY)
498
499	wg.Wait()
500
501	if err := AssertProvisioned(tcY); err != nil {
502		t.Fatal(err)
503	}
504}
505
506func TestProvisionWithRevoke(t *testing.T) {
507	// device X (provisioner) context:
508	tcX := SetupEngineTest(t, "kex2provision")
509	defer tcX.Cleanup()
510
511	// device Y (provisionee) context:
512	tcY := SetupEngineTest(t, "template")
513	defer tcY.Cleanup()
514
515	// provisioner needs to be logged in
516	userX := CreateAndSignupFakeUserPaper(tcX, "login")
517	var secretX kex2.Secret
518	if _, err := rand.Read(secretX[:]); err != nil {
519		t.Fatal(err)
520	}
521
522	secretCh := make(chan kex2.Secret)
523
524	// provisionee calls login:
525	uis := libkb.UIs{
526		ProvisionUI: newTestProvisionUISecretCh(secretCh),
527		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
528		LogUI:       tcY.G.UI.GetLogUI(),
529		SecretUI:    &libkb.TestSecretUI{},
530		GPGUI:       &gpgtestui{},
531	}
532	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_MOBILE, "", keybase1.ClientType_CLI)
533
534	var wg sync.WaitGroup
535
536	// start provisionee
537	wg.Add(1)
538	go func() {
539		defer wg.Done()
540		m := NewMetaContextForTest(tcY).WithUIs(uis)
541		if err := RunEngine2(m, eng); err != nil {
542			t.Errorf("login error: %s", err)
543			return
544		}
545	}()
546
547	// start provisioner
548	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
549	wg.Add(1)
550	go func() {
551		defer wg.Done()
552
553		uis := libkb.UIs{
554			SecretUI:    userX.NewSecretUI(),
555			ProvisionUI: newTestProvisionUI(),
556		}
557		m := NewMetaContextForTest(tcX).WithUIs(uis)
558		if err := RunEngine2(m, provisioner); err != nil {
559			t.Errorf("provisioner error: %s", err)
560			return
561		}
562	}()
563
564	secretFromY := <-secretCh
565
566	// x is going to revoke a device here to change the sigchain
567	revoked := revokeAnyPaperKey(tcX, userX)
568	if revoked == nil {
569		t.Fatal("revokeAnyPaperKey for user x did not revoke anything")
570	}
571
572	provisioner.AddSecret(secretFromY)
573
574	wg.Wait()
575
576	if err := AssertProvisioned(tcY); err != nil {
577		t.Fatal(err)
578	}
579}
580
581// If a user has device keys and no pgp keys, not selecting a device
582// should trigger the autoreset flow.
583func TestProvisionAutoreset(t *testing.T) {
584	// device X (provisioner) context:
585	tcX := SetupEngineTest(t, "provision_x")
586	defer tcX.Cleanup()
587
588	// create user (and device X)
589	userX := CreateAndSignupFakeUser(tcX, "login")
590	require.NoError(t, AssertLoggedIn(tcX), "should be logged in on device x")
591	require.NoError(t, AssertProvisioned(tcX), "should be provisioned on device x")
592
593	// device Y (provisionee) context:
594	tcY := SetupEngineTest(t, "provision_y")
595	defer tcY.Cleanup()
596
597	uis := libkb.UIs{
598		ProvisionUI: newTestProvisionUIChooseNoDevice(),
599		LoginUI: &libkb.TestLoginUI{
600			Username:     userX.Username,
601			ResetAccount: keybase1.ResetPromptResponse_CONFIRM_RESET,
602		},
603		LogUI:    tcY.G.UI.GetLogUI(),
604		SecretUI: &libkb.TestSecretUI{Passphrase: userX.Passphrase},
605		GPGUI:    &gpgtestui{},
606	}
607	m := NewMetaContextForTest(tcY).WithUIs(uis)
608	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
609	require.NoError(t, RunEngine2(m, eng), "expected login engine to succeed")
610	require.NotNil(t, AssertLoggedIn(tcY), "should not be logged in")
611
612	// Travel 5 days into future + 1h to make sure that it all runs
613	require.NoError(t, timeTravelReset(tcX, time.Hour*121))
614	require.NoError(t, processReset(tcX))
615
616	// Rather than sleeping we'll wait for autoreset by analyzing its state
617	var lastErr error
618	for i := 0; i < 60; i++ {
619		// up to 60 iters * 100ms = 6s
620		lastErr = assertAutoreset(tcX, userX.UID(), libkb.AutoresetEventReady)
621		if lastErr == nil {
622			break
623		}
624		time.Sleep(100 * time.Millisecond)
625	}
626	require.NoError(t, lastErr)
627
628	// Second iteration on device Y should result in a reset + provision
629	uis = libkb.UIs{
630		ProvisionUI: newTestProvisionUIChooseNoDevice(),
631		LoginUI: &libkb.TestLoginUI{
632			Username:     userX.Username,
633			ResetAccount: keybase1.ResetPromptResponse_CONFIRM_RESET,
634		},
635		LogUI:    tcY.G.UI.GetLogUI(),
636		SecretUI: &libkb.TestSecretUI{Passphrase: userX.Passphrase},
637		GPGUI:    &gpgtestui{},
638	}
639	m = NewMetaContextForTest(tcY).WithUIs(uis)
640	eng = NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
641	require.NoError(t, RunEngine2(m, eng), "expected 2nd login engine to succeed")
642	require.NoError(t, AssertLoggedIn(tcY), "should be logged in")
643	require.NoError(t, AssertProvisioned(tcY), "should be provisioned on device y")
644}
645
646func timeTravelReset(tc libkb.TestContext, duration time.Duration) error {
647	mctx := libkb.NewMetaContextForTest(tc)
648	_, err := tc.G.API.Post(mctx, libkb.APIArg{
649		Endpoint:    "autoreset/timetravel",
650		SessionType: libkb.APISessionTypeREQUIRED,
651		Args: libkb.HTTPArgs{
652			"duration_sec": libkb.I{Val: int(gregor1.ToDurationSec(duration))},
653		},
654	})
655	return err
656}
657
658func processReset(tc libkb.TestContext) error {
659	mctx := libkb.NewMetaContextForTest(tc)
660	_, err := tc.G.API.Post(mctx, libkb.APIArg{
661		Endpoint:    "autoreset/process_dev",
662		SessionType: libkb.APISessionTypeNONE,
663		RetryCount:  5,
664	})
665	return err
666}
667
668func TestProvisionPassphraseNoKeysSolo(t *testing.T) {
669	testProvisionPassphraseNoKeysSolo(t, false)
670}
671
672func TestProvisionPassphraseNoKeysSoloPUK(t *testing.T) {
673	testProvisionPassphraseNoKeysSolo(t, true)
674}
675
676// If a user has no keys, provision via passphrase should work.
677func testProvisionPassphraseNoKeysSolo(t *testing.T, upgradePerUserKey bool) {
678	tcWeb := SetupEngineTest(t, "web")
679	defer tcWeb.Cleanup()
680	tcWeb.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
681
682	username, passphrase := createFakeUserWithNoKeys(tcWeb)
683
684	Logout(tcWeb)
685
686	hasZeroPaperDev(tcWeb, &FakeUser{Username: username, Passphrase: passphrase})
687
688	tc := SetupEngineTest(t, "login")
689	defer tc.Cleanup()
690	tc.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
691
692	uis := libkb.UIs{
693		ProvisionUI: newTestProvisionUIPassphrase(),
694		LoginUI:     &libkb.TestLoginUI{Username: username},
695		LogUI:       tc.G.UI.GetLogUI(),
696		SecretUI:    &libkb.TestSecretUI{Passphrase: passphrase},
697		GPGUI:       &gpgtestui{},
698	}
699	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
700	m := NewMetaContextForTest(tc).WithUIs(uis)
701	if err := RunEngine2(m, eng); err != nil {
702		t.Fatal(err)
703	}
704
705	// since this user didn't have any keys, login should have fixed that:
706	testUserHasDeviceKey(tc)
707
708	// and they should not have a paper backup key
709	hasZeroPaperDev(tc, &FakeUser{Username: username, Passphrase: passphrase})
710
711	if err := AssertProvisioned(tc); err != nil {
712		t.Fatal(err)
713	}
714
715	// secret should be stored
716	assertSecretStored(tc, username)
717}
718
719// Test bad name input (not valid username or email address).
720func TestProvisionPassphraseBadName(t *testing.T) {
721	tcWeb := SetupEngineTest(t, "web")
722	defer tcWeb.Cleanup()
723
724	_, passphrase := createFakeUserWithNoKeys(tcWeb)
725
726	Logout(tcWeb)
727
728	tc := SetupEngineTest(t, "login")
729	defer tc.Cleanup()
730
731	uis := libkb.UIs{
732		ProvisionUI: newTestProvisionUIPassphrase(),
733		LoginUI:     &libkb.TestLoginUI{Username: strings.Repeat("X", 20)},
734		LogUI:       tc.G.UI.GetLogUI(),
735		SecretUI:    &libkb.TestSecretUI{Passphrase: passphrase},
736		GPGUI:       &gpgtestui{},
737	}
738	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
739	m := NewMetaContextForTest(tc).WithUIs(uis)
740	err := RunEngine2(m, eng)
741	require.Error(t, err)
742	require.IsType(t, libkb.BadUsernameError{}, err)
743}
744
745// If a user has (only) a synced pgp key, provision via passphrase
746// should work.
747func TestProvisionPassphraseSyncedPGP(t *testing.T) {
748	tc := SetupEngineTest(t, "login")
749	u1 := createFakeUserWithPGPOnly(t, tc)
750	t.Log("Created fake user")
751	Logout(tc)
752	tc.Cleanup()
753
754	// redo SetupEngineTest to get a new home directory...should look like a new device.
755	tc = SetupEngineTest(t, "login")
756	defer tc.Cleanup()
757
758	uis := libkb.UIs{
759		ProvisionUI: newTestProvisionUIPassphrase(),
760		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
761		LogUI:       tc.G.UI.GetLogUI(),
762		SecretUI:    u1.NewSecretUI(),
763		GPGUI:       &gpgtestui{},
764	}
765	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
766	m := NewMetaContextForTest(tc).WithUIs(uis)
767	if err := RunEngine2(m, eng); err != nil {
768		t.Fatal(err)
769	}
770
771	// since this user didn't have any device keys, login should have fixed that:
772	testUserHasDeviceKey(tc)
773
774	// and they should not have a paper backup key
775	hasZeroPaperDev(tc, u1)
776
777	if err := AssertProvisioned(tc); err != nil {
778		t.Fatal(err)
779	}
780
781	// after provisioning, the secret should be stored
782	assertSecretStored(tc, u1.Username)
783
784	// should be able to sign and to track someone (no passphrase prompt)
785	testSign(t, tc)
786	simulateServiceRestart(t, tc, u1)
787	testSign(t, tc)
788}
789
790// If a user has (only) a synced pgp key, provision via passphrase
791// should work, if they specify email address as username.
792func TestProvisionPassphraseSyncedPGPEmail(t *testing.T) {
793	tc := SetupEngineTest(t, "login")
794	u1 := createFakeUserWithPGPOnly(t, tc)
795	Logout(tc)
796	tc.Cleanup()
797
798	// redo SetupEngineTest to get a new home directory...should look like a new device.
799	tc = SetupEngineTest(t, "login")
800	defer tc.Cleanup()
801
802	uis := libkb.UIs{
803		ProvisionUI: newTestProvisionUIPassphrase(),
804		LoginUI:     &libkb.TestLoginUI{Username: u1.Email},
805		LogUI:       tc.G.UI.GetLogUI(),
806		SecretUI:    u1.NewSecretUI(),
807		GPGUI:       &gpgtestui{},
808	}
809	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
810	m := NewMetaContextForTest(tc).WithUIs(uis)
811	err := RunEngine2(m, eng)
812	require.Error(t, err)
813	require.IsType(t, libkb.BadUsernameError{}, err)
814	require.Contains(t, err.Error(), "not supported")
815}
816
817// Check that a bad passphrase fails to unlock a synced pgp key
818func TestProvisionSyncedPGPBadPassphrase(t *testing.T) {
819	tc := SetupEngineTest(t, "login")
820	u1 := createFakeUserWithPGPOnly(t, tc)
821	t.Log("Created fake user")
822	Logout(tc)
823	tc.Cleanup()
824
825	// redo SetupEngineTest to get a new home directory...should look like a new device.
826	tc = SetupEngineTest(t, "login")
827	defer tc.Cleanup()
828
829	uis := libkb.UIs{
830		ProvisionUI: newTestProvisionUIPassphrase(),
831		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
832		LogUI:       tc.G.UI.GetLogUI(),
833		SecretUI:    &libkb.TestSecretUI{Passphrase: u1.Passphrase + u1.Passphrase},
834		GPGUI:       &gpgtestui{},
835	}
836	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
837	m := NewMetaContextForTest(tc).WithUIs(uis)
838	if err := RunEngine2(m, eng); err == nil {
839		t.Fatal("sync pgp provision worked with bad passphrase")
840	} else if _, ok := err.(libkb.PassphraseError); !ok {
841		t.Errorf("error: %T, expected libkb.PassphraseError", err)
842	}
843}
844
845// If a user is logged in as alice, then logs in as bob (who has
846// no keys), provision via passphrase should work.
847// Bug https://keybase.atlassian.net/browse/CORE-2605
848func TestProvisionPassphraseNoKeysSwitchUser(t *testing.T) {
849	// this is the web user
850	tcWeb := SetupEngineTest(t, "web")
851	username, passphrase := createFakeUserWithNoKeys(tcWeb)
852	Logout(tcWeb)
853	tcWeb.Cleanup()
854
855	tc := SetupEngineTest(t, "login")
856	defer tc.Cleanup()
857
858	// this is a provisioned user.  stay logged in as this user
859	// and start login process for web user.
860	CreateAndSignupFakeUser(tc, "alice")
861
862	Logout(tc)
863
864	uis := libkb.UIs{
865		ProvisionUI: newTestProvisionUIPassphrase(),
866		LoginUI:     &libkb.TestLoginUI{Username: username},
867		LogUI:       tc.G.UI.GetLogUI(),
868		SecretUI:    &libkb.TestSecretUI{Passphrase: passphrase},
869		GPGUI:       &gpgtestui{},
870	}
871	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, username, keybase1.ClientType_CLI)
872	m := NewMetaContextForTest(tc).WithUIs(uis)
873	if err := RunEngine2(m, eng); err != nil {
874		t.Fatal(err)
875	}
876
877	// since this user didn't have any keys, login should have fixed that:
878	testUserHasDeviceKey(tc)
879
880	t.Logf("user has device key")
881
882	// and they should not have a paper backup key
883	hasZeroPaperDev(tc, &FakeUser{Username: username, Passphrase: passphrase})
884
885	t.Logf("user has paper device")
886
887	if err := AssertProvisioned(tc); err != nil {
888		t.Fatal(err)
889	}
890
891	// after provisioning, the secret should be stored
892	assertSecretStored(tc, username)
893}
894
895// If a user has a synced pgp key, they can use it to provision their first device.
896// After that, if they have a PUK, then they should not be able to provision with
897// the synced pgp key again.
898func TestProvisionSyncedPGPWithPUK(t *testing.T) {
899	tc := SetupEngineTest(t, "login")
900	u1 := createFakeUserWithPGPOnly(t, tc)
901	Logout(tc)
902	tc.Cleanup()
903
904	// redo SetupEngineTest to get a new home directory...should look like a new device.
905	// (PUK is on)
906	tc = SetupEngineTest(t, "login")
907	defer tc.Cleanup()
908
909	uis := libkb.UIs{
910		ProvisionUI: newTestProvisionUIPassphrase(),
911		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
912		LogUI:       tc.G.UI.GetLogUI(),
913		SecretUI:    u1.NewSecretUI(),
914		GPGUI:       &gpgtestui{},
915	}
916	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
917	m := NewMetaContextForTest(tc).WithUIs(uis)
918	if err := RunEngine2(m, eng); err != nil {
919		t.Fatal(err)
920	}
921
922	// since this user didn't have any device keys, login should have fixed that:
923	testUserHasDeviceKey(tc)
924	if err := AssertProvisioned(tc); err != nil {
925		t.Fatal(err)
926	}
927
928	// force them to have a puk
929	ForcePUK(tc)
930
931	// redo SetupEngineTest to get a new home directory...should look like a new device.
932	// (PUK is on)
933	tc2 := SetupEngineTest(t, "login")
934	defer tc2.Cleanup()
935
936	uis2 := libkb.UIs{
937		ProvisionUI: newTestProvisionUIPassphrase(),
938		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
939		LogUI:       tc2.G.UI.GetLogUI(),
940		SecretUI:    u1.NewSecretUI(),
941		GPGUI:       &gpgtestui{},
942	}
943
944	// this should fail, the user should not be allowed to use synced pgp key to provision
945	// second device when PUK is on:
946	eng2 := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
947	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
948	err := RunEngine2(m2, eng2)
949	require.Error(t, err, "Provision w/ synced pgp key on device 2 w/ PUK enabled should fail")
950	require.IsType(t, err, libkb.ProvisionViaDeviceRequiredError{})
951}
952
953// Provision device using a private GPG key (not synced to keybase
954// server), import private key to lksec. With PUK, shouldn't be allowed on
955// device 2.
956func TestProvisionGPGWithPUK(t *testing.T) {
957	tc := SetupEngineTest(t, "login")
958	defer tc.Cleanup()
959
960	u1 := createFakeUserWithPGPPubOnly(t, tc)
961	Logout(tc)
962
963	// redo SetupEngineTest to get a new home directory...should look like a new device.
964	tc2 := SetupEngineTest(t, "login")
965	defer tc2.Cleanup()
966
967	// we need the gpg keyring that's in the first homedir
968	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
969		t.Fatal(err)
970	}
971
972	// run login on new device
973	uis2 := libkb.UIs{
974		ProvisionUI: newTestProvisionUIGPGImport(),
975		LogUI:       tc2.G.UI.GetLogUI(),
976		SecretUI:    u1.NewSecretUI(),
977		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
978		GPGUI:       &gpgtestui{},
979	}
980	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
981	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
982	if err := RunEngine2(m2, eng); err != nil {
983		t.Fatal(err)
984	}
985
986	testUserHasDeviceKey(tc2)
987	if err := AssertProvisioned(tc2); err != nil {
988		t.Fatal(err)
989	}
990
991	// force them to have a puk
992	ForcePUK(tc2)
993
994	// redo SetupEngineTest to get a new home directory...should look like a new device.
995	tc3 := SetupEngineTest(t, "login")
996	defer tc3.Cleanup()
997
998	// we need the gpg keyring
999	if err := tc2.MoveGpgKeyringTo(tc3); err != nil {
1000		t.Fatal(err)
1001	}
1002
1003	// run login on new device
1004	uis3 := libkb.UIs{
1005		ProvisionUI: newTestProvisionUIGPGImport(),
1006		LogUI:       tc3.G.UI.GetLogUI(),
1007		SecretUI:    u1.NewSecretUI(),
1008		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1009		GPGUI:       &gpgtestui{},
1010	}
1011	eng3 := NewLogin(tc3.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1012	m3 := NewMetaContextForTest(tc3).WithUIs(uis3)
1013	err := RunEngine2(m3, eng3)
1014	require.Error(t, err, "Provision w/ gpg key on device 2 w/ PUK enabled should fail")
1015	require.IsType(t, err, libkb.ProvisionViaDeviceRequiredError{})
1016}
1017
1018// Test provisioning where we use one username, but suddenly we are
1019// key-exchanging with someone else.
1020func TestProvisionWithUnexpectedX(t *testing.T) {
1021	t.Logf("Setting contexts and users")
1022
1023	// We want to provision as this user:
1024	tcV := SetupEngineTest(t, "kex2provision")
1025	defer tcV.Cleanup()
1026	wantedUser := CreateAndSignupFakeUserPaper(tcV, "usr1")
1027
1028	// But actually this one will respond:
1029	tcF := SetupEngineTest(t, "unexpected")
1030	defer tcF.Cleanup()
1031	actualUser := CreateAndSignupFakeUserPaper(tcF, "usr2")
1032	var secretX kex2.Secret
1033	_, err := rand.Read(secretX[:])
1034	require.NoError(t, err)
1035
1036	// device Y (provisionee) context:
1037	tcY := SetupEngineTest(t, "template")
1038	defer tcY.Cleanup()
1039
1040	secretCh := make(chan kex2.Secret)
1041
1042	// provisionee calls login:
1043	t.Logf("provisionee login")
1044	uis := libkb.UIs{
1045		ProvisionUI: newTestProvisionUISecretCh(secretCh),
1046		LoginUI:     &libkb.TestLoginUI{Username: wantedUser.Username},
1047		LogUI:       tcY.G.UI.GetLogUI(),
1048		SecretUI:    &libkb.TestSecretUI{},
1049		GPGUI:       &gpgtestui{},
1050	}
1051
1052	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1053
1054	var wg sync.WaitGroup
1055
1056	// start provisionee
1057	t.Logf("start provisionee")
1058	wg.Add(1)
1059	go func() {
1060		defer wg.Done()
1061		m := NewMetaContextForTest(tcY).WithUIs(uis)
1062		err := RunEngine2(m, eng)
1063		require.Error(t, err)
1064		require.Contains(t, err.Error(), "different user")
1065	}()
1066
1067	// start provisioner
1068	t.Logf("start provisioner")
1069	provisioner := NewKex2Provisioner(tcF.G, secretX, nil)
1070	wg.Add(1)
1071	go func() {
1072		defer wg.Done()
1073
1074		uis := libkb.UIs{
1075			SecretUI:    actualUser.NewSecretUI(),
1076			ProvisionUI: newTestProvisionUI(),
1077		}
1078		m := NewMetaContextForTest(tcF).WithUIs(uis)
1079		err := RunEngine2(m, provisioner)
1080		require.Error(t, err)
1081		require.Contains(t, err.Error(), "different user")
1082	}()
1083
1084	secretFromY := <-secretCh
1085	provisioner.AddSecret(secretFromY)
1086
1087	t.Logf("wait")
1088	wg.Wait()
1089}
1090
1091func testSign(t *testing.T, tc libkb.TestContext) {
1092	// should be able to sign something with new device keys without
1093	// entering a passphrase
1094	var sink bytes.Buffer
1095
1096	sarg := &SaltpackSignArg{
1097		Sink:   libkb.NopWriteCloser{W: &sink},
1098		Source: ioutil.NopCloser(bytes.NewBufferString("hello")),
1099	}
1100
1101	signEng := NewSaltpackSign(tc.G, sarg)
1102	uis := libkb.UIs{
1103		IdentifyUI: &FakeIdentifyUI{},
1104		SecretUI:   &libkb.TestSecretUI{}, // empty
1105	}
1106
1107	m := NewMetaContextForTest(tc).WithUIs(uis)
1108	if err := RunEngine2(m, signEng); err != nil {
1109		t.Fatal(err)
1110	}
1111}
1112
1113func testTrack(t *testing.T, tc libkb.TestContext, sigVersion libkb.SigVersion, whom string) {
1114
1115	if sigVersion == libkb.KeybaseNullSigVersion {
1116		sigVersion = libkb.GetDefaultSigVersion(tc.G)
1117	}
1118	// should be able to track someone (no passphrase prompt)
1119	targ := &TrackEngineArg{
1120		UserAssertion: whom,
1121		Options:       keybase1.TrackOptions{BypassConfirm: true},
1122		SigVersion:    sigVersion,
1123	}
1124	uis := libkb.UIs{
1125		LogUI:      tc.G.UI.GetLogUI(),
1126		IdentifyUI: &FakeIdentifyUI{},
1127		SecretUI:   &libkb.TestSecretUI{},
1128	}
1129
1130	teng := NewTrackEngine(tc.G, targ)
1131	m := NewMetaContextForTest(tc).WithUIs(uis)
1132	err := RunEngine2(m, teng)
1133	require.NoError(t, err)
1134}
1135
1136func testProvisionPaperOnly(t *testing.T, changePaperkey func(s string) string) {
1137	tc := SetupEngineTest(t, "login")
1138	defer tc.Cleanup()
1139
1140	fu := NewFakeUserOrBust(t, "paper")
1141	arg := MakeTestSignupEngineRunArg(fu)
1142	arg.SkipPaper = false
1143	loginUI := &paperLoginUI{Username: fu.Username}
1144	uis := libkb.UIs{
1145		LogUI:    tc.G.UI.GetLogUI(),
1146		GPGUI:    &gpgtestui{},
1147		SecretUI: fu.NewSecretUI(),
1148		LoginUI:  loginUI,
1149	}
1150	s := NewSignupEngine(tc.G, &arg)
1151	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
1152	if err != nil {
1153		tc.T.Fatal(err)
1154	}
1155
1156	assertNumDevicesAndKeys(tc, fu, 2, 4)
1157
1158	Logout(tc)
1159
1160	if len(loginUI.PaperPhrase) == 0 {
1161		t.Fatal("login ui has no paper key phrase")
1162	}
1163
1164	// redo SetupEngineTest to get a new home directory...should look like a new device.
1165	tc2 := SetupEngineTest(t, "login")
1166	fakeClock := clockwork.NewFakeClockAt(time.Now())
1167	tc2.G.SetClock(fakeClock)
1168	// to pick up the new clock...
1169	defer tc2.Cleanup()
1170
1171	secUI := fu.NewSecretUI()
1172	secUI.Passphrase = changePaperkey(loginUI.PaperPhrase)
1173	provUI := newTestProvisionUIPaper()
1174	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
1175	uis2 := libkb.UIs{
1176		ProvisionUI: provUI,
1177		LogUI:       tc2.G.UI.GetLogUI(),
1178		SecretUI:    secUI,
1179		LoginUI:     provLoginUI,
1180		GPGUI:       &gpgtestui{},
1181	}
1182	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1183	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1184	if err := RunEngine2(m2, eng); err != nil {
1185		t.Fatal(err)
1186	}
1187
1188	testUserHasDeviceKey(tc2)
1189
1190	assertNumDevicesAndKeys(tc, fu, 3, 6)
1191
1192	if err := AssertProvisioned(tc2); err != nil {
1193		t.Fatal(err)
1194	}
1195
1196	if provUI.calledChooseDeviceType != 0 {
1197		t.Errorf("expected 0 calls to ChooseDeviceType, got %d", provUI.calledChooseDeviceType)
1198	}
1199	if provLoginUI.CalledGetEmailOrUsername != 1 {
1200		t.Errorf("expected 1 call to GetEmailOrUsername, got %d", provLoginUI.CalledGetEmailOrUsername)
1201	}
1202	var device *libkb.DeviceWithKeys
1203
1204	ch := make(chan struct{})
1205	pch := func() {
1206		ch <- struct{}{}
1207	}
1208
1209	wrapper := m2.ActiveDevice().ProvisioningKeyWrapper(m2)
1210	if wrapper != nil {
1211		device = wrapper.DeviceWithKeys()
1212		wrapper.SetTestPostCleanHook(pch)
1213	}
1214
1215	if device == nil || device.EncryptionKey() == nil {
1216		t.Errorf("Got a null paper encryption key")
1217	}
1218
1219	fakeClock.Advance(libkb.ProvisioningKeyMemoryTimeout + 1*time.Minute)
1220	<-ch
1221
1222	device = m2.ActiveDevice().ProvisioningKey(m2)
1223	if device != nil {
1224		t.Errorf("Got a non-null paper encryption key after timeout")
1225	}
1226
1227	testSign(t, tc2)
1228
1229	testTrack(t, tc2, libkb.KeybaseNullSigVersion, "t_alice")
1230
1231	simulateServiceRestart(t, tc2, fu)
1232
1233	// should be able to sign and to track someone (no passphrase prompt)
1234	testSign(t, tc2)
1235	testTrack(t, tc2, libkb.KeybaseNullSigVersion, "t_bob")
1236}
1237
1238func removePaperkeyPrefix(paperkey string) string {
1239	return strings.Join(strings.Split(paperkey, " ")[2:], " ")
1240}
1241
1242func TestProvisionPaperOnlyNoPrefix(t *testing.T) {
1243	testProvisionPaperOnly(t, removePaperkeyPrefix)
1244}
1245
1246func TestProvisionPaperOnly(t *testing.T) {
1247	testProvisionPaperOnly(t, func(s string) string { return s })
1248}
1249
1250func simulateServiceRestart(t *testing.T, tc libkb.TestContext, fu *FakeUser) {
1251
1252	// Simulate restarting the service by wiping out the
1253	// passphrase stream cache and cached secret keys
1254	tc.SimulateServiceRestart()
1255
1256	// now assert we can login without a passphrase
1257	uis := libkb.UIs{
1258		LoginUI:     &libkb.TestLoginUI{Username: fu.Username},
1259		LogUI:       tc.G.UI.GetLogUI(),
1260		SecretUI:    &libkb.TestSecretUI{},
1261		GPGUI:       &gpgtestui{},
1262		ProvisionUI: newTestProvisionUI(),
1263	}
1264	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1265	m := NewMetaContextForTest(tc).WithUIs(uis)
1266	err := RunEngine2(m, eng)
1267	require.NoError(t, err)
1268}
1269
1270func TestProvisionPaperCommandLine(t *testing.T) {
1271	tc := SetupEngineTest(t, "login")
1272	defer tc.Cleanup()
1273	fu := NewFakeUserOrBust(t, "paper")
1274	arg := MakeTestSignupEngineRunArg(fu)
1275	arg.SkipPaper = false
1276	loginUI := &paperLoginUI{Username: fu.Username}
1277	uis := libkb.UIs{
1278		LogUI:    tc.G.UI.GetLogUI(),
1279		GPGUI:    &gpgtestui{},
1280		SecretUI: fu.NewSecretUI(),
1281		LoginUI:  loginUI,
1282	}
1283	s := NewSignupEngine(tc.G, &arg)
1284	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
1285	require.NoError(t, err)
1286
1287	assertNumDevicesAndKeys(tc, fu, 2, 4)
1288
1289	Logout(tc)
1290
1291	require.True(t, len(loginUI.PaperPhrase) > 0)
1292
1293	// redo SetupEngineTest to get a new home directory...should look like a
1294	// new device.
1295	tc2 := SetupEngineTest(t, "login")
1296	defer tc2.Cleanup()
1297
1298	secUI := fu.NewSecretUI()
1299	provUI := newTestProvisionUIPaper()
1300	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
1301	uis = libkb.UIs{
1302		ProvisionUI: provUI,
1303		LogUI:       tc2.G.UI.GetLogUI(),
1304		SecretUI:    secUI,
1305		LoginUI:     provLoginUI,
1306		GPGUI:       &gpgtestui{},
1307	}
1308
1309	m := NewMetaContextForTest(tc2).WithUIs(uis)
1310	eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase)
1311	err = RunEngine2(m, eng)
1312	require.NoError(t, err)
1313
1314	testUserHasDeviceKey(tc2)
1315
1316	assertNumDevicesAndKeys(tc, fu, 3, 6)
1317	err = AssertProvisioned(tc2)
1318	require.NoError(t, err)
1319
1320	require.Equal(t, provUI.calledChooseDeviceType, 0)
1321	require.Equal(t, provLoginUI.CalledGetEmailOrUsername, 0)
1322}
1323
1324func TestSelfProvision(t *testing.T) {
1325	tc := SetupEngineTest(t, "login")
1326	defer tc.Cleanup()
1327
1328	user := CreateAndSignupFakeUser(tc, "clone")
1329	secUI := user.NewSecretUI()
1330	provUI := newTestProvisionUIPaper()
1331	provLoginUI := &libkb.TestLoginUI{Username: user.Username}
1332	uis := libkb.UIs{
1333		ProvisionUI: provUI,
1334		LogUI:       tc.G.UI.GetLogUI(),
1335		SecretUI:    secUI,
1336		LoginUI:     provLoginUI,
1337		GPGUI:       &gpgtestui{},
1338	}
1339
1340	m := NewMetaContextForTest(tc).WithUIs(uis)
1341	// Test the happy case of successfully self provisioning.
1342	assertValidSelfProvision(t, tc, m, user)
1343}
1344
1345func assertValidSelfProvision(t *testing.T, tc libkb.TestContext, m libkb.MetaContext, user *FakeUser) {
1346	libkb.CreateClonedDevice(tc, m)
1347	newName := tc.G.ActiveDevice.Name() + "uncloneme"
1348	eng := NewSelfProvisionEngine(tc.G, newName)
1349	err := RunEngine2(m, eng)
1350	require.NoError(t, err)
1351
1352	testUserHasDeviceKey(tc)
1353	assertNumDevicesAndKeys(tc, user, 2, 4)
1354	err = AssertProvisioned(tc)
1355	require.NoError(t, err)
1356	require.Equal(t, tc.G.ActiveDevice.Name(), newName)
1357
1358	assertDeviceKeysCached(tc)
1359	assertPassphraseStreamCache(tc)
1360	assertSecretStored(tc, user.Username)
1361
1362	// GetBootstrapStatus should return without error and with LoggedIn set to
1363	// true.
1364	beng := NewBootstrap(tc.G)
1365	err = RunEngine2(m, beng)
1366	require.NoError(t, err)
1367	status := beng.Status()
1368	require.True(t, status.LoggedIn)
1369	require.True(t, status.Registered)
1370
1371	t.Logf("test tracks")
1372	testTrack(t, tc, libkb.KeybaseNullSigVersion, "t_alice")
1373	testSign(t, tc)
1374
1375	// Make sure that we can still track without a passphrase
1376	// after a simulated service restart.  In other words, that
1377	// the full LKSec secret was written to the secret store.
1378	simulateServiceRestart(t, tc, user)
1379	testSign(t, tc)
1380	testTrack(t, tc, libkb.KeybaseNullSigVersion, "t_bob")
1381	assertDeviceKeysCached(tc)
1382	require.Equal(t, tc.G.ActiveDevice.Name(), newName)
1383
1384	Logout(tc)
1385	user.LoginOrBust(tc)
1386}
1387
1388func TestSelfProvisionFailNoClone(t *testing.T) {
1389	// If we don't have a clone, we can't run this engine
1390	testFailSelfProvision(t, func(tc libkb.TestContext, m libkb.MetaContext) string {
1391		return "new"
1392	})
1393}
1394
1395func TestSelfProvisionFailDuplicateName(t *testing.T) {
1396	testFailSelfProvision(t, func(tc libkb.TestContext, m libkb.MetaContext) string {
1397		libkb.CreateClonedDevice(tc, m)
1398		// Use the default name so we get an error when provisioning.
1399		return ""
1400	})
1401}
1402
1403func testFailSelfProvision(t *testing.T, fn func(tc libkb.TestContext, m libkb.MetaContext) string) {
1404
1405	tc := SetupEngineTest(t, "login")
1406	defer tc.Cleanup()
1407
1408	user := CreateAndSignupFakeUser(tc, "clone")
1409
1410	secUI := user.NewSecretUI()
1411	provUI := newTestProvisionUIPaper()
1412	provLoginUI := &libkb.TestLoginUI{Username: user.Username}
1413	uis := libkb.UIs{
1414		ProvisionUI: provUI,
1415		LogUI:       tc.G.UI.GetLogUI(),
1416		SecretUI:    secUI,
1417		LoginUI:     provLoginUI,
1418		GPGUI:       &gpgtestui{},
1419	}
1420	m := NewMetaContextForTest(tc).WithUIs(uis)
1421
1422	suffix := fn(tc, m)
1423	oldName := tc.G.ActiveDevice.Name()
1424	newName := oldName + suffix
1425
1426	t.Logf("self provision running %v %v", oldName, newName)
1427	eng := NewSelfProvisionEngine(tc.G, newName)
1428	err := RunEngine2(m, eng)
1429	require.Error(t, err)
1430	t.Logf("self provision failed successfully %v %v", oldName, newName)
1431
1432	testUserHasDeviceKey(tc)
1433	// Since provisioning failed, we still just have a single device.
1434	assertNumDevicesAndKeys(tc, user, 1, 2)
1435	require.Equal(t, tc.G.ActiveDevice.Name(), oldName)
1436
1437	// GetBootstrapStatus should return without error and with LoggedIn set
1438	// to true.
1439	beng := NewBootstrap(tc.G)
1440	err = RunEngine2(m, beng)
1441	require.NoError(t, err)
1442	status := beng.Status()
1443	require.True(t, status.LoggedIn)
1444	require.True(t, status.Registered)
1445
1446	Logout(tc)
1447	user.LoginOrBust(tc)
1448
1449	// Make sure we can successfully self provision after we've failed.
1450	assertValidSelfProvision(t, tc, m, user)
1451}
1452
1453// Provision device using a private GPG key (not synced to keybase
1454// server), import private key to lksec.
1455func TestProvisionGPGImportOK(t *testing.T) {
1456	tc := SetupEngineTest(t, "login")
1457	defer tc.Cleanup()
1458
1459	u1 := createFakeUserWithPGPPubOnly(t, tc)
1460	Logout(tc)
1461
1462	// redo SetupEngineTest to get a new home directory...should look like a new device.
1463	tc2 := SetupEngineTest(t, "login")
1464	defer tc2.Cleanup()
1465
1466	// we need the gpg keyring that's in the first homedir
1467	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1468		t.Fatal(err)
1469	}
1470
1471	// run login on new device
1472	uis2 := libkb.UIs{
1473		ProvisionUI: newTestProvisionUIGPGImport(),
1474		LogUI:       tc2.G.UI.GetLogUI(),
1475		SecretUI:    u1.NewSecretUI(),
1476		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1477		GPGUI:       &gpgtestui{},
1478	}
1479	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1480	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1481	if err := RunEngine2(m2, eng); err != nil {
1482		t.Fatal(err)
1483	}
1484
1485	testUserHasDeviceKey(tc2)
1486
1487	// highly possible they didn't have a paper key, so make sure they still don't have one:
1488	hasZeroPaperDev(tc2, u1)
1489
1490	if err := AssertProvisioned(tc2); err != nil {
1491		t.Fatal(err)
1492	}
1493
1494	// since they imported their pgp key, they should be able to pgp sign something:
1495	if err := signString(tc2, "sign me", u1.NewSecretUI()); err != nil {
1496		t.Error("pgp sign failed after gpg provision w/ import")
1497		t.Fatal(err)
1498	}
1499
1500	// after provisioning, the secret should be stored
1501	assertSecretStored(tc2, u1.Username)
1502}
1503
1504// Provision device using a private GPG key (not synced to keybase
1505// server), import private key to lksec.  User selects key from
1506// several matching keys.
1507func TestProvisionGPGImportMultiple(t *testing.T) {
1508	tc := SetupEngineTest(t, "login")
1509	defer tc.Cleanup()
1510
1511	u1 := createFakeUserWithPGPMult(t, tc)
1512	Logout(tc)
1513
1514	// redo SetupEngineTest to get a new home directory...should look like a new device.
1515	tc2 := SetupEngineTest(t, "login")
1516	defer tc2.Cleanup()
1517
1518	// we need the gpg keyring that's in the first homedir
1519	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1520		t.Fatal(err)
1521	}
1522
1523	// run login on new device
1524	uis2 := libkb.UIs{
1525		ProvisionUI: newTestProvisionUIGPGImport(),
1526		LogUI:       tc2.G.UI.GetLogUI(),
1527		SecretUI:    u1.NewSecretUI(),
1528		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1529		GPGUI:       &gpgtestui{},
1530	}
1531	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1532	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1533	if err := RunEngine2(m2, eng); err != nil {
1534		t.Fatal(err)
1535	}
1536
1537	testUserHasDeviceKey(tc2)
1538
1539	// highly possible they didn't have a paper key, so make sure they still don't have one:
1540	hasZeroPaperDev(tc2, u1)
1541
1542	if err := AssertProvisioned(tc2); err != nil {
1543		t.Fatal(err)
1544	}
1545
1546	// since they imported their pgp key, they should be able to pgp sign something:
1547	if err := signString(tc2, "sign me", u1.NewSecretUI()); err != nil {
1548		t.Error("pgp sign failed after gpg provision w/ import")
1549		t.Fatal(err)
1550	}
1551
1552	// after provisioning, the secret should be stored
1553	assertSecretStored(tc2, u1.Username)
1554}
1555
1556// Provision device using a private GPG key (not synced to keybase
1557// server), use gpg to sign (no private key import).
1558func TestProvisionGPGSign(t *testing.T) {
1559	// use tcCheck just to check gpg version
1560	tcCheck := SetupEngineTest(t, "check")
1561	defer tcCheck.Cleanup()
1562	skipOldGPG(tcCheck)
1563
1564	// this test sometimes fails at the GPG level with a "Bad signature" error,
1565	// so we're going to retry it several times to hopefully get past it.
1566	attempts := 10
1567	for i := 0; i < attempts; i++ {
1568		tc := SetupEngineTest(t, "login")
1569		defer tc.Cleanup()
1570
1571		u1 := createFakeUserWithPGPPubOnly(t, tc)
1572		Logout(tc)
1573
1574		// redo SetupEngineTest to get a new home directory...should look like a new device.
1575		tc2 := SetupEngineTest(t, "login")
1576		defer tc2.Cleanup()
1577
1578		// we need the gpg keyring that's in the first homedir
1579		if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1580			t.Fatal(err)
1581		}
1582
1583		// run login on new device
1584		uis2 := libkb.UIs{
1585			ProvisionUI: newTestProvisionUIGPGSign(),
1586			LogUI:       tc2.G.UI.GetLogUI(),
1587			SecretUI:    u1.NewSecretUI(),
1588			LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1589			GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1590		}
1591		eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1592		m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1593		if err := RunEngine2(m2, eng); err != nil {
1594			t.Logf("test run %d:  RunEngine(Login) error: %s", i+1, err)
1595			continue
1596		}
1597
1598		t.Logf("test run %d: RunEngine(Login) succeeded", i+1)
1599
1600		testUserHasDeviceKey(tc2)
1601
1602		// highly possible they didn't have a paper key, so make sure they still don't have one:
1603		hasZeroPaperDev(tc2, u1)
1604
1605		if err := AssertProvisioned(tc2); err != nil {
1606			t.Fatal(err)
1607		}
1608
1609		// after provisioning, the secret should be stored
1610		assertSecretStored(tc2, u1.Username)
1611
1612		checkPerUserKeyCount(&tc2, 1)
1613
1614		// since they *did not* import a pgp key, they should *not* be able to pgp sign something:
1615		if err := signString(tc2, "sign me", u1.NewSecretUI()); err == nil {
1616			t.Error("pgp sign worked after gpg provision w/o import")
1617			t.Fatal(err)
1618		}
1619
1620		t.Logf("test run %d: all checks passed, returning", i+1)
1621		return
1622	}
1623
1624	t.Fatalf("TestProvisionGPGSign failed %d times", attempts)
1625}
1626
1627func TestProvisionGPGSignFailedSign(t *testing.T) {
1628	tc := SetupEngineTest(t, "login")
1629	defer tc.Cleanup()
1630
1631	u1 := createFakeUserWithPGPPubOnly(t, tc)
1632	Logout(tc)
1633
1634	// redo SetupEngineTest to get a new home directory...should look like a new device.
1635	tc2 := SetupEngineTest(t, "login")
1636	defer tc2.Cleanup()
1637
1638	// we need the gpg keyring that's in the first homedir
1639	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1640		t.Fatal(err)
1641	}
1642
1643	// run login on new device
1644	uis2 := libkb.UIs{
1645		ProvisionUI: newTestProvisionUIGPGSign(),
1646		LogUI:       tc2.G.UI.GetLogUI(),
1647		SecretUI:    u1.NewSecretUI(),
1648		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1649		GPGUI:       &gpgTestUIBadSign{},
1650	}
1651	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1652	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1653	if err := RunEngine2(m2, eng); err == nil {
1654		t.Fatal("expected a failure in login")
1655	}
1656
1657	cf := tc2.G.Env.GetConfigFilename()
1658	jf := libkb.NewJSONConfigFile(tc2.G, cf)
1659	if err := jf.Load(true); err != nil {
1660		t.Fatal(err)
1661	}
1662	devid := jf.GetDeviceID()
1663	if !devid.IsNil() {
1664		t.Fatalf("got a non-nil Device ID after failed GPG provision (%v)", devid)
1665	}
1666}
1667
1668// Provision device using a private GPG key (not synced to keybase
1669// server), use gpg to sign (no private key import).
1670// Enable secret storage.  keybase-issues#1822
1671func TestProvisionGPGSignSecretStore(t *testing.T) {
1672	tcCheck := SetupEngineTest(t, "check")
1673	defer tcCheck.Cleanup()
1674	skipOldGPG(tcCheck)
1675
1676	// this test sometimes fails at the GPG level with a "Bad signature" error,
1677	// so we're going to retry it several times to hopefully get past it.
1678	attempts := 10
1679	for i := 0; i < attempts; i++ {
1680		tc := SetupEngineTest(t, "login")
1681		defer tc.Cleanup()
1682
1683		u1 := createFakeUserWithPGPPubOnly(t, tc)
1684		Logout(tc)
1685
1686		// redo SetupEngineTest to get a new home directory...should look like a new device.
1687		tc2 := SetupEngineTest(t, "login")
1688		defer tc2.Cleanup()
1689
1690		// we need the gpg keyring that's in the first homedir
1691		if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1692			t.Fatal(err)
1693		}
1694
1695		// create a secret UI that stores the secret
1696		secUI := u1.NewSecretUI()
1697		secUI.StoreSecret = true
1698
1699		// run login on new device
1700		uis2 := libkb.UIs{
1701			ProvisionUI: newTestProvisionUIGPGSign(),
1702			LogUI:       tc2.G.UI.GetLogUI(),
1703			SecretUI:    secUI,
1704			LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1705			GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1706		}
1707		eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1708		m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1709		if err := RunEngine2(m2, eng); err != nil {
1710			t.Logf("test run %d:  RunEngine(Login) error: %s", i+1, err)
1711			continue
1712		}
1713
1714		t.Logf("test run %d: RunEngine(Login) succeeded", i+1)
1715
1716		testUserHasDeviceKey(tc2)
1717
1718		// highly possible they didn't have a paper key, so make sure they still don't have one:
1719		hasZeroPaperDev(tc2, u1)
1720
1721		if err := AssertProvisioned(tc2); err != nil {
1722			t.Fatal(err)
1723		}
1724
1725		// after provisioning, the secret should be stored
1726		assertSecretStored(tc2, u1.Username)
1727
1728		t.Logf("test run %d: all checks passed, returning", i+1)
1729		return
1730	}
1731
1732	t.Fatalf("TestProvisionGPGSignSecretStore failed %d times", attempts)
1733}
1734
1735// Provision device using a private GPG key (not synced to keybase
1736// server). Import private key to lksec fails, switches to gpg
1737// sign, which works.
1738func TestProvisionGPGSwitchToSign(t *testing.T) {
1739	tcCheck := SetupEngineTest(t, "check")
1740	defer tcCheck.Cleanup()
1741	skipOldGPG(tcCheck)
1742
1743	// this test sometimes fails at the GPG level with a "Bad signature" error,
1744	// so we're going to retry it several times to hopefully get past it.
1745	attempts := 10
1746	for i := 0; i < attempts; i++ {
1747		tc := SetupEngineTest(t, "login")
1748		defer tc.Cleanup()
1749
1750		u1 := createFakeUserWithPGPPubOnly(t, tc)
1751		Logout(tc)
1752
1753		// redo SetupEngineTest to get a new home directory...should look like a new device.
1754		tc2 := SetupEngineTest(t, "login")
1755		defer tc2.Cleanup()
1756
1757		// we need the gpg keyring that's in the first homedir
1758		if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1759			t.Fatal(err)
1760		}
1761
1762		// load the user (bypassing LoginUsername for this test...)
1763		user, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc2.G, u1.Username))
1764		if err != nil {
1765			t.Fatal(err)
1766		}
1767
1768		// run login on new device
1769		uis := libkb.UIs{
1770			ProvisionUI: newTestProvisionUIGPGImport(),
1771			LogUI:       tc2.G.UI.GetLogUI(),
1772			SecretUI:    u1.NewSecretUI(),
1773			LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1774			GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1775		}
1776
1777		arg := loginProvisionArg{
1778			DeviceType: keybase1.DeviceTypeV2_DESKTOP,
1779			ClientType: keybase1.ClientType_CLI,
1780			User:       user,
1781		}
1782
1783		eng := newLoginProvision(tc2.G, &arg)
1784		// use a gpg client that will fail to import any gpg key
1785		eng.gpgCli = newGPGImportFailer(tc2.G)
1786		m := NewMetaContextForTest(tc2).WithUIs(uis).WithNewProvisionalLoginContext()
1787
1788		if err := RunEngine2(m, eng); err != nil {
1789			t.Logf("test run %d:  RunEngine(Login) error: %s", i+1, err)
1790			continue
1791		}
1792
1793		t.Logf("test run %d: RunEngine(Login) succeeded", i+1)
1794
1795		testUserHasDeviceKey(tc2)
1796
1797		// highly possible they didn't have a paper key, so make sure they still don't have one:
1798		hasZeroPaperDev(tc2, u1)
1799
1800		if err := AssertProvisioned(tc2); err != nil {
1801			t.Fatal(err)
1802		}
1803
1804		// after provisioning, the secret should be stored
1805		assertSecretStored(tc2, u1.Username)
1806
1807		// since they did not import their pgp key, they should not be able
1808		// to pgp sign something:
1809		if err := signString(tc2, "sign me", u1.NewSecretUI()); err == nil {
1810			t.Fatal("pgp sign worked after gpg sign provisioning")
1811		}
1812		t.Logf("test run %d: all checks passed, returning", i+1)
1813		return
1814	}
1815
1816	t.Fatalf("TestProvisionGPGSwitchToSign failed %d times", attempts)
1817}
1818
1819// Try provision device using a private GPG key (not synced to keybase
1820// server). Import private key to lksec fails, user does not want
1821// to switch to gpg sign, so provisioning fails.
1822func TestProvisionGPGNoSwitchToSign(t *testing.T) {
1823	tc := SetupEngineTest(t, "login")
1824	defer tc.Cleanup()
1825
1826	u1 := createFakeUserWithPGPPubOnly(t, tc)
1827	Logout(tc)
1828
1829	// redo SetupEngineTest to get a new home directory...should look like a new device.
1830	tc2 := SetupEngineTest(t, "login")
1831	defer tc2.Cleanup()
1832
1833	// we need the gpg keyring that's in the first homedir
1834	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
1835		t.Fatal(err)
1836	}
1837
1838	// load the user (bypassing LoginUsername for this test...)
1839	user, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc2.G, u1.Username))
1840	if err != nil {
1841		t.Fatal(err)
1842	}
1843
1844	// instruct provisioning ui to not allow the switch to gpg sign:
1845	provUI := newTestProvisionUIGPGImport()
1846	provUI.abortSwitchToGPGSign = true
1847
1848	// run login on new device
1849	uis := libkb.UIs{
1850		ProvisionUI: provUI,
1851		LogUI:       tc2.G.UI.GetLogUI(),
1852		SecretUI:    u1.NewSecretUI(),
1853		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1854		GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1855	}
1856
1857	arg := loginProvisionArg{
1858		DeviceType: keybase1.DeviceTypeV2_DESKTOP,
1859		ClientType: keybase1.ClientType_CLI,
1860		User:       user,
1861	}
1862
1863	eng := newLoginProvision(tc2.G, &arg)
1864	// use a gpg client that will fail to import any gpg key
1865	eng.gpgCli = newGPGImportFailer(tc2.G)
1866
1867	m := NewMetaContextForTest(tc2).WithUIs(uis)
1868
1869	if err := RunEngine2(m, eng); err == nil {
1870		t.Fatal("provisioning worked despite not allowing switch to gpg sign")
1871	}
1872}
1873
1874// User with pgp keys, but on a device without any gpg keyring.
1875func TestProvisionGPGNoKeyring(t *testing.T) {
1876	tc := SetupEngineTest(t, "login")
1877	u1 := createFakeUserWithPGPPubOnly(t, tc)
1878	Logout(tc)
1879	tc.Cleanup()
1880
1881	// redo SetupEngineTest to get a new home directory...should look like a new device.
1882	tc2 := SetupEngineTest(t, "login")
1883	defer tc2.Cleanup()
1884
1885	// run login on new device
1886	uis2 := libkb.UIs{
1887		ProvisionUI: newTestProvisionUIGPGImport(),
1888		LogUI:       tc2.G.UI.GetLogUI(),
1889		SecretUI:    u1.NewSecretUI(),
1890		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1891		GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc.G)},
1892	}
1893	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1894	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1895	if err := RunEngine2(m2, eng); err == nil {
1896		t.Fatal("provision worked without gpg keyring")
1897	} else if _, ok := err.(libkb.NoMatchingGPGKeysError); !ok {
1898		t.Errorf("error %T, expected libkb.NoMatchingGPGKeysError", err)
1899	}
1900}
1901
1902// User with pgp keys, but on a device with gpg keys that don't
1903// match.
1904func TestProvisionGPGNoMatch(t *testing.T) {
1905	tc := SetupEngineTest(t, "login")
1906	u1 := createFakeUserWithPGPPubOnly(t, tc)
1907	Logout(tc)
1908	tc.Cleanup()
1909
1910	// redo SetupEngineTest to get a new home directory...should look like a new device.
1911	tc2 := SetupEngineTest(t, "login")
1912	defer tc2.Cleanup()
1913
1914	// make a new keyring, not associated with keybase
1915	if err := tc2.GenerateGPGKeyring(u1.Email); err != nil {
1916		t.Fatal(err)
1917	}
1918
1919	// run login on new device
1920	uis2 := libkb.UIs{
1921		ProvisionUI: newTestProvisionUIGPGImport(),
1922		LogUI:       tc2.G.UI.GetLogUI(),
1923		SecretUI:    u1.NewSecretUI(),
1924		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1925		GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1926	}
1927	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1928	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1929	if err := RunEngine2(m2, eng); err == nil {
1930		t.Fatal("provision worked without matching gpg key")
1931	} else if _, ok := err.(libkb.NoMatchingGPGKeysError); !ok {
1932		t.Errorf("error %T, expected libkb.NoMatchingGPGKeysError", err)
1933	}
1934}
1935
1936// User with pgp keys, but on a device without gpg.
1937func TestProvisionGPGNoGPGExecutable(t *testing.T) {
1938	tc := SetupEngineTest(t, "login")
1939	u1 := createFakeUserWithPGPPubOnly(t, tc)
1940	Logout(tc)
1941	tc.Cleanup()
1942
1943	// redo SetupEngineTest to get a new home directory...should look like a new device.
1944	tc2 := SetupEngineTest(t, "login")
1945	defer tc2.Cleanup()
1946
1947	// this should make it unable to find gpg
1948	tc2.G.Env.Test.GPG = filepath.Join(string(filepath.Separator), "dev", "null")
1949
1950	// run login on new device
1951	uis2 := libkb.UIs{
1952		ProvisionUI: newTestProvisionUIGPGImport(),
1953		LogUI:       tc2.G.UI.GetLogUI(),
1954		SecretUI:    u1.NewSecretUI(),
1955		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1956		GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1957	}
1958	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1959	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1960	err := RunEngine2(m2, eng)
1961	if err == nil {
1962		t.Fatal("provision worked without gpg")
1963	}
1964	if _, ok := err.(libkb.GPGUnavailableError); !ok {
1965		t.Errorf("login run err type: %T, expected libkb.GPGUnavailableError", err)
1966	}
1967}
1968
1969// User with pgp keys, but on a device where gpg executable
1970// specified is not found.
1971func TestProvisionGPGNoGPGFound(t *testing.T) {
1972	tc := SetupEngineTest(t, "login")
1973	u1 := createFakeUserWithPGPPubOnly(t, tc)
1974	Logout(tc)
1975	tc.Cleanup()
1976
1977	// redo SetupEngineTest to get a new home directory...should look like a new device.
1978	tc2 := SetupEngineTest(t, "login")
1979	defer tc2.Cleanup()
1980
1981	// this should make it unable to find gpg
1982	tc2.G.Env.Test.GPG = filepath.Join(string(filepath.Separator), "not", "a", "directory", "that", "ever", "exists", "bin", "gpg")
1983
1984	// run login on new device
1985	uis2 := libkb.UIs{
1986		ProvisionUI: newTestProvisionUIGPGImport(),
1987		LogUI:       tc2.G.UI.GetLogUI(),
1988		SecretUI:    u1.NewSecretUI(),
1989		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
1990		GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
1991	}
1992	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
1993	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
1994	err := RunEngine2(m2, eng)
1995	if err == nil {
1996		t.Fatal("provision worked without gpg")
1997	}
1998	if _, ok := err.(libkb.GPGUnavailableError); !ok {
1999		t.Errorf("login run err type: %T, expected libkb.GPGUnavailableError", err)
2000	}
2001}
2002
2003func TestProvisionDupDevice(t *testing.T) {
2004	// device X (provisioner) context:
2005	tcX := SetupEngineTest(t, "kex2provision")
2006	defer tcX.Cleanup()
2007
2008	// device Y (provisionee) context:
2009	tcY := SetupEngineTest(t, "template")
2010	defer tcY.Cleanup()
2011
2012	// provisioner needs to be logged in
2013	userX := CreateAndSignupFakeUser(tcX, "login")
2014
2015	secretCh := make(chan kex2.Secret)
2016
2017	provui := &testProvisionDupDeviceUI{newTestProvisionUISecretCh(secretCh)}
2018
2019	// provisionee calls login:
2020	uis := libkb.UIs{
2021		ProvisionUI: provui,
2022		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
2023		LogUI:       tcY.G.UI.GetLogUI(),
2024		SecretUI:    &libkb.TestSecretUI{},
2025		GPGUI:       &gpgtestui{},
2026	}
2027	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2028	m := NewMetaContextForTest(tcY).WithUIs(uis)
2029
2030	// start provisionee
2031	if err := RunEngine2(m, eng); err == nil {
2032		t.Errorf("login ran without error")
2033		return
2034	}
2035
2036	// Note: there is no need to start the provisioner as the provisionee will
2037	// fail because of the duplicate device name before the provisioner
2038	// is needed.
2039
2040	// double-check that provisioning failed
2041	if err := AssertProvisioned(tcY); err == nil {
2042		t.Fatal("device provisioned using existing name")
2043	}
2044}
2045
2046// If a user has no keys, provision via passphrase should work.
2047// This tests when they have another account on the same machine.
2048func TestProvisionPassphraseNoKeysMultipleAccounts(t *testing.T) {
2049	tcWeb := SetupEngineTest(t, "login")
2050
2051	// create a "web" user with no keys
2052	username, passphrase := createFakeUserWithNoKeys(tcWeb)
2053	Logout(tcWeb)
2054	tcWeb.Cleanup()
2055
2056	// create a new test context
2057	tc := SetupEngineTest(t, "fake")
2058	defer tc.Cleanup()
2059
2060	// create a user to fill up config with something
2061	CreateAndSignupFakeUser(tc, "fake")
2062	Logout(tc)
2063
2064	// now try to log in as the web user
2065	uis := libkb.UIs{
2066		ProvisionUI: newTestProvisionUIPassphrase(),
2067		LoginUI:     &libkb.TestLoginUI{Username: username},
2068		LogUI:       tc.G.UI.GetLogUI(),
2069		SecretUI:    &libkb.TestSecretUI{Passphrase: passphrase},
2070		GPGUI:       &gpgtestui{},
2071	}
2072	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, username, keybase1.ClientType_CLI)
2073	m := NewMetaContextForTest(tc).WithUIs(uis)
2074	if err := RunEngine2(m, eng); err != nil {
2075		t.Fatal(err)
2076	}
2077
2078	// since this user didn't have any keys, login should have fixed that:
2079	testUserHasDeviceKey(tc)
2080
2081	// and they should not have a paper backup key by default
2082	hasZeroPaperDev(tc, &FakeUser{Username: username, Passphrase: passphrase})
2083
2084	if err := AssertProvisioned(tc); err != nil {
2085		t.Fatal(err)
2086	}
2087
2088	// after provisioning, the secret should be stored
2089	assertSecretStored(tc, username)
2090}
2091
2092// We have obviated the unlock command by combining it with login.
2093func TestLoginStreamCache(t *testing.T) {
2094	tc := SetupEngineTest(t, "login")
2095	defer tc.Cleanup()
2096
2097	u1 := SignupFakeUserStoreSecret(tc, "login")
2098	assertSecretStored(tc, u1.Username)
2099
2100	if !assertStreamCache(tc, true) {
2101		t.Fatal("expected valid stream cache after signup")
2102	}
2103
2104	clearCaches(tc.G)
2105
2106	if !assertStreamCache(tc, false) {
2107		t.Fatal("expected invalid stream cache after clear")
2108	}
2109
2110	// This should not unlock the stream cache
2111	u1.LoginOrBust(tc)
2112
2113	if !assertStreamCache(tc, false) {
2114		t.Fatal("expected no valid stream cache after login")
2115	}
2116	assertDeviceKeysCached(tc)
2117	assertSecretStored(tc, u1.Username)
2118}
2119
2120// Check the device type
2121func TestLoginInvalidDeviceType(t *testing.T) {
2122	tcWeb := SetupEngineTest(t, "web")
2123	defer tcWeb.Cleanup()
2124
2125	username, passphrase := createFakeUserWithNoKeys(tcWeb)
2126
2127	Logout(tcWeb)
2128
2129	tc := SetupEngineTest(t, "login")
2130	defer tc.Cleanup()
2131
2132	uis := libkb.UIs{
2133		ProvisionUI: newTestProvisionUIPassphrase(),
2134		LoginUI:     &libkb.TestLoginUI{Username: username},
2135		LogUI:       tc.G.UI.GetLogUI(),
2136		SecretUI:    &libkb.TestSecretUI{Passphrase: passphrase},
2137		GPGUI:       &gpgtestui{},
2138	}
2139	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_PAPER, "", keybase1.ClientType_CLI)
2140	m := NewMetaContextForTest(tc).WithUIs(uis)
2141	if err := RunEngine2(m, eng); err == nil {
2142		t.Fatal("login with paper device type worked")
2143	} else if _, ok := err.(libkb.InvalidArgumentError); !ok {
2144		t.Errorf("err type: %T, expected libkb.InvalidArgumentError", err)
2145	}
2146}
2147
2148// Test that login provision checks for nil user in argument.
2149func TestProvisionNilUser(t *testing.T) {
2150	tc := SetupEngineTest(t, "login")
2151	defer tc.Cleanup()
2152
2153	arg := loginProvisionArg{
2154		DeviceType: keybase1.DeviceTypeV2_DESKTOP,
2155		ClientType: keybase1.ClientType_CLI,
2156		User:       nil,
2157	}
2158	eng := newLoginProvision(tc.G, &arg)
2159	uis := libkb.UIs{
2160		ProvisionUI: newTestProvisionUIPassphrase(),
2161		LoginUI:     &libkb.TestLoginUI{},
2162		LogUI:       tc.G.UI.GetLogUI(),
2163		SecretUI:    &libkb.TestSecretUI{},
2164		GPGUI:       &gpgtestui{},
2165	}
2166	m := NewMetaContextForTest(tc).WithUIs(uis)
2167	if err := RunEngine2(m, eng); err == nil {
2168		t.Fatal("loginprovision with nil user worked")
2169	} else if _, ok := err.(libkb.InvalidArgumentError); !ok {
2170		t.Errorf("err type: %T, expected libkb.InvalidArgumentError", err)
2171	}
2172}
2173
2174func userPlusPaper(t *testing.T) (*FakeUser, string) {
2175	tc := SetupEngineTest(t, "fake")
2176	defer tc.Cleanup()
2177	fu := NewFakeUserOrBust(t, "fake")
2178	arg := MakeTestSignupEngineRunArg(fu)
2179	arg.SkipPaper = false
2180	loginUI := &paperLoginUI{Username: fu.Username}
2181	uis := libkb.UIs{
2182		LogUI:    tc.G.UI.GetLogUI(),
2183		GPGUI:    &gpgtestui{},
2184		SecretUI: fu.NewSecretUI(),
2185		LoginUI:  loginUI,
2186	}
2187	s := NewSignupEngine(tc.G, &arg)
2188	if err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s); err != nil {
2189		t.Fatal(err)
2190	}
2191	Logout(tc)
2192	return fu, loginUI.PaperPhrase
2193}
2194
2195func TestProvisionPaperFailures(t *testing.T) {
2196	// create two users
2197	ux, uxPaper := userPlusPaper(t)
2198	_, uyPaper := userPlusPaper(t)
2199
2200	// try provision as ux on a new device with uy's paper key
2201	tc := SetupEngineTest(t, "login")
2202	defer tc.Cleanup()
2203
2204	secUI := ux.NewSecretUI()
2205	secUI.Passphrase = uyPaper
2206	provUI := newTestProvisionUIPaper()
2207	provLoginUI := &libkb.TestLoginUI{Username: ux.Username}
2208	uis := libkb.UIs{
2209		ProvisionUI: provUI,
2210		LogUI:       tc.G.UI.GetLogUI(),
2211		SecretUI:    secUI,
2212		LoginUI:     provLoginUI,
2213		GPGUI:       &gpgtestui{},
2214	}
2215	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2216	m := NewMetaContextForTest(tc).WithUIs(uis)
2217	if err := RunEngine2(m, eng); err == nil {
2218		t.Fatal("provision with another user's paper key worked")
2219	}
2220
2221	// try provision as ux on a new device with swapped word paper key
2222	tcSwap := SetupEngineTest(t, "login")
2223	defer tcSwap.Cleanup()
2224
2225	words := strings.Fields(uxPaper)
2226	didSwap := false
2227	for i := 2; i < len(words)-1; i++ {
2228		if words[i] != words[i+1] {
2229			tc.G.Log.Debug("swapped word %d (%s) and %d (%s)", i, words[i], i+1, words[i+1])
2230			didSwap = true
2231			words[i], words[i+1] = words[i+1], words[i]
2232			break
2233		}
2234	}
2235	if !didSwap {
2236		t.Fatalf("paper key words were all the same; could not swap: %s", uxPaper)
2237	}
2238	swapped := strings.Join(words, " ")
2239	secUI = ux.NewSecretUI()
2240	secUI.Passphrase = swapped
2241	provUI = newTestProvisionUIPaper()
2242	provLoginUI = &libkb.TestLoginUI{Username: ux.Username}
2243	uis = libkb.UIs{
2244		ProvisionUI: provUI,
2245		LogUI:       tcSwap.G.UI.GetLogUI(),
2246		SecretUI:    secUI,
2247		LoginUI:     provLoginUI,
2248		GPGUI:       &gpgtestui{},
2249	}
2250	eng = NewLogin(tcSwap.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2251	m = NewMetaContextForTest(tcSwap).WithUIs(uis)
2252	err := RunEngine2(m, eng)
2253	if err == nil {
2254		t.Fatal("provision with swapped word paper key worked")
2255	}
2256	if _, ok := err.(libkb.NotFoundError); !ok {
2257		t.Fatalf("error type: %T, expected libkb.NotFoundError", err)
2258	}
2259
2260	// try provision as ux on a new device first with fu's paper key
2261	// then with ux's paper key (testing retry works)
2262	tc2 := SetupEngineTest(t, "login")
2263	defer tc2.Cleanup()
2264
2265	retrySecUI := &testRetrySecretUI{
2266		Passphrases: []string{uyPaper, uxPaper},
2267	}
2268	provUI = newTestProvisionUIPaper()
2269	provLoginUI = &libkb.TestLoginUI{Username: ux.Username}
2270	uis = libkb.UIs{
2271		ProvisionUI: provUI,
2272		LogUI:       tc2.G.UI.GetLogUI(),
2273		SecretUI:    retrySecUI,
2274		LoginUI:     provLoginUI,
2275		GPGUI:       &gpgtestui{},
2276	}
2277	eng = NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2278	m = NewMetaContextForTest(tc2).WithUIs(uis)
2279	if err := RunEngine2(m, eng); err != nil {
2280		t.Fatal(err)
2281	}
2282	if retrySecUI.index != len(retrySecUI.Passphrases) {
2283		t.Errorf("retry sec ui index: %d, expected %d", retrySecUI.index, len(retrySecUI.Passphrases))
2284	}
2285
2286	// try provision as ux on a new device first with garbage paper key
2287	// then with ux's paper key (testing retry works)
2288	tc3 := SetupEngineTest(t, "login")
2289	defer tc3.Cleanup()
2290
2291	retrySecUI = &testRetrySecretUI{
2292		Passphrases: []string{"garbage garbage garbage", uxPaper},
2293	}
2294	provUI = newTestProvisionUIPaper()
2295	provLoginUI = &libkb.TestLoginUI{Username: ux.Username}
2296	uis = libkb.UIs{
2297		ProvisionUI: provUI,
2298		LogUI:       tc3.G.UI.GetLogUI(),
2299		SecretUI:    retrySecUI,
2300		LoginUI:     provLoginUI,
2301		GPGUI:       &gpgtestui{},
2302	}
2303	eng = NewLogin(tc3.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2304	m = NewMetaContextForTest(tc3).WithUIs(uis)
2305	if err := RunEngine2(m, eng); err != nil {
2306		t.Fatal(err)
2307	}
2308	if retrySecUI.index != len(retrySecUI.Passphrases) {
2309		t.Errorf("retry sec ui index: %d, expected %d", retrySecUI.index, len(retrySecUI.Passphrases))
2310	}
2311
2312	// try provision as ux on a new device first with invalid version paper key
2313	// then with ux's paper key (testing retry works)
2314	tc4 := SetupEngineTest(t, "login")
2315	defer tc4.Cleanup()
2316
2317	paperNextVer, err := libkb.MakePaperKeyPhrase(libkb.PaperKeyVersion + 1)
2318	if err != nil {
2319		t.Fatal(err)
2320	}
2321	retrySecUI = &testRetrySecretUI{
2322		Passphrases: []string{paperNextVer.String(), uxPaper},
2323	}
2324	provUI = newTestProvisionUIPaper()
2325	provLoginUI = &libkb.TestLoginUI{Username: ux.Username}
2326	uis = libkb.UIs{
2327		ProvisionUI: provUI,
2328		LogUI:       tc4.G.UI.GetLogUI(),
2329		SecretUI:    retrySecUI,
2330		LoginUI:     provLoginUI,
2331		GPGUI:       &gpgtestui{},
2332	}
2333	eng = NewLogin(tc4.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2334	m = NewMetaContextForTest(tc4).WithUIs(uis)
2335	if err := RunEngine2(m, eng); err != nil {
2336		t.Fatal(err)
2337	}
2338	if retrySecUI.index != len(retrySecUI.Passphrases) {
2339		t.Errorf("retry sec ui index: %d, expected %d", retrySecUI.index, len(retrySecUI.Passphrases))
2340	}
2341
2342}
2343
2344// After kex provisioning, try using a synced pgp key to sign
2345// something.
2346func TestProvisionKexUseSyncPGP(t *testing.T) {
2347	// device X (provisioner) context:
2348	tcX := SetupEngineTestRealTriplesec(t, "kex2provision")
2349	defer tcX.Cleanup()
2350
2351	// device Y (provisionee) context:
2352	tcY := SetupEngineTestRealTriplesec(t, "template")
2353	defer tcY.Cleanup()
2354
2355	// create provisioner with synced pgp key
2356	userX := createFakeUserWithPGPSibkeyPushedPaper(tcX)
2357	var secretX kex2.Secret
2358	if _, err := rand.Read(secretX[:]); err != nil {
2359		t.Fatal(err)
2360	}
2361
2362	secretCh := make(chan kex2.Secret)
2363
2364	// provisionee calls login:
2365	uis := libkb.UIs{
2366		ProvisionUI: newTestProvisionUISecretCh(secretCh),
2367		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
2368		LogUI:       tcY.G.UI.GetLogUI(),
2369		SecretUI:    &libkb.TestSecretUI{},
2370		GPGUI:       &gpgtestui{},
2371	}
2372	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2373
2374	var wg sync.WaitGroup
2375
2376	// start provisionee
2377	wg.Add(1)
2378	go func() {
2379		defer wg.Done()
2380		m := NewMetaContextForTest(tcY).WithUIs(uis)
2381		if err := RunEngine2(m, eng); err != nil {
2382			t.Errorf("login error: %s", err)
2383			return
2384		}
2385	}()
2386
2387	// start provisioner
2388	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
2389	wg.Add(1)
2390	go func() {
2391		defer wg.Done()
2392
2393		uis := libkb.UIs{
2394			SecretUI:    userX.NewSecretUI(),
2395			ProvisionUI: newTestProvisionUI(),
2396		}
2397		m := NewMetaContextForTest(tcX).WithUIs(uis)
2398		if err := RunEngine2(m, provisioner); err != nil {
2399			t.Errorf("provisioner error: %s", err)
2400			return
2401		}
2402	}()
2403	secretFromY := <-secretCh
2404	provisioner.AddSecret(secretFromY)
2405
2406	wg.Wait()
2407
2408	if err := AssertProvisioned(tcY); err != nil {
2409		t.Fatal(err)
2410	}
2411
2412	t.Logf(strings.Repeat("*", 100))
2413	t.Logf("provisioned")
2414	t.Logf(strings.Repeat("*", 100))
2415
2416	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_alice")
2417
2418	// tsec isn't cached on device Y, so this should fail since the
2419	// secret ui doesn't know the passphrase:
2420	if err := signString(tcY, "sign me", &libkb.TestSecretUI{}); err == nil {
2421		t.Fatal("sign worked on device Y after provisioning without knowing passphrase")
2422	}
2423
2424	// but if we know the passphrase, it should prompt for it
2425	// and use it
2426	if err := signString(tcY, "sign me", userX.NewSecretUI()); err != nil {
2427		t.Fatalf("sign failed on device Y with passphrase in secret ui: %s", err)
2428	}
2429}
2430
2431// Provision one (physical) device with multiple users.
2432func TestProvisionMultipleUsers(t *testing.T) {
2433	// make some users with synced pgp keys
2434	users := make([]*FakeUser, 3)
2435	for i := 0; i < len(users); i++ {
2436		tc := SetupEngineTest(t, "login")
2437		users[i] = createFakeUserWithPGPOnly(t, tc)
2438		Logout(tc)
2439		tc.Cleanup()
2440	}
2441
2442	// provision user[0] on a new device
2443	tc := SetupEngineTest(t, "login")
2444	defer tc.Cleanup()
2445
2446	uis := libkb.UIs{
2447		ProvisionUI: newTestProvisionUIPassphrase(),
2448		LoginUI:     &libkb.TestLoginUI{Username: users[0].Username},
2449		LogUI:       tc.G.UI.GetLogUI(),
2450		SecretUI:    users[0].NewSecretUI(),
2451		GPGUI:       &gpgtestui{},
2452	}
2453	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2454	m := NewMetaContextForTest(tc).WithUIs(uis)
2455	if err := RunEngine2(m, eng); err != nil {
2456		t.Fatal(err)
2457	}
2458
2459	testUserHasDeviceKey(tc)
2460	hasZeroPaperDev(tc, users[0])
2461	if err := AssertProvisioned(tc); err != nil {
2462		t.Fatal(err)
2463	}
2464
2465	Logout(tc)
2466
2467	// provision user[1] on the same device, specifying username
2468	uis = libkb.UIs{
2469		ProvisionUI: newTestProvisionUIPassphrase(),
2470		LoginUI:     &libkb.TestLoginUI{},
2471		LogUI:       tc.G.UI.GetLogUI(),
2472		SecretUI:    users[1].NewSecretUI(),
2473		GPGUI:       &gpgtestui{},
2474	}
2475	eng = NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, users[1].Username, keybase1.ClientType_CLI)
2476	m = NewMetaContextForTest(tc).WithUIs(uis)
2477	if err := RunEngine2(m, eng); err != nil {
2478		t.Fatal(err)
2479	}
2480
2481	testUserHasDeviceKey(tc)
2482	hasZeroPaperDev(tc, users[1])
2483	if err := AssertProvisioned(tc); err != nil {
2484		t.Fatal(err)
2485	}
2486
2487	Logout(tc)
2488
2489	// provision user[2] on the same device, specifying email
2490	uis = libkb.UIs{
2491		ProvisionUI: newTestProvisionUIPassphrase(),
2492		LoginUI:     &libkb.TestLoginUI{},
2493		LogUI:       tc.G.UI.GetLogUI(),
2494		SecretUI:    users[2].NewSecretUI(),
2495		GPGUI:       &gpgtestui{},
2496	}
2497	eng = NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, users[2].Username, keybase1.ClientType_CLI)
2498	m = NewMetaContextForTest(tc).WithUIs(uis)
2499	if err := RunEngine2(m, eng); err != nil {
2500		t.Fatal(err)
2501	}
2502
2503	testUserHasDeviceKey(tc)
2504	hasZeroPaperDev(tc, users[2])
2505	if err := AssertProvisioned(tc); err != nil {
2506		t.Fatal(err)
2507	}
2508
2509	Logout(tc)
2510
2511	// login via email does not work anymore (CORE-10470)
2512	uis = libkb.UIs{
2513		ProvisionUI: newTestProvisionUIPassphrase(),
2514		LoginUI:     &libkb.TestLoginUI{},
2515		LogUI:       tc.G.UI.GetLogUI(),
2516		SecretUI:    users[2].NewSecretUI(),
2517		GPGUI:       &gpgtestui{},
2518	}
2519	eng = NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, users[2].Email, keybase1.ClientType_CLI)
2520	m = NewMetaContextForTest(tc).WithUIs(uis)
2521	err := RunEngine2(m, eng)
2522	require.Error(t, err)
2523	require.IsType(t, libkb.BadUsernameError{}, err)
2524	require.Contains(t, err.Error(), "not supported")
2525}
2526
2527// create a standard user with device keys, reset account, login.
2528func TestResetAccount(t *testing.T) {
2529	tc := SetupEngineTest(t, "login")
2530	defer tc.Cleanup()
2531
2532	u := CreateAndSignupFakeUser(tc, "login")
2533	originalDevice := tc.G.Env.GetDeviceID()
2534	ResetAccount(tc, u)
2535
2536	// this will reprovision as an eldest device:
2537	u.LoginOrBust(tc)
2538	if err := AssertProvisioned(tc); err != nil {
2539		t.Fatal(err)
2540	}
2541
2542	newDevice := tc.G.Env.GetDeviceID()
2543
2544	if newDevice == originalDevice {
2545		t.Errorf("device id did not change: %s", newDevice)
2546	}
2547
2548	testUserHasDeviceKey(tc)
2549}
2550
2551// create a standard user with device keys, reset account (but don't logout), login.
2552func TestResetAccountNoLogout(t *testing.T) {
2553	tc := SetupEngineTest(t, "login")
2554	defer tc.Cleanup()
2555
2556	u := CreateAndSignupFakeUser(tc, "login")
2557	originalDevice := tc.G.Env.GetDeviceID()
2558	ResetAccountNoLogout(tc, u)
2559
2560	// this will reprovision as an eldest device:
2561	u.LoginOrBust(tc)
2562	if err := AssertProvisioned(tc); err != nil {
2563		t.Fatal(err)
2564	}
2565
2566	newDevice := tc.G.Env.GetDeviceID()
2567
2568	if newDevice == originalDevice {
2569		t.Errorf("device id did not change: %s", newDevice)
2570	}
2571
2572	testUserHasDeviceKey(tc)
2573}
2574
2575// create a standard user with device keys, reset account (but don't logout), login.
2576// Prime the FullSelfer cache before reset.
2577func TestResetAccountNoLogoutSelfCache(t *testing.T) {
2578	tc := SetupEngineTest(t, "login")
2579	defer tc.Cleanup()
2580
2581	u := CreateAndSignupFakeUser(tc, "login")
2582	originalDevice := tc.G.Env.GetDeviceID()
2583
2584	// make sure FullSelf is cached
2585	err := tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
2586		t.Logf("full self user: %s", u.GetName())
2587		return nil
2588	})
2589	require.NoError(t, err)
2590
2591	ResetAccountNoLogout(tc, u)
2592
2593	// this will reprovision as an eldest device:
2594	u.LoginOrBust(tc)
2595	if err := AssertProvisioned(tc); err != nil {
2596		t.Fatal(err)
2597	}
2598
2599	newDevice := tc.G.Env.GetDeviceID()
2600
2601	if newDevice == originalDevice {
2602		t.Errorf("device id did not change: %s", newDevice)
2603	}
2604
2605	testUserHasDeviceKey(tc)
2606}
2607
2608// After resetting account, try provisioning in a clean home dir.
2609func TestResetAccountNewHome(t *testing.T) {
2610	tc := SetupEngineTest(t, "login")
2611	defer tc.Cleanup()
2612
2613	u := CreateAndSignupFakeUser(tc, "login")
2614	originalDevice := tc.G.Env.GetDeviceID()
2615	ResetAccount(tc, u)
2616
2617	tcp := SetupEngineTest(t, "login")
2618	// this will reprovision as an eldest device:
2619	u.LoginOrBust(tcp)
2620	if err := AssertProvisioned(tcp); err != nil {
2621		t.Fatal(err)
2622	}
2623
2624	newDevice := tcp.G.Env.GetDeviceID()
2625
2626	if newDevice == originalDevice {
2627		t.Errorf("device id did not change: %s", newDevice)
2628	}
2629
2630	testUserHasDeviceKey(tcp)
2631}
2632
2633// After account reset, establish new eldest keys, paper key.
2634// Provision another device with paper key.
2635func TestResetAccountPaper(t *testing.T) {
2636	tc := SetupEngineTest(t, "login")
2637	defer tc.Cleanup()
2638
2639	u := CreateAndSignupFakeUser(tc, "login")
2640	ResetAccount(tc, u)
2641
2642	// login, creating new eldest key, new paper keys
2643	loginUI := &paperLoginUI{Username: u.Username}
2644	uis := libkb.UIs{
2645		ProvisionUI: newTestProvisionUI(),
2646		LogUI:       tc.G.UI.GetLogUI(),
2647		GPGUI:       &gpgtestui{},
2648		SecretUI:    u.NewSecretUI(),
2649		LoginUI:     loginUI,
2650	}
2651	li := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, u.Username, keybase1.ClientType_CLI)
2652	m := NewMetaContextForTest(tc).WithUIs(uis)
2653	if err := RunEngine2(m, li); err != nil {
2654		t.Fatal(err)
2655	}
2656	paper := loginUI.PaperPhrase
2657	if len(paper) != 0 {
2658		t.Fatal("paper phrase exists in login ui")
2659	}
2660	testUserHasDeviceKey(tc)
2661
2662	uis = libkb.UIs{
2663		LogUI:    tc.G.UI.GetLogUI(),
2664		LoginUI:  &libkb.TestLoginUI{},
2665		SecretUI: &libkb.TestSecretUI{},
2666	}
2667	peng := NewPaperKey(tc.G)
2668	m = NewMetaContextForTest(tc).WithUIs(uis)
2669	if err := RunEngine2(m, peng); err != nil {
2670		t.Fatal(err)
2671	}
2672	if len(peng.Passphrase()) == 0 {
2673		t.Fatal("empty paper phrase")
2674	}
2675	paper = peng.Passphrase()
2676
2677	// provision on new device with paper key
2678	tcp := SetupEngineTest(t, "login")
2679	defer tcp.Cleanup()
2680
2681	secUI := u.NewSecretUI()
2682	secUI.Passphrase = paper
2683	provUI := newTestProvisionUIPaper()
2684	provLoginUI := &libkb.TestLoginUI{Username: u.Username}
2685	uis = libkb.UIs{
2686		ProvisionUI: provUI,
2687		LogUI:       tcp.G.UI.GetLogUI(),
2688		SecretUI:    secUI,
2689		LoginUI:     provLoginUI,
2690		GPGUI:       &gpgtestui{},
2691	}
2692	eng := NewLogin(tcp.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2693	m = NewMetaContextForTest(tcp).WithUIs(uis)
2694	if err := RunEngine2(m, eng); err != nil {
2695		t.Fatal(err)
2696	}
2697
2698	testUserHasDeviceKey(tcp)
2699}
2700
2701// After resetting account, try kex2 provisioning.
2702func TestResetAccountKexProvision(t *testing.T) {
2703	tc := SetupEngineTest(t, "login")
2704	defer tc.Cleanup()
2705
2706	u := CreateAndSignupFakeUser(tc, "login")
2707
2708	ResetAccount(tc, u)
2709
2710	// create provisioner device
2711	tcX := SetupEngineTest(t, "login")
2712	defer tcX.Cleanup()
2713	// this will reprovision as an eldest device:
2714	u.LoginOrBust(tcX)
2715	if err := AssertProvisioned(tcX); err != nil {
2716		t.Fatal(err)
2717	}
2718	testUserHasDeviceKey(tcX)
2719	var secretX kex2.Secret
2720	if _, err := rand.Read(secretX[:]); err != nil {
2721		t.Fatal(err)
2722	}
2723	secretCh := make(chan kex2.Secret)
2724
2725	// provisionee context:
2726	tcY := SetupEngineTest(t, "template")
2727	defer tcY.Cleanup()
2728
2729	// provisionee calls login:
2730	uis := libkb.UIs{
2731		ProvisionUI: newTestProvisionUISecretCh(secretCh),
2732		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
2733		LogUI:       tcY.G.UI.GetLogUI(),
2734		SecretUI:    &libkb.TestSecretUI{},
2735		GPGUI:       &gpgtestui{},
2736	}
2737	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2738
2739	var wg sync.WaitGroup
2740
2741	// start provisionee
2742	wg.Add(1)
2743	go func() {
2744		defer wg.Done()
2745		m := NewMetaContextForTest(tcY).WithUIs(uis)
2746		if err := RunEngine2(m, eng); err != nil {
2747			t.Errorf("login error: %s", err)
2748			return
2749		}
2750	}()
2751
2752	// start provisioner
2753	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
2754	wg.Add(1)
2755	go func() {
2756		defer wg.Done()
2757
2758		uis := libkb.UIs{
2759			SecretUI:    u.NewSecretUI(),
2760			ProvisionUI: newTestProvisionUI(),
2761		}
2762		m := NewMetaContextForTest(tcX).WithUIs(uis)
2763		if err := RunEngine2(m, provisioner); err != nil {
2764			t.Errorf("provisioner error: %s", err)
2765			return
2766		}
2767	}()
2768	secretFromY := <-secretCh
2769	provisioner.AddSecret(secretFromY)
2770
2771	wg.Wait()
2772
2773	err := AssertProvisioned(tcY)
2774	require.NoError(t, err)
2775	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_alice")
2776}
2777
2778// Try to replicate @nistur sigchain.
2779// github issue: https://github.com/keybase/client/issues/2356
2780func TestResetThenPGPOnlyThenProvision(t *testing.T) {
2781	tc0 := SetupEngineTest(t, "login")
2782	defer tc0.Cleanup()
2783
2784	// user with synced pgp key
2785	u := createFakeUserWithPGPOnly(t, tc0)
2786	Logout(tc0)
2787
2788	// provision a device with that key
2789	tc := SetupEngineTest(t, "login")
2790	defer tc.Cleanup()
2791
2792	uis := libkb.UIs{
2793		ProvisionUI: newTestProvisionUIPassphrase(),
2794		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
2795		LogUI:       tc.G.UI.GetLogUI(),
2796		SecretUI:    u.NewSecretUI(),
2797		GPGUI:       &gpgtestui{},
2798	}
2799	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2800	m := NewMetaContextForTest(tc).WithUIs(uis)
2801	if err := RunEngine2(m, eng); err != nil {
2802		t.Fatal(err)
2803	}
2804
2805	// since this user didn't have any device keys, login should have fixed that:
2806	testUserHasDeviceKey(tc)
2807
2808	// now reset account
2809	ResetAccount(tc, u)
2810
2811	// Now login again so we can post a PGP key
2812	m = m.WithNewProvisionalLoginContext()
2813	err := libkb.PassphraseLoginNoPrompt(m, u.Username, u.Passphrase)
2814	require.NoError(t, err, "passphrase login no prompt worked")
2815
2816	// Generate a new test PGP key for the user, and specify the PushSecret
2817	// flag so that their triplesec'ed key is pushed to the server.
2818	gen := libkb.PGPGenArg{
2819		PrimaryBits: 768,
2820		SubkeyBits:  768,
2821	}
2822	gen.AddDefaultUID(tc.G)
2823	peng := NewPGPKeyImportEngine(tc.G, PGPKeyImportEngineArg{
2824		Gen:        &gen,
2825		PushSecret: true,
2826		NoSave:     true,
2827	})
2828
2829	if err := RunEngine2(m, peng); err != nil {
2830		tc.T.Fatal(err)
2831	}
2832
2833	m = m.CommitProvisionalLogin()
2834	Logout(tc)
2835
2836	// Now finally try a login
2837	eng = NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2838	if err := RunEngine2(m, eng); err != nil {
2839		t.Fatal(err)
2840	}
2841
2842	// since this user didn't have any device keys, login should have fixed that:
2843	testUserHasDeviceKey(tc)
2844}
2845
2846// Try to replicate @nistur sigchain.
2847// github issue: https://github.com/keybase/client/issues/2356
2848func TestResetAccountLikeNistur(t *testing.T) {
2849	tc0 := SetupEngineTest(t, "login")
2850	defer tc0.Cleanup()
2851
2852	// user with synced pgp key
2853	u := createFakeUserWithPGPOnly(t, tc0)
2854	Logout(tc0)
2855
2856	// provision a device with that key
2857	tc := SetupEngineTest(t, "login")
2858	defer tc.Cleanup()
2859
2860	uis := libkb.UIs{
2861		ProvisionUI: newTestProvisionUIPassphrase(),
2862		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
2863		LogUI:       tc.G.UI.GetLogUI(),
2864		SecretUI:    u.NewSecretUI(),
2865		GPGUI:       &gpgtestui{},
2866	}
2867	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2868	m := NewMetaContextForTest(tc).WithUIs(uis)
2869	if err := RunEngine2(m, eng); err != nil {
2870		t.Fatal(err)
2871	}
2872
2873	// since this user didn't have any device keys, login should have fixed that:
2874	testUserHasDeviceKey(tc)
2875
2876	// now reset account
2877	ResetAccount(tc, u)
2878
2879	// create provisioner device
2880	tcX := SetupEngineTest(t, "login")
2881	defer tcX.Cleanup()
2882
2883	// this will reprovision as an eldest device:
2884	u.LoginOrBust(tcX)
2885	if err := AssertProvisioned(tcX); err != nil {
2886		t.Fatal(err)
2887	}
2888	testUserHasDeviceKey(tcX)
2889	var secretX kex2.Secret
2890	if _, err := rand.Read(secretX[:]); err != nil {
2891		t.Fatal(err)
2892	}
2893	secretCh := make(chan kex2.Secret)
2894
2895	// provisionee context:
2896	tcY := SetupEngineTest(t, "template")
2897	defer tcY.Cleanup()
2898
2899	// provisionee calls login:
2900	uis = libkb.UIs{
2901		ProvisionUI: newTestProvisionUISecretCh(secretCh),
2902		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
2903		LogUI:       tcY.G.UI.GetLogUI(),
2904		SecretUI:    &libkb.TestSecretUI{},
2905		GPGUI:       &gpgtestui{},
2906	}
2907	eng = NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2908
2909	var wg sync.WaitGroup
2910
2911	// start provisionee
2912	wg.Add(1)
2913	go func() {
2914		defer wg.Done()
2915		m := NewMetaContextForTest(tcY).WithUIs(uis)
2916		if err := RunEngine2(m, eng); err != nil {
2917			t.Errorf("login error: %s", err)
2918			return
2919		}
2920	}()
2921
2922	// start provisioner
2923	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
2924	wg.Add(1)
2925	go func() {
2926		defer wg.Done()
2927
2928		uis := libkb.UIs{
2929			SecretUI:    u.NewSecretUI(),
2930			ProvisionUI: newTestProvisionUI(),
2931		}
2932		m := NewMetaContextForTest(tcX).WithUIs(uis)
2933		if err := RunEngine2(m, provisioner); err != nil {
2934			t.Errorf("provisioner error: %s", err)
2935			return
2936		}
2937	}()
2938	secretFromY := <-secretCh
2939	provisioner.AddSecret(secretFromY)
2940
2941	wg.Wait()
2942
2943	err := AssertProvisioned(tcY)
2944	require.NoError(t, err)
2945	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_alice")
2946}
2947
2948// Establish two devices.  Reset on one of them, login on the other.
2949func TestResetMultipleDevices(t *testing.T) {
2950	tcX := SetupEngineTest(t, "login")
2951	defer tcX.Cleanup()
2952
2953	// create provisioner device
2954	u := CreateAndSignupFakeUser(tcX, "login")
2955	if err := AssertProvisioned(tcX); err != nil {
2956		t.Fatal(err)
2957	}
2958	testUserHasDeviceKey(tcX)
2959	var secretX kex2.Secret
2960	if _, err := rand.Read(secretX[:]); err != nil {
2961		t.Fatal(err)
2962	}
2963	secretCh := make(chan kex2.Secret)
2964
2965	// provisionee context:
2966	tcY := SetupEngineTest(t, "template")
2967	defer tcY.Cleanup()
2968
2969	// provisionee calls login:
2970	uis := libkb.UIs{
2971		ProvisionUI: newTestProvisionUISecretCh(secretCh),
2972		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
2973		LogUI:       tcY.G.UI.GetLogUI(),
2974		SecretUI:    &libkb.TestSecretUI{},
2975		GPGUI:       &gpgtestui{},
2976	}
2977	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
2978
2979	var wg sync.WaitGroup
2980
2981	// start provisionee
2982	wg.Add(1)
2983	go func() {
2984		defer wg.Done()
2985		m := NewMetaContextForTest(tcY).WithUIs(uis)
2986		if err := RunEngine2(m, eng); err != nil {
2987			t.Errorf("login error: %s", err)
2988			return
2989		}
2990	}()
2991
2992	// start provisioner
2993	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
2994	wg.Add(1)
2995	go func() {
2996		defer wg.Done()
2997
2998		uis := libkb.UIs{
2999			SecretUI:    u.NewSecretUI(),
3000			ProvisionUI: newTestProvisionUI(),
3001		}
3002		m := NewMetaContextForTest(tcX).WithUIs(uis)
3003		if err := RunEngine2(m, provisioner); err != nil {
3004			t.Errorf("provisioner error: %s", err)
3005			return
3006		}
3007	}()
3008	secretFromY := <-secretCh
3009	provisioner.AddSecret(secretFromY)
3010
3011	wg.Wait()
3012
3013	if err := AssertProvisioned(tcY); err != nil {
3014		t.Fatal(err)
3015	}
3016
3017	// have two devices in contexts tcX and tcY
3018	deviceX := tcX.G.Env.GetDeviceID()
3019
3020	// logout on tcX
3021	Logout(tcX)
3022
3023	// reset on tcY
3024	ResetAccount(tcY, u)
3025
3026	// login on tcX
3027	u.LoginOrBust(tcX)
3028
3029	if err := AssertProvisioned(tcX); err != nil {
3030		t.Fatal(err)
3031	}
3032
3033	if tcX.G.Env.GetDeviceID() == deviceX {
3034		t.Error("device id did not change")
3035	}
3036}
3037
3038// If there is a bad device id in the config file, provisioning
3039// appears to succeed and provision the new device, but the config
3040// file retains the bad device id and further attempts to
3041// do anything fail (they say login required, and running login
3042// results in provisioning again...)
3043// Seems to only happen w/ kex2.
3044func TestProvisionWithBadConfig(t *testing.T) {
3045	// device X (provisioner) context:
3046	tcX := SetupEngineTest(t, "kex2provision")
3047	defer tcX.Cleanup()
3048
3049	// device Y (provisionee) context:
3050	tcY := SetupEngineTest(t, "template")
3051	defer tcY.Cleanup()
3052
3053	// provisioner needs to be logged in
3054	userX := CreateAndSignupFakeUserPaper(tcX, "login")
3055	var secretX kex2.Secret
3056	if _, err := rand.Read(secretX[:]); err != nil {
3057		t.Fatal(err)
3058	}
3059
3060	// copy the config info from device X to device Y
3061	uc, err := tcX.G.Env.GetConfig().GetUserConfig()
3062	if err != nil {
3063		t.Fatal(err)
3064	}
3065	if err := tcY.G.Env.GetConfigWriter().SetUserConfig(uc, true); err != nil {
3066		t.Fatal(err)
3067	}
3068	// but give device Y a new random device ID that doesn't exist:
3069	newID, err := libkb.NewDeviceID()
3070	if err != nil {
3071		t.Fatal(err)
3072	}
3073	if err := tcY.G.Env.GetConfigWriter().SetDeviceID(newID); err != nil {
3074		t.Fatal(err)
3075	}
3076
3077	secretCh := make(chan kex2.Secret)
3078
3079	// provisionee calls login:
3080	uis := libkb.UIs{
3081		ProvisionUI: newTestProvisionUISecretCh(secretCh),
3082		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
3083		LogUI:       tcY.G.UI.GetLogUI(),
3084		SecretUI:    &libkb.TestSecretUI{},
3085		GPGUI:       &gpgtestui{},
3086	}
3087	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3088
3089	var wg sync.WaitGroup
3090
3091	// start provisionee
3092	wg.Add(1)
3093	go func() {
3094		defer wg.Done()
3095		m := NewMetaContextForTest(tcY).WithUIs(uis)
3096		if err := RunEngine2(m, eng); err != nil {
3097			t.Errorf("login error: %s", err)
3098			return
3099		}
3100	}()
3101
3102	// start provisioner
3103	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
3104	wg.Add(1)
3105	go func() {
3106		defer wg.Done()
3107
3108		uis := libkb.UIs{
3109			SecretUI:    userX.NewSecretUI(),
3110			ProvisionUI: newTestProvisionUI(),
3111		}
3112		m := NewMetaContextForTest(tcX).WithUIs(uis)
3113		if err := RunEngine2(m, provisioner); err != nil {
3114			t.Errorf("provisioner error: %s", err)
3115			return
3116		}
3117	}()
3118	secretFromY := <-secretCh
3119	provisioner.AddSecret(secretFromY)
3120
3121	wg.Wait()
3122
3123	if tcY.G.Env.GetDeviceID() == newID {
3124		t.Errorf("y device id: %s, same as %s.  expected it to change.", tcY.G.Env.GetDeviceID(), newID)
3125	}
3126	if tcY.G.Env.GetDeviceID() == tcX.G.Env.GetDeviceID() {
3127		t.Error("y device id matches x device id, they should be different")
3128	}
3129
3130	err = AssertProvisioned(tcY)
3131	require.NoError(t, err)
3132	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_alice")
3133}
3134
3135// If the provisioner has their secret stored, they should not be
3136// prompted to enter a passphrase when they provision a device.
3137func TestProvisionerSecretStore(t *testing.T) {
3138	// device X (provisioner) context:
3139	tcX := SetupEngineTest(t, "kex2provision")
3140	defer tcX.Cleanup()
3141
3142	// device Y (provisionee) context:
3143	tcY := SetupEngineTest(t, "template")
3144	defer tcY.Cleanup()
3145
3146	// create provisioner w/ stored secret
3147	userX := SignupFakeUserStoreSecret(tcX, "login")
3148	// userX := CreateAndSignupFakeUser(tcX, "login")
3149	var secretX kex2.Secret
3150	if _, err := rand.Read(secretX[:]); err != nil {
3151		t.Fatal(err)
3152	}
3153	clearCaches(tcX.G)
3154
3155	secretCh := make(chan kex2.Secret)
3156
3157	// provisionee calls login:
3158	uis := libkb.UIs{
3159		ProvisionUI: newTestProvisionUISecretCh(secretCh),
3160		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
3161		LogUI:       tcY.G.UI.GetLogUI(),
3162		SecretUI:    &libkb.TestSecretUI{},
3163		GPGUI:       &gpgtestui{},
3164	}
3165	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3166	m := NewMetaContextForTest(tcY).WithUIs(uis)
3167
3168	// start provisionee
3169	errY := make(chan error, 1)
3170	go func() {
3171		errY <- RunEngine2(m, eng)
3172	}()
3173
3174	// start provisioner
3175	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
3176	errX := make(chan error, 1)
3177	go func() {
3178		uis := libkb.UIs{
3179			SecretUI:    &testNoPromptSecretUI{},
3180			ProvisionUI: newTestProvisionUI(),
3181		}
3182		m := NewMetaContextForTest(tcX).WithUIs(uis)
3183		errX <- RunEngine2(m, provisioner)
3184	}()
3185	secretFromY := <-secretCh
3186	go provisioner.AddSecret(secretFromY)
3187
3188	var xDone, yDone bool
3189	for {
3190		select {
3191		case e := <-errY:
3192			if e != nil {
3193				t.Fatalf("provisionee error: %s", e)
3194			}
3195			yDone = true
3196		case e := <-errX:
3197			if e != nil {
3198				t.Fatalf("provisioner error: %s", e)
3199			}
3200			xDone = true
3201		}
3202		if xDone && yDone {
3203			break
3204		}
3205	}
3206
3207	if err := AssertProvisioned(tcY); err != nil {
3208		t.Fatal(err)
3209	}
3210
3211	// On device Y, logout and login. This should tickle Bug3964
3212	Logout(tcY)
3213	uis = libkb.UIs{
3214		ProvisionUI: newTestProvisionUIPassphrase(),
3215		LoginUI:     &libkb.TestLoginUI{},
3216		LogUI:       tcY.G.UI.GetLogUI(),
3217		SecretUI:    userX.NewSecretUI(),
3218		GPGUI:       &gpgtestui{},
3219	}
3220	eng = NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, userX.Username, keybase1.ClientType_CLI)
3221	m = NewMetaContextForTest(tcY).WithUIs(uis)
3222	if err := RunEngine2(m, eng); err != nil {
3223		t.Fatal(err)
3224	}
3225}
3226
3227// GPG key required for provisioning, but user on a mobile device.
3228func TestProvisionGPGMobile(t *testing.T) {
3229	tc := SetupEngineTest(t, "login")
3230	defer tc.Cleanup()
3231
3232	u1 := createFakeUserWithPGPPubOnly(t, tc)
3233	Logout(tc)
3234
3235	// redo SetupEngineTest to get a new home directory...should look like a new device.
3236	tc2 := SetupEngineTest(t, "login")
3237	defer tc2.Cleanup()
3238
3239	// we need the gpg keyring that's in the first homedir
3240	if err := tc.MoveGpgKeyringTo(tc2); err != nil {
3241		t.Fatal(err)
3242	}
3243
3244	// run login on new device
3245	uis := libkb.UIs{
3246		ProvisionUI: newTestProvisionUIGPGImport(),
3247		LogUI:       tc2.G.UI.GetLogUI(),
3248		SecretUI:    u1.NewSecretUI(),
3249		LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
3250		GPGUI:       &gpgtestui{},
3251	}
3252	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_MOBILE, "", keybase1.ClientType_CLI)
3253	m := NewMetaContextForTest(tc2).WithUIs(uis)
3254	err := RunEngine2(m, eng)
3255	if err == nil {
3256		t.Fatal("no error provisioning with gpg on mobile")
3257	}
3258	if _, ok := err.(libkb.GPGUnavailableError); !ok {
3259		t.Errorf("error type: %T, expected libkb.GPGUnavailableError", err)
3260	}
3261}
3262
3263func TestProvisionEnsureNoPaperKey(t *testing.T) {
3264	testProvisionEnsureNoPaperKey(t, false)
3265}
3266
3267func TestProvisionEnsureNoPaperKeyPUK(t *testing.T) {
3268	testProvisionEnsureNoPaperKey(t, true)
3269}
3270
3271// Provisioning a new device when the user has no paper keys should work
3272// and not generate a paper key.
3273func testProvisionEnsureNoPaperKey(t *testing.T, upgradePerUserKey bool) {
3274	// This test is based on TestProvisionDesktop.
3275
3276	t.Logf("create 2 contexts")
3277
3278	// device X (provisioner) context:
3279	tcX := SetupEngineTest(t, "kex2provision")
3280	defer tcX.Cleanup()
3281	tcX.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
3282
3283	// device Y (provisionee) context:
3284	tcY := SetupEngineTest(t, "template")
3285	defer tcY.Cleanup()
3286	tcY.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
3287
3288	// provisioner needs to be logged in
3289	userX := CreateAndSignupFakeUserPaper(tcX, "login")
3290	var secretX kex2.Secret
3291	if _, err := rand.Read(secretX[:]); err != nil {
3292		t.Fatal(err)
3293	}
3294
3295	t.Logf("check for initial paper key")
3296	originalPaperKey := hasOnePaperDev(tcY, userX)
3297
3298	t.Logf("revoke paper keys from X")
3299	{
3300		uis := libkb.UIs{
3301			LoginUI:  &libkb.TestLoginUI{Username: userX.Username},
3302			LogUI:    tcX.G.UI.GetLogUI(),
3303			SecretUI: &libkb.TestSecretUI{},
3304		}
3305		eng := NewRevokeDeviceEngine(tcX.G, RevokeDeviceEngineArgs{
3306			ID:        originalPaperKey,
3307			ForceSelf: false,
3308		})
3309		m := libkb.NewMetaContextTODO(tcX.G).WithUIs(uis)
3310		err := RunEngine2(m, eng)
3311		require.NoError(t, err, "revoke original paper key")
3312	}
3313
3314	t.Logf("check for no paper key")
3315	hasZeroPaperDev(tcX, userX)
3316
3317	t.Logf("do kex provision")
3318
3319	secretCh := make(chan kex2.Secret)
3320
3321	// provisionee calls login:
3322	uis := libkb.UIs{
3323		ProvisionUI: newTestProvisionUISecretCh(secretCh),
3324		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
3325		LogUI:       tcY.G.UI.GetLogUI(),
3326		SecretUI:    &libkb.TestSecretUI{},
3327		GPGUI:       &gpgtestui{},
3328	}
3329	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3330
3331	var wg sync.WaitGroup
3332
3333	// start provisionee
3334	wg.Add(1)
3335	go func() {
3336		defer wg.Done()
3337		m := NewMetaContextForTest(tcY).WithUIs(uis)
3338		if err := RunEngine2(m, eng); err != nil {
3339			t.Errorf("provisionee login error: %s", err)
3340			return
3341		}
3342	}()
3343
3344	// start provisioner
3345	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
3346	wg.Add(1)
3347	go func() {
3348		defer wg.Done()
3349
3350		uis := libkb.UIs{
3351			SecretUI:    userX.NewSecretUI(),
3352			ProvisionUI: newTestProvisionUI(),
3353		}
3354		m := NewMetaContextForTest(tcX).WithUIs(uis)
3355		if err := RunEngine2(m, provisioner); err != nil {
3356			t.Errorf("provisioner error: %s", err)
3357			return
3358		}
3359	}()
3360	secretFromY := <-secretCh
3361	provisioner.AddSecret(secretFromY)
3362
3363	wg.Wait()
3364
3365	require.False(t, t.Failed(), "prior failure in a goroutine")
3366
3367	if err := AssertProvisioned(tcY); err != nil {
3368		t.Fatal(err)
3369	}
3370
3371	t.Logf("kex finished")
3372
3373	// after provisioning, the passphrase stream should be cached
3374	// (note that this just checks the passphrase stream, not 3sec)
3375	assertPassphraseStreamCache(tcY)
3376
3377	// after provisioning, the device keys should be cached
3378	assertDeviceKeysCached(tcY)
3379
3380	// Make sure that we can still track without a passphrase
3381	// after a simulated service restart.  In other words, that
3382	// the full LKSec secret was written to the secret store.
3383	simulateServiceRestart(t, tcY, userX)
3384	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_bob")
3385
3386	t.Logf("check for no paper key")
3387	hasZeroPaperDev(tcY, userX)
3388	hasZeroPaperDev(tcX, userX)
3389}
3390
3391// Device X provisions device Y, then device Y revokes X.
3392func TestProvisionAndRevoke(t *testing.T) {
3393	// This test is based on TestProvisionDesktop.
3394
3395	t.Logf("create 2 contexts")
3396
3397	// device X (provisioner) context:
3398	tcX := SetupEngineTest(t, "kex2provision")
3399	defer tcX.Cleanup()
3400
3401	// device Y (provisionee) context:
3402	tcY := SetupEngineTest(t, "template")
3403	defer tcY.Cleanup()
3404
3405	// provisioner needs to be logged in
3406	userX := CreateAndSignupFakeUserPaper(tcX, "login")
3407	var secretX kex2.Secret
3408	if _, err := rand.Read(secretX[:]); err != nil {
3409		t.Fatal(err)
3410	}
3411
3412	t.Logf("check for initial paper key")
3413	_ = hasOnePaperDev(tcY, userX)
3414
3415	t.Logf("do kex provision")
3416
3417	secretCh := make(chan kex2.Secret)
3418
3419	// provisionee calls login:
3420	uis := libkb.UIs{
3421		ProvisionUI: newTestProvisionUISecretCh(secretCh),
3422		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
3423		LogUI:       tcY.G.UI.GetLogUI(),
3424		SecretUI:    &libkb.TestSecretUI{},
3425		GPGUI:       &gpgtestui{},
3426	}
3427	eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3428
3429	var wg sync.WaitGroup
3430
3431	// start provisionee
3432	wg.Add(1)
3433	go func() {
3434		defer wg.Done()
3435		m := NewMetaContextForTest(tcY).WithUIs(uis)
3436		if err := RunEngine2(m, eng); err != nil {
3437			t.Errorf("provisionee login error: %s", err)
3438			return
3439		}
3440	}()
3441
3442	// start provisioner
3443	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
3444	wg.Add(1)
3445	go func() {
3446		defer wg.Done()
3447
3448		uis := libkb.UIs{
3449			SecretUI:    userX.NewSecretUI(),
3450			ProvisionUI: newTestProvisionUI(),
3451		}
3452		m := NewMetaContextForTest(tcX).WithUIs(uis)
3453		if err := RunEngine2(m, provisioner); err != nil {
3454			t.Errorf("provisioner error: %s", err)
3455			return
3456		}
3457	}()
3458	secretFromY := <-secretCh
3459	provisioner.AddSecret(secretFromY)
3460
3461	wg.Wait()
3462
3463	require.False(t, t.Failed(), "prior failure in a goroutine")
3464
3465	if err := AssertProvisioned(tcY); err != nil {
3466		t.Fatal(err)
3467	}
3468
3469	t.Logf("kex finished")
3470
3471	// after provisioning, the passphrase stream should be cached
3472	// (note that this just checks the passphrase stream, not 3sec)
3473	assertPassphraseStreamCache(tcY)
3474
3475	// after provisioning, the device keys should be cached
3476	assertDeviceKeysCached(tcY)
3477
3478	t.Logf("revoke device X from Y")
3479
3480	// require.NoError(t, doRevokeDevice(tcY, userX, tcX.G.ActiveDevice.DeviceID(), false))
3481	{
3482		uis := libkb.UIs{
3483			LoginUI:  &libkb.TestLoginUI{Username: userX.Username},
3484			LogUI:    tcY.G.UI.GetLogUI(),
3485			SecretUI: &libkb.TestSecretUI{},
3486		}
3487		eng := NewRevokeDeviceEngine(tcY.G, RevokeDeviceEngineArgs{
3488			ID:        tcX.G.ActiveDevice.DeviceID(),
3489			ForceSelf: false,
3490		})
3491		m := libkb.NewMetaContextTODO(tcY.G).WithUIs(uis)
3492		err := RunEngine2(m, eng)
3493		require.NoError(t, err, "revoke original paper key")
3494	}
3495
3496	t.Logf("revoke finished")
3497
3498	// Make sure that we can still track without a passphrase
3499	// after a simulated service restart.  In other words, that
3500	// the full LKSec secret was written to the secret store.
3501	simulateServiceRestart(t, tcY, userX)
3502	testTrack(t, tcY, libkb.KeybaseNullSigVersion, "t_bob")
3503
3504	t.Logf("check for paper key")
3505	hasOnePaperDev(tcY, userX)
3506	hasOnePaperDev(tcX, userX)
3507}
3508
3509// Test bootstrap, login offline after service restart when provisioned via
3510// GPG sign.
3511func TestBootstrapAfterGPGSign(t *testing.T) {
3512	// use tcCheck just to check gpg version
3513	tcCheck := SetupEngineTest(t, "check")
3514	defer tcCheck.Cleanup()
3515	skipOldGPG(tcCheck)
3516
3517	// this test sometimes fails at the GPG level with a "Bad signature" error,
3518	// so we're going to retry it several times to hopefully get past it.
3519	attempts := 10
3520	for i := 0; i < attempts; i++ {
3521		tc := SetupEngineTest(t, "login")
3522		defer tc.Cleanup()
3523
3524		u1 := createFakeUserWithPGPPubOnly(t, tc)
3525		Logout(tc)
3526
3527		// redo SetupEngineTest to get a new home directory...should look like a new device.
3528		tc2 := SetupEngineTest(t, "login")
3529		defer tc2.Cleanup()
3530
3531		// we need the gpg keyring that's in the first homedir
3532		if err := tc.MoveGpgKeyringTo(tc2); err != nil {
3533			t.Fatal(err)
3534		}
3535
3536		// run login on new device
3537		uis := libkb.UIs{
3538			ProvisionUI: newTestProvisionUIGPGSign(),
3539			LogUI:       tc2.G.UI.GetLogUI(),
3540			SecretUI:    u1.NewSecretUI(),
3541			LoginUI:     &libkb.TestLoginUI{Username: u1.Username},
3542			GPGUI:       &gpgtestui{Contextified: libkb.NewContextified(tc2.G)},
3543		}
3544		eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3545		m := NewMetaContextForTest(tc2).WithUIs(uis)
3546		if err := RunEngine2(m, eng); err != nil {
3547			t.Logf("test run %d:  RunEngine(Login) error: %s", i+1, err)
3548			continue
3549		}
3550
3551		t.Logf("test run %d: RunEngine(Login) succeeded", i+1)
3552
3553		testUserHasDeviceKey(tc2)
3554
3555		// highly possible they didn't have a paper key, so make sure they still don't have one:
3556		hasZeroPaperDev(tc2, u1)
3557
3558		if err := AssertProvisioned(tc2); err != nil {
3559			t.Fatal(err)
3560		}
3561
3562		// do a upak load to make sure it is cached
3563		arg := libkb.NewLoadUserByUIDArg(context.TODO(), tc2.G, u1.UID())
3564		_, _, err := tc2.G.GetUPAKLoader().Load(arg)
3565		require.NoError(t, err)
3566
3567		// Simulate restarting the service by wiping out the
3568		// passphrase stream cache and cached secret keys
3569		tc2.SimulateServiceRestart()
3570		tc2.G.GetUPAKLoader().ClearMemory()
3571
3572		// LoginOffline will run when service restarts.
3573		// Since this was GPG sign, there will be no secret stored.
3574		oeng := NewLoginOffline(tc2.G)
3575		oerr := RunEngine2(m, oeng)
3576		if oerr != nil {
3577			t.Fatalf("LoginOffline failed after gpg sign + svc restart: %s", oerr)
3578		}
3579
3580		// GetBootstrapStatus should return without error and with LoggedIn set
3581		// to true.
3582		beng := NewBootstrap(tc2.G)
3583		m = NewMetaContextForTest(tc2)
3584		if err := RunEngine2(m, beng); err != nil {
3585			t.Fatal(err)
3586		}
3587		status := beng.Status()
3588		if status.LoggedIn != true {
3589			t.Error("bootstrap status -> logged out, expected logged in")
3590		}
3591		if !status.Registered {
3592			t.Error("registered false")
3593		}
3594
3595		t.Logf("test run %d: all checks passed, returning", i+1)
3596		return
3597	}
3598
3599	t.Fatalf("TestBootstrapAfterGPGSign failed %d times", attempts)
3600}
3601
3602func TestLoginAlready(t *testing.T) {
3603	tc := SetupEngineTest(t, "login")
3604	defer tc.Cleanup()
3605
3606	u1 := CreateAndSignupFakeUser(tc, "login")
3607	Logout(tc)
3608	u1.LoginOrBust(tc)
3609
3610	// Logging in again with same username should not return an error
3611	if err := u1.Login(tc.G); err != nil {
3612		t.Fatal(err)
3613	}
3614
3615	// Logging in with a different username should returh LoggedInError
3616	u1.Username = "x" + u1.Username
3617	err := u1.Login(tc.G)
3618	if err == nil {
3619		t.Fatal("login with different username should return an error")
3620	}
3621	if _, ok := err.(libkb.LoggedInError); !ok {
3622		t.Fatalf("err type: %T (%s), expected libkb.LoggedInError", err, err)
3623	}
3624}
3625
3626func TestLoginUsernameOnProvisionedDevice(t *testing.T) {
3627	tc := SetupEngineTest(t, "login")
3628	defer tc.Cleanup()
3629
3630	u1 := CreateAndSignupFakeUser(tc, "login")
3631	Logout(tc)
3632
3633	secui := u1.NewCountSecretUI()
3634	uis := libkb.UIs{
3635		ProvisionUI: newTestProvisionUIPassphrase(),
3636		LoginUI:     &libkb.TestLoginUI{},
3637		LogUI:       tc.G.UI.GetLogUI(),
3638		SecretUI:    secui,
3639		GPGUI:       &gpgtestui{},
3640	}
3641	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, u1.Username, keybase1.ClientType_CLI)
3642	m := NewMetaContextForTest(tc).WithUIs(uis)
3643	if err := RunEngine2(m, eng); err != nil {
3644		t.Fatalf("login with email should work now, got error: %s (%T)", err, err)
3645	}
3646
3647	assertPassphraseStreamCache(tc)
3648	assertDeviceKeysCached(tc)
3649	assertSecretStored(tc, u1.Username)
3650
3651	require.Equal(t, 1, secui.CallCount, "expecting a passphrase prompt when logging in with username")
3652}
3653
3654func TestLoginEmailOnProvisionedDevice(t *testing.T) {
3655	tc := SetupEngineTest(t, "login")
3656	defer tc.Cleanup()
3657
3658	u1 := CreateAndSignupFakeUser(tc, "login")
3659	Logout(tc)
3660
3661	secui := u1.NewCountSecretUI()
3662	uis := libkb.UIs{
3663		ProvisionUI: newTestProvisionUIPassphrase(),
3664		LoginUI:     &libkb.TestLoginUI{},
3665		LogUI:       tc.G.UI.GetLogUI(),
3666		SecretUI:    secui,
3667		GPGUI:       &gpgtestui{},
3668	}
3669	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, u1.Email, keybase1.ClientType_CLI)
3670	m := NewMetaContextForTest(tc).WithUIs(uis)
3671	err := RunEngine2(m, eng)
3672	require.Error(t, err)
3673	require.IsType(t, libkb.BadUsernameError{}, err)
3674	require.Contains(t, err.Error(), "not supported")
3675}
3676func TestBeforeResetDeviceName(t *testing.T) {
3677	tc := SetupEngineTest(t, "login")
3678	defer tc.Cleanup()
3679
3680	u := CreateAndSignupFakeUser(tc, "login")
3681	originalDeviceName := u.DeviceName
3682	t.Logf("original device name: %s", originalDeviceName)
3683	ResetAccount(tc, u)
3684
3685	provui := &testProvisionSetNameUI{
3686		testProvisionUI: newTestProvisionUI(),
3687		DeviceName:      originalDeviceName,
3688	}
3689	uis := libkb.UIs{
3690		ProvisionUI: provui,
3691		LoginUI:     &libkb.TestLoginUI{Username: u.Username},
3692		LogUI:       tc.G.UI.GetLogUI(),
3693		SecretUI:    u.NewSecretUI(),
3694		GPGUI:       &gpgtestui{},
3695	}
3696	eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3697	m := NewMetaContextForTest(tc).WithUIs(uis)
3698	err := RunEngine2(m, eng)
3699	if err == nil {
3700		t.Errorf("Login worked with pre-reset device name")
3701	}
3702	if len(provui.ExistingDevicesFromArg) == 0 {
3703		t.Fatalf("no existing devices provided to provision ui, expected 1 (pre reset)")
3704	}
3705	if provui.ExistingDevicesFromArg[0] != originalDeviceName {
3706		t.Errorf("existing device name 0: %q, expected %q", provui.ExistingDevicesFromArg[0], originalDeviceName)
3707	}
3708}
3709
3710func TestProvisioningWithSmartPunctuationDeviceName(t *testing.T) {
3711	tc := SetupEngineTest(t, "login")
3712	defer tc.Cleanup()
3713
3714	fu := NewFakeUserOrBust(tc.T, "login")
3715	tc.G.Log.Debug("New test user: %s / %s", fu.Username, fu.Email)
3716
3717	arg := MakeTestSignupEngineRunArg(fu)
3718	desiredName := arg.DeviceName + "'s test's cool-thing-device"
3719	arg.DeviceName += "’s test‘s cool—thing–device"
3720	SignupFakeUserWithArg(tc, fu, arg)
3721	fu.LoginOrBust(tc)
3722	if err := fu.LoadUser(tc); err != nil {
3723		t.Errorf("unable to load user: %q", err)
3724	}
3725	deviceNames, err := fu.User.DeviceNames()
3726	if err != nil {
3727		t.Errorf("unable to list device names: %q", err)
3728	}
3729	if len(deviceNames) < 1 {
3730		t.Error("no devices returned")
3731	}
3732	if deviceNames[0] != desiredName {
3733		t.Errorf("device name 0: %q, should be %q", deviceNames[0], desiredName)
3734	}
3735}
3736
3737func TestProvisionAutomatedPaperKey(t *testing.T) {
3738	tc := SetupEngineTest(t, "login")
3739	defer tc.Cleanup()
3740
3741	fu := NewFakeUserOrBust(t, "paper")
3742	arg := MakeTestSignupEngineRunArg(fu)
3743	arg.SkipPaper = false
3744	loginUI := &paperLoginUI{Username: fu.Username}
3745	uis := libkb.UIs{
3746		LogUI:    tc.G.UI.GetLogUI(),
3747		GPGUI:    &gpgtestui{},
3748		SecretUI: fu.NewSecretUI(),
3749		LoginUI:  loginUI,
3750	}
3751	s := NewSignupEngine(tc.G, &arg)
3752	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
3753	if err != nil {
3754		tc.T.Fatal(err)
3755	}
3756
3757	assertNumDevicesAndKeys(tc, fu, 2, 4)
3758
3759	Logout(tc)
3760
3761	if len(loginUI.PaperPhrase) == 0 {
3762		t.Fatal("login ui has no paper key phrase")
3763	}
3764
3765	// redo SetupEngineTest to get a new home directory
3766	tc2 := SetupEngineTest(t, "login")
3767	defer tc2.Cleanup()
3768
3769	provUI := newTestProvisionUIPaper()
3770	provLoginUI := &libkb.TestLoginUI{}
3771	uis2 := libkb.UIs{
3772		ProvisionUI: provUI,
3773		LogUI:       tc2.G.UI.GetLogUI(),
3774		SecretUI:    fu.NewSecretUI(),
3775		LoginUI:     provLoginUI,
3776		GPGUI:       &gpgtestui{},
3777	}
3778	eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, fu.Username, keybase1.ClientType_CLI)
3779	eng.PaperKey = loginUI.PaperPhrase
3780	eng.DeviceName = "a different device name"
3781	m2 := NewMetaContextForTest(tc2).WithUIs(uis2)
3782	require.NoError(t, RunEngine2(m2, eng), "run login engine")
3783
3784	assertNumDevicesAndKeys(tc, fu, 3, 6)
3785	require.NoError(t, AssertProvisioned(tc2), "provisioned")
3786
3787	require.Equal(t, provLoginUI.CalledGetEmailOrUsername, 0, "expected no calls to GetEmailOrUsername")
3788	require.Equal(t, provUI.calledChooseDevice, 0, "expected no calls to ChooseDevice")
3789}
3790
3791// Device X provisions device Y (which has a cached passphrase stream), device X changes the password,
3792// device Y provisions device Z (which could break because of the outdated passphrase stream)
3793func TestProvisionAfterPasswordChange(t *testing.T) {
3794	t.Logf("create 3 contexts")
3795
3796	// device X (initial provisioner and passphrase changer) context:
3797	tcX := SetupEngineTest(t, "kex2race1")
3798	defer tcX.Cleanup()
3799
3800	// device Y (second provisioner and race condition culprit) context:
3801	tcY := SetupEngineTest(t, "kex2race2")
3802	defer tcY.Cleanup()
3803
3804	// device Z (provisionee) context:
3805	tcZ := SetupEngineTest(t, "kex2race3")
3806	defer tcZ.Cleanup()
3807
3808	// the initial needs to sign up and log in
3809	userX := CreateAndSignupFakeUserPaper(tcX, "login")
3810	var secretX kex2.Secret
3811	if _, err := rand.Read(secretX[:]); err != nil {
3812		t.Fatal(err)
3813	}
3814
3815	t.Logf("kex#1 starting")
3816
3817	secretCh := make(chan kex2.Secret)
3818
3819	// provisionee calls login:
3820	uis := libkb.UIs{
3821		ProvisionUI: newTestProvisionUISecretCh(secretCh),
3822		LoginUI:     &libkb.TestLoginUI{Username: userX.Username},
3823		LogUI:       tcX.G.UI.GetLogUI(),
3824		SecretUI:    &libkb.TestSecretUI{},
3825		GPGUI:       &gpgtestui{},
3826	}
3827
3828	var wg sync.WaitGroup
3829
3830	// start provisionee for step #1
3831	wg.Add(1)
3832	go func() {
3833		defer wg.Done()
3834		m := NewMetaContextForTest(tcY).WithUIs(uis)
3835		eng := NewLogin(tcY.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3836		if err := RunEngine2(m, eng); err != nil {
3837			t.Errorf("provisionee login error: %s", err)
3838			return
3839		}
3840	}()
3841
3842	// start provisioner for step #1
3843	provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
3844	wg.Add(1)
3845	go func() {
3846		defer wg.Done()
3847
3848		// We're reusing the m from the PGP key generation
3849		m := NewMetaContextForTest(tcX).WithUIs(uis)
3850		if err := RunEngine2(m, provisioner); err != nil {
3851			t.Errorf("provisioner error: %s", err)
3852			return
3853		}
3854	}()
3855	secretFromY := <-secretCh
3856	provisioner.AddSecret(secretFromY)
3857
3858	wg.Wait()
3859
3860	require.False(t, t.Failed(), "prior failure in a goroutine")
3861
3862	// 2nd device should be provisioned
3863	if err := AssertProvisioned(tcY); err != nil {
3864		t.Fatal(err)
3865	}
3866
3867	t.Logf("kex#1 finished")
3868
3869	// after provisioning, the passphrase stream in device Y should be cached
3870	assertPassphraseStreamCache(tcY)
3871	assertDeviceKeysCached(tcY)
3872
3873	// now we have the following:
3874	// 1) the initial provisioner
3875	// 2) an already provisioned device with a cached passphrase stream
3876	// 3) a totally clean device
3877	// in order to trigger the race we have to make the cached ppstream in (2) outdated
3878
3879	// Change the password on device 1 to modify ppgen
3880	newPassphrase := "password1234"
3881	require.NoError(t, RunEngine2(
3882		NewMetaContextForTest(tcX).WithUIs(libkb.UIs{
3883			SecretUI: &libkb.TestSecretUI{},
3884		}),
3885		NewPassphraseChange(tcX.G, &keybase1.PassphraseChangeArg{
3886			OldPassphrase: userX.Passphrase,
3887			Passphrase:    newPassphrase,
3888		}),
3889	))
3890
3891	// Now provision Z from Y
3892	t.Logf("kex#2 starting")
3893
3894	// start provisionee for step #2
3895	wg.Add(1)
3896	go func() {
3897		defer wg.Done()
3898		m := NewMetaContextForTest(tcZ).WithUIs(uis)
3899		eng := NewLogin(tcZ.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI)
3900		if err := RunEngine2(m, eng); err != nil {
3901			t.Errorf("provisionee login error: %s", err)
3902			return
3903		}
3904	}()
3905
3906	// start provisioner for step #2
3907	var secretY kex2.Secret
3908	if _, err := rand.Read(secretY[:]); err != nil {
3909		t.Fatal(err)
3910	}
3911	provisioner = NewKex2Provisioner(tcY.G, secretY, nil)
3912	wg.Add(1)
3913	go func() {
3914		defer wg.Done()
3915
3916		// We're reusing the m from the PGP key generation
3917		m := NewMetaContextForTest(tcY).WithUIs(uis)
3918		// m.ActiveDevice().ClearPassphraseStreamCache()
3919		if err := RunEngine2(m, provisioner); err != nil {
3920			t.Errorf("provisioner error: %s", err)
3921			return
3922		}
3923	}()
3924	secretFromZ := <-secretCh
3925	provisioner.AddSecret(secretFromZ)
3926
3927	wg.Wait()
3928
3929	require.False(t, t.Failed(), "prior failure in a goroutine")
3930
3931	// 3rd device should be provisioned
3932	if err := AssertProvisioned(tcZ); err != nil {
3933		t.Fatal(err)
3934	}
3935
3936	t.Logf("kex#2 finished")
3937}
3938
3939type testProvisionUI struct {
3940	secretCh               chan kex2.Secret
3941	method                 keybase1.ProvisionMethod
3942	gpgMethod              keybase1.GPGMethod
3943	chooseDevice           keybase1.DeviceTypeV2
3944	calledChooseDevice     int
3945	verbose                bool
3946	calledChooseDeviceType int
3947	abortSwitchToGPGSign   bool
3948	lastDevices            []keybase1.Device
3949}
3950
3951func newTestProvisionUI() *testProvisionUI {
3952	ui := &testProvisionUI{method: keybase1.ProvisionMethod_DEVICE}
3953	if len(os.Getenv("KB_TEST_VERBOSE")) > 0 {
3954		ui.verbose = true
3955	}
3956	ui.gpgMethod = keybase1.GPGMethod_GPG_IMPORT
3957	return ui
3958}
3959
3960func newTestProvisionUISecretCh(ch chan kex2.Secret) *testProvisionUI {
3961	ui := newTestProvisionUI()
3962	ui.secretCh = ch
3963	ui.chooseDevice = keybase1.DeviceTypeV2_DESKTOP
3964	return ui
3965}
3966
3967func newTestProvisionUINoSecret() *testProvisionUI {
3968	ui := newTestProvisionUI()
3969	ui.chooseDevice = keybase1.DeviceTypeV2_DESKTOP
3970	return ui
3971}
3972
3973func newTestProvisionUIPassphrase() *testProvisionUI {
3974	ui := newTestProvisionUI()
3975	ui.method = keybase1.ProvisionMethod_PASSPHRASE
3976	return ui
3977}
3978
3979func newTestProvisionUIChooseNoDevice() *testProvisionUI {
3980	ui := newTestProvisionUI()
3981	ui.chooseDevice = keybase1.DeviceTypeV2_NONE
3982	return ui
3983}
3984
3985func newTestProvisionUIPaper() *testProvisionUI {
3986	ui := newTestProvisionUI()
3987	ui.method = keybase1.ProvisionMethod_PAPER_KEY
3988	ui.chooseDevice = keybase1.DeviceTypeV2_PAPER
3989	return ui
3990}
3991
3992func newTestProvisionUIGPGImport() *testProvisionUI {
3993	ui := newTestProvisionUI()
3994	ui.method = keybase1.ProvisionMethod_GPG_IMPORT
3995	ui.gpgMethod = keybase1.GPGMethod_GPG_IMPORT
3996	return ui
3997}
3998
3999func newTestProvisionUIGPGSign() *testProvisionUI {
4000	ui := newTestProvisionUI()
4001	ui.method = keybase1.ProvisionMethod_GPG_SIGN
4002	ui.gpgMethod = keybase1.GPGMethod_GPG_SIGN
4003	return ui
4004}
4005
4006func (u *testProvisionUI) printf(format string, a ...interface{}) {
4007	if !u.verbose {
4008		return
4009	}
4010	fmt.Printf("testProvisionUI: "+format+"\n", a...)
4011}
4012
4013func (u *testProvisionUI) ChooseProvisioningMethod(_ context.Context, _ keybase1.ChooseProvisioningMethodArg) (keybase1.ProvisionMethod, error) {
4014	panic("ChooseProvisioningMethod deprecated")
4015}
4016
4017func (u *testProvisionUI) ChooseGPGMethod(_ context.Context, _ keybase1.ChooseGPGMethodArg) (keybase1.GPGMethod, error) {
4018	u.printf("ChooseGPGMethod")
4019	return u.gpgMethod, nil
4020}
4021
4022func (u *testProvisionUI) SwitchToGPGSignOK(ctx context.Context, arg keybase1.SwitchToGPGSignOKArg) (bool, error) {
4023	if u.abortSwitchToGPGSign {
4024		return false, nil
4025	}
4026	return true, nil
4027}
4028
4029func (u *testProvisionUI) ChooseDevice(_ context.Context, arg keybase1.ChooseDeviceArg) (keybase1.DeviceID, error) {
4030	u.printf("ChooseDevice")
4031	u.calledChooseDevice++
4032
4033	u.lastDevices = arg.Devices
4034
4035	if len(arg.Devices) == 0 {
4036		return "", nil
4037	}
4038
4039	if u.chooseDevice == keybase1.DeviceTypeV2_NONE {
4040		return "", nil
4041	}
4042
4043	for _, d := range arg.Devices {
4044		if d.Type == u.chooseDevice {
4045			return d.DeviceID, nil
4046		}
4047	}
4048	return "", nil
4049}
4050
4051func (u *testProvisionUI) ChooseDeviceType(_ context.Context, _ keybase1.ChooseDeviceTypeArg) (keybase1.DeviceType, error) {
4052	u.printf("ChooseDeviceType")
4053	u.calledChooseDeviceType++
4054	return keybase1.DeviceType_DESKTOP, nil
4055}
4056
4057func (u *testProvisionUI) DisplayAndPromptSecret(_ context.Context, arg keybase1.DisplayAndPromptSecretArg) (keybase1.SecretResponse, error) {
4058	u.printf("DisplayAndPromptSecret")
4059	var ks kex2.Secret
4060	copy(ks[:], arg.Secret)
4061	u.secretCh <- ks
4062	var sr keybase1.SecretResponse
4063	return sr, nil
4064}
4065
4066func (u *testProvisionUI) PromptNewDeviceName(_ context.Context, arg keybase1.PromptNewDeviceNameArg) (string, error) {
4067	u.printf("PromptNewDeviceName")
4068	return libkb.RandString("device", 5)
4069}
4070
4071func (u *testProvisionUI) DisplaySecretExchanged(_ context.Context, _ int) error {
4072	u.printf("DisplaySecretExchanged")
4073	return nil
4074}
4075
4076func (u *testProvisionUI) ProvisioneeSuccess(_ context.Context, _ keybase1.ProvisioneeSuccessArg) error {
4077	u.printf("ProvisioneeSuccess")
4078	return nil
4079}
4080
4081func (u *testProvisionUI) ProvisionerSuccess(_ context.Context, _ keybase1.ProvisionerSuccessArg) error {
4082	u.printf("ProvisionerSuccess")
4083	return nil
4084}
4085
4086type testProvisionDupDeviceUI struct {
4087	*testProvisionUI
4088}
4089
4090// return an existing device name
4091func (u *testProvisionDupDeviceUI) PromptNewDeviceName(_ context.Context, arg keybase1.PromptNewDeviceNameArg) (string, error) {
4092	return arg.ExistingDevices[0], nil
4093}
4094
4095type testProvisionSetNameUI struct {
4096	*testProvisionUI
4097	DeviceName             string
4098	ExistingDevicesFromArg []string
4099}
4100
4101// return an existing device name
4102func (u *testProvisionSetNameUI) PromptNewDeviceName(_ context.Context, arg keybase1.PromptNewDeviceNameArg) (string, error) {
4103	u.ExistingDevicesFromArg = arg.ExistingDevices
4104	return u.DeviceName, nil
4105}
4106
4107type paperLoginUI struct {
4108	Username    string
4109	PaperPhrase string
4110}
4111
4112var _ libkb.LoginUI = (*paperLoginUI)(nil)
4113
4114func (p *paperLoginUI) GetEmailOrUsername(_ context.Context, _ int) (string, error) {
4115	return p.Username, nil
4116}
4117
4118func (p *paperLoginUI) PromptRevokePaperKeys(_ context.Context, arg keybase1.PromptRevokePaperKeysArg) (bool, error) {
4119	return false, nil
4120}
4121
4122func (p *paperLoginUI) DisplayPaperKeyPhrase(_ context.Context, arg keybase1.DisplayPaperKeyPhraseArg) error {
4123	return nil
4124}
4125
4126func (p *paperLoginUI) DisplayPrimaryPaperKey(_ context.Context, arg keybase1.DisplayPrimaryPaperKeyArg) error {
4127	p.PaperPhrase = arg.Phrase
4128	return nil
4129}
4130
4131func (p *paperLoginUI) PromptResetAccount(_ context.Context, arg keybase1.PromptResetAccountArg) (keybase1.ResetPromptResponse, error) {
4132	return keybase1.ResetPromptResponse_NOTHING, nil
4133}
4134
4135func (p *paperLoginUI) DisplayResetProgress(_ context.Context, arg keybase1.DisplayResetProgressArg) error {
4136	return nil
4137}
4138
4139func (p *paperLoginUI) ExplainDeviceRecovery(_ context.Context, arg keybase1.ExplainDeviceRecoveryArg) error {
4140	return nil
4141}
4142
4143func (p *paperLoginUI) PromptPassphraseRecovery(_ context.Context, arg keybase1.PromptPassphraseRecoveryArg) (bool, error) {
4144	return false, nil
4145}
4146
4147func (p *paperLoginUI) ChooseDeviceToRecoverWith(_ context.Context, arg keybase1.ChooseDeviceToRecoverWithArg) (keybase1.DeviceID, error) {
4148	return "", nil
4149}
4150
4151func (p *paperLoginUI) DisplayResetMessage(_ context.Context, arg keybase1.DisplayResetMessageArg) error {
4152	return nil
4153}
4154
4155func signString(tc libkb.TestContext, input string, secUI libkb.SecretUI) error {
4156	var sink bytes.Buffer
4157
4158	earg := PGPSignArg{
4159		Sink:   libkb.NopWriteCloser{W: &sink},
4160		Source: ioutil.NopCloser(bytes.NewBufferString(input)),
4161		Opts: keybase1.PGPSignOptions{
4162			Mode: keybase1.SignMode_ATTACHED,
4163		},
4164	}
4165
4166	eng := NewPGPSignEngine(tc.G, &earg)
4167	m := NewMetaContextForTest(tc).WithUIs(libkb.UIs{
4168		PgpUI:    &TestPgpUI{},
4169		SecretUI: secUI,
4170	})
4171	return RunEngine2(m, eng)
4172}
4173
4174type testRetrySecretUI struct {
4175	Passphrases []string
4176	StoreSecret bool
4177	index       int
4178}
4179
4180func (t *testRetrySecretUI) GetPassphrase(p keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (keybase1.GetPassphraseRes, error) {
4181	n := t.index
4182	if n >= len(t.Passphrases) {
4183		n = len(t.Passphrases) - 1
4184	}
4185	t.index++
4186	return keybase1.GetPassphraseRes{
4187		Passphrase:  t.Passphrases[n],
4188		StoreSecret: t.StoreSecret,
4189	}, nil
4190}
4191
4192type testNoPromptSecretUI struct {
4193}
4194
4195func (t *testNoPromptSecretUI) GetPassphrase(p keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (res keybase1.GetPassphraseRes, err error) {
4196	err = errors.New("GetPassphrase called on testNoPromptSecretUI")
4197	return res, err
4198}
4199
4200type gpgImportFailer struct {
4201	g *libkb.GlobalContext
4202}
4203
4204func newGPGImportFailer(g *libkb.GlobalContext) *gpgImportFailer {
4205	return &gpgImportFailer{g: g}
4206}
4207
4208func (g *gpgImportFailer) ImportKey(_ libkb.MetaContext, secret bool, fp libkb.PGPFingerprint, tty string) (*libkb.PGPKeyBundle, error) {
4209	return nil, errors.New("failed to import key")
4210}
4211
4212func (g *gpgImportFailer) Index(mctx libkb.MetaContext, secret bool, query string) (ki *libkb.GpgKeyIndex, w libkb.Warnings, err error) {
4213	// use real gpg for this part
4214	gpg := g.g.GetGpgClient()
4215	if err := gpg.Configure(mctx); err != nil {
4216		return nil, w, err
4217	}
4218	return gpg.Index(mctx, secret, query)
4219}
4220
4221func skipOldGPG(tc libkb.TestContext) {
4222	gpg := tc.G.GetGpgClient()
4223	if err := gpg.Configure(tc.MetaContext()); err != nil {
4224		tc.T.Skip(fmt.Sprintf("skipping test due to gpg configure error: %s", err))
4225	}
4226	ok, err := gpg.VersionAtLeast("2.0.29")
4227	if err != nil {
4228		tc.T.Fatal(err)
4229	}
4230	if ok {
4231		return
4232	}
4233
4234	v, err := gpg.SemanticVersion()
4235	if err != nil {
4236		tc.T.Fatal(err)
4237	}
4238	tc.T.Skip(fmt.Sprintf("skipping test due to gpg version < 2.0.29 (%v)", v))
4239}
4240
4241func assertDeviceKeysCached(tc libkb.TestContext) {
4242	_, _, _, sk, ek := tc.G.ActiveDevice.AllFields()
4243	if sk == nil {
4244		tc.T.Error("cached signing key nil")
4245	}
4246	if ek == nil {
4247		tc.T.Error("cached encryption key nil")
4248	}
4249}
4250
4251func assertPassphraseStreamCache(tc libkb.TestContext) {
4252	var ppsValid bool
4253	if ppsc := tc.G.ActiveDevice.PassphraseStreamCache(); ppsc != nil {
4254		ppsValid = ppsc.ValidPassphraseStream()
4255	}
4256	if !ppsValid {
4257		tc.T.Fatal("passphrase stream not cached")
4258	}
4259}
4260
4261func assertSecretStored(tc libkb.TestContext, username string) {
4262	secret, err := tc.G.SecretStore().RetrieveSecret(NewMetaContextForTest(tc), libkb.NewNormalizedUsername(username))
4263	require.NoError(tc.T, err, "no error fetching secret")
4264	require.False(tc.T, secret.IsNil(), "secret was non-nil")
4265}
4266
4267func assertSecretNotStored(tc libkb.TestContext, username string) {
4268	nun := libkb.NewNormalizedUsername(username)
4269	_, err := tc.G.SecretStore().RetrieveSecret(NewMetaContextForTest(tc), nun)
4270	require.Error(tc.T, err, "no secret found")
4271	require.Equal(tc.T, err, libkb.NewErrSecretForUserNotFound(nun))
4272}
4273
4274func assertAutoreset(tc libkb.TestContext, uid keybase1.UID, expectedStatus int) error {
4275	mctx := libkb.NewMetaContextForTest(tc)
4276	resp, err := tc.G.API.Get(mctx, libkb.APIArg{
4277		Endpoint:    "autoreset/status_dev",
4278		SessionType: libkb.APISessionTypeOPTIONAL,
4279		Args: libkb.HTTPArgs{
4280			"uid": libkb.S{Val: uid.String()},
4281		},
4282	})
4283	if err != nil {
4284		return err
4285	}
4286
4287	status, ok := resp.Body.AtPathGetInt("autoreset.type")
4288	if expectedStatus == -1 {
4289		if ok {
4290			return fmt.Errorf("expected account %s to not be in reset pipeline", uid.String())
4291		}
4292		return nil
4293	}
4294	if !ok {
4295		return fmt.Errorf("expected account %s to be in %d state (got null)", uid.String(), expectedStatus)
4296	}
4297	if status != expectedStatus {
4298		return fmt.Errorf("expected account %s to be in %d state (got %d)", uid.String(), expectedStatus, status)
4299	}
4300	return nil
4301}
4302