1// Copyright 2017 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"testing"
8
9	"github.com/keybase/client/go/libkb"
10	"github.com/keybase/client/go/protocol/keybase1"
11	"github.com/stretchr/testify/require"
12)
13
14// Test logging in with paper key when
15// loggedin: true
16// unlocked: true
17// Does not ask for anything.
18func TestLoginWithPaperKeyAlreadyIn(t *testing.T) {
19	tc := SetupEngineTest(t, "loginwithpaperkey")
20	defer tc.Cleanup()
21	_, paperkey := CreateAndSignupLPK(tc, "login")
22
23	t.Logf("checking logged in status [before]")
24	AssertLoggedInLPK(&tc, true)
25	t.Logf("checking unlocked status [before]")
26	AssertDeviceKeysLock(&tc, true)
27
28	t.Logf("running LoginWithPaperKey")
29	uis := libkb.UIs{
30		LogUI: tc.G.UI.GetLogUI(),
31		SecretUI: &TestSecretUIPaperKey{
32			T:                         t,
33			Paperkey:                  paperkey,
34			AllowedGetPassphraseCalls: 0,
35		},
36	}
37	eng := NewLoginWithPaperKey(tc.G, "")
38	m := NewMetaContextForTest(tc).WithUIs(uis)
39	err := RunEngine2(m, eng)
40	require.NoError(t, err)
41
42	t.Logf("checking logged in status [after]")
43	AssertLoggedInLPK(&tc, true)
44	t.Logf("checking unlocked status [after]")
45	AssertDeviceKeysLock(&tc, true)
46}
47
48// Test logging in with paper key when
49// loggedin: false
50// unlocked: false
51// Asks for a paperky.
52func TestLoginWithPaperKeyFromScratch(t *testing.T) {
53	tc := SetupEngineTest(t, "loginwithpaperkey")
54	defer tc.Cleanup()
55	_, paperkey := CreateAndSignupLPK(tc, "login")
56
57	t.Logf("logging out")
58	Logout(tc)
59
60	t.Logf("checking logged in status [before]")
61	AssertLoggedInLPK(&tc, false)
62	t.Logf("checking unlocked status [before]")
63	AssertDeviceKeysLock(&tc, false)
64
65	t.Logf("running LoginWithPaperKey")
66	uis := libkb.UIs{
67		LogUI: tc.G.UI.GetLogUI(),
68		SecretUI: &TestSecretUIPaperKey{
69			T:                         t,
70			Paperkey:                  paperkey,
71			AllowedGetPassphraseCalls: 1,
72		},
73	}
74	eng := NewLoginWithPaperKey(tc.G, "")
75	m := NewMetaContextForTest(tc).WithUIs(uis)
76	err := RunEngine2(m, eng)
77	require.NoError(t, err)
78
79	t.Logf("checking logged in status [after]")
80	AssertLoggedInLPK(&tc, true)
81	t.Logf("checking unlocked status [after]")
82	AssertDeviceKeysLock(&tc, true)
83}
84
85// Test logging in with paper key when
86// loggedin: true
87// unlocked: false
88// Asks for a paperkey.
89func TestLoginWithPaperKeyLoggedInAndLocked(t *testing.T) {
90	tc := SetupEngineTest(t, "loginwithpaperkey")
91	defer tc.Cleanup()
92	u, paperkey := CreateAndSignupLPK(tc, "login")
93
94	t.Logf("locking keys")
95	tc.SimulateServiceRestart()
96	err := tc.G.SecretStore().ClearSecret(NewMetaContextForTest(tc), libkb.NormalizedUsername(u.Username))
97	require.NoError(t, err)
98
99	t.Logf("checking logged in status [before]")
100	AssertLoggedInLPK(&tc, false)
101	t.Logf("checking unlocked status [before]")
102	AssertDeviceKeysLock(&tc, false)
103
104	t.Logf("running LoginWithPaperKey")
105	uis := libkb.UIs{
106		LogUI: tc.G.UI.GetLogUI(),
107		SecretUI: &TestSecretUIPaperKey{
108			T:                         t,
109			Paperkey:                  paperkey,
110			AllowedGetPassphraseCalls: 1,
111		},
112	}
113	eng := NewLoginWithPaperKey(tc.G, "")
114	m := NewMetaContextForTest(tc).WithUIs(uis)
115	err = RunEngine2(m, eng)
116	require.NoError(t, err)
117
118	t.Logf("checking logged in status [after]")
119	AssertLoggedInLPK(&tc, true)
120	t.Logf("checking unlocked status [after]")
121	AssertDeviceKeysLock(&tc, true)
122}
123
124// full flow, login with username
125func TestLoginWithPaperKeyByUsername(t *testing.T) {
126	tc := SetupEngineTest(t, "loginwithpaperkey")
127	defer tc.Cleanup()
128	fu, paperkey := CreateAndSignupLPK(tc, "login")
129
130	t.Logf("logging out")
131	Logout(tc)
132
133	t.Logf("checking logged in status [before]")
134	AssertLoggedInLPK(&tc, false)
135	t.Logf("checking unlocked status [before]")
136	AssertDeviceKeysLock(&tc, false)
137
138	t.Logf("running LoginWithPaperKey")
139	uis := libkb.UIs{
140		LogUI: tc.G.UI.GetLogUI(),
141		SecretUI: &TestSecretUIPaperKey{
142			T:                         t,
143			Paperkey:                  paperkey,
144			AllowedGetPassphraseCalls: 1,
145		},
146	}
147	eng := NewLoginWithPaperKey(tc.G, fu.NormalizedUsername().String())
148	m := NewMetaContextForTest(tc).WithUIs(uis)
149	err := RunEngine2(m, eng)
150	require.NoError(t, err)
151
152	t.Logf("checking logged in status [after]")
153	AssertLoggedInLPK(&tc, true)
154	t.Logf("checking unlocked status [after]")
155	AssertDeviceKeysLock(&tc, true)
156}
157
158// full flow, fail to login due to an invalid username
159func TestLoginWithInvalidUsername(t *testing.T) {
160	tc := SetupEngineTest(t, "loginwithpaperkey")
161	defer tc.Cleanup()
162
163	// We're creating the user to make sure that we have _some_ keys
164	// and differentiate from the no-username-passed flow
165	_, paperkey := CreateAndSignupLPK(tc, "login")
166
167	t.Logf("logging out")
168	Logout(tc)
169
170	t.Logf("checking logged in status [before]")
171	AssertLoggedInLPK(&tc, false)
172	t.Logf("checking unlocked status [before]")
173	AssertDeviceKeysLock(&tc, false)
174
175	t.Logf("running LoginWithPaperKey")
176	uis := libkb.UIs{
177		LogUI: tc.G.UI.GetLogUI(),
178		SecretUI: &TestSecretUIPaperKey{
179			T:                         t,
180			Paperkey:                  paperkey,
181			AllowedGetPassphraseCalls: 1,
182		},
183	}
184	eng := NewLoginWithPaperKey(tc.G, "doesnotexist")
185	m := NewMetaContextForTest(tc).WithUIs(uis)
186	err := RunEngine2(m, eng)
187	require.Equal(t, libkb.NotFoundError{}, err)
188
189	t.Logf("checking logged in status [after]")
190	AssertLoggedInLPK(&tc, false)
191	t.Logf("checking unlocked status [after]")
192	AssertDeviceKeysLock(&tc, false)
193}
194
195// Returns the user and paper key.
196func CreateAndSignupLPK(tc libkb.TestContext, prefix string) (*FakeUser, string) {
197	u := CreateAndSignupFakeUser(tc, prefix)
198
199	uis := libkb.UIs{
200		LogUI:    tc.G.UI.GetLogUI(),
201		LoginUI:  &libkb.TestLoginUI{},
202		SecretUI: &libkb.TestSecretUI{},
203	}
204	beng := NewPaperKey(tc.G)
205	m := NewMetaContextForTest(tc).WithUIs(uis)
206	if err := RunEngine2(m, beng); err != nil {
207		tc.T.Fatal(err)
208	}
209
210	backupPassphrase := beng.Passphrase()
211
212	return u, backupPassphrase
213}
214
215func AssertLoggedInLPK(tc *libkb.TestContext, shouldBeLoggedIn bool) {
216	activeDeviceIsValid := tc.G.ActiveDevice.Valid()
217	t := tc.T
218	if shouldBeLoggedIn {
219		require.Equal(t, true, activeDeviceIsValid, "user should be logged in")
220	} else {
221		require.Equal(t, false, activeDeviceIsValid, "user should not be logged in")
222	}
223}
224
225func AssertDeviceKeysLock(tc *libkb.TestContext, shouldBeUnlocked bool) {
226	_, _, _, sk, ek := tc.G.ActiveDevice.AllFields()
227
228	if shouldBeUnlocked {
229		require.NotNil(tc.T, sk, "device signing key should be available")
230		require.NotNil(tc.T, ek, "device encryption key should be available")
231	} else {
232		require.Nil(tc.T, sk, "device signing key should be unavailable")
233		require.Nil(tc.T, ek, "device encryption key should be unavailable")
234	}
235}
236
237type TestSecretUIPaperKey struct {
238	T                         *testing.T
239	Paperkey                  string
240	AllowedGetPassphraseCalls int
241	nGetPassphraseCalls       int
242}
243
244func (t *TestSecretUIPaperKey) GetPassphrase(p keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (keybase1.GetPassphraseRes, error) {
245	require.Equal(t.T, keybase1.PassphraseType_PAPER_KEY, p.Type, "TestSecretUIPaperKey prompted for non-paperkey")
246	t.nGetPassphraseCalls++
247	require.True(t.T, t.nGetPassphraseCalls <= t.AllowedGetPassphraseCalls, "GetPassphrase called too many times on paperkey")
248	return keybase1.GetPassphraseRes{
249		Passphrase: t.Paperkey,
250		// What's this?
251		StoreSecret: false,
252	}, nil
253}
254