github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/passphrase_change.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 package engine 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 // PassphraseChange engine is used for changing the user's passphrase, either 15 // by replacement or by force. 16 type PassphraseChange struct { 17 arg *keybase1.PassphraseChangeArg 18 me *libkb.User 19 usingPaper bool 20 libkb.Contextified 21 } 22 23 // NewPassphraseChange creates a new engine for changing user passphrases, 24 // either if the current passphrase is known, or in "force" mode 25 func NewPassphraseChange(g *libkb.GlobalContext, a *keybase1.PassphraseChangeArg) *PassphraseChange { 26 return &PassphraseChange{ 27 arg: a, 28 Contextified: libkb.NewContextified(g), 29 } 30 } 31 32 // Name provides the name of the engine for the engine interface 33 func (c *PassphraseChange) Name() string { 34 return "PassphraseChange" 35 } 36 37 // Prereqs returns engine prereqs 38 func (c *PassphraseChange) Prereqs() Prereqs { 39 if c.arg.Force { 40 return Prereqs{} 41 } 42 43 return Prereqs{Device: true} 44 } 45 46 // RequiredUIs returns the required UIs. 47 func (c *PassphraseChange) RequiredUIs() []libkb.UIKind { 48 return []libkb.UIKind{ 49 libkb.SecretUIKind, 50 } 51 } 52 53 // SubConsumers requires the other UI consumers of this engine 54 func (c *PassphraseChange) SubConsumers() []libkb.UIConsumer { 55 return []libkb.UIConsumer{ 56 &PaperKeyGen{}, 57 } 58 } 59 60 // Run the engine 61 func (c *PassphraseChange) Run(m libkb.MetaContext) (err error) { 62 63 m = m.WithLogTag("PPCHNG") 64 65 defer m.Trace("PassphraseChange#Run", &err)() 66 defer func() { 67 m.G().SKBKeyringMu.Unlock() 68 }() 69 m.G().SKBKeyringMu.Lock() 70 m.Debug("| Acquired SKBKeyringMu mutex") 71 72 if len(c.arg.Passphrase) < libkb.MinPassphraseLength { 73 return libkb.PassphraseError{Msg: "too short"} 74 } 75 76 if err = c.loadMe(); err != nil { 77 return 78 } 79 80 if _, w := m.ActiveDevice().SyncSecrets(m); w != nil { 81 m.Debug("| failed to run secret syncer: %s", w) 82 } 83 84 m = m.WithNewProvisionalLoginContextForUser(c.me) 85 86 if c.arg.Force { 87 err = c.runForcedUpdate(m) 88 } else { 89 err = c.runStandardUpdate(m) 90 } 91 if err != nil { 92 return err 93 } 94 95 // If the passphrase changes, it's known. Do this in case we aren't getting 96 // gregors for some reason (standalone or test) - it will at least update 97 // this device. 98 libkb.MaybeSavePassphraseState(m, keybase1.PassphraseState_KNOWN) 99 100 // We used to sync secrets here, but sync secrets in runForceUpdate 101 // or runStandardUpdate, since the temporary login information won't 102 // persist past the scope of these functions. 103 return nil 104 } 105 106 // findPaperKeys checks if the user has paper keys. If he/she 107 // does, it prompts for a paper key phrase. This is used to 108 // regenerate paper keys, which are then matched against the 109 // paper keys found in the keyfamily. 110 func (c *PassphraseChange) findPaperKeys(m libkb.MetaContext) (*libkb.DeviceWithKeys, error) { 111 kp, err := findPaperKeys(m, c.me) 112 if err != nil { 113 m.Debug("findPaperKeys error: %s", err) 114 return nil, err 115 } 116 m.Debug("findPaperKeys success") 117 c.usingPaper = true 118 return kp, nil 119 } 120 121 // findUpdateKeys looks for keys to perform the passphrase update. 122 // The first choice is device keys. If that fails, it will look 123 // for backup keys. If backup keys are necessary, then it will 124 // also log the user in with the backup keys. 125 func (c *PassphraseChange) findUpdateDevice(m libkb.MetaContext) (ad *libkb.ActiveDevice, err error) { 126 defer m.Trace("PassphraseChange#findUpdateDevice", &err)() 127 if ad = m.G().ActiveDevice; ad.Valid() { 128 m.Debug("| returning globally active device key") 129 return ad, nil 130 } 131 kp, err := c.findPaperKeys(m) 132 if err != nil { 133 m.Debug("| error fetching paper keys") 134 return nil, err 135 } 136 ad = libkb.NewActiveDeviceWithDeviceWithKeys(m, m.CurrentUserVersion(), kp) 137 m.Debug("| installing paper key as thread-local active device") 138 return ad, nil 139 } 140 141 func (c *PassphraseChange) forceUpdatePassphrase(m libkb.MetaContext, sigKey libkb.GenericKey, ppGen libkb.PassphraseGeneration, oldClientHalf libkb.LKSecClientHalf) (err error) { 142 143 defer m.Trace("PassphraseChange#forceUpdatePassphrase", &err)() 144 145 // Don't update server-synced pgp keys when recovering. 146 // This will render any server-synced pgp keys unrecoverable from the server. 147 // TODO would it responsible to ask the server to delete them? 148 pgpKeys, nPgpKeysLost, err := c.findAndDecryptPrivatePGPKeysLossy(m) 149 if err != nil { 150 return err 151 } 152 153 if nPgpKeysLost > 0 { 154 m.Debug("PassphraseChange.runForcedUpdate: Losing %v synced keys", nPgpKeysLost) 155 } 156 157 // Ready the update argument; almost done, but we need some more stuff. 158 payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, ppGen) 159 if err != nil { 160 return err 161 } 162 163 // get the new passphrase hash out of the args 164 pwh, ok := payload["pwh"].(string) 165 if !ok || len(pwh) == 0 { 166 return errors.New("no pwh found in common args") 167 } 168 169 // get the new PDPKA5 KID out of the args 170 pdpka5kid, ok := payload["pdpka5_kid"].(string) 171 if !ok || len(pdpka5kid) == 0 { 172 return errors.New("no pdpka5kid found in common args") 173 } 174 175 // Generate a signature with our unlocked sibling key from device. 176 proof, err := c.me.UpdatePassphraseProof(m, sigKey, pwh, ppGen+1, pdpka5kid) 177 if err != nil { 178 return err 179 } 180 181 sig, _, _, err := libkb.SignJSON(proof, sigKey) 182 if err != nil { 183 return err 184 } 185 payload["sig"] = sig 186 payload["signing_kid"] = sigKey.GetKID() 187 188 postArg := libkb.APIArg{ 189 Endpoint: "passphrase/sign", 190 SessionType: libkb.APISessionTypeREQUIRED, 191 JSONPayload: payload, 192 } 193 194 // Important to pass a MetaContext here to pick up the provisional login context 195 // or an ActiveDevice that is thread-local. 196 _, err = m.G().API.PostJSON(m, postArg) 197 if err != nil { 198 return fmt.Errorf("api post to passphrase/sign error: %s", err) 199 } 200 return nil 201 } 202 203 // 1. Get keys for decryption and signing 204 // 2. If necessary, log in with backup keys 205 // 3. Get lks client half from server 206 // 4. Post an update passphrase proof 207 func (c *PassphraseChange) runForcedUpdate(m libkb.MetaContext) (err error) { 208 defer m.Trace("PassphraseChange#runForcedUpdate", &err)() 209 210 ad, err := c.findUpdateDevice(m) 211 if err != nil { 212 return 213 } 214 if ad == nil { 215 return libkb.NoSecretKeyError{} 216 } 217 218 enc, err := ad.EncryptionKey() 219 if err != nil { 220 return err 221 } 222 sig, err := ad.SigningKey() 223 if err != nil { 224 return err 225 } 226 227 m = m.WithActiveDevice(ad) 228 ppGen, oldClientHalf, err := fetchLKS(m, enc) 229 if err != nil { 230 return 231 } 232 233 err = c.forceUpdatePassphrase(m, sig, ppGen, oldClientHalf) 234 if err != nil { 235 return err 236 } 237 238 _, err = m.SyncSecrets() 239 if err != nil { 240 return err 241 } 242 243 m = m.WithGlobalActiveDevice() 244 // Reset passphrase stream cache so that subsequent updates go through 245 // without a problem (see CORE-3933) 246 m.ActiveDevice().ClearCaches() 247 248 return nil 249 } 250 251 // runStandardUpdate is for when the user knows the current password. 252 func (c *PassphraseChange) runStandardUpdate(m libkb.MetaContext) (err error) { 253 254 defer m.Trace("PassphraseChange.runStandardUpdate", &err)() 255 256 var ppStream *libkb.PassphraseStream 257 if len(c.arg.OldPassphrase) == 0 { 258 ppStream, err = libkb.GetPassphraseStreamViaPromptInLoginContext(m) 259 } else { 260 ppStream, err = libkb.VerifyPassphraseGetStreamInLoginContext(m, c.arg.OldPassphrase) 261 } 262 if err != nil { 263 return err 264 } 265 266 pgpKeys, err := c.findAndDecryptPrivatePGPKeys(m) 267 if err != nil { 268 return err 269 } 270 271 gen := m.PassphraseStream().Generation() 272 oldClientHalf := m.PassphraseStream().LksClientHalf() 273 274 payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, gen) 275 if err != nil { 276 return err 277 } 278 279 lp, err := libkb.ComputeLoginPackage2(m, ppStream) 280 if err != nil { 281 return err 282 } 283 284 payload["ppgen"] = gen 285 payload["old_pdpka4"] = lp.PDPKA4() 286 payload["old_pdpka5"] = lp.PDPKA5() 287 288 postArg := libkb.APIArg{ 289 Endpoint: "passphrase/replace", 290 SessionType: libkb.APISessionTypeREQUIRED, 291 JSONPayload: payload, 292 } 293 294 _, err = c.G().API.PostJSON(m, postArg) 295 if err != nil { 296 return err 297 } 298 299 _, err = m.SyncSecrets() 300 if err != nil { 301 return err 302 } 303 304 // Reset the passphrase stream cache on the global Active Device, since if it exists, 305 // it was for a previous version of the passphrase. 306 m = m.WithGlobalActiveDevice() 307 m.ActiveDevice().ClearCaches() 308 309 return nil 310 } 311 312 func (c *PassphraseChange) commonArgs(m libkb.MetaContext, oldClientHalf libkb.LKSecClientHalf, pgpKeys []libkb.GenericKey, existingGen libkb.PassphraseGeneration) (libkb.JSONPayload, error) { 313 314 salt, err := c.me.GetSalt() 315 if err != nil { 316 return nil, err 317 } 318 319 tsec, newPPStream, err := libkb.StretchPassphrase(c.G(), c.arg.Passphrase, salt) 320 if err != nil { 321 return nil, err 322 } 323 newPWH := newPPStream.PWHash() 324 newClientHalf := newPPStream.LksClientHalf() 325 pdpka5kid, err := newPPStream.PDPKA5KID() 326 if err != nil { 327 return nil, err 328 } 329 330 mask := oldClientHalf.ComputeMask(newClientHalf) 331 332 lksch := make(map[keybase1.KID]string) 333 devices := c.me.GetComputedKeyFamily().GetAllDevices() 334 for _, dev := range devices { 335 if !dev.IsActive() { 336 continue 337 } 338 key, err := c.me.GetComputedKeyFamily().GetEncryptionSubkeyForDevice(dev.ID) 339 if err != nil { 340 return nil, err 341 } 342 ctext, err := key.EncryptToString(newClientHalf.Bytes(), nil) 343 if err != nil { 344 return nil, err 345 } 346 lksch[key.GetKID()] = ctext 347 } 348 349 payload := make(libkb.JSONPayload) 350 payload["pwh"] = libkb.HexArg(newPWH).String() 351 payload["pwh_version"] = libkb.ClientTriplesecVersion 352 payload["lks_mask"] = mask.EncodeToHex() 353 payload["lks_client_halves"] = lksch 354 payload["pdpka5_kid"] = pdpka5kid.String() 355 356 var encodedKeys []string 357 for _, key := range pgpKeys { 358 encoded, err := c.encodePrivatePGPKey(key, tsec, existingGen+1) 359 if err != nil { 360 return nil, err 361 } 362 encodedKeys = append(encodedKeys, encoded) 363 } 364 payload["private_keys"] = encodedKeys 365 366 return payload, nil 367 } 368 369 func (c *PassphraseChange) loadMe() (err error) { 370 c.me, err = libkb.LoadMe(libkb.NewLoadUserForceArg(c.G())) 371 return 372 } 373 374 // findAndDecryptPrivatePGPKeys gets the user's private pgp keys if any exist and decrypts them. 375 func (c *PassphraseChange) findAndDecryptPrivatePGPKeys(m libkb.MetaContext) ([]libkb.GenericKey, error) { 376 377 var keyList []libkb.GenericKey 378 379 // Using a paper key makes TripleSec-synced keys unrecoverable 380 if c.usingPaper { 381 m.Debug("using a paper key, thus TripleSec-synced keys are unrecoverable") 382 return keyList, nil 383 } 384 385 // Only use the synced secret keys: 386 blocks, err := c.me.AllSyncedSecretKeys(m) 387 if err != nil { 388 return nil, err 389 } 390 391 secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName()) 392 393 for _, block := range blocks { 394 parg := m.SecretKeyPromptArg(libkb.SecretKeyArg{}, "passphrase change") 395 key, err := block.PromptAndUnlock(m, parg, secretRetriever, c.me) 396 if err != nil { 397 return nil, err 398 } 399 keyList = append(keyList, key) 400 } 401 402 return keyList, nil 403 } 404 405 // findAndDecryptPrivatePGPKeysLossy gets the user's private pgp keys if any exist and attempts 406 // to decrypt them without prompting the user. If any fail to decrypt, they are silently not returned. 407 // The second return value is the number of keys which were not decrypted. 408 func (c *PassphraseChange) findAndDecryptPrivatePGPKeysLossy(m libkb.MetaContext) ([]libkb.GenericKey, int, error) { 409 410 var keyList []libkb.GenericKey 411 nLost := 0 412 413 // Only use the synced secret keys: 414 blocks, err := c.me.AllSyncedSecretKeys(m) 415 if err != nil { 416 return nil, 0, err 417 } 418 419 secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName()) 420 421 for _, block := range blocks { 422 key, err := block.UnlockNoPrompt(m, secretRetriever) 423 if err == nil { 424 keyList = append(keyList, key) 425 } else { 426 if err != libkb.ErrUnlockNotPossible { 427 return nil, 0, err 428 } 429 nLost++ 430 m.Debug("findAndDecryptPrivatePGPKeysLossy: ignoring failure to decrypt key without prompt") 431 } 432 } 433 434 return keyList, nLost, nil 435 } 436 437 // encodePrivatePGPKey encrypts key with tsec and armor-encodes it. 438 // It includes the passphrase generation in the data. 439 func (c *PassphraseChange) encodePrivatePGPKey(key libkb.GenericKey, tsec libkb.Triplesec, gen libkb.PassphraseGeneration) (string, error) { 440 skb, err := libkb.ToServerSKB(c.G(), key, tsec, gen) 441 if err != nil { 442 return "", err 443 } 444 445 return skb.ArmoredEncode() 446 }