github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/bootstrap_active_device.go (about) 1 package libkb 2 3 import ( 4 "fmt" 5 6 "github.com/keybase/client/go/protocol/keybase1" 7 ) 8 9 type LoadUnlockedDeviceKeysMode int 10 11 const ( 12 // Normal checks the cache and polls if it is stale. StaleOK checks the cache 13 // and polls if it is empty. Offline uses the cache and errors if it is empty. 14 LoadUnlockedDeviceKeysModeNormal LoadUnlockedDeviceKeysMode = iota 15 LoadUnlockedDeviceKeysModeStaleOK 16 LoadUnlockedDeviceKeysModeOffline 17 ) 18 19 func loadAndUnlockKey(m MetaContext, kr *SKBKeyringFile, secretStore SecretStore, uid keybase1.UID, kid keybase1.KID) (key GenericKey, err error) { 20 defer m.Trace(fmt.Sprintf("loadAndUnlockKey(%s)", kid), &err)() 21 22 locked := kr.LookupByKid(kid) 23 if locked == nil { 24 m.Debug("loadAndUnlockKey: no locked key for %s", kid) 25 return nil, NoKeyError{fmt.Sprintf("no key for %s", kid)} 26 } 27 locked.SetUID(uid) 28 unlocked, err := locked.UnlockNoPrompt(m, secretStore) 29 if err != nil { 30 m.Debug("Failed to unlock key %s: %s", kid, err) 31 return nil, err 32 } 33 return unlocked, err 34 } 35 36 // BootstrapActiveDeviceFromConfig takes the user's config.json, keys.mpack file and 37 // secret store to populate ActiveDevice, and to have all credentials necessary 38 // to sign NIST tokens, allowing the user to act as if "logged in". Will return 39 // nil if everything work, LoginRequiredError if a real "login" is required to 40 // make the app work, and various errors on unexpected failures. 41 func BootstrapActiveDeviceFromConfig(m MetaContext, online bool) (uid keybase1.UID, err error) { 42 uid, err = bootstrapActiveDeviceFromConfigReturnRawError(m, online, keybase1.UID("")) 43 err = fixupBootstrapError(err) 44 return uid, err 45 } 46 47 func bootstrapActiveDeviceFromConfigReturnRawError(m MetaContext, online bool, assertUID keybase1.UID) (uid keybase1.UID, err error) { 48 uid = m.G().Env.GetUID() 49 if uid.IsNil() { 50 return uid, NoUIDError{} 51 } 52 if assertUID.Exists() && !assertUID.Equal(uid) { 53 return uid, NewUIDMismatchError(fmt.Sprintf("wanted %s but got %s", assertUID, uid)) 54 } 55 deviceID := m.G().Env.GetDeviceIDForUID(uid) 56 if deviceID.IsNil() { 57 return uid, NoDeviceError{fmt.Sprintf("no device in config for UID=%s", uid)} 58 } 59 err = bootstrapActiveDeviceReturnRawError(m, uid, deviceID, online) 60 return uid, err 61 } 62 63 func isBootstrapLoggedOutError(err error) bool { 64 if _, ok := err.(NoUIDError); ok { 65 return true 66 } 67 if err == ErrUnlockNotPossible { 68 return true 69 } 70 return false 71 } 72 73 func fixupBootstrapError(err error) error { 74 if err == nil { 75 return nil 76 } 77 if isBootstrapLoggedOutError(err) { 78 return LoginRequiredError{err.Error()} 79 } 80 return err 81 } 82 83 func bootstrapActiveDeviceReturnRawError(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, online bool) (err error) { 84 defer m.Trace(fmt.Sprintf("bootstrapActiveDeviceReturnRaw(%s,%s)", uid, deviceID), &err)() 85 86 ad := m.ActiveDevice() 87 if ad.IsValidFor(uid, deviceID) { 88 m.Debug("active device is current") 89 return nil 90 } 91 mode := LoadUnlockedDeviceKeysModeNormal 92 if !online { 93 mode = LoadUnlockedDeviceKeysModeOffline 94 } 95 uv, sib, sub, deviceName, err := LoadUnlockedDeviceKeys(m, uid, deviceID, mode) 96 if err != nil { 97 return err 98 } 99 err = m.SetActiveDevice(uv, deviceID, sib, sub, deviceName, KeychainModeOS) 100 return err 101 } 102 103 func LoadUnlockedDeviceKeys(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, mode LoadUnlockedDeviceKeysMode) (uv keybase1.UserVersion, sib GenericKey, sub GenericKey, deviceName string, err error) { 104 defer m.Trace(fmt.Sprintf("LoadUnlockedDeviceKeys(uid=%q)", uid), &err)() 105 106 // use the UPAKLoader with StaleOK, CachedOnly in order to get cached upak 107 arg := NewLoadUserArgWithMetaContext(m).WithUID(uid).WithPublicKeyOptional() 108 109 if mode == LoadUnlockedDeviceKeysModeOffline { 110 arg = arg.WithStaleOK(true).WithCachedOnly(true) 111 } else if mode == LoadUnlockedDeviceKeysModeStaleOK { 112 arg = arg.WithStaleOK(true) 113 } 114 upak, _, err := m.G().GetUPAKLoader().LoadV2(arg) 115 if err != nil { 116 m.Debug("BootstrapActiveDevice: upak.Load err: %s", err) 117 return uv, nil, nil, deviceName, err 118 } 119 120 if upak.Current.Status == keybase1.StatusCode_SCDeleted { 121 m.Debug("User %s was deleted", uid) 122 return uv, nil, nil, deviceName, UserDeletedError{} 123 } 124 125 device := upak.Current.FindSigningDeviceKey(deviceID) 126 if device == nil { 127 m.Debug("BootstrapActiveDevice: no sibkey found for device %s", deviceID) 128 return uv, nil, nil, deviceName, NoKeyError{"no signing device key found for user"} 129 } 130 131 if device.Base.Revocation != nil { 132 m.Debug("BootstrapActiveDevice: device %s was revoked", deviceID) 133 return uv, nil, nil, deviceName, NewKeyRevokedError("active device") 134 } 135 136 sibkeyKID := device.Base.Kid 137 deviceName = device.DeviceDescription 138 139 subkeyKID := upak.Current.FindEncryptionKIDFromSigningKID(sibkeyKID) 140 if subkeyKID.IsNil() { 141 m.Debug("BootstrapActiveDevice: no subkey found for device: %s", deviceID) 142 return uv, nil, nil, deviceName, NoKeyError{"no encryption device key found for user"} 143 } 144 145 // load the keyring file 146 username := NewNormalizedUsername(upak.Current.Username) 147 kr, err := LoadSKBKeyring(m, username) 148 if err != nil { 149 m.Debug("BootstrapActiveDevice: error loading keyring for %s: %s", username, err) 150 return uv, nil, nil, deviceName, err 151 } 152 153 secretStore := NewSecretStore(m, username) 154 sib, err = loadAndUnlockKey(m, kr, secretStore, uid, sibkeyKID) 155 if err != nil { 156 return uv, nil, nil, deviceName, err 157 } 158 sub, err = loadAndUnlockKey(m, kr, secretStore, uid, subkeyKID) 159 if err != nil { 160 return uv, nil, nil, deviceName, err 161 } 162 163 return upak.Current.ToUserVersion(), sib, sub, deviceName, nil 164 } 165 166 func LoadProvisionalActiveDevice(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, online bool) (ret *ActiveDevice, err error) { 167 defer m.Trace("LoadProvisionalActiveDevice", &err)() 168 mode := LoadUnlockedDeviceKeysModeStaleOK 169 if !online { 170 mode = LoadUnlockedDeviceKeysModeOffline 171 } 172 uv, sib, sub, deviceName, err := LoadUnlockedDeviceKeys(m, uid, deviceID, mode) 173 if err != nil { 174 return nil, err 175 } 176 ret = NewProvisionalActiveDevice(m, uv, deviceID, sib, sub, deviceName, KeychainModeOS) 177 return ret, nil 178 } 179 180 // BootstrapActiveDeviceWithMetaContext will setup an ActiveDevice with a NIST Factory 181 // for the caller. The m.loginContext passed through isn't really needed 182 // for anything aside from assertions, but as we phase out LoginState, we'll 183 // leave it here so that assertions in LoginState can still pass. 184 func BootstrapActiveDeviceWithMetaContext(m MetaContext) (ok bool, uid keybase1.UID, err error) { 185 uid, err = BootstrapActiveDeviceFromConfig(m, true) 186 ok = false 187 if err == nil { 188 ok = true 189 } else if _, isLRE := err.(LoginRequiredError); isLRE { 190 err = nil 191 } 192 return ok, uid, err 193 } 194 195 // BootstrapActiveDeviceWithMetaContextAndAssertUID will setup an ActiveDevice with a NIST Factory 196 // for the caller. It only works if we're logged in as the given UID 197 func BootstrapActiveDeviceWithMetaContextAndAssertUID(m MetaContext, uid keybase1.UID) (ok bool, err error) { 198 _, err = bootstrapActiveDeviceFromConfigReturnRawError(m, true, uid) 199 switch err.(type) { 200 case nil: 201 return true, nil 202 case LoginRequiredError: 203 return false, nil 204 case UIDMismatchError: 205 return false, nil 206 case NoUIDError: 207 return false, nil 208 default: 209 if err == ErrUnlockNotPossible { 210 return false, nil 211 } 212 return false, err 213 } 214 }