1package libkb
2
3import (
4	"encoding/base64"
5	"fmt"
6	"io"
7	"os"
8	"sync"
9	"time"
10
11	"github.com/keybase/client/go/kbcrypto"
12	keybase1 "github.com/keybase/client/go/protocol/keybase1"
13	"github.com/keybase/go-codec/codec"
14	context "golang.org/x/net/context"
15)
16
17type SKBKeyringFile struct {
18	Contextified
19	sync.Mutex
20	username NormalizedUsername
21	filename string
22	Blocks   []*SKB
23	fpIndex  map[PGPFingerprint]*SKB
24	kidIndex map[keybase1.KID]*SKB
25	dirty    bool
26}
27
28func NewSKBKeyringFile(g *GlobalContext, un NormalizedUsername) *SKBKeyringFile {
29	return &SKBKeyringFile{
30		Contextified: NewContextified(g),
31		username:     un,
32		filename:     g.SKBFilenameForUser(un),
33		fpIndex:      make(map[PGPFingerprint]*SKB),
34		kidIndex:     make(map[keybase1.KID]*SKB),
35		dirty:        false,
36	}
37}
38
39func (k *SKBKeyringFile) Load(ctx context.Context) (err error) {
40	k.Lock()
41	defer k.Unlock()
42	return k.loadLocked(ctx)
43}
44
45func (k *SKBKeyringFile) Username() NormalizedUsername {
46	return k.username
47}
48
49func (k *SKBKeyringFile) IsForUsername(un NormalizedUsername) bool {
50	return k.username.Eq(un)
51}
52
53func (k *SKBKeyringFile) MTime() (mtime time.Time, err error) {
54	k.Lock()
55	defer k.Unlock()
56	var fi os.FileInfo
57	fi, err = os.Stat(k.filename)
58	if err != nil {
59		return mtime, err
60	}
61	return fi.ModTime(), err
62}
63
64func (k *SKBKeyringFile) MarkDirty() {
65	k.Lock()
66	defer k.Unlock()
67	k.dirty = true
68}
69
70type skbPacket struct {
71	skb *SKB
72}
73
74// Okay to panic in Codec{Encode,Decode}Self, since the
75// encoder/decoder catches panics and turns them back into errors.
76
77func (s *skbPacket) CodecEncodeSelf(e *codec.Encoder) {
78	err := kbcrypto.EncodePacket(s.skb, e)
79	if err != nil {
80		panic(err)
81	}
82}
83
84func (s *skbPacket) CodecDecodeSelf(d *codec.Decoder) {
85	var skb SKB
86	err := kbcrypto.DecodePacket(d, &skb)
87	if err != nil {
88		panic(err)
89	}
90	s.skb = &skb
91}
92
93func encodeSKBPacketList(skbs []*SKB, w io.Writer) error {
94	ch := kbcrypto.CodecHandle()
95	encoder := codec.NewEncoder(w, ch)
96
97	packets := make([]skbPacket, len(skbs))
98	for i := range skbs {
99		packets[i].skb = skbs[i]
100	}
101
102	return encoder.Encode(packets)
103}
104
105func decodeSKBPacketList(r io.Reader, g *GlobalContext) ([]*SKB, error) {
106	ch := kbcrypto.CodecHandle()
107	decoder := codec.NewDecoder(r, ch)
108
109	var packets []skbPacket
110	err := decoder.Decode(&packets)
111	if err != nil {
112		return nil, err
113	}
114
115	skbs := make([]*SKB, len(packets))
116	for i, s := range packets {
117		s.skb.SetGlobalContext(g)
118		skbs[i] = s.skb
119	}
120	return skbs, nil
121}
122
123func (k *SKBKeyringFile) loadLocked(ctx context.Context) (err error) {
124	k.G().Log.CDebugf(ctx, "+ Loading SKB keyring: %s", k.filename)
125
126	file, err := os.OpenFile(k.filename, os.O_RDONLY, 0)
127	if err != nil {
128		if os.IsNotExist(err) {
129			k.G().Log.CDebugf(ctx, "| Keybase secret keyring doesn't exist: %s", k.filename)
130		} else {
131			k.G().Log.CWarningf(ctx, "Error opening %s: %s", k.filename, err)
132
133			MobilePermissionDeniedCheck(k.G(), err, fmt.Sprintf("skb keyring: %s", k.filename))
134		}
135		return err
136	}
137	defer func() {
138		closeErr := file.Close()
139		if err == nil {
140			err = closeErr
141		}
142	}()
143
144	stream := base64.NewDecoder(base64.StdEncoding, file)
145	skbs, err := decodeSKBPacketList(stream, k.G())
146	if err != nil {
147		return err
148	}
149
150	k.Blocks = skbs
151
152	k.G().Log.CDebugf(ctx, "- Loaded SKB keyring: %s -> %s", k.filename, ErrToOk(err))
153	return nil
154}
155
156func (k *SKBKeyringFile) addToIndexLocked(g GenericKey, b *SKB) {
157	if g == nil {
158		return
159	}
160	if fp := GetPGPFingerprintFromGenericKey(g); fp != nil {
161		k.fpIndex[*fp] = b
162	}
163	k.kidIndex[g.GetKID()] = b
164}
165
166func (k *SKBKeyringFile) Index() (err error) {
167	k.Lock()
168	defer k.Unlock()
169	return k.indexLocked()
170}
171
172func (k *SKBKeyringFile) indexLocked() (err error) {
173	for _, b := range k.Blocks {
174		var key GenericKey
175		key, err = b.GetPubKey()
176		if err != nil {
177			return
178		}
179		// Last-writer wins!
180		k.addToIndexLocked(key, b)
181	}
182	k.G().Log.Debug("| Indexed %d secret keys", len(k.Blocks))
183	return
184}
185
186func (k *SKBKeyringFile) SearchWithComputedKeyFamily(ckf *ComputedKeyFamily, ska SecretKeyArg) []*SKB {
187	k.Lock()
188	defer k.Unlock()
189
190	var kid keybase1.KID
191	k.G().Log.Debug("+ SKBKeyringFile.SearchWithComputedKeyFamily")
192	defer func() {
193		var res string
194		if kid.Exists() {
195			res = kid.String()
196		} else {
197			res = "<nil>"
198		}
199		k.G().Log.Debug("- SKBKeyringFile.SearchWithComputedKeyFamily -> %s\n", res)
200	}()
201	k.G().Log.Debug("| Searching %d possible blocks", len(k.Blocks))
202	var blocks []*SKB
203	for i := len(k.Blocks) - 1; i >= 0; i-- {
204		k.G().Log.Debug("| trying key index# -> %d", i)
205		if key, err := k.Blocks[i].GetPubKey(); err == nil && key != nil {
206			kid = key.GetKID()
207			active := ckf.GetKeyRole(kid)
208			k.G().Log.Debug("| Checking KID: %s -> %d", kid, int(active))
209			if !ska.KeyType.nonDeviceKeyMatches(key) {
210				k.G().Log.Debug("| Skipped, doesn't match type=%s", ska.KeyType)
211			} else if !KeyMatchesQuery(key, ska.KeyQuery, ska.ExactMatch) {
212				k.G().Log.Debug("| Skipped, doesn't match query=%s", ska.KeyQuery)
213
214			} else if active != DLGSibkey {
215				k.G().Log.Debug("| Skipped, active=%d", int(active))
216			} else {
217				blocks = append(blocks, k.Blocks[i])
218			}
219		} else {
220			k.G().Log.Debug("| failed --> %v", err)
221		}
222	}
223	return blocks
224}
225
226func (k *SKBKeyringFile) PushAndSave(skb *SKB) error {
227	k.Lock()
228	defer k.Unlock()
229	if err := k.pushLocked(skb); err != nil {
230		return err
231	}
232	return k.saveLocked()
233}
234
235func (k *SKBKeyringFile) HasPGPKeys() bool {
236	k.Lock()
237	defer k.Unlock()
238	return len(k.fpIndex) > 0
239}
240
241func (k *SKBKeyringFile) AllPGPBlocks() ([]*SKB, error) {
242	k.Lock()
243	defer k.Unlock()
244	var pgpBlocks []*SKB
245	for _, block := range k.Blocks {
246		k, err := block.GetPubKey()
247		if err != nil {
248			return nil, err
249		}
250		if fp := GetPGPFingerprintFromGenericKey(k); fp != nil {
251			pgpBlocks = append(pgpBlocks, block)
252		}
253	}
254	return pgpBlocks, nil
255}
256
257func (k *SKBKeyringFile) RemoveAllPGPBlocks() error {
258	k.Lock()
259	defer k.Unlock()
260	var blocks []*SKB
261	for _, block := range k.Blocks {
262		k, err := block.GetPubKey()
263		if err != nil {
264			return err
265		}
266		if fp := GetPGPFingerprintFromGenericKey(k); fp == nil {
267			blocks = append(blocks, block)
268		}
269	}
270	k.Blocks = blocks
271	k.fpIndex = make(map[PGPFingerprint]*SKB)
272	k.kidIndex = make(map[keybase1.KID]*SKB)
273	err := k.indexLocked()
274	if err != nil {
275		return err
276	}
277	k.dirty = true
278
279	return nil
280}
281
282func (k *SKBKeyringFile) Push(skb *SKB) error {
283	k.Lock()
284	defer k.Unlock()
285	return k.pushLocked(skb)
286}
287
288func (k *SKBKeyringFile) pushLocked(skb *SKB) error {
289	key, err := skb.GetPubKey()
290	if err != nil {
291		return fmt.Errorf("Failed to get pubkey: %s", err)
292	}
293	k.dirty = true
294	k.Blocks = append(k.Blocks, skb)
295	k.addToIndexLocked(key, skb)
296	return nil
297}
298
299func (k *SKBKeyringFile) Save() error {
300	k.Lock()
301	defer k.Unlock()
302	return k.saveLocked()
303}
304
305func (k *SKBKeyringFile) saveLocked() error {
306	if !k.dirty {
307		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: not dirty, so skipping save", k.filename)
308		return nil
309	}
310	if err := MakeParentDirs(k.G().Log, k.filename); err != nil {
311		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: failed to make parent dirs: %s", k.filename, err)
312		return err
313	}
314	k.G().Log.Debug("SKBKeyringFile: saveLocked %s: dirty, safe saving", k.filename)
315	if err := SafeWriteToFile(k.G().Log, k, 0); err != nil {
316		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: SafeWriteToFile error: %s", k.filename, err)
317		return err
318	}
319	k.dirty = false
320	k.G().Log.Debug("SKBKeyringFile: saveLocked success for %s", k.filename)
321	return nil
322}
323
324func (k *SKBKeyringFile) LookupByFingerprint(fp PGPFingerprint) *SKB {
325	k.Lock()
326	defer k.Unlock()
327	ret, ok := k.fpIndex[fp]
328	if !ok {
329		ret = nil
330	}
331	return ret
332}
333
334// FindSecretKey will, given a list of KIDs, find the first one in the
335// list that has a corresponding secret key in the keyring file.
336func (k *SKBKeyringFile) FindSecretKey(kids []keybase1.KID) (ret *SKB) {
337	k.Lock()
338	defer k.Unlock()
339	for _, kid := range kids {
340		if ret = k.lookupByKidLocked(kid); ret != nil {
341			return
342		}
343	}
344	return
345}
346
347func (k *SKBKeyringFile) LookupByKid(kid keybase1.KID) *SKB {
348	k.Lock()
349	defer k.Unlock()
350	return k.lookupByKidLocked(kid)
351}
352
353func (k *SKBKeyringFile) lookupByKidLocked(kid keybase1.KID) *SKB {
354	ret, ok := k.kidIndex[kid]
355	if !ok {
356		ret = nil
357	}
358	return ret
359}
360
361func (k *SKBKeyringFile) LoadAndIndex(ctx context.Context) error {
362	k.Lock()
363	defer k.Unlock()
364	err := k.loadLocked(ctx)
365	if err == nil {
366		err = k.indexLocked()
367	}
368	return err
369}
370
371// GetFilename is only called from within Save(), so it's called
372// with a lock. Needs to be called GetFilename() to meet the interface
373// required by SafeSaveToFile
374func (k *SKBKeyringFile) GetFilename() string { return k.filename }
375
376// WriteTo is similar to GetFilename described just above in terms of
377// locking discipline.
378func (k *SKBKeyringFile) WriteTo(w io.Writer) (n int64, err error) {
379	k.G().Log.Debug("+ SKBKeyringFile WriteTo")
380	defer k.G().Log.Debug("- SKBKeyringFile WriteTo")
381	b64 := base64.NewEncoder(base64.StdEncoding, w)
382	defer func() {
383		// explicitly check for error on Close:
384		if closeErr := b64.Close(); closeErr != nil {
385			k.G().Log.Warning("SKBKeyringFile: WriteTo b64.Close() error: %s", closeErr)
386			if err == nil {
387				n = 0
388				err = closeErr
389				return
390			}
391		}
392		k.G().Log.Debug("SKBKeyringFile: b64 stream closed successfully")
393	}()
394
395	if err := encodeSKBPacketList(k.Blocks, b64); err != nil {
396		k.G().Log.Warning("Encoding problem: %s", err)
397		return 0, err
398	}
399
400	return 0, nil
401}
402
403func (k *SKBKeyringFile) Bug3964Repair(m MetaContext, lks *LKSec, dkm DeviceKeyMap) (ret *SKBKeyringFile, serverHalfSet *LKSecServerHalfSet, err error) {
404	defer m.Trace("SKBKeyringFile#Bug3964Repair", &err)()
405
406	var newBlocks []*SKB
407	var hitBug3964 bool
408
409	m.Debug("| # of blocks=%d", len(k.Blocks))
410
411	for i, b := range k.Blocks {
412
413		if b.Priv.Data == nil {
414			m.Debug("| Null private data at block=%d", i)
415			newBlocks = append(newBlocks, b)
416			continue
417		}
418
419		if b.Priv.Encryption != LKSecVersion {
420			m.Debug("| Skipping non-LKSec encryption (%d) at block=%d", b.Priv.Encryption, i)
421			newBlocks = append(newBlocks, b)
422			continue
423		}
424
425		var decryption, reencryption []byte
426		var badMask LKSecServerHalf
427		decryption, badMask, err = lks.decryptForBug3964Repair(m, b.Priv.Data, dkm)
428		if err != nil {
429			m.Debug("| Decryption failed at block=%d; keeping as is (%s)", i, err)
430			newBlocks = append(newBlocks, b)
431			continue
432		}
433
434		if badMask.IsNil() {
435			m.Debug("| Nil badmask at block=%d", i)
436			newBlocks = append(newBlocks, b)
437			continue
438		}
439
440		hitBug3964 = true
441		m.Debug("| Hit bug 3964 at SKB block=%d", i)
442		if serverHalfSet == nil {
443			serverHalfSet = NewLKSecServerHalfSet()
444		}
445		serverHalfSet.Add(badMask)
446
447		reencryption, err = lks.Encrypt(m, decryption)
448		if err != nil {
449			m.Debug("| reencryption bug at block=%d", i)
450			return nil, nil, err
451		}
452
453		newSKB := &SKB{
454			Contextified: NewContextified(m.G()),
455			Pub:          b.Pub,
456			Type:         b.Type,
457			Priv: SKBPriv{
458				Encryption:           b.Priv.Encryption,
459				PassphraseGeneration: b.Priv.PassphraseGeneration,
460				Data:                 reencryption,
461			},
462		}
463
464		newBlocks = append(newBlocks, newSKB)
465	}
466	if !hitBug3964 {
467		return nil, nil, nil
468	}
469
470	ret = NewSKBKeyringFile(k.G(), k.username)
471	ret.dirty = true
472	ret.Blocks = newBlocks
473
474	err = ret.Index()
475	if err != nil {
476		return nil, nil, err
477	}
478
479	return ret, serverHalfSet, nil
480}
481