1package engine
2
3import (
4	"fmt"
5	"reflect"
6	"testing"
7
8	"github.com/keybase/client/go/libkb"
9	keybase1 "github.com/keybase/client/go/protocol/keybase1"
10	"github.com/stretchr/testify/require"
11	"golang.org/x/net/context"
12)
13
14type _deviceKeys map[keybase1.KID]keybase1.PublicKeyV2NaCl
15
16func deviceKeysMatch(dk1, dk2 _deviceKeys) bool {
17	highKeys1 := make(_deviceKeys)
18	highKeys2 := make(_deviceKeys)
19	for kid, key := range dk1 {
20		if key.Base.IsSibkey || key.Base.IsEldest {
21			highKeys1[kid] = key
22		}
23	}
24	for kid, key := range dk2 {
25		if key.Base.IsSibkey || key.Base.IsEldest {
26			highKeys2[kid] = key
27		}
28	}
29	return reflect.DeepEqual(highKeys1, highKeys2)
30}
31
32func assertUpkInstanceMatch(t *testing.T, upkLite keybase1.UPKLiteV1, upkFull keybase1.UserPlusKeysV2) {
33	require.Equal(t, upkLite.Uid, upkFull.Uid)
34	require.Equal(t, upkLite.Username, upkFull.Username)
35	require.Equal(t, upkLite.Status, upkFull.Status)
36	require.Equal(t, upkLite.EldestSeqno, upkFull.EldestSeqno)
37	// device keys (not subkeys, just sibkeys and eldest)
38	msg := fmt.Sprintf("device keys match. lite: %v || full: %v", upkLite.DeviceKeys, upkFull.DeviceKeys)
39	require.True(t, deviceKeysMatch(upkLite.DeviceKeys, upkFull.DeviceKeys), msg)
40	// reset summary
41	msg = fmt.Sprintf("resets match. lite: %v || full: %v", upkLite.Reset, upkFull.Reset)
42	require.True(t, reflect.DeepEqual(upkFull.Reset, upkLite.Reset), msg)
43}
44
45func assertUPAKLiteMatchesUPAK(t *testing.T, tc libkb.TestContext, uid keybase1.UID) {
46	ctx := context.TODO()
47	loadArgLite := libkb.NewLoadUserByUIDArg(ctx, tc.G, uid).ForUPAKLite().WithForceReload()
48	upakLite, err := tc.G.GetUPAKLoader().LoadLite(loadArgLite)
49	require.NoError(t, err)
50	loadArg := libkb.NewLoadUserByUIDArg(ctx, tc.G, uid).WithForceReload().WithPublicKeyOptional()
51	upak, _, err := tc.G.GetUPAKLoader().LoadV2(loadArg)
52	require.NoError(t, err)
53	assertUpkInstanceMatch(t, upakLite.Current, upak.Current)
54	require.Equal(t, len(upak.PastIncarnations), len(upakLite.PastIncarnations), "same number of past incarnations")
55	for idx, prevUpakFull := range upak.PastIncarnations {
56		prevUpakLite := upakLite.PastIncarnations[idx]
57		assertUpkInstanceMatch(t, prevUpakLite, prevUpakFull)
58	}
59	// seqno,linkID pairs in the upakLite must exist in the full upak
60	for seqno, linkID := range upakLite.SeqnoLinkIDs {
61		require.Equal(t, upak.SeqnoLinkIDs[seqno], linkID)
62	}
63}
64
65func TestLoadLiteBasicUser(t *testing.T) {
66	tc := SetupEngineTest(t, "loadlite")
67	defer tc.Cleanup()
68
69	// basic new user
70	fu := CreateAndSignupFakeUser(tc, "jim")
71	uid := fu.UID()
72	t.Logf("create new user %s, %s", fu.Username, uid)
73	assertUPAKLiteMatchesUPAK(t, tc, uid)
74
75	// add a couple low links and test again
76	sigVersion := libkb.GetDefaultSigVersion(tc.G)
77	trackAlice(tc, fu, sigVersion)
78	untrackAlice(tc, fu, sigVersion)
79	t.Logf("with a couple of low links")
80	assertUPAKLiteMatchesUPAK(t, tc, uid)
81
82	// add a new high link (a new PGP key) and test
83	uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: fu.NewSecretUI()}
84	_, _, key := genPGPKeyAndArmor(t, tc, fu.Email)
85	eng, err := NewPGPKeyImportEngineFromBytes(tc.G, []byte(key), true)
86	require.NoError(t, err)
87	m := NewMetaContextForTest(tc).WithUIs(uis)
88	err = RunEngine2(m, eng)
89	require.NoError(t, err)
90	t.Logf("with a new PGP key")
91	assertUPAKLiteMatchesUPAK(t, tc, uid)
92
93	// reset the user and test immediately
94	ResetAccountNoLogout(tc, fu)
95	t.Logf("reset the account with no new links")
96	assertUPAKLiteMatchesUPAK(t, tc, uid)
97	// add a couple low links and test
98	fu.LoginOrBust(tc)
99	trackAlice(tc, fu, sigVersion)
100	untrackAlice(tc, fu, sigVersion)
101	t.Logf("reset the account and add a couple more low links")
102	assertUPAKLiteMatchesUPAK(t, tc, uid)
103	// reset again!
104	ResetAccountNoLogout(tc, fu)
105	fu.LoginOrBust(tc)
106	// add a low link
107	trackAlice(tc, fu, sigVersion)
108	// add a high link (new paper key)
109	uis = libkb.UIs{
110		LogUI:    tc.G.UI.GetLogUI(),
111		LoginUI:  &libkb.TestLoginUI{},
112		SecretUI: &libkb.TestSecretUI{},
113	}
114	peng := NewPaperKey(tc.G)
115	m = NewMetaContextForTest(tc).WithUIs(uis)
116	err = RunEngine2(m, peng)
117	require.NoError(t, err)
118	// add another low link
119	untrackAlice(tc, fu, sigVersion)
120	t.Logf("reset the account again and add a low link, a new paper key, and another low link")
121	assertUPAKLiteMatchesUPAK(t, tc, uid)
122
123	// with a logged out user
124	Logout(tc)
125	assertUPAKLiteMatchesUPAK(t, tc, uid)
126}
127