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	"io/ioutil"
9	"strings"
10	"testing"
11
12	"github.com/keybase/client/go/libkb"
13	keybase1 "github.com/keybase/client/go/protocol/keybase1"
14	"github.com/keybase/go-codec/codec"
15	"github.com/keybase/saltpack"
16)
17
18func TestSaltpackSignDeviceRequired(t *testing.T) {
19	tc := SetupEngineTest(t, "sign")
20	defer tc.Cleanup()
21
22	uis := libkb.UIs{
23		SecretUI: &libkb.TestSecretUI{},
24	}
25	eng := NewSaltpackSign(tc.G, nil)
26	m := NewMetaContextForTest(tc).WithUIs(uis)
27	err := RunEngine2(m, eng)
28	if err == nil {
29		t.Fatal("sign not logged in returned no error")
30	}
31	if _, ok := err.(libkb.DeviceRequiredError); !ok {
32		t.Errorf("error type: %T, expected DeviceRequiredError", err)
33	}
34}
35
36func TestSaltpackSignVerify(t *testing.T) {
37	tc := SetupEngineTest(t, "sign")
38	defer tc.Cleanup()
39
40	fu := CreateAndSignupFakeUser(tc, "sign")
41
42	// signTests are defined in pgp_sign_test.  Make sure that saltpack sign can
43	// sign/verify the same messages as pgp.
44	for _, test := range signTests {
45		var sink bytes.Buffer
46
47		sarg := &SaltpackSignArg{
48			Sink:   libkb.NopWriteCloser{W: &sink},
49			Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
50		}
51
52		eng := NewSaltpackSign(tc.G, sarg)
53		uis := libkb.UIs{
54			IdentifyUI: &FakeIdentifyUI{},
55			SecretUI:   fu.NewSecretUI(),
56		}
57
58		m := NewMetaContextForTest(tc).WithUIs(uis)
59		if err := RunEngine2(m, eng); err != nil {
60			t.Errorf("%s: run error: %s", test.name, err)
61			continue
62		}
63
64		sig := sink.String()
65
66		if len(sig) == 0 {
67			t.Errorf("%s: empty sig", test.name)
68		}
69
70		varg := &SaltpackVerifyArg{
71			Sink:   libkb.NopWriteCloser{W: &sink},
72			Source: strings.NewReader(sig),
73		}
74		veng := NewSaltpackVerify(tc.G, varg)
75
76		m = m.WithSaltpackUI(fakeSaltpackUI{})
77
78		if err := RunEngine2(m, veng); err != nil {
79			t.Errorf("%s: verify error: %s", test.name, err)
80			continue
81		}
82
83		// test SignedBy option:
84		varg = &SaltpackVerifyArg{
85			Sink:   libkb.NopWriteCloser{W: &sink},
86			Source: strings.NewReader(sig),
87			Opts: keybase1.SaltpackVerifyOptions{
88				SignedBy: fu.Username,
89			},
90		}
91		veng = NewSaltpackVerify(tc.G, varg)
92		if err := RunEngine2(m, veng); err != nil {
93			t.Errorf("%s: verify w/ SignedBy error: %s", test.name, err)
94			continue
95		}
96
97		varg = &SaltpackVerifyArg{
98			Sink:   libkb.NopWriteCloser{W: &sink},
99			Source: strings.NewReader(sig),
100			Opts: keybase1.SaltpackVerifyOptions{
101				SignedBy: "unknown",
102			},
103		}
104		veng = NewSaltpackVerify(tc.G, varg)
105		if err := RunEngine2(m, veng); err == nil {
106			t.Errorf("%s: verify w/ SignedBy=unknown worked, should have failed", test.name)
107			continue
108		}
109	}
110
111	// now try the same messages, but generate detached signatures
112	for _, test := range signTests {
113		var sink bytes.Buffer
114
115		sarg := &SaltpackSignArg{
116			Sink:   libkb.NopWriteCloser{W: &sink},
117			Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
118			Opts: keybase1.SaltpackSignOptions{
119				Detached: true,
120			},
121		}
122
123		eng := NewSaltpackSign(tc.G, sarg)
124		uis := libkb.UIs{
125			IdentifyUI: &FakeIdentifyUI{},
126			SecretUI:   fu.NewSecretUI(),
127		}
128		m := NewMetaContextForTest(tc).WithUIs(uis)
129		if err := RunEngine2(m, eng); err != nil {
130			t.Errorf("(detached) %s: run error: %s", test.name, err)
131			continue
132		}
133
134		sig := sink.Bytes()
135
136		if len(sig) == 0 {
137			t.Errorf("(detached) %s: empty sig", test.name)
138		}
139
140		varg := &SaltpackVerifyArg{
141			Sink:   libkb.NopWriteCloser{W: &sink},
142			Source: strings.NewReader(test.input),
143			Opts: keybase1.SaltpackVerifyOptions{
144				Signature: sig,
145			},
146		}
147
148		veng := NewSaltpackVerify(tc.G, varg)
149		m = m.WithSaltpackUI(fakeSaltpackUI{})
150		if err := RunEngine2(m, veng); err != nil {
151			t.Errorf("(detached) %s: verify error: %s", test.name, err)
152			continue
153		}
154	}
155}
156
157func TestSaltpackSignVerifyBinary(t *testing.T) {
158	tc := SetupEngineTest(t, "sign")
159	defer tc.Cleanup()
160
161	fu := CreateAndSignupFakeUser(tc, "sign")
162
163	// signTests are defined in pgp_sign_test.  Make sure that saltpack sign can
164	// sign/verify the same messages as pgp.
165	for _, test := range signTests {
166		var sink bytes.Buffer
167
168		sarg := &SaltpackSignArg{
169			Sink:   libkb.NopWriteCloser{W: &sink},
170			Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
171			Opts: keybase1.SaltpackSignOptions{
172				Binary: true,
173			},
174		}
175
176		eng := NewSaltpackSign(tc.G, sarg)
177		uis := libkb.UIs{
178			IdentifyUI: &FakeIdentifyUI{},
179			SecretUI:   fu.NewSecretUI(),
180		}
181		m := NewMetaContextForTest(tc).WithUIs(uis)
182		if err := RunEngine2(m, eng); err != nil {
183			t.Errorf("%s: run error: %s", test.name, err)
184			continue
185		}
186
187		sig := sink.String()
188
189		if len(sig) == 0 {
190			t.Errorf("%s: empty sig", test.name)
191		}
192
193		varg := &SaltpackVerifyArg{
194			Sink:   libkb.NopWriteCloser{W: &sink},
195			Source: strings.NewReader(sig),
196		}
197		veng := NewSaltpackVerify(tc.G, varg)
198
199		m = m.WithSaltpackUI(fakeSaltpackUI{})
200
201		if err := RunEngine2(m, veng); err != nil {
202			t.Errorf("%s: verify error: %s", test.name, err)
203			continue
204		}
205	}
206
207	// now try the same messages, but generate detached signatures
208	for _, test := range signTests {
209		var sink bytes.Buffer
210
211		sarg := &SaltpackSignArg{
212			Sink:   libkb.NopWriteCloser{W: &sink},
213			Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
214			Opts: keybase1.SaltpackSignOptions{
215				Binary:   true,
216				Detached: true,
217			},
218		}
219
220		eng := NewSaltpackSign(tc.G, sarg)
221		uis := libkb.UIs{
222			IdentifyUI: &FakeIdentifyUI{},
223			SecretUI:   fu.NewSecretUI(),
224		}
225		m := NewMetaContextForTest(tc).WithUIs(uis)
226		if err := RunEngine2(m, eng); err != nil {
227			t.Errorf("(detached) %s: run error: %s", test.name, err)
228			continue
229		}
230
231		sig := sink.Bytes()
232
233		if len(sig) == 0 {
234			t.Errorf("(detached) %s: empty sig", test.name)
235		}
236
237		varg := &SaltpackVerifyArg{
238			Sink:   libkb.NopWriteCloser{W: &sink},
239			Source: strings.NewReader(test.input),
240			Opts: keybase1.SaltpackVerifyOptions{
241				Signature: sig,
242			},
243		}
244		veng := NewSaltpackVerify(tc.G, varg)
245		m = m.WithSaltpackUI(fakeSaltpackUI{})
246
247		if err := RunEngine2(m, veng); err != nil {
248			t.Errorf("(detached) %s: verify error: %s", test.name, err)
249			continue
250		}
251	}
252}
253
254func TestSaltpackSignVerifyNotSelf(t *testing.T) {
255	tc := SetupEngineTest(t, "sign")
256	defer tc.Cleanup()
257
258	signer := CreateAndSignupFakeUser(tc, "sign")
259
260	var sink bytes.Buffer
261
262	sarg := &SaltpackSignArg{
263		Sink:   libkb.NopWriteCloser{W: &sink},
264		Source: ioutil.NopCloser(bytes.NewBufferString("this is from me")),
265	}
266
267	eng := NewSaltpackSign(tc.G, sarg)
268	uis := libkb.UIs{
269		IdentifyUI: &FakeIdentifyUI{},
270		SecretUI:   signer.NewSecretUI(),
271	}
272
273	m := NewMetaContextForTest(tc).WithUIs(uis)
274	if err := RunEngine2(m, eng); err != nil {
275		t.Fatal(err)
276	}
277
278	sig := sink.String()
279
280	if len(sig) == 0 {
281		t.Fatal("empty sig")
282	}
283
284	Logout(tc)
285
286	_ = CreateAndSignupFakeUser(tc, "sign")
287
288	// no user assertion
289	varg := &SaltpackVerifyArg{
290		Sink:   libkb.NopWriteCloser{W: &sink},
291		Source: strings.NewReader(sig),
292	}
293	veng := NewSaltpackVerify(tc.G, varg)
294
295	m = m.WithSaltpackUI(fakeSaltpackUI{})
296
297	if err := RunEngine2(m, veng); err != nil {
298		t.Fatalf("verify error: %s", err)
299	}
300
301	// valid user assertion
302	varg = &SaltpackVerifyArg{
303		Sink:   libkb.NopWriteCloser{W: &sink},
304		Source: strings.NewReader(sig),
305		Opts: keybase1.SaltpackVerifyOptions{
306			SignedBy: signer.Username,
307		},
308	}
309	veng = NewSaltpackVerify(tc.G, varg)
310	if err := RunEngine2(m, veng); err != nil {
311		t.Fatalf("verify w/ SignedBy error: %s", err)
312	}
313
314	// invalid user assertion
315	varg = &SaltpackVerifyArg{
316		Sink:   libkb.NopWriteCloser{W: &sink},
317		Source: strings.NewReader(sig),
318		Opts: keybase1.SaltpackVerifyOptions{
319			SignedBy: "unknown",
320		},
321	}
322	veng = NewSaltpackVerify(tc.G, varg)
323	if err := RunEngine2(m, veng); err == nil {
324		t.Errorf("verify w/ SignedBy unknown didn't fail")
325	}
326}
327
328func TestSaltpackVerifyRevoked(t *testing.T) {
329	tc := SetupEngineTest(t, "sign")
330	defer tc.Cleanup()
331
332	fu := CreateAndSignupFakeUser(tc, "sign")
333
334	var sink bytes.Buffer
335
336	sarg := &SaltpackSignArg{
337		Sink:   libkb.NopWriteCloser{W: &sink},
338		Source: ioutil.NopCloser(bytes.NewBufferString("test input wooo")),
339	}
340
341	eng := NewSaltpackSign(tc.G, sarg)
342	uis := libkb.UIs{
343		LogUI:      tc.G.UI.GetLogUI(),
344		LoginUI:    &libkb.TestLoginUI{},
345		IdentifyUI: &FakeIdentifyUI{},
346		SecretUI:   fu.NewSecretUI(),
347	}
348	m := NewMetaContextForTest(tc).WithUIs(uis)
349	if err := RunEngine2(m, eng); err != nil {
350		t.Fatal(err)
351	}
352
353	// Get the current device
354	devices, _ := getActiveDevicesAndKeys(tc, fu)
355	if len(devices) != 1 {
356		t.Fatalf("Expected a single device, but found %d", len(devices))
357	}
358	currentDevice := devices[0]
359
360	// Delegate a new paper key so that we have something active after we
361	// revoke the current device.
362	paperEng := NewPaperKey(tc.G)
363	if err := RunEngine2(m, paperEng); err != nil {
364		t.Fatal(err)
365	}
366
367	// Revoke the current device.
368	err := doRevokeDevice(tc, fu, currentDevice.ID, false, false)
369	if err == nil {
370		tc.T.Fatal("Expected revoking the current device to fail.")
371	}
372	// force=true is required for the current device
373	err = doRevokeDevice(tc, fu, currentDevice.ID, true, false)
374	if err != nil {
375		tc.T.Fatal(err)
376	}
377
378	// Finally verify the sig. This should be an error, because the signing
379	// device is revoked. The revoked status will get passed to our
380	// fakeSaltpackUI's SaltpackVerifyBadSender method, made into an error, and
381	// propagated all the way back here. Unfortunately we can't really test the
382	// force option here, because that's implemented in the real client
383	// SaltpackUI.
384	sig := sink.String()
385	if len(sig) == 0 {
386		t.Fatal("empty sig")
387	}
388	varg := &SaltpackVerifyArg{
389		Sink:   libkb.NopWriteCloser{W: &sink},
390		Source: strings.NewReader(sig),
391	}
392	veng := NewSaltpackVerify(tc.G, varg)
393	m = m.WithSaltpackUI(fakeSaltpackUI{})
394	err = RunEngine2(m, veng)
395	if err == nil {
396		t.Fatal("expected error during verify")
397	}
398	verificationError, ok := err.(libkb.VerificationError)
399	if !ok {
400		t.Fatal("expected VerificationError during verify")
401	}
402	badSenderError, ok := verificationError.Cause.Err.(*FakeBadSenderError)
403	if !ok {
404		t.Fatal("expected FakeBadSenderError during verify")
405	}
406
407	if badSenderError.senderType != keybase1.SaltpackSenderType_REVOKED {
408		t.Fatalf("expected keybase1.SaltpackSenderType_REVOKED, got %s", badSenderError.senderType.String())
409	}
410}
411
412func TestSaltpackSignForceVersion(t *testing.T) {
413	tc := SetupEngineTest(t, "sign")
414	defer tc.Cleanup()
415
416	fu := CreateAndSignupFakeUser(tc, "sign")
417
418	run := func(versionFlag int, majorVersionExpected int) {
419		// For each run, test both the attached and detached sig modes.
420		for _, isAttached := range []bool{true, false} {
421			var sink bytes.Buffer
422			sarg := &SaltpackSignArg{
423				Sink:   libkb.NopWriteCloser{W: &sink},
424				Source: ioutil.NopCloser(bytes.NewBufferString("some test input")),
425				Opts: keybase1.SaltpackSignOptions{
426					Binary:          true,
427					SaltpackVersion: versionFlag,
428					Detached:        !isAttached,
429				},
430			}
431			eng := NewSaltpackSign(tc.G, sarg)
432			uis := libkb.UIs{
433				IdentifyUI: &FakeIdentifyUI{},
434				SecretUI:   fu.NewSecretUI(),
435			}
436			m := NewMetaContextForTest(tc).WithUIs(uis)
437			if err := RunEngine2(m, eng); err != nil {
438				t.Fatal(err)
439			}
440
441			// Double decode the header and inspect it.
442			var header saltpack.EncryptionHeader
443			dec := codec.NewDecoderBytes(sink.Bytes(), &codec.MsgpackHandle{WriteExt: true})
444			var b []byte
445			if err := dec.Decode(&b); err != nil {
446				t.Fatal(err)
447			}
448			dec = codec.NewDecoderBytes(b, &codec.MsgpackHandle{WriteExt: true})
449			if err := dec.Decode(&header); err != nil {
450				t.Fatal(err)
451			}
452
453			if header.Version.Major != majorVersionExpected {
454				t.Fatalf("passed saltpack version %d (attached: %t) and expected major version %d, found %d", versionFlag, isAttached, majorVersionExpected, header.Version.Major)
455			}
456		}
457	}
458
459	// 0 means the default, which is major version 2.
460	run(0, 2)
461	run(1, 1)
462	run(2, 2)
463}
464