1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4// A module for syncing secrets with the server, such as SKB PGP keys,
5// and server-halves of our various secret keys.
6package libkb
7
8import (
9	"fmt"
10	"strings"
11	"sync"
12
13	keybase1 "github.com/keybase/client/go/protocol/keybase1"
14)
15
16type ServerPrivateKey struct {
17	Kid     string  `json:"kid"`
18	KeyType KeyType `json:"key_type"`
19	Bundle  string  `json:"bundle"`
20	Mtime   int     `json:"mtime"`
21	Ctime   int     `json:"ctime"`
22	KeyBits int     `json:"key_bits"`
23	KeyAlgo int     `json:"key_algo"`
24}
25
26type ServerPrivateKeyMap map[string]ServerPrivateKey
27
28type DeviceKey struct {
29	Type          keybase1.DeviceTypeV2 `json:"type"`
30	CTime         int64                 `json:"ctime"`
31	MTime         int64                 `json:"mtime"`
32	Description   string                `json:"name"`
33	Status        int                   `json:"status"`
34	LksServerHalf string                `json:"lks_server_half"`
35	PPGen         PassphraseGeneration  `json:"passphrase_generation"`
36	LastUsedTime  int64                 `json:"last_used_time"`
37}
38
39func (d DeviceKey) Display() string {
40	if d.Type == keybase1.DeviceTypeV2_PAPER {
41		// XXX not sure if we need to support our existing paper keys, but without this
42		// someone is surely going to complain:
43		if strings.HasPrefix(d.Description, "Paper Key") {
44			return d.Description
45		}
46		return fmt.Sprintf("Paper Key (%s...)", d.Description)
47	}
48	return d.Description
49}
50
51type DeviceKeyMap map[keybase1.DeviceID]DeviceKey
52
53type ServerPrivateKeys struct {
54	Status      APIStatus           `json:"status"`
55	Version     int                 `json:"version"`
56	Mtime       *int                `json:"mtime"`
57	PrivateKeys ServerPrivateKeyMap `json:"private_keys"` // note these are only PGP keys
58	Devices     DeviceKeyMap        `json:"devices"`
59}
60
61type SecretSyncer struct {
62	sync.Mutex
63	Contextified
64	dirty bool
65	keys  *ServerPrivateKeys
66}
67
68type DeviceTypeSet map[keybase1.DeviceTypeV2]bool
69
70var DefaultDeviceTypes = DeviceTypeSet{
71	keybase1.DeviceTypeV2_DESKTOP: true,
72	keybase1.DeviceTypeV2_MOBILE:  true,
73}
74
75var AllDeviceTypes = DeviceTypeSet{
76	keybase1.DeviceTypeV2_DESKTOP: true,
77	keybase1.DeviceTypeV2_MOBILE:  true,
78	keybase1.DeviceTypeV2_PAPER:   true,
79}
80
81func NewSecretSyncer(g *GlobalContext) *SecretSyncer {
82	return &SecretSyncer{
83		Contextified: NewContextified(g),
84	}
85}
86
87func (ss *SecretSyncer) Clear() error {
88	ss.keys = nil
89
90	return nil
91}
92
93func (ss *SecretSyncer) loadFromStorage(m MetaContext, uid keybase1.UID, useExpiration bool) (err error) {
94	var tmp ServerPrivateKeys
95	var found bool
96	found, err = ss.G().LocalDb.GetInto(&tmp, ss.dbKey(uid))
97	m.Debug("| loadFromStorage -> found=%v, err=%s", found, ErrToOk(err))
98	if err != nil {
99		return err
100	}
101	if !found {
102		m.Debug("| Loaded empty record set")
103		return nil
104	}
105	if ss.cachedSyncedSecretsOutOfDate(&tmp) {
106		m.Debug("| Synced secrets out of date")
107		return nil
108	}
109
110	// only set ss.keys to something if found.
111	//
112	// This is part of keybase-issues#1783:  an (old) user with a synced
113	// private key fell back to gpg instead of using a synced key.
114	//
115
116	m.Debug("| Loaded version %d", tmp.Version)
117	ss.keys = &tmp
118
119	return nil
120}
121
122func (ss *SecretSyncer) syncFromServer(m MetaContext, uid keybase1.UID, forceReload bool) (err error) {
123	hargs := HTTPArgs{}
124
125	if ss.keys != nil && !forceReload {
126		m.Debug("| adding version %d to fetch_private call", ss.keys.Version)
127		hargs.Add("version", I{ss.keys.Version})
128	}
129	var res *APIRes
130	res, err = ss.G().API.Get(m, APIArg{
131		Endpoint:    "key/fetch_private",
132		Args:        hargs,
133		SessionType: APISessionTypeREQUIRED,
134		RetryCount:  5, // It's pretty bad to fail this, so retry.
135	})
136	m.Debug("| syncFromServer -> %s", ErrToOk(err))
137	if err != nil {
138		return
139	}
140
141	var obj ServerPrivateKeys
142	if err = res.Body.UnmarshalAgain(&obj); err != nil {
143		return
144	}
145
146	m.Debug("| Returned object: {Status: %v, Version: %d, #pgpkeys: %d, #devices: %d}", obj.Status, obj.Version, len(obj.PrivateKeys), len(obj.Devices))
147	if forceReload || ss.keys == nil || obj.Version > ss.keys.Version {
148		m.Debug("| upgrade to version -> %d", obj.Version)
149		ss.keys = &obj
150		ss.dirty = true
151	} else {
152		m.Debug("| not changing synced keys: synced version %d not newer than existing version %d", obj.Version, ss.keys.Version)
153	}
154
155	return
156}
157
158func (ss *SecretSyncer) dbKey(uid keybase1.UID) DbKey {
159	return DbKeyUID(DBUserSecretKeys, uid)
160}
161
162func (ss *SecretSyncer) store(m MetaContext, uid keybase1.UID) (err error) {
163	if !ss.dirty {
164		return
165	}
166	if err = m.G().LocalDb.PutObj(ss.dbKey(uid), nil, ss.keys); err != nil {
167		return
168	}
169	ss.dirty = false
170	return
171}
172
173// FindActiveKey examines the synced keys, looking for one that's currently
174// active. The key will be chosen at random due to non-deterministic order of
175// FindActiveKeys output.
176// Returns ret=nil if none was found.
177func (ss *SecretSyncer) FindActiveKey(ckf *ComputedKeyFamily) (ret *SKB, err error) {
178	keys, err := ss.FindActiveKeys(ckf)
179	if err != nil {
180		return nil, err
181	}
182	if len(keys) == 0 {
183		return nil, nil
184	}
185	ss.G().Log.Debug("NOTE: calling SecretSyncer.FindActiveKey: returning first secret key from randomly ordered map", err)
186	return keys[0], nil
187}
188
189// FindActiveKey examines the synced keys, and returns keys that are currently
190// active.
191func (ss *SecretSyncer) FindActiveKeys(ckf *ComputedKeyFamily) (ret []*SKB, err error) {
192	ss.Lock()
193	defer ss.Unlock()
194
195	if ss.keys == nil {
196		return ret, nil
197	}
198	for _, key := range ss.keys.PrivateKeys {
199		keyRet, err := key.FindActiveKey(ss.G(), ckf)
200		if err != nil {
201			ss.G().Log.Debug("SecretSyncer.FindActiveKeys: error from key.FindActiveKey, skipping key: %s", err)
202		} else {
203			ret = append(ret, keyRet)
204		}
205	}
206	return ret, nil
207}
208
209// AllActiveKeys returns all the active synced PGP keys.
210func (ss *SecretSyncer) AllActiveKeys(ckf *ComputedKeyFamily) []*SKB {
211	ss.Lock()
212	defer ss.Unlock()
213	var res []*SKB
214	for _, key := range ss.keys.PrivateKeys {
215		if ret, _ := key.FindActiveKey(ss.G(), ckf); ret != nil {
216			res = append(res, ret)
217		}
218	}
219	return res
220}
221
222func (ss *SecretSyncer) FindPrivateKey(kid string) (ServerPrivateKey, bool) {
223	ss.Lock()
224	defer ss.Unlock()
225	k, ok := ss.keys.PrivateKeys[kid]
226	return k, ok
227}
228
229func (k *ServerPrivateKey) FindActiveKey(g *GlobalContext, ckf *ComputedKeyFamily) (ret *SKB, err error) {
230	kid := keybase1.KIDFromString(k.Kid)
231	if ckf.GetKeyRole(kid) != DLGSibkey {
232		return
233	}
234	if ret, err = DecodeArmoredSKBPacket(k.Bundle); err != nil {
235		return
236	}
237	ret.SetGlobalContext(g)
238	return ret, nil
239}
240
241func (ss *SecretSyncer) FindDevice(id keybase1.DeviceID) (DeviceKey, error) {
242	ss.Lock()
243	defer ss.Unlock()
244	if ss.keys == nil {
245		return DeviceKey{}, DeviceNotFoundError{"SecretSyncer", id, false}
246	}
247	dev, ok := ss.keys.Devices[id]
248	if !ok {
249		return DeviceKey{}, DeviceNotFoundError{"SecretSyncer", id, true}
250	}
251	return dev, nil
252}
253
254func (ss *SecretSyncer) AllDevices() DeviceKeyMap {
255	ss.Lock()
256	defer ss.Unlock()
257	if ss.keys == nil {
258		return nil
259	}
260	return ss.keys.Devices
261}
262
263func (ss *SecretSyncer) HasDevices() bool {
264	if ss.keys == nil {
265		return false
266	}
267	return len(ss.keys.Devices) > 0
268}
269
270func (ss *SecretSyncer) Devices() (DeviceKeyMap, error) {
271	ss.Lock()
272	defer ss.Unlock()
273	if ss.keys == nil {
274		return nil, fmt.Errorf("no keys")
275	}
276	return ss.keys.Devices, nil
277}
278
279// IsDeviceNameTaken returns true if a desktop or mobile device is
280// using a name already.
281func (ss *SecretSyncer) IsDeviceNameTaken(name string, includeTypesSet DeviceTypeSet) bool {
282	devs, err := ss.ActiveDevices(includeTypesSet)
283	if err != nil {
284		return false
285	}
286	for _, v := range devs {
287		if NameCmp(v.Description, name) {
288			return true
289		}
290	}
291	return false
292}
293
294// HasActiveDevice returns true if there is an active desktop or
295// mobile device available.
296func (ss *SecretSyncer) HasActiveDevice(includeTypesSet DeviceTypeSet) (bool, error) {
297	devs, err := ss.ActiveDevices(includeTypesSet)
298	if err != nil {
299		return false, err
300	}
301	return len(devs) > 0, nil
302}
303
304// ActiveDevices returns all the active desktop and mobile devices.
305func (ss *SecretSyncer) ActiveDevices(includeTypesSet DeviceTypeSet) (DeviceKeyMap, error) {
306	ss.Lock()
307	defer ss.Unlock()
308	if ss.keys == nil {
309		return nil, fmt.Errorf("no keys")
310	}
311
312	if includeTypesSet == nil {
313		return nil, fmt.Errorf("need valid includeTypesSet")
314	}
315
316	res := make(DeviceKeyMap)
317	for k, v := range ss.keys.Devices {
318		if v.Status != DeviceStatusActive {
319			continue
320		}
321
322		if includeTypesSet[v.Type] {
323			res[k] = v
324		}
325	}
326	return res, nil
327}
328
329func (ss *SecretSyncer) DumpPrivateKeys() {
330	ss.Lock()
331	defer ss.Unlock()
332	for s, key := range ss.keys.PrivateKeys {
333		ss.G().Log.Info("Private key: %s", s)
334		ss.G().Log.Info("  -- kid: %s, keytype: %d, bits: %d, algo: %d", key.Kid, key.KeyType, key.KeyBits, key.KeyAlgo)
335	}
336}
337
338// As we add more fields to the data we're caching here, we need to detect the
339// cases where our cached data is missing the new fields. We can extend this
340// function with more cases as we add more fields.
341func (ss *SecretSyncer) cachedSyncedSecretsOutOfDate(cached *ServerPrivateKeys) bool {
342	for _, dev := range cached.Devices {
343		if dev.LastUsedTime == 0 {
344			ss.G().Log.Debug("cachedSyncedSecretsOutOfDate noticed a cached device with no last used time")
345			return true
346		}
347	}
348	return false
349}
350
351func (k ServerPrivateKey) ToSKB(gc *GlobalContext) (*SKB, error) {
352	if k.KeyType != KeyTypeP3skbPrivate {
353		return nil, fmt.Errorf("invalid key type for skb conversion: %d", k.KeyType)
354	}
355	skb, err := DecodeArmoredSKBPacket(k.Bundle)
356	if err != nil {
357		return nil, err
358	}
359	return skb, nil
360}
361
362func (ss *SecretSyncer) needsLogin(m MetaContext) bool { return true }
363
364func (d DeviceKey) ToLKSec() (LKSecServerHalf, error) {
365	return NewLKSecServerHalfFromHex(d.LksServerHalf)
366}
367