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	"testing"
8
9	"github.com/keybase/client/go/libkb"
10	"github.com/keybase/go-crypto/openpgp"
11)
12
13func doUpdate(fingerprints []string, all bool, fu *FakeUser, tc libkb.TestContext) (err error) {
14	eng := NewPGPUpdateEngine(tc.G, fingerprints, all)
15	uis := libkb.UIs{
16		LogUI:    tc.G.UI.GetLogUI(),
17		SecretUI: fu.NewSecretUI(),
18	}
19	m := NewMetaContextForTest(tc).WithUIs(uis)
20	err = RunEngine2(m, eng)
21	return
22}
23
24func getFakeUsersKeyBundleFromServer(tc libkb.TestContext, fu *FakeUser) *libkb.PGPKeyBundle {
25	arg := libkb.NewLoadUserForceArg(tc.G).WithName(fu.Username)
26	user, err := libkb.LoadUser(arg)
27	if err != nil {
28		tc.T.Fatal("Failed loading user", err)
29	}
30	ckf := user.GetComputedKeyFamily()
31	keys := ckf.GetActivePGPKeys(true /* sibkeys */)
32	if len(keys) != 1 {
33		tc.T.Fatal("Expected only one key.")
34	}
35	return keys[0]
36}
37
38func getFakeUsersBundlesList(tc libkb.TestContext, fu *FakeUser) []string {
39	arg := libkb.NewLoadUserForceArg(tc.G).WithName(fu.Username)
40	user, err := libkb.LoadUser(arg)
41	if err != nil {
42		tc.T.Fatal("Failed loading user", err)
43	}
44	return user.GetKeyFamily().BundlesForTesting
45}
46
47func TestPGPUpdate(t *testing.T) {
48	tc := SetupEngineTest(t, "pgp_update")
49	defer tc.Cleanup()
50
51	// Note that this user's key is not created in the GPG keyring. For the
52	// purposes of this test that's ok.
53	fakeUser := createFakeUserWithPGPSibkey(tc)
54	bundle := getFakeUsersKeyBundleFromServer(tc, fakeUser)
55	if len(bundle.Subkeys) != 1 {
56		t.Fatal("expected exactly 1 subkey")
57	}
58	originalBundlesLen := len(getFakeUsersBundlesList(tc, fakeUser))
59
60	// Modify the key by deleting the subkey.
61	bundle.Subkeys = []openpgp.Subkey{}
62
63	gpgCLI := libkb.NewGpgCLI(tc.G, tc.G.UI.GetLogUI())
64	err := gpgCLI.Configure(tc.MetaContext())
65	if err != nil {
66		t.Fatal("Error initializing GpgCLI", err)
67	}
68
69	// Add the modified key to the gpg keyring
70	if err := gpgCLI.ExportKey(tc.MetaContext(), *bundle, false /* export public key only */, false /* no batch mode */); err != nil {
71		t.Fatal(err)
72	}
73
74	// Now run `client pgp update` with a fingerprint that doesn't match.
75	err = doUpdate([]string{"not_a_real_fingerprint"}, false, fakeUser, tc)
76	if err != nil {
77		t.Fatal("Error in PGPUpdateEngine:", err)
78	}
79	// Get the list of bundles from the server.
80	bundles := getFakeUsersBundlesList(tc, fakeUser)
81	// Check that the key hasn't been modified.
82	if len(bundles) != originalBundlesLen {
83		t.Fatal("Key changes should not have been uploaded.")
84	}
85
86	// Do the same thing without the fingerprint. It should go through this time.
87	err = doUpdate([]string{}, false, fakeUser, tc)
88	if err != nil {
89		t.Fatal("Error in PGPUpdateEngine:", err)
90	}
91	// Load the user from the server again.
92	reloadedBundles := getFakeUsersBundlesList(tc, fakeUser)
93	// Check that the key hasn't been modified.
94	if len(reloadedBundles) != originalBundlesLen+1 {
95		t.Fatal("Key changes should have been uploaded.")
96	}
97}
98
99func TestPGPUpdateMultiKey(t *testing.T) {
100	tc := SetupEngineTest(t, "pgp_update")
101	defer tc.Cleanup()
102
103	// Get a user with one PGP sibkey. Note that this user's key is not created
104	// in the GPG keyring. For the purposes of this test that's ok.
105	fu := createFakeUserWithPGPSibkey(tc)
106
107	// Generate a second PGP sibkey.
108	arg := PGPKeyImportEngineArg{
109		AllowMulti: true,
110		DoExport:   true,
111		Gen: &libkb.PGPGenArg{
112			PrimaryBits: 768,
113			SubkeyBits:  768,
114		},
115	}
116	err := arg.Gen.MakeAllIds(tc.G)
117	if err != nil {
118		tc.T.Fatal(err)
119	}
120	uis := libkb.UIs{
121		LogUI:    tc.G.UI.GetLogUI(),
122		SecretUI: fu.NewSecretUI(),
123	}
124	eng := NewPGPKeyImportEngine(tc.G, arg)
125	m := NewMetaContextForTest(tc).WithUIs(uis)
126	err = RunEngine2(m, eng)
127	if err != nil {
128		tc.T.Fatal(err)
129	}
130
131	// `client pgp update` should fail by default, because there are multiple keys.
132	err = doUpdate([]string{}, false /* all */, fu, tc)
133	if err == nil {
134		t.Fatal("Update should fail with multiple keys and no --all.")
135	}
136
137	// `client pgp update` should fail with both specific fingerprints and --all.
138	err = doUpdate([]string{"foo"}, true /* all */, fu, tc)
139	if err == nil {
140		t.Fatal("Update should fail with explicit fingerprint and --all.")
141	}
142
143	// It should finally succeed with just --all.
144	err = doUpdate([]string{}, true /* all */, fu, tc)
145	if err != nil {
146		t.Fatal("Update should succeed with --all. Error:", err)
147	}
148}
149