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 }