github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/selfprovision.go (about) 1 package engine 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/keybase/client/go/libkb" 8 ) 9 10 type SelfProvisionEngine struct { 11 libkb.Contextified 12 DeviceName string 13 result error 14 lks *libkb.LKSec 15 User *libkb.User 16 perUserKeyring *libkb.PerUserKeyring 17 ekReboxer *ephemeralKeyReboxer 18 19 deviceWrapEng *DeviceWrap 20 } 21 22 // If a device is cloned, we can provision a new device from the current device 23 // to get out of the cloned state. 24 func NewSelfProvisionEngine(g *libkb.GlobalContext, deviceName string) *SelfProvisionEngine { 25 return &SelfProvisionEngine{ 26 Contextified: libkb.NewContextified(g), 27 DeviceName: deviceName, 28 } 29 } 30 31 func (e *SelfProvisionEngine) Name() string { 32 return "SelfProvision" 33 } 34 35 func (e *SelfProvisionEngine) Prereqs() Prereqs { 36 return Prereqs{} 37 } 38 39 func (e *SelfProvisionEngine) RequiredUIs() []libkb.UIKind { 40 return []libkb.UIKind{ 41 libkb.ProvisionUIKind, 42 libkb.LogUIKind, 43 libkb.SecretUIKind, 44 libkb.LoginUIKind, 45 } 46 } 47 48 func (e *SelfProvisionEngine) SubConsumers() []libkb.UIConsumer { 49 return []libkb.UIConsumer{ 50 &loginLoadUser{}, 51 } 52 } 53 54 func (e *SelfProvisionEngine) Result() error { 55 return e.result 56 } 57 58 func (e *SelfProvisionEngine) Run(mctx libkb.MetaContext) (err error) { 59 defer mctx.Trace("SelfProvisionEngine#Run", &err)() 60 return retryOnEphemeralRace(mctx, e.run) 61 } 62 63 func (e *SelfProvisionEngine) run(m libkb.MetaContext) (err error) { 64 m.G().LocalSigchainGuard().Set(m.Ctx(), "SelfProvisionEngine") 65 defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "SelfProvisionEngine") 66 67 if d, err := libkb.GetDeviceCloneState(m); err != nil { 68 return err 69 } else if !d.IsClone() { 70 return fmt.Errorf("to self provision, you must be a cloned device") 71 } 72 73 if err = m.G().SecretStore().PrimeSecretStores(m); err != nil { 74 return SecretStoreNotFunctionalError{err} 75 } 76 77 uv, _ := e.G().ActiveDevice.GetUsernameAndUserVersionIfValid(m) 78 // Pass the UV here so the passphrase stream is cached on the provisional 79 // login context 80 m = m.WithNewProvisionalLoginContextForUserVersionAndUsername(uv, e.G().Env.GetUsername()) 81 82 // From this point on, if there's an error, we abort the transaction. 83 defer func() { 84 if err == nil { 85 // cache the passphrase stream from the login context to the active 86 // device. 87 m.CommitProvisionalLogin() 88 } 89 }() 90 91 keys, err := e.loadUserAndActiveDeviceKeys(m) 92 if err != nil { 93 return err 94 } 95 96 e.ekReboxer = newEphemeralKeyReboxer() 97 98 // Make new device keys and sign them with current device keys 99 if err := e.provision(m, keys); err != nil { 100 return err 101 } 102 103 // Finish provisoning by calling SwitchConfigAndActiveDevice. we 104 // can't undo that, so do not error out after that. 105 if err := e.deviceWrapEng.SwitchConfigAndActiveDevice(m); err != nil { 106 return err 107 } 108 109 // Cleanup EKs belonging to the old device. 110 if deviceEKStorage := m.G().GetDeviceEKStorage(); deviceEKStorage != nil { 111 if err = deviceEKStorage.ForceDeleteAll(m, e.User.GetNormalizedName()); err != nil { 112 m.Debug("unable to remove old ephemeral keys: %v", err) 113 } 114 } 115 116 // Store and encrypt the new deviceEK with the new globally set 117 // active device. 118 if err := e.ekReboxer.storeEKs(m); err != nil { 119 m.Debug("unable to store ephemeral keys: %v", err) 120 } 121 122 verifyLocalStorage(m, e.User.GetNormalizedName().String(), e.User.GetUID()) 123 if err := e.syncSecretStore(m); err != nil { 124 m.Debug("unable to syncSecretStore: %v", err) 125 } 126 127 e.clearCaches(m) 128 e.sendNotification(m) 129 return nil 130 } 131 132 func (e *SelfProvisionEngine) loadUserAndActiveDeviceKeys(m libkb.MetaContext) (*libkb.DeviceWithKeys, error) { 133 // run the LoginLoadUser sub-engine to load a user 134 ueng := newLoginLoadUser(e.G(), e.G().Env.GetUsername().String()) 135 if err := RunEngine2(m, ueng); err != nil { 136 return nil, err 137 } 138 e.User = ueng.User() 139 pukRing, err := libkb.NewPerUserKeyring(e.G(), e.User.GetUID()) 140 if err != nil { 141 return nil, err 142 } 143 e.perUserKeyring = pukRing 144 145 keys, err := e.G().ActiveDevice.DeviceKeys() 146 if err != nil { 147 return nil, err 148 } 149 if _, err := keys.Populate(m); err != nil { 150 return nil, err 151 } 152 153 return keys, nil 154 } 155 156 func (e *SelfProvisionEngine) provision(m libkb.MetaContext, keys *libkb.DeviceWithKeys) error { 157 // Set the active device to be a special provisional key active device, 158 // which keeps a cached copy around for DeviceKeyGen, which requires it to 159 // be in memory. It also will establish a NIST so that API calls can 160 // proceed on behalf of the user. 161 m = m.WithProvisioningKeyActiveDevice(keys, e.User.ToUserVersion()) 162 163 // need lksec to store device keys locally 164 if err := e.fetchLKS(m, keys.EncryptionKey()); err != nil { 165 return err 166 } 167 return e.makeDeviceKeysWithSigner(m, keys.SigningKey()) 168 } 169 170 // copied from loginProvision 171 func (e *SelfProvisionEngine) fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) error { 172 gen, clientLKS, err := fetchLKS(m, encKey) 173 if err != nil { 174 return err 175 } 176 e.lks = libkb.NewLKSecWithClientHalf(clientLKS, gen, e.User.GetUID()) 177 return nil 178 } 179 180 // makeDeviceKeysWithSigner creates device keys given a signing key. 181 func (e *SelfProvisionEngine) makeDeviceKeysWithSigner(m libkb.MetaContext, signer libkb.GenericKey) error { 182 if err := e.ensureLKSec(m); err != nil { 183 return err 184 } 185 186 _, _, deviceType, err := m.G().GetUPAKLoader().LookupUsernameAndDevice(m.Ctx(), e.User.GetUID(), e.G().ActiveDevice.DeviceID()) 187 if err != nil { 188 return err 189 } 190 191 args := &DeviceWrapArgs{ 192 Me: e.User, 193 DeviceName: e.DeviceName, 194 DeviceType: deviceType, 195 Lks: e.lks, 196 IsEldest: false, // just to be explicit 197 IsSelfProvision: true, 198 PerUserKeyring: e.perUserKeyring, 199 EldestKID: e.User.GetEldestKID(), 200 Signer: signer, 201 EkReboxer: e.ekReboxer, 202 } 203 204 e.deviceWrapEng = NewDeviceWrap(m.G(), args) 205 return RunEngine2(m, e.deviceWrapEng) 206 } 207 208 // copied from loginProvision 209 // ensureLKSec ensures we have LKSec for saving device keys. 210 func (e *SelfProvisionEngine) ensureLKSec(m libkb.MetaContext) error { 211 if e.lks != nil { 212 return nil 213 } 214 215 pps, err := e.ppStream(m) 216 if err != nil { 217 return err 218 } 219 220 e.lks = libkb.NewLKSec(pps, e.User.GetUID()) 221 return nil 222 } 223 224 // copied from loginProvision 225 // ppStream gets the passphrase stream from the cache 226 func (e *SelfProvisionEngine) ppStream(m libkb.MetaContext) (*libkb.PassphraseStream, error) { 227 if m.LoginContext() == nil { 228 return nil, errors.New("SelfProvisionEngine: ppStream() -> nil ctx.LoginContext") 229 } 230 cached := m.LoginContext().PassphraseStreamCache() 231 if cached == nil { 232 return nil, errors.New("SelfProvisionEngine: ppStream() -> nil PassphraseStreamCache") 233 } 234 return cached.PassphraseStream(), nil 235 } 236 237 func (e *SelfProvisionEngine) syncSecretStore(m libkb.MetaContext) error { 238 // now store the secrets for our new device 239 encKey, err := m.ActiveDevice().EncryptionKey() 240 if err != nil { 241 return err 242 } 243 if err := e.fetchLKS(m, encKey); err != nil { 244 return err 245 } 246 247 // Get the LKS server half. 248 if err := e.lks.Load(m); err != nil { 249 return err 250 } 251 252 options := libkb.LoadAdvisorySecretStoreOptionsFromRemote(m) 253 return libkb.StoreSecretAfterLoginWithLKSWithOptions(m, e.User.GetNormalizedName(), e.lks, &options) 254 } 255 256 func (e *SelfProvisionEngine) clearCaches(mctx libkb.MetaContext) { 257 // Any caches that are encrypted with the old device key should be cleared 258 // out here so we can re-populate and encrypt with the new key. 259 if _, err := e.G().LocalChatDb.Nuke(); err != nil { 260 mctx.Debug("unable to nuke LocalChatDb: %v", err) 261 } 262 if ekLib := e.G().GetEKLib(); ekLib != nil { 263 ekLib.ClearCaches(mctx) 264 } 265 } 266 267 func (e *SelfProvisionEngine) sendNotification(m libkb.MetaContext) { 268 e.G().KeyfamilyChanged(m.Ctx(), e.User.GetUID()) 269 e.G().NotifyRouter.HandleLogin(m.Ctx(), string(e.G().Env.GetUsername())) 270 }