github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/sync_secret.go (about)

     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.
     6  package libkb
     7  
     8  import (
     9  	"fmt"
    10  	"strings"
    11  	"sync"
    12  
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  )
    15  
    16  type 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  
    26  type ServerPrivateKeyMap map[string]ServerPrivateKey
    27  
    28  type 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  
    39  func (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  
    51  type DeviceKeyMap map[keybase1.DeviceID]DeviceKey
    52  
    53  type 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  
    61  type SecretSyncer struct {
    62  	sync.Mutex
    63  	Contextified
    64  	dirty bool
    65  	keys  *ServerPrivateKeys
    66  }
    67  
    68  type DeviceTypeSet map[keybase1.DeviceTypeV2]bool
    69  
    70  var DefaultDeviceTypes = DeviceTypeSet{
    71  	keybase1.DeviceTypeV2_DESKTOP: true,
    72  	keybase1.DeviceTypeV2_MOBILE:  true,
    73  }
    74  
    75  var AllDeviceTypes = DeviceTypeSet{
    76  	keybase1.DeviceTypeV2_DESKTOP: true,
    77  	keybase1.DeviceTypeV2_MOBILE:  true,
    78  	keybase1.DeviceTypeV2_PAPER:   true,
    79  }
    80  
    81  func NewSecretSyncer(g *GlobalContext) *SecretSyncer {
    82  	return &SecretSyncer{
    83  		Contextified: NewContextified(g),
    84  	}
    85  }
    86  
    87  func (ss *SecretSyncer) Clear() error {
    88  	ss.keys = nil
    89  
    90  	return nil
    91  }
    92  
    93  func (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  
   122  func (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  
   158  func (ss *SecretSyncer) dbKey(uid keybase1.UID) DbKey {
   159  	return DbKeyUID(DBUserSecretKeys, uid)
   160  }
   161  
   162  func (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.
   177  func (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.
   191  func (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.
   210  func (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  
   222  func (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  
   229  func (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  
   241  func (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  
   254  func (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  
   263  func (ss *SecretSyncer) HasDevices() bool {
   264  	if ss.keys == nil {
   265  		return false
   266  	}
   267  	return len(ss.keys.Devices) > 0
   268  }
   269  
   270  func (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.
   281  func (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.
   296  func (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.
   305  func (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  
   329  func (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.
   341  func (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  
   351  func (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  
   362  func (ss *SecretSyncer) needsLogin(m MetaContext) bool { return true }
   363  
   364  func (d DeviceKey) ToLKSec() (LKSecServerHalf, error) {
   365  	return NewLKSecServerHalfFromHex(d.LksServerHalf)
   366  }