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	"fmt"
9	"runtime/debug"
10	"testing"
11
12	"golang.org/x/crypto/nacl/box"
13	"golang.org/x/net/context"
14
15	"github.com/keybase/client/go/kbcrypto"
16	"github.com/keybase/client/go/libkb"
17	keybase1 "github.com/keybase/client/go/protocol/keybase1"
18)
19
20// Test that SignED25519() signs the given message with the device
21// signing key, and that the signature is verifiable by the returned
22// public key.
23//
24// (For general tests that valid signatures are accepted and invalid
25// signatures are rejected, see naclwrap_test.go.)
26func TestCryptoSignED25519(t *testing.T) {
27	tc := SetupEngineTest(t, "crypto")
28	defer tc.Cleanup()
29
30	CreateAndSignupFakeUser(tc, "fu")
31
32	msg := []byte("test message")
33	ret, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
34		Msg: msg,
35	})
36	if err != nil {
37		t.Fatal(err)
38	}
39
40	publicKey := kbcrypto.NaclSigningKeyPublic(ret.PublicKey)
41	if !publicKey.Verify(msg, kbcrypto.NaclSignature(ret.Sig)) {
42		t.Error(kbcrypto.VerificationError{})
43	}
44}
45
46// Test that SignToString() signs the given message with the device
47// signing key and that the signature is verifiable and contains the message.
48func TestCryptoSignToString(t *testing.T) {
49	tc := SetupEngineTest(t, "crypto")
50	defer tc.Cleanup()
51
52	CreateAndSignupFakeUser(tc, "fu")
53
54	msg := []byte("test message")
55	signature, err := SignToString(context.TODO(), tc.G, keybase1.SignToStringArg{
56		Msg: msg,
57	})
58	if err != nil {
59		t.Fatal(err)
60	}
61
62	_, msg2, _, err := kbcrypto.NaclVerifyAndExtract(signature)
63	if err != nil {
64		t.Fatal(err)
65	}
66	if !bytes.Equal(msg, msg2) {
67		t.Fatal(fmt.Errorf("message mismatch, expected: %s, got: %s",
68			string(msg), string(msg2)))
69	}
70}
71
72// Test that CryptoHandler.SignED25519() propagates any error
73// encountered when getting the device signing key.
74func TestCryptoSignED25519NoSigningKey(t *testing.T) {
75	tc := SetupEngineTest(t, "crypto")
76	defer tc.Cleanup()
77
78	_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
79		Msg: []byte("test message"),
80	})
81
82	if _, ok := err.(libkb.LoginRequiredError); !ok {
83		t.Errorf("expected LoginRequiredError, got %v", err)
84	}
85}
86
87func BenchmarkCryptoSignED25519(b *testing.B) {
88	tc := SetupEngineTest(b, "crypto")
89	defer tc.Cleanup()
90
91	CreateAndSignupFakeUser(tc, "fu")
92
93	b.ResetTimer()
94	for i := 0; i < b.N; i++ {
95		msg := []byte("test message")
96		_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
97			Msg: msg,
98		})
99		if err != nil {
100			b.Fatal(err)
101		}
102	}
103}
104
105// Test that CryptoHandler.UnboxBytes32() decrypts a boxed 32-byte
106// array correctly.
107func TestCryptoUnboxBytes32(t *testing.T) {
108	tc := SetupEngineTest(t, "crypto")
109	defer tc.Cleanup()
110
111	u := CreateAndSignupFakeUser(tc, "fu")
112	f := func() libkb.SecretUI {
113		return &libkb.TestSecretUI{Passphrase: u.Passphrase}
114	}
115
116	key, err := GetMySecretKey(
117		context.TODO(),
118		tc.G, libkb.DeviceEncryptionKeyType, "test")
119	if err != nil {
120		t.Fatal(err)
121	}
122	kp, ok := key.(libkb.NaclDHKeyPair)
123	if !ok || kp.Private == nil {
124		t.Fatalf("unexpected key %v", key)
125	}
126
127	peerKp, err := libkb.GenerateNaclDHKeyPair()
128	if err != nil {
129		t.Fatal(err)
130	}
131
132	expectedBytes32 := keybase1.Bytes32{0, 1, 2, 3, 4, 5}
133	nonce := [24]byte{6, 7, 8, 9, 10}
134	peersPublicKey := keybase1.BoxPublicKey(peerKp.Public)
135
136	encryptedData := box.Seal(nil, expectedBytes32[:], &nonce, (*[32]byte)(&kp.Public), (*[32]byte)(peerKp.Private))
137
138	var encryptedBytes32 keybase1.EncryptedBytes32
139	if len(encryptedBytes32) != len(encryptedData) {
140		t.Fatalf("Expected %d bytes, got %d", len(encryptedBytes32), len(encryptedData))
141	}
142
143	copy(encryptedBytes32[:], encryptedData)
144
145	bytes32, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{
146		EncryptedBytes32: encryptedBytes32,
147		Nonce:            nonce,
148		PeersPublicKey:   peersPublicKey,
149	})
150
151	if err != nil {
152		t.Fatal(err)
153	}
154
155	if bytes32 != expectedBytes32 {
156		t.Errorf("expected %s, got %s", expectedBytes32, bytes32)
157	}
158
159	// also test UnboxBytes32Any:
160	arg := keybase1.UnboxBytes32AnyArg{
161		Bundles: []keybase1.CiphertextBundle{
162			{Kid: kp.GetKID(), Ciphertext: encryptedBytes32, Nonce: nonce, PublicKey: peersPublicKey},
163		},
164	}
165	res, err := UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
166	if err != nil {
167		t.Fatal(err)
168	}
169	if res.Plaintext != expectedBytes32 {
170		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
171	}
172	if res.Kid.IsNil() {
173		t.Errorf("UnboxBytes32Any kid is nil")
174	}
175}
176
177// Test that CryptoHandler.UnboxBytes32() propagates any decryption
178// errors correctly.
179//
180// For now, we're assuming that nacl/box works correctly (i.e., we're
181// not testing the ways in which decryption can fail).
182func TestCryptoUnboxBytes32DecryptionError(t *testing.T) {
183	tc := SetupEngineTest(t, "crypto")
184	defer tc.Cleanup()
185
186	CreateAndSignupFakeUser(tc, "fu")
187
188	_, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{})
189	if _, ok := err.(libkb.DecryptionError); !ok {
190		t.Errorf("expected libkb.DecryptionError, got %T", err)
191	}
192}
193
194// Test that CryptoHandler.UnboxBytes32() propagates any error
195// encountered when getting the device encryption key.
196func TestCryptoUnboxBytes32NoEncryptionKey(t *testing.T) {
197	tc := SetupEngineTest(t, "crypto")
198	defer tc.Cleanup()
199
200	_, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{})
201
202	if _, ok := err.(libkb.LoginRequiredError); !ok {
203		t.Errorf("expected LoginRequiredError, got %v", err)
204	}
205}
206
207func cachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) (key libkb.GenericKey, err error) {
208	return tc.G.ActiveDevice.KeyByType(ktype)
209}
210
211func assertCachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) {
212	skey, err := cachedSecretKey(tc, ktype)
213	if err != nil {
214		debug.PrintStack()
215		tc.T.Fatalf("error getting cached secret key: %s", err)
216	}
217	if skey == nil {
218		tc.T.Fatalf("expected cached key, got nil")
219	}
220}
221
222func assertNotCachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) {
223	skey, err := cachedSecretKey(tc, ktype)
224	if err == nil {
225		tc.T.Fatal("expected err getting cached secret key, got nil")
226	}
227	if _, notFound := err.(libkb.NotFoundError); !notFound {
228		tc.T.Fatalf("expected not found error, got %s (%T)", err, err)
229	}
230	if skey != nil {
231		tc.T.Fatalf("expected nil cached key, got %v", skey)
232	}
233}
234
235// TestCachedSecretKey tests that secret device keys are cached
236// properly.
237func TestCachedSecretKey(t *testing.T) {
238	tc := SetupEngineTest(t, "login")
239	defer tc.Cleanup()
240
241	u := CreateAndSignupFakeUser(tc, "login")
242
243	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
244	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
245
246	Logout(tc)
247
248	assertNotCachedSecretKey(tc, libkb.DeviceSigningKeyType)
249	assertNotCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
250
251	u.LoginOrBust(tc)
252
253	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
254	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
255
256	msg := []byte("test message")
257	_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
258		Msg: msg,
259	})
260	if err != nil {
261		t.Fatal(err)
262	}
263
264	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
265	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
266
267	Logout(tc)
268
269	assertNotCachedSecretKey(tc, libkb.DeviceSigningKeyType)
270	assertNotCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
271
272	u.LoginOrBust(tc)
273
274	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
275	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
276}
277
278func TestCryptoUnboxBytes32AnyPaper(t *testing.T) {
279	tc := SetupEngineTest(t, "crypto")
280	defer tc.Cleanup()
281
282	u := CreateAndSignupFakeUser(tc, "fu")
283
284	// create a paper key and cache it
285	uis := libkb.UIs{
286		LogUI:    tc.G.UI.GetLogUI(),
287		LoginUI:  &libkb.TestLoginUI{},
288		SecretUI: u.NewSecretUI(),
289	}
290	peng := NewPaperKey(tc.G)
291	m := NewMetaContextForTest(tc).WithUIs(uis)
292	if err := RunEngine2(m, peng); err != nil {
293		t.Fatal(err)
294	}
295
296	m.ActiveDevice().CacheProvisioningKey(m, libkb.NewDeviceWithKeysOnly(peng.SigKey(), peng.EncKey(), libkb.KeychainModeNone))
297
298	key := peng.EncKey()
299	kp, ok := key.(libkb.NaclDHKeyPair)
300	if !ok {
301		t.Fatalf("paper enc key type: %T, expected libkb.NaclDHKeyPair", key)
302	}
303	if kp.Private == nil {
304		t.Fatalf("paper enc key has nil private key")
305	}
306
307	peerKp, err := libkb.GenerateNaclDHKeyPair()
308	if err != nil {
309		t.Fatal(err)
310	}
311
312	expectedBytes32 := keybase1.Bytes32{0, 1, 2, 3, 4, 5}
313	nonce := [24]byte{6, 7, 8, 9, 10}
314	peersPublicKey := keybase1.BoxPublicKey(peerKp.Public)
315
316	encryptedData := box.Seal(nil, expectedBytes32[:], &nonce, (*[32]byte)(&kp.Public), (*[32]byte)(peerKp.Private))
317
318	var encryptedBytes32 keybase1.EncryptedBytes32
319	if len(encryptedBytes32) != len(encryptedData) {
320		t.Fatalf("Expected %d bytes, got %d", len(encryptedBytes32), len(encryptedData))
321	}
322
323	copy(encryptedBytes32[:], encryptedData)
324
325	f := func() libkb.SecretUI {
326		return u.NewSecretUI()
327	}
328
329	_, err = UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{
330		EncryptedBytes32: encryptedBytes32,
331		Nonce:            nonce,
332		PeersPublicKey:   peersPublicKey,
333	})
334
335	// this should fail
336	if err == nil {
337		t.Fatal("UnboxBytes32 worked with paper key encrypted data")
338	}
339	if _, ok := err.(libkb.DecryptionError); !ok {
340		t.Fatalf("error %T, expected libkb.DecryptionError", err)
341	}
342
343	// this should work
344	arg := keybase1.UnboxBytes32AnyArg{
345		Bundles: []keybase1.CiphertextBundle{
346			{Kid: kp.GetKID(), Ciphertext: encryptedBytes32, Nonce: nonce, PublicKey: peersPublicKey},
347		},
348		PromptPaper: true,
349	}
350	res, err := UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
351	if err != nil {
352		t.Fatal(err)
353	}
354	if res.Plaintext != expectedBytes32 {
355		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
356	}
357	if res.Kid.IsNil() {
358		t.Errorf("UnboxBytes32Any kid is nil")
359	}
360
361	// clear the paper key cache to test getting a paper key via UI
362	clearCaches(tc.G)
363	if err != nil {
364		t.Fatal(err)
365	}
366
367	f = func() libkb.SecretUI {
368		// set the passphrase in the secretUI to the paper key
369		secretUI := u.NewSecretUI()
370		secretUI.Passphrase = peng.Passphrase()
371		return secretUI
372	}
373
374	res, err = UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
375	if err != nil {
376		t.Fatal(err)
377	}
378	if res.Plaintext != expectedBytes32 {
379		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
380	}
381	if res.Kid.IsNil() {
382		t.Errorf("UnboxBytes32Any kid is nil")
383	}
384}
385