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	"sync"
8
9	"github.com/keybase/client/go/kbcrypto"
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12	"golang.org/x/crypto/nacl/box"
13	"golang.org/x/net/context"
14)
15
16// getKeyMu synchronizes all accesses to the need to pull in pinentries/secret keys
17// for this user.
18var getKeyMu sync.Mutex
19
20// GetMySecretKey uses ActiveDevice to get a secret key for the current user.
21//
22// It used to have functionality to load the user and prompt for a passphrase to
23// unlock the keys, but that is outdated now.  Either you are logged in and
24// have your device keys cached, or you aren't.
25//
26// If the key isn't found in the ActiveDevice cache, this will return LoginRequiredError.
27func GetMySecretKey(ctx context.Context, g *libkb.GlobalContext, secretKeyType libkb.SecretKeyType, reason string) (libkb.GenericKey, error) {
28	key, err := g.ActiveDevice.KeyByType(secretKeyType)
29	if err != nil {
30		if _, ok := err.(libkb.NotFoundError); ok {
31			g.Log.CDebugf(ctx, "GetMySecretKey: no device key of type %s in ActiveDevice, returning LoginRequiredError", secretKeyType)
32			return nil, libkb.LoginRequiredError{Context: "GetMySecretKey"}
33		}
34		g.Log.CDebugf(ctx, "GetMySecretKey(%s), unexpected error: %s", secretKeyType, err)
35		return nil, err
36	}
37	return key, nil
38}
39
40// GetMySecretKeyWithUID is like GetMySecretKey but returns an error if uid is not active.
41func GetMySecretKeyWithUID(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, secretKeyType libkb.SecretKeyType, reason string) (libkb.GenericKey, error) {
42	key, err := g.ActiveDevice.KeyByTypeWithUID(uid, secretKeyType)
43	if err != nil {
44		if _, ok := err.(libkb.NotFoundError); ok {
45			g.Log.CDebugf(ctx, "GetMySecretKeyWithUID: no device key of type %s in ActiveDevice, returning LoginRequiredError", secretKeyType)
46			return nil, libkb.LoginRequiredError{Context: "GetMySecretKey"}
47		}
48		g.Log.CDebugf(ctx, "GetMySecretKeyWithUID(%s), unexpected error: %s", secretKeyType, err)
49		return nil, err
50	}
51	return key, nil
52}
53
54// SignED25519 signs the given message with the current user's private
55// signing key.
56func SignED25519(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignED25519Arg) (ret keybase1.ED25519SignatureInfo, err error) {
57	signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason)
58	if err != nil {
59		return
60	}
61
62	kp, ok := signingKey.(libkb.NaclSigningKeyPair)
63	if !ok || kp.Private == nil {
64		err = libkb.KeyCannotSignError{}
65		return
66	}
67
68	sig := kp.Private.Sign(arg.Msg)
69	publicKey := kp.Public
70	ret = keybase1.ED25519SignatureInfo{
71		Sig:       keybase1.ED25519Signature(sig),
72		PublicKey: keybase1.ED25519PublicKey(publicKey),
73	}
74	return
75}
76
77// SignED25519ForKBFS signs the given message with the current user's private
78// signing key on behalf of KBFS.
79func SignED25519ForKBFS(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignED25519ForKBFSArg) (
80	ret keybase1.ED25519SignatureInfo, err error) {
81	signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason)
82	if err != nil {
83		return
84	}
85
86	kp, ok := signingKey.(libkb.NaclSigningKeyPair)
87	if !ok || kp.Private == nil {
88		err = libkb.KeyCannotSignError{}
89		return
90	}
91
92	var sigInfo kbcrypto.NaclSigInfo
93	sigInfo, err = kp.SignV2(arg.Msg, kbcrypto.SignaturePrefixKBFS)
94	if err != nil {
95		return
96	}
97	publicKey := kp.Public
98	ret = keybase1.ED25519SignatureInfo{
99		Sig:       keybase1.ED25519Signature(sigInfo.Sig),
100		PublicKey: keybase1.ED25519PublicKey(publicKey),
101	}
102	return
103}
104
105// SignToString signs the given message with the current user's private
106// signing key and outputs the serialized NaclSigInfo string.
107func SignToString(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignToStringArg) (sig string, err error) {
108	signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason)
109	if err != nil {
110		return
111	}
112
113	kp, ok := signingKey.(libkb.NaclSigningKeyPair)
114	if !ok || kp.Private == nil {
115		err = libkb.KeyCannotSignError{}
116		return
117	}
118
119	sig, _, err = kp.SignToString(arg.Msg)
120	return
121}
122
123// UnboxBytes32 decrypts the given message with the current user's
124// private encryption key and the given nonce and peer public key.
125func UnboxBytes32(ctx context.Context, g *libkb.GlobalContext, arg keybase1.UnboxBytes32Arg) (bytes32 keybase1.Bytes32, err error) {
126	encryptionKey, err := GetMySecretKey(ctx, g, libkb.DeviceEncryptionKeyType, arg.Reason)
127	if err != nil {
128		return
129	}
130
131	return unboxBytes32(encryptionKey, arg.EncryptedBytes32, arg.Nonce, arg.PeersPublicKey)
132}
133
134// UnboxBytes32Any will decrypt any of the KID, ciphertext, nonce
135// bundles in arg.Bundles.  Key preference order:  cached device keys,
136// cached paper keys, local device key, user-entered paper key.
137// It returns the KID and bundle index along with the plaintext.
138func UnboxBytes32Any(m libkb.MetaContext, getSecretUI func() libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg) (res keybase1.UnboxAnyRes, err error) {
139	defer m.Trace("UnboxBytes32Any", &err)()
140
141	// find a matching secret key for a bundle in arg.Bundles
142	key, index, err := getMatchingSecretKey(m, getSecretUI, arg)
143	if err != nil {
144		return res, err
145	}
146
147	// decrypt the bundle's ciphertext
148	plaintext, err := unboxBytes32(key, arg.Bundles[index].Ciphertext, arg.Bundles[index].Nonce, arg.Bundles[index].PublicKey)
149	if err != nil {
150		return res, err
151	}
152
153	// return plaintext, kid, and index
154	res.Plaintext = plaintext
155	res.Kid = key.GetKID()
156	res.Index = index
157
158	return res, nil
159}
160
161func unboxBytes32(encryptionKey libkb.GenericKey, ciphertext keybase1.EncryptedBytes32, nonce keybase1.BoxNonce, peerPubKey keybase1.BoxPublicKey) (bytes32 keybase1.Bytes32, err error) {
162	kp, ok := encryptionKey.(libkb.NaclDHKeyPair)
163	if !ok {
164		err = libkb.KeyCannotDecryptError{}
165		return
166	}
167	if kp.Private == nil {
168		err = libkb.NoSecretKeyError{}
169		return
170	}
171
172	decryptedData, ok := box.Open(nil, ciphertext[:], (*[24]byte)(&nonce), (*[32]byte)(&peerPubKey), (*[32]byte)(kp.Private))
173	if !ok {
174		err = libkb.DecryptionError{}
175		return
176	}
177
178	if len(decryptedData) != len(bytes32) {
179		err = libkb.DecryptionError{}
180		return
181	}
182
183	copy(bytes32[:], decryptedData)
184	return
185
186}
187
188func getMatchingSecretKey(m libkb.MetaContext, getSecretUI func() libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg) (key libkb.GenericKey, index int, err error) {
189	// first check cached keys
190	key, index, err = matchingCachedKey(m, arg)
191	if err != nil {
192		return nil, 0, err
193	}
194	if key != nil {
195		return key, index, nil
196	}
197
198	m.Debug("getMatchingSecretKey: acquiring lock")
199	getKeyMu.Lock()
200	defer func() {
201		getKeyMu.Unlock()
202		m.Debug("getMatchingSecretKey: lock released")
203	}()
204	m.Debug("getMatchingSecretKey: lock acquired")
205
206	// check cache after acquiring lock
207	key, index, err = matchingCachedKey(m, arg)
208	if err != nil {
209		return nil, 0, err
210	}
211	if key != nil {
212		return key, index, nil
213	}
214	m.Debug("getMatchingSecretKey: no matching cached device key found")
215
216	// load the user
217	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
218	if err != nil {
219		return nil, 0, err
220	}
221
222	// need secretUI now:
223	secretUI := getSecretUI()
224
225	// check the device key for this user
226	key, index, err = matchingDeviceKey(m, secretUI, arg, me)
227	if err != nil {
228		return nil, 0, err
229	}
230	if key != nil {
231		return key, index, nil
232	}
233	m.Debug("getMatchingSecretKey: no matching device key found")
234
235	if !arg.PromptPaper {
236		m.Debug("UnboxBytes32Any/getMatchingSecretKey: not checking paper keys (promptPaper == false)")
237		return nil, 0, libkb.NoSecretKeyError{}
238	}
239
240	// check the paper keys for this user
241	key, index, err = matchingPaperKey(m, secretUI, arg, me)
242	if err != nil {
243		return nil, 0, err
244	}
245	if key != nil {
246		return key, index, nil
247	}
248
249	return nil, 0, libkb.NoSecretKeyError{}
250}
251
252// check cached keys for arg.Bundles match.
253func matchingCachedKey(m libkb.MetaContext, arg keybase1.UnboxBytes32AnyArg) (key libkb.GenericKey, index int, err error) {
254	// check device key first
255	dkey, err := m.ActiveDevice().EncryptionKey()
256	if err == nil && dkey != nil {
257		if n, ok := kidMatch(dkey, arg.Bundles); ok {
258			return dkey, n, nil
259		}
260	}
261
262	device := m.ActiveDevice().ProvisioningKey(m)
263	if device != nil {
264		pkey := device.EncryptionKey()
265		if n, ok := kidMatch(pkey, arg.Bundles); ok {
266			return pkey, n, nil
267		}
268	}
269	return nil, 0, nil
270}
271
272// check device key for arg.Bundles match.
273func matchingDeviceKey(m libkb.MetaContext, secretUI libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg, me *libkb.User) (key libkb.GenericKey, index int, err error) {
274	ekey, err := me.GetDeviceSubkey()
275	if err == nil {
276		if n, ok := kidMatch(ekey, arg.Bundles); ok {
277			// unlock this key
278			parg := libkb.SecretKeyPromptArg{
279				Ska: libkb.SecretKeyArg{
280					Me:      me,
281					KeyType: libkb.DeviceEncryptionKeyType,
282				},
283				SecretUI:       secretUI,
284				Reason:         arg.Reason,
285				UseCancelCache: true,
286			}
287			key, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, parg)
288			if err != nil {
289				return nil, 0, err
290			}
291			return key, n, nil
292		}
293
294		m.Debug("matchingDeviceKey: no match found for ekey in arg.Bundles")
295		logNoMatch(m, ekey, arg.Bundles)
296	} else {
297		m.Debug("matchingDeviceKey: ignoring error getting device subkey: %s", err)
298	}
299
300	return nil, 0, nil
301}
302
303// check all the user's paper keys for arg.Bundles match
304func matchingPaperKey(m libkb.MetaContext, secretUI libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg, me *libkb.User) (key libkb.GenericKey, index int, err error) {
305	cki := me.GetComputedKeyInfos()
306	if cki == nil {
307		return nil, 0, nil
308	}
309	var matchingPaper []*libkb.Device
310	for _, pdev := range cki.PaperDevices() {
311		enckey, err := me.GetComputedKeyFamily().GetEncryptionSubkeyForDevice(pdev.ID)
312		if err != nil {
313			return nil, 0, err
314		}
315		if _, ok := kidMatch(enckey, arg.Bundles); ok {
316			m.Debug("matching paper key: %s", *pdev.Description)
317			matchingPaper = append(matchingPaper, pdev)
318		}
319	}
320	if len(matchingPaper) == 0 {
321		m.Debug("no matching paper keys found")
322		return nil, 0, nil
323	}
324
325	phrase, err := libkb.GetPaperKeyForCryptoPassphrase(m, secretUI, arg.Reason, matchingPaper)
326	if err != nil {
327		return nil, 0, err
328	}
329	paperPhrase, err := libkb.NewPaperKeyPhraseCheckVersion(m, phrase)
330	if err != nil {
331		return nil, 0, err
332	}
333
334	bkarg := &PaperKeyGenArg{
335		Passphrase: paperPhrase,
336		SkipPush:   true,
337	}
338	bkeng := NewPaperKeyGen(m.G(), bkarg)
339	if err := RunEngine2(m, bkeng); err != nil {
340		return nil, 0, err
341	}
342
343	// find the index for the key they entered (and make sure the key they entered matches)
344	if n, ok := kidMatch(bkeng.EncKey(), arg.Bundles); ok {
345		m.ActiveDevice().CacheProvisioningKey(m, bkeng.DeviceWithKeys())
346		return bkeng.EncKey(), n, nil
347	}
348
349	return nil, 0, nil
350}
351
352func kidMatch(key libkb.GenericKey, bundles []keybase1.CiphertextBundle) (int, bool) {
353	if key == nil {
354		return -1, false
355	}
356	kid := key.GetKID()
357	for i, bundle := range bundles {
358		if kid.Equal(bundle.Kid) {
359			return i, true
360		}
361	}
362	return -1, false
363}
364
365func logNoMatch(m libkb.MetaContext, key libkb.GenericKey, bundles []keybase1.CiphertextBundle) {
366	if key == nil {
367		m.Debug("logNoMatch: key is nil")
368		return
369	}
370	kid := key.GetKID()
371	m.Debug("logNoMatch: desired kid: %s", kid)
372	for i, bundle := range bundles {
373		m.Debug("logNoMatch: kid %d: %s (%v)", i, bundle.Kid, kid.Equal(bundle.Kid))
374	}
375}
376