github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/login_provision.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 "sort" 10 11 "golang.org/x/net/context" 12 13 "github.com/keybase/client/go/kex2" 14 "github.com/keybase/client/go/libkb" 15 keybase1 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 // loginProvision is an engine that will provision the current 19 // device. Only the Login engine should run it. 20 type loginProvision struct { 21 libkb.Contextified 22 arg *loginProvisionArg 23 lks *libkb.LKSec 24 signingKey libkb.GenericKey 25 encryptionKey libkb.NaclDHKeyPair 26 gpgCli gpgInterface 27 username string 28 devname string 29 hasPGP bool 30 hasDevice bool 31 perUserKeyring *libkb.PerUserKeyring 32 33 skippedLogin bool 34 resetComplete bool 35 } 36 37 // gpgInterface defines the portions of gpg client that provision 38 // needs. This allows tests to stub out gpg client calls. 39 type gpgInterface interface { 40 ImportKey(mctx libkb.MetaContext, secret bool, fp libkb.PGPFingerprint, tty string) (*libkb.PGPKeyBundle, error) 41 Index(mctx libkb.MetaContext, secret bool, query string) (ki *libkb.GpgKeyIndex, w libkb.Warnings, err error) 42 } 43 44 type loginProvisionArg struct { 45 DeviceType keybase1.DeviceTypeV2 // desktop or mobile 46 ClientType keybase1.ClientType 47 User *libkb.User 48 49 // Used for non-interactive provisioning 50 PaperKey string 51 DeviceName string 52 53 // Used in tests for reproducible key generation 54 naclSigningKeyPair libkb.NaclKeyPair 55 naclEncryptionKeyPair libkb.NaclKeyPair 56 } 57 58 // newLoginProvision creates a loginProvision engine. 59 func newLoginProvision(g *libkb.GlobalContext, arg *loginProvisionArg) *loginProvision { 60 return &loginProvision{ 61 Contextified: libkb.NewContextified(g), 62 arg: arg, 63 } 64 } 65 66 // Name is the unique engine name. 67 func (e *loginProvision) Name() string { 68 return "loginProvision" 69 } 70 71 // GetPrereqs returns the engine prereqs. 72 func (e *loginProvision) Prereqs() Prereqs { 73 return Prereqs{} 74 } 75 76 // RequiredUIs returns the required UIs. 77 func (e *loginProvision) RequiredUIs() []libkb.UIKind { 78 return []libkb.UIKind{ 79 libkb.ProvisionUIKind, 80 libkb.LoginUIKind, 81 libkb.SecretUIKind, 82 libkb.GPGUIKind, 83 } 84 } 85 86 // SubConsumers returns the other UI consumers for this engine. 87 func (e *loginProvision) SubConsumers() []libkb.UIConsumer { 88 return []libkb.UIConsumer{ 89 &DeviceWrap{}, 90 &PaperKeyPrimary{}, 91 &AccountReset{}, 92 } 93 } 94 95 // Run starts the engine. 96 func (e *loginProvision) Run(m libkb.MetaContext) error { 97 m.G().LocalSigchainGuard().Set(m.Ctx(), "loginProvision") 98 defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "loginProvision") 99 100 if err := e.checkArg(); err != nil { 101 return err 102 } 103 104 if err := m.G().SecretStore().PrimeSecretStores(m); err != nil { 105 return SecretStoreNotFunctionalError{err} 106 } 107 108 var err error 109 e.perUserKeyring, err = libkb.NewPerUserKeyring(m.G(), e.arg.User.GetUID()) 110 if err != nil { 111 return err 112 } 113 114 // based on information in e.arg.User, route the user 115 // through the provisioning options. 116 if err := e.route(m); err != nil { 117 switch err.(type) { 118 case libkb.APINetError: 119 m.Debug("provision failed with an APINetError: %s, returning ProvisionFailedOfflineError", err) 120 return libkb.ProvisionFailedOfflineError{} 121 default: 122 return err 123 } 124 } 125 if e.skippedLogin || e.resetComplete { 126 return nil 127 } 128 129 // e.route is point of no return. If it succeeds, it means that 130 // config has already been written and there is no way to roll 131 // back. 132 133 _ = e.displaySuccess(m) 134 135 m.G().KeyfamilyChanged(m.Ctx(), e.arg.User.GetUID()) 136 137 // check to make sure local files stored correctly 138 verifyLocalStorage(m, e.username, e.arg.User.GetUID()) 139 140 // initialize a stellar wallet for the user if they don't already have one. 141 m.G().LocalSigchainGuard().Clear(m.Ctx(), "loginProvision") 142 m.G().GetStellar().CreateWalletSoft(context.Background()) 143 144 return nil 145 } 146 147 func (e *loginProvision) saveToSecretStore(m libkb.MetaContext) { 148 e.saveToSecretStoreWithLKS(m, e.lks) 149 } 150 151 func (e *loginProvision) saveToSecretStoreWithLKS(m libkb.MetaContext, lks *libkb.LKSec) { 152 nun := e.arg.User.GetNormalizedName() 153 var err error 154 defer m.Trace(fmt.Sprintf("loginProvision.saveToSecretStoreWithLKS(%s)", nun), &err)() 155 options := libkb.LoadAdvisorySecretStoreOptionsFromRemote(m) 156 err = libkb.StoreSecretAfterLoginWithLKSWithOptions(m, nun, lks, &options) 157 } 158 159 // deviceWithType provisions this device with an existing device using the 160 // kex2 protocol. provisionerType is the existing device type. 161 func (e *loginProvision) deviceWithType(m libkb.MetaContext, provisionerType keybase1.DeviceType) (err error) { 162 defer m.Trace("loginProvision#deviceWithType", &err)() 163 164 // make a new device: 165 deviceID, err := libkb.NewDeviceID() 166 if err != nil { 167 return err 168 } 169 device := &libkb.Device{ 170 ID: deviceID, 171 Type: e.arg.DeviceType, 172 } 173 174 // prompt for the device name here so there's no delay during kex: 175 m.Debug("deviceWithType: prompting for device name") 176 name, err := e.deviceName(m) 177 if err != nil { 178 m.Debug("deviceWithType: error getting device name from user: %s", err) 179 return err 180 } 181 device.Description = &name 182 m.Debug("deviceWithType: got device name: %q", name) 183 184 // make a new secret: 185 uid := e.arg.User.GetUID() 186 187 // Continue to generate legacy Kex2 secret types 188 kex2SecretTyp := libkb.Kex2SecretTypeV1Desktop 189 if e.arg.DeviceType == keybase1.DeviceTypeV2_MOBILE || provisionerType == keybase1.DeviceType_MOBILE { 190 kex2SecretTyp = libkb.Kex2SecretTypeV1Mobile 191 } 192 m.Debug("Generating Kex2 secret for uid=%s, typ=%d", uid, kex2SecretTyp) 193 secret, err := libkb.NewKex2SecretFromTypeAndUID(kex2SecretTyp, uid) 194 if err != nil { 195 return err 196 } 197 198 // create provisionee engine 199 salt, err := e.arg.User.GetSalt() 200 if err != nil { 201 m.Debug("Failed to get salt") 202 return err 203 } 204 provisionee := NewKex2Provisionee(m.G(), device, secret.Secret(), uid, salt) 205 206 var canceler func() 207 208 // display secret and prompt for secret from X in a goroutine: 209 go func() { 210 sb := secret.Secret() 211 arg := keybase1.DisplayAndPromptSecretArg{ 212 Secret: sb[:], 213 Phrase: secret.Phrase(), 214 OtherDeviceType: provisionerType, 215 } 216 var contxt context.Context 217 contxt, canceler = context.WithCancel(context.Background()) 218 for i := 0; i < 10; i++ { 219 receivedSecret, err := m.UIs().ProvisionUI.DisplayAndPromptSecret(contxt, arg) 220 if err != nil { 221 // cancel provisionee run: 222 provisionee.Cancel() 223 m.Warning("DisplayAndPromptSecret error: %s", err) 224 break 225 } else if receivedSecret.Secret != nil && len(receivedSecret.Secret) > 0 { 226 m.Debug("received secret, adding to provisionee") 227 var ks kex2.Secret 228 copy(ks[:], receivedSecret.Secret) 229 provisionee.AddSecret(ks) 230 break 231 } else if len(receivedSecret.Phrase) > 0 { 232 m.Debug("received secret phrase, checking validity") 233 checker := libkb.MakeCheckKex2SecretPhrase(m.G()) 234 if !checker.F(receivedSecret.Phrase) { 235 m.Debug("secret phrase failed validity check (attempt %d)", i) 236 arg.PreviousErr = checker.Hint 237 continue 238 } 239 m.Debug("received secret phrase, adding to provisionee") 240 ks, err := libkb.NewKex2SecretFromUIDAndPhrase(uid, receivedSecret.Phrase) 241 if err != nil { 242 m.Warning("DisplayAndPromptSecret error: %s", err) 243 } else { 244 provisionee.AddSecret(ks.Secret()) 245 } 246 break 247 } else { 248 // empty secret, so must have been a display-only case. 249 // ok to stop the loop 250 m.Debug("login provision DisplayAndPromptSecret returned empty secret, stopping retry loop") 251 break 252 } 253 } 254 }() 255 256 defer func() { 257 if canceler != nil { 258 m.Debug("canceling DisplayAndPromptSecret call") 259 canceler() 260 } 261 }() 262 263 err = RunEngine2(m, provisionee) 264 if err != nil { 265 return err 266 } 267 268 e.saveToSecretStoreWithLKS(m, provisionee.GetLKSec()) 269 270 e.signingKey, err = provisionee.SigningKey() 271 if err != nil { 272 return err 273 } 274 e.encryptionKey, err = provisionee.EncryptionKey() 275 if err != nil { 276 return err 277 } 278 279 // Load me again so that keys will be up to date. 280 loadArg := libkb.NewLoadUserArgWithMetaContext(m).WithSelf(true).WithUID(uid) 281 e.arg.User, err = libkb.LoadUser(loadArg) 282 if err != nil { 283 return err 284 } 285 286 // need username, device name for ProvisionUI.ProvisioneeSuccess() 287 e.username = provisionee.GetName() 288 pdevice := provisionee.Device() 289 if pdevice == nil { 290 m.Warning("nil provisionee device") 291 } else if pdevice.Description == nil { 292 m.Warning("nil provisionee device description") 293 } else { 294 e.devname = *pdevice.Description 295 } 296 297 return nil 298 } 299 300 // paper attempts to provision the device via a paper key. 301 func (e *loginProvision) paper(m libkb.MetaContext, device *libkb.DeviceWithDeviceNumber, keys *libkb.DeviceWithKeys) (err error) { 302 defer m.Trace("loginProvision#paper", &err)() 303 304 // get the paper key from the user if we're in the interactive flow 305 if keys == nil { 306 expectedPrefix := device.Description 307 keys, err = e.getValidPaperKey(m, expectedPrefix) 308 if err != nil { 309 return err 310 } 311 } 312 313 u := e.arg.User 314 uv := u.ToUserVersion() 315 nn := u.GetNormalizedName() 316 317 // Set the active device to be a special paper key active device, which keeps 318 // a cached copy around for DeviceKeyGen, which requires it to be in memory. 319 // It also will establish a NIST so that API calls can proceed on behalf of the user. 320 m = m.WithProvisioningKeyActiveDevice(keys, uv) 321 if err := m.LoginContext().SetUsernameUserVersion(nn, uv); err != nil { 322 return err 323 } 324 325 // need lksec to store device keys locally 326 if err := e.fetchLKS(m, keys.EncryptionKey()); err != nil { 327 return err 328 } 329 330 if err := e.makeDeviceKeysWithSigner(m, keys.SigningKey()); err != nil { 331 return err 332 } 333 334 // The DeviceWrap engine (called via makeDeviceKeysWithSigner) sets 335 // the global ActiveDevice to be a valid device. So we're OK to remove 336 // our temporary thread-local paperkey device installed just above. 337 m = m.WithGlobalActiveDevice() 338 339 // Cache the paper keys globally now that we're logged in. Note we must call 340 // this after the m.WithGlobalActiveDevice() above, since we want to cache 341 // the paper key on the global and not thread-local active device. 342 m.ActiveDevice().CacheProvisioningKey(m, keys) 343 344 e.saveToSecretStore(m) 345 return nil 346 } 347 348 var paperKeyNotFound = libkb.NotFoundError{ 349 Msg: "paper key not found, most likely due to a typo in one of the words in the phrase", 350 } 351 352 func (e *loginProvision) getValidPaperKey(m libkb.MetaContext, expectedPrefix *string) (keys *libkb.DeviceWithKeys, err error) { 353 defer m.Trace("loginProvision#getValidPaperKey", &err)() 354 355 for i := 0; i < 10; i++ { 356 keys, err = e.getValidPaperKeyOnce(m, i, err, expectedPrefix) 357 if err == nil { 358 return keys, err 359 } 360 if _, ok := err.(libkb.InputCanceledError); ok { 361 return nil, err 362 } 363 } 364 m.Debug("getValidPaperKey retry attempts exhausted") 365 return nil, err 366 } 367 368 func (e *loginProvision) getValidPaperKeyOnce(m libkb.MetaContext, i int, lastErr error, expectedPrefix *string) (keys *libkb.DeviceWithKeys, err error) { 369 defer m.Trace("loginProvision#getValidPaperKeyOnce", &err)() 370 371 // get the paper key from the user 372 var prefix string 373 keys, prefix, err = getPaperKey(m, lastErr, expectedPrefix) 374 if err != nil { 375 m.Debug("getValidPaperKeyOnce attempt %d (%s): %s", i, prefix, err) 376 return nil, err 377 } 378 379 // use the KID to find the uid, deviceID and deviceName 380 var uid keybase1.UID 381 uid, err = keys.Populate(m) 382 if err != nil { 383 m.Debug("getValidPaperKeyOnce attempt %d (%s): %s", i, prefix, err) 384 385 switch err := err.(type) { 386 case libkb.NotFoundError: 387 return nil, paperKeyNotFound 388 case libkb.AppStatusError: 389 if err.Code == libkb.SCNotFound { 390 return nil, paperKeyNotFound 391 } 392 } 393 return nil, err 394 } 395 396 if uid.NotEqual(e.arg.User.GetUID()) { 397 return nil, paperKeyNotFound 398 } 399 400 // found a paper key that can be used for signing 401 m.Debug("found paper key (%s) match for %s", prefix, e.arg.User.GetName()) 402 return keys, nil 403 } 404 405 // pgpProvision attempts to provision with a synced pgp key. It 406 // needs to get a session first to look for a synced pgp key. 407 func (e *loginProvision) pgpProvision(m libkb.MetaContext) (err error) { 408 defer m.Trace("loginProvision#pgpProvision", &err)() 409 410 err = e.passphraseLogin(m) 411 if err != nil { 412 return err 413 } 414 415 // After obtaining login session, this will be called before the login state is released. 416 // It tries to get the pgp key and uses it to provision new device keys for this device. 417 signer, err := e.syncedPGPKey(m) 418 if err != nil { 419 return err 420 } 421 422 if err = e.makeDeviceKeysWithSigner(m, signer); err != nil { 423 return err 424 } 425 426 e.saveToSecretStore(m) 427 return nil 428 } 429 430 // makeDeviceKeysWithSigner creates device keys given a signing 431 // key. 432 func (e *loginProvision) makeDeviceKeysWithSigner(m libkb.MetaContext, signer libkb.GenericKey) error { 433 args, err := e.makeDeviceWrapArgs(m) 434 if err != nil { 435 return err 436 } 437 args.Signer = signer 438 args.IsEldest = false // just to be explicit 439 args.EldestKID = e.arg.User.GetEldestKID() 440 441 return e.makeDeviceKeys(m, args) 442 } 443 444 // makeDeviceWrapArgs creates a base set of args for DeviceWrap. 445 // It ensures that LKSec is created. It also gets a new device 446 // name for this device. 447 func (e *loginProvision) makeDeviceWrapArgs(m libkb.MetaContext) (*DeviceWrapArgs, error) { 448 if err := e.ensureLKSec(m); err != nil { 449 return nil, err 450 } 451 452 devname, err := e.deviceName(m) 453 if err != nil { 454 return nil, err 455 } 456 e.devname = devname 457 458 return &DeviceWrapArgs{ 459 Me: e.arg.User, 460 DeviceName: e.devname, 461 DeviceType: e.arg.DeviceType, 462 Lks: e.lks, 463 PerUserKeyring: e.perUserKeyring, 464 naclSigningKeyPair: e.arg.naclSigningKeyPair, 465 naclEncryptionKeyPair: e.arg.naclEncryptionKeyPair, 466 }, nil 467 } 468 469 // ensureLKSec ensures we have LKSec for saving device keys. 470 func (e *loginProvision) ensureLKSec(m libkb.MetaContext) error { 471 if e.lks != nil { 472 return nil 473 } 474 475 pps, err := e.recoverAfterFailedSignup(m) 476 if err != nil { 477 m.Debug("recoverAfterFailedSignup not possible: %s, continuing with e.ppStream", err) 478 pps, err = e.ppStream(m) 479 if err != nil { 480 return err 481 } 482 } 483 484 e.lks = libkb.NewLKSec(pps, e.arg.User.GetUID()) 485 return nil 486 } 487 488 func (e *loginProvision) recoverAfterFailedSignup(mctx libkb.MetaContext) (ret *libkb.PassphraseStream, err error) { 489 mctx = mctx.WithLogTag("RSGNUP") 490 user := e.arg.User 491 defer mctx.Trace(fmt.Sprintf("recoverAfterFailedSignup(%q)", user.GetNormalizedName()), 492 &err)() 493 494 if !user.GetCurrentEldestSeqno().Eq(keybase1.Seqno(0)) { 495 return nil, errors.New("user has live sigchain, cannot do recover-after-signup login") 496 } 497 498 username := user.GetNormalizedName() 499 uid := user.GetUID() 500 501 stream, err := libkb.RetrievePwhashEddsaPassphraseStream(mctx, username, uid) 502 if err != nil { 503 return nil, err 504 } 505 506 err = libkb.LoginFromPassphraseStream(mctx, username.String(), stream) 507 if err != nil { 508 return nil, err 509 } 510 511 ok, err := mctx.LoginContext().LoggedInLoad() 512 mctx.Debug("LoggedInLoad: ok=%t err=%v", ok, err) 513 return stream, nil 514 } 515 516 // ppStream gets the passphrase stream, either cached or via 517 // SecretUI. 518 func (e *loginProvision) ppStream(m libkb.MetaContext) (ret *libkb.PassphraseStream, err error) { 519 defer m.Trace("loginProvision#ppStream", &err)() 520 if ret = m.PassphraseStream(); ret != nil { 521 return ret, nil 522 } 523 if err = e.passphraseLogin(m); err != nil { 524 return nil, err 525 } 526 if ret = m.PassphraseStream(); ret != nil { 527 return ret, nil 528 } 529 return nil, errors.New("no passphrase available") 530 } 531 532 func (e *loginProvision) passphraseLogin(m libkb.MetaContext) (err error) { 533 defer m.Trace("loginProvision#passphraseLogin", &err)() 534 535 if m.LoginContext() != nil { 536 ok, _ := m.LoginContext().LoggedInLoad() 537 if ok { 538 m.Debug("already logged in") 539 return nil 540 } 541 } 542 543 return libkb.PassphraseLoginPromptThenSecretStore(m, e.arg.User.GetName(), 5, false) 544 } 545 546 // deviceName gets a new device name from the user. 547 func (e *loginProvision) deviceName(m libkb.MetaContext) (string, error) { 548 var names []string 549 upk, _, err := m.G().GetUPAKLoader().LoadV2(libkb.NewLoadUserArgWithMetaContext(m).WithUID(e.arg.User.GetUID()).WithPublicKeyOptional().WithForcePoll(true).WithSelf(true)) 550 if err != nil { 551 m.Debug("error getting device names via upak: %s", err) 552 m.Debug("proceeding to ask user for a device name despite error...") 553 } else { 554 names = upk.AllDeviceNames() 555 } 556 557 // Fully non-interactive flow 558 if e.arg.DeviceName != "" { 559 return e.automatedDeviceName(m, names, e.arg.DeviceName) 560 } 561 562 arg := keybase1.PromptNewDeviceNameArg{ 563 ExistingDevices: names, 564 } 565 566 for i := 0; i < 10; i++ { 567 devname, err := m.UIs().ProvisionUI.PromptNewDeviceName(m.Ctx(), arg) 568 if err != nil { 569 return "", err 570 } 571 if !libkb.CheckDeviceName.F(devname) { 572 m.Debug("invalid device name supplied: %s", devname) 573 arg.ErrorMessage = "Invalid device name. Device names should be " + libkb.CheckDeviceName.Hint 574 continue 575 } 576 devname = libkb.CheckDeviceName.Transform(devname) 577 var dupname string 578 normalizedDevName := libkb.CheckDeviceName.Normalize(devname) 579 for _, name := range names { 580 if normalizedDevName == libkb.CheckDeviceName.Normalize(name) { 581 dupname = name 582 break 583 } 584 } 585 586 if dupname != "" { 587 m.Debug("Device name reused: %q == %q", devname, dupname) 588 var dupnameErrMsg string 589 // if we have a collision on the normalized values add some extra 590 // info the error message so the user isn't confused why we 591 // consider the names equal. 592 if devname != dupname { 593 dupnameErrMsg = fmt.Sprintf(" as %q", dupname) 594 } 595 arg.ErrorMessage = fmt.Sprintf("You've already used this device name%s. For security reasons, pick another name.", dupnameErrMsg) 596 continue 597 } 598 599 return devname, nil 600 } 601 return "", libkb.RetryExhaustedError{} 602 } 603 604 func (e *loginProvision) automatedDeviceName(m libkb.MetaContext, existing []string, devname string) (string, error) { 605 if !libkb.CheckDeviceName.F(devname) { 606 return "", libkb.DeviceBadNameError{} 607 } 608 609 devname = libkb.CheckDeviceName.Transform(devname) 610 normalizedDevName := libkb.CheckDeviceName.Normalize(devname) 611 for _, name := range existing { 612 if normalizedDevName == libkb.CheckDeviceName.Normalize(name) { 613 m.Debug("Device name reused: %q == %q", devname, name) 614 return "", libkb.DeviceNameInUseError{} 615 } 616 } 617 618 return devname, nil 619 } 620 621 // makeDeviceKeys uses DeviceWrap to generate device keys. 622 func (e *loginProvision) makeDeviceKeys(m libkb.MetaContext, args *DeviceWrapArgs) error { 623 eng := NewDeviceWrap(m.G(), args) 624 if err := RunEngine2(m, eng); err != nil { 625 return err 626 } 627 // Finish provisoning by calling SwitchConfigAndActiveDevice. we 628 // can't undo that, so do not error out after that. 629 if err := eng.SwitchConfigAndActiveDevice(m); err != nil { 630 return err 631 } 632 633 e.signingKey = eng.SigningKey() 634 e.encryptionKey = eng.EncryptionKey() 635 636 return nil 637 } 638 639 // syncedPGPKey looks for a synced pgp key for e.user. If found, 640 // it unlocks it. 641 func (e *loginProvision) syncedPGPKey(m libkb.MetaContext) (ret libkb.GenericKey, err error) { 642 defer m.Trace("loginProvision#syncedPGPKey", &err)() 643 644 key, err := e.arg.User.SyncedSecretKey(m) 645 if err != nil { 646 return nil, err 647 } 648 if key == nil { 649 return nil, libkb.NoSyncedPGPKeyError{} 650 } 651 652 m.Debug("got synced secret key") 653 654 // unlock it 655 // XXX improve this prompt 656 parg := m.SecretKeyPromptArg(libkb.SecretKeyArg{}, "sign new device") 657 unlocked, err := key.PromptAndUnlock(m, parg, nil, e.arg.User) 658 if err != nil { 659 return nil, err 660 } 661 662 m.Debug("unlocked secret key") 663 return unlocked, nil 664 } 665 666 // gpgPrivateIndex returns an index of the private gpg keys. 667 func (e *loginProvision) gpgPrivateIndex(m libkb.MetaContext) (*libkb.GpgKeyIndex, error) { 668 cli, err := e.gpgClient(m) 669 if err != nil { 670 return nil, err 671 } 672 673 // get an index of all the secret keys 674 index, _, err := cli.Index(m, true, "") 675 if err != nil { 676 return nil, err 677 } 678 679 return index, nil 680 } 681 682 // gpgClient returns a gpg client. 683 func (e *loginProvision) gpgClient(m libkb.MetaContext) (gpgInterface, error) { 684 if e.arg.DeviceType == keybase1.DeviceTypeV2_MOBILE { 685 return nil, libkb.GPGUnavailableError{} 686 } 687 if e.gpgCli != nil { 688 return e.gpgCli, nil 689 } 690 691 gpg := m.G().GetGpgClient() 692 ok, err := gpg.CanExec(m) 693 if err != nil { 694 return nil, err 695 } 696 if !ok { 697 return nil, libkb.GPGUnavailableError{} 698 } 699 e.gpgCli = gpg 700 return e.gpgCli, nil 701 } 702 703 // checkArg checks loginProvisionArg for sane arguments. 704 func (e *loginProvision) checkArg() error { 705 // check we have a good device type: 706 if e.arg.DeviceType != keybase1.DeviceTypeV2_DESKTOP && e.arg.DeviceType != keybase1.DeviceTypeV2_MOBILE { 707 return libkb.InvalidArgumentError{Msg: fmt.Sprintf("device type must be %q or %q, not %q", keybase1.DeviceTypeV2_DESKTOP, keybase1.DeviceTypeV2_MOBILE, e.arg.DeviceType)} 708 } 709 710 if e.arg.User == nil { 711 return libkb.InvalidArgumentError{Msg: "User cannot be nil"} 712 } 713 714 return nil 715 } 716 717 func (e *loginProvision) route(m libkb.MetaContext) (err error) { 718 719 defer m.Trace("loginProvision#route", &err)() 720 721 // check if User has any pgp keys, active devices 722 ckf := e.arg.User.GetComputedKeyFamily() 723 if ckf != nil { 724 e.hasPGP = len(ckf.GetActivePGPKeys(false)) > 0 725 e.hasDevice = ckf.HasActiveDevice() 726 } 727 728 if e.hasDevice { 729 return e.chooseDevice(m, e.hasPGP) 730 } 731 732 if e.hasPGP { 733 return e.tryPGP(m) 734 } 735 736 if !e.arg.User.GetEldestKID().IsNil() { 737 // The user has no PGP keys and no devices, but they do have an eldest 738 // KID. That means they've revoked all their devices. They have to 739 // reset their account at this point. 740 // TODO: Once we make your account auto-reset after revoking your last 741 // device, change this error message. 742 return errors.New("Cannot add a new device when all existing devices are revoked. Reset your account on keybase.io.") 743 } 744 745 // User has no existing devices or pgp keys, so create 746 // the eldest device. 747 return e.makeEldestDevice(m) 748 } 749 750 func (e *loginProvision) chooseDevice(m libkb.MetaContext, pgp bool) (err error) { 751 defer m.Trace("loginProvision#chooseDevice", &err)() 752 753 ckf := e.arg.User.GetComputedKeyFamily() 754 // TODO: switch this to getting all devices 755 // Then insert the number data and then filter out the incorrect devices 756 devices := partitionDeviceList(ckf.GetAllActiveDevices()) 757 sort.Sort(devices) 758 759 // Fully non-interactive flow 760 if e.arg.PaperKey != "" { 761 return e.preloadedPaperKey(m, devices, e.arg.PaperKey) 762 } 763 764 expDevices := make([]keybase1.Device, len(devices)) 765 idMap := make(map[keybase1.DeviceID]libkb.DeviceWithDeviceNumber) 766 for i, d := range devices { 767 expDevices[i] = *d.ProtExportWithDeviceNum() 768 idMap[d.ID] = d 769 } 770 771 // check to see if they have a PUK, in which case they must select a device 772 hasPUK, err := e.hasPerUserKey(m) 773 if err != nil { 774 return err 775 } 776 777 arg := keybase1.ChooseDeviceArg{ 778 Devices: expDevices, 779 CanSelectNoDevice: true, 780 } 781 id, err := m.UIs().ProvisionUI.ChooseDevice(m.Ctx(), arg) 782 if err != nil { 783 return err 784 } 785 786 if len(id) == 0 { 787 // they chose not to use a device 788 m.Debug("user has devices, but chose not to use any of them") 789 790 if pgp && !hasPUK { 791 // they have pgp keys, so try that: 792 err = e.tryPGP(m) 793 if err == nil { 794 // Provisioning succeeded 795 return nil 796 } 797 798 // Error here passes through into autoreset. 799 m.Warning("Unable to log in with a PGP signature: %s", err.Error()) 800 } 801 802 // Prompt the user whether they'd like to enter the reset flow. 803 // We will ask them for a password in AccountReset. 804 enterReset, err := m.UIs().LoginUI.PromptResetAccount(m.Ctx(), keybase1.PromptResetAccountArg{ 805 Prompt: keybase1.NewResetPromptDefault(keybase1.ResetPromptType_ENTER_NO_DEVICES), 806 }) 807 if err != nil { 808 return err 809 } 810 811 if enterReset != keybase1.ResetPromptResponse_CONFIRM_RESET { 812 m.Debug("User decided not to enter the reset pipeline") 813 // User had to explicitly decline entering the pipeline so in order to prevent 814 // confusion prevent further prompts by completing a noop login flow. 815 e.skippedLogin = true 816 if pgp && hasPUK { 817 return libkb.ProvisionViaDeviceRequiredError{} 818 } 819 return libkb.ProvisionUnavailableError{} 820 } 821 822 // go into the reset flow 823 eng := NewAccountReset(m.G(), e.arg.User.GetName()) 824 eng.completeReset = true 825 if err := eng.Run(m); err != nil { 826 return err 827 } 828 829 e.skippedLogin = eng.ResetPending() 830 e.resetComplete = eng.ResetComplete() 831 return nil 832 } 833 834 m.Debug("user selected device %s", id) 835 selected, ok := idMap[id] 836 if !ok { 837 return fmt.Errorf("selected device %s not in local device map", id) 838 } 839 m.Debug("device details: %+v", selected) 840 841 switch selected.Type { 842 case keybase1.DeviceTypeV2_PAPER: 843 return e.paper(m, &selected, nil) 844 case keybase1.DeviceTypeV2_DESKTOP: 845 return e.deviceWithType(m, keybase1.DeviceType_DESKTOP) 846 case keybase1.DeviceTypeV2_MOBILE: 847 return e.deviceWithType(m, keybase1.DeviceType_MOBILE) 848 default: 849 return fmt.Errorf("unknown device type: %v", selected.Type) 850 } 851 } 852 853 func (e *loginProvision) preloadedPaperKey(m libkb.MetaContext, devices []libkb.DeviceWithDeviceNumber, paperKey string) error { 854 // User has requested non-interactive provisioning - first parse their key 855 keys, prefix, err := getPaperKeyFromString(m, e.arg.PaperKey) 856 if err != nil { 857 return err 858 } 859 860 // ... then match it to the paper keys that can be used with this account 861 var matchedDevice *libkb.DeviceWithDeviceNumber 862 for _, d := range devices { 863 if d.Type != keybase1.DeviceTypeV2_PAPER { 864 continue 865 } 866 if prefix != *d.Description { 867 continue 868 } 869 870 matchedDevice = &d 871 break 872 } 873 874 if matchedDevice == nil { 875 return libkb.NoPaperKeysError{} 876 } 877 878 // use the KID to find the uid, deviceID and deviceName 879 uid, err := keys.Populate(m) 880 if err != nil { 881 switch err := err.(type) { 882 case libkb.NotFoundError: 883 return paperKeyNotFound 884 case libkb.AppStatusError: 885 if err.Code == libkb.SCNotFound { 886 return paperKeyNotFound 887 } 888 } 889 return err 890 } 891 if uid.NotEqual(e.arg.User.GetUID()) { 892 return paperKeyNotFound 893 } 894 895 return e.paper(m, matchedDevice, keys) 896 } 897 898 func (e *loginProvision) tryPGP(m libkb.MetaContext) (err error) { 899 defer m.Trace("loginProvision#tryPGP", &err)() 900 901 err = e.pgpProvision(m) 902 if err == nil { 903 return nil 904 } 905 906 if _, ok := err.(libkb.NoSyncedPGPKeyError); !ok { 907 // error during pgpProvision was not about no synced pgp key, 908 // so return it 909 return err 910 } 911 912 m.Debug("no synced pgp key found, trying GPG") 913 return e.tryGPG(m) 914 } 915 916 func (e *loginProvision) tryGPG(m libkb.MetaContext) (err error) { 917 defer m.Trace("loginProvision#tryGPG", &err)() 918 key, method, err := e.chooseGPGKeyAndMethod(m) 919 if err != nil { 920 return err 921 } 922 923 // depending on the method, get a signing key 924 var signingKey libkb.GenericKey 925 switch method { 926 case keybase1.GPGMethod_GPG_IMPORT: 927 signingKey, err = e.gpgImportKey(m, key.GetFingerprint()) 928 if err != nil { 929 // There was an error importing the key. 930 // So offer to switch to using gpg to sign 931 // the provisioning statement: 932 signingKey, err = e.switchToGPGSign(m, key, err) 933 if err != nil { 934 return err 935 } 936 method = keybase1.GPGMethod_GPG_SIGN 937 } 938 case keybase1.GPGMethod_GPG_SIGN: 939 signingKey, err = e.gpgSignKey(m, key.GetFingerprint()) 940 if err != nil { 941 return err 942 } 943 default: 944 return fmt.Errorf("invalid gpg provisioning method: %v", method) 945 } 946 947 if err = e.passphraseLogin(m); err != nil { 948 return err 949 } 950 951 if err := e.makeDeviceKeysWithSigner(m, signingKey); err != nil { 952 if appErr, ok := err.(libkb.AppStatusError); ok && appErr.Code == libkb.SCKeyCorrupted { 953 // Propagate the error, but display a more descriptive message to the user. 954 m.G().Log.Error("during GPG provisioning.\nWe were able to generate a PGP signature " + 955 "with gpg client, but it was rejected by the server. This often means that this " + 956 "PGP key is expired or unusable. You can update your key on https://keybase.io") 957 } 958 return err 959 } 960 e.saveToSecretStore(m) 961 962 if method == keybase1.GPGMethod_GPG_IMPORT { 963 // store the key in lksec 964 _, err := libkb.WriteLksSKBToKeyring(m, signingKey, e.lks) 965 if err != nil { 966 m.Warning("error saving exported gpg key in lksec: %s", err) 967 return err 968 } 969 } 970 971 return nil 972 } 973 974 func (e *loginProvision) chooseGPGKeyAndMethod(m libkb.MetaContext) (*libkb.GpgPrimaryKey, keybase1.GPGMethod, error) { 975 nilMethod := keybase1.GPGMethod_GPG_NONE 976 // find any local private gpg keys that are in user's key family 977 matches, err := e.matchingGPGKeys(m) 978 if err != nil { 979 // If this is a libkb.NoSecretKeyError, then no match found. 980 // Tell the user they need to get a gpg 981 // key onto this device. 982 return nil, nilMethod, err 983 } 984 985 // have a match 986 for _, match := range matches { 987 m.Debug("matching gpg key: %+v", match) 988 } 989 990 // create protocol array of keys 991 var gks []keybase1.GPGKey 992 gkmap := make(map[string]*libkb.GpgPrimaryKey) 993 for _, key := range matches { 994 gk := keybase1.GPGKey{ 995 Algorithm: key.AlgoString(), 996 KeyID: key.ID64, 997 Creation: key.CreatedString(), 998 Identities: key.GetPGPIdentities(), 999 } 1000 gks = append(gks, gk) 1001 gkmap[key.ID64] = key 1002 } 1003 1004 // ask if they want to import or sign 1005 arg := keybase1.ChooseGPGMethodArg{ 1006 Keys: gks, 1007 } 1008 method, err := m.UIs().ProvisionUI.ChooseGPGMethod(m.Ctx(), arg) 1009 if err != nil { 1010 return nil, nilMethod, err 1011 } 1012 1013 // select the key to use 1014 var key *libkb.GpgPrimaryKey 1015 if len(matches) == 1 { 1016 key = matches[0] 1017 } else { 1018 // if more than one match, show the user the matching keys, ask for selection 1019 keyid, err := m.UIs().GPGUI.SelectKey(m.Ctx(), keybase1.SelectKeyArg{Keys: gks}) 1020 if err != nil { 1021 return nil, nilMethod, err 1022 } 1023 1024 var ok bool 1025 key, ok = gkmap[keyid] 1026 if !ok { 1027 return nil, nilMethod, fmt.Errorf("key id %v from select key not in local gpg key map", keyid) 1028 } 1029 } 1030 1031 m.Debug("using gpg key %v for provisioning", key) 1032 1033 return key, method, nil 1034 } 1035 1036 func (e *loginProvision) switchToGPGSign(m libkb.MetaContext, key *libkb.GpgPrimaryKey, importError error) (libkb.GenericKey, error) { 1037 gk := keybase1.GPGKey{ 1038 Algorithm: key.AlgoString(), 1039 KeyID: key.ID64, 1040 Creation: key.CreatedString(), 1041 Identities: key.GetPGPIdentities(), 1042 } 1043 arg := keybase1.SwitchToGPGSignOKArg{ 1044 Key: gk, 1045 ImportError: importError.Error(), 1046 } 1047 ok, err := m.UIs().ProvisionUI.SwitchToGPGSignOK(m.Ctx(), arg) 1048 if err != nil { 1049 return nil, err 1050 } 1051 if !ok { 1052 return nil, fmt.Errorf("user chose not to switch to GPG sign, original import error: %s", importError) 1053 } 1054 1055 m.Debug("switching to GPG sign") 1056 return e.gpgSignKey(m, key.GetFingerprint()) 1057 } 1058 1059 func (e *loginProvision) matchingGPGKeys(m libkb.MetaContext) ([]*libkb.GpgPrimaryKey, error) { 1060 index, err := e.gpgPrivateIndex(m) 1061 if err != nil { 1062 return nil, err 1063 } 1064 1065 kfKeys := e.arg.User.GetComputedKeyFamily().GetActivePGPKeys(false) 1066 1067 if index.Len() == 0 { 1068 m.Debug("no private gpg keys found") 1069 return nil, e.newGPGMatchErr(kfKeys) 1070 } 1071 1072 // iterate through pgp keys in keyfamily 1073 var matches []*libkb.GpgPrimaryKey 1074 for _, kfKey := range kfKeys { 1075 // find matches in gpg index 1076 gpgKeys := index.Fingerprints.Get(kfKey.GetFingerprint().String()) 1077 if len(gpgKeys) > 0 { 1078 matches = append(matches, gpgKeys...) 1079 } 1080 } 1081 1082 if len(matches) == 0 { 1083 // if none exist, then abort with error that they need to get 1084 // the private key for one of the pgp keys in the keyfamily 1085 // onto this device. 1086 m.Debug("no matching private gpg keys found") 1087 return nil, e.newGPGMatchErr(kfKeys) 1088 } 1089 1090 return matches, nil 1091 } 1092 1093 func (e *loginProvision) newGPGMatchErr(keys []*libkb.PGPKeyBundle) error { 1094 fps := make([]string, len(keys)) 1095 for i, k := range keys { 1096 fps[i] = k.GetFingerprint().ToQuads() 1097 } 1098 return libkb.NoMatchingGPGKeysError{Fingerprints: fps, HasActiveDevice: e.hasDevice} 1099 } 1100 1101 func (e *loginProvision) gpgSignKey(m libkb.MetaContext, fp *libkb.PGPFingerprint) (libkb.GenericKey, error) { 1102 kf := e.arg.User.GetComputedKeyFamily() 1103 if kf == nil { 1104 return nil, libkb.KeyFamilyError{Msg: "no key family for user"} 1105 } 1106 kid, err := kf.FindKIDFromFingerprint(*fp) 1107 if err != nil { 1108 return nil, err 1109 } 1110 1111 // create a GPGKey shell around gpg cli with fp, kid 1112 return libkb.NewGPGKey(m.G(), fp, kid, m.UIs().GPGUI, e.arg.ClientType), nil 1113 } 1114 1115 func (e *loginProvision) gpgImportKey(m libkb.MetaContext, fp *libkb.PGPFingerprint) (libkb.GenericKey, error) { 1116 1117 // import it with gpg 1118 cli, err := e.gpgClient(m) 1119 if err != nil { 1120 return nil, err 1121 } 1122 1123 tty, err := m.UIs().GPGUI.GetTTY(m.Ctx()) 1124 if err != nil { 1125 m.Warning("error getting TTY for GPG: %s", err) 1126 } 1127 1128 bundle, err := cli.ImportKey(m, true, *fp, tty) 1129 if err != nil { 1130 return nil, err 1131 } 1132 1133 // unlock it 1134 if err := bundle.Unlock(m, "sign new device", m.UIs().SecretUI); err != nil { 1135 return nil, err 1136 } 1137 1138 return bundle, nil 1139 } 1140 1141 func (e *loginProvision) makeEldestDevice(m libkb.MetaContext) error { 1142 args, err := e.makeDeviceWrapArgs(m) 1143 if err != nil { 1144 return err 1145 } 1146 args.IsEldest = true 1147 1148 if err = e.makeDeviceKeys(m, args); err != nil { 1149 return err 1150 } 1151 e.saveToSecretStore(m) 1152 1153 if cErr := libkb.ClearPwhashEddsaPassphraseStream(m, e.arg.User.GetNormalizedName()); cErr != nil { 1154 m.Debug("ClearPwhashEddsaPassphraseStream failed with: %s", cErr) 1155 } 1156 1157 return nil 1158 } 1159 1160 // This is used by SaltpackDecrypt as well. 1161 func getPaperKey(m libkb.MetaContext, lastErr error, expectedPrefix *string) (keys *libkb.DeviceWithKeys, prefix string, err error) { 1162 passphrase, err := libkb.GetPaperKeyPassphrase(m, m.UIs().SecretUI, "", lastErr, expectedPrefix) 1163 if err != nil { 1164 return nil, "", err 1165 } 1166 1167 return getPaperKeyFromString(m, passphrase) 1168 } 1169 1170 func getPaperKeyFromString(m libkb.MetaContext, passphrase string) (keys *libkb.DeviceWithKeys, prefix string, err error) { 1171 paperPhrase, err := libkb.NewPaperKeyPhraseCheckVersion(m, passphrase) 1172 if err != nil { 1173 return nil, "", err 1174 } 1175 prefix = paperPhrase.Prefix() 1176 1177 bkarg := &PaperKeyGenArg{ 1178 Passphrase: paperPhrase, 1179 SkipPush: true, 1180 } 1181 bkeng := NewPaperKeyGen(m.G(), bkarg) 1182 if err := RunEngine2(m, bkeng); err != nil { 1183 return nil, prefix, err 1184 } 1185 keys = bkeng.DeviceWithKeys() 1186 return keys, prefix, nil 1187 } 1188 1189 func (e *loginProvision) fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) error { 1190 gen, clientLKS, err := fetchLKS(m, encKey) 1191 if err != nil { 1192 return err 1193 } 1194 e.lks = libkb.NewLKSecWithClientHalf(clientLKS, gen, e.arg.User.GetUID()) 1195 return nil 1196 } 1197 1198 func (e *loginProvision) hasPerUserKey(m libkb.MetaContext) (bool, error) { 1199 if e.arg.User == nil { 1200 return false, errors.New("no user object in arg") 1201 } 1202 return len(e.arg.User.ExportToUserPlusKeys().PerUserKeys) > 0, nil 1203 } 1204 1205 func (e *loginProvision) displaySuccess(m libkb.MetaContext) error { 1206 if len(e.username) == 0 && e.arg.User != nil { 1207 e.username = e.arg.User.GetName() 1208 } 1209 sarg := keybase1.ProvisioneeSuccessArg{ 1210 Username: e.username, 1211 DeviceName: e.devname, 1212 } 1213 return m.UIs().ProvisionUI.ProvisioneeSuccess(m.Ctx(), sarg) 1214 } 1215 1216 func (e *loginProvision) LoggedIn() bool { 1217 return !e.skippedLogin 1218 } 1219 1220 func (e *loginProvision) AccountReset() bool { 1221 return e.resetComplete 1222 } 1223 1224 var devtypeSortOrder = map[keybase1.DeviceTypeV2]int{keybase1.DeviceTypeV2_MOBILE: 0, keybase1.DeviceTypeV2_DESKTOP: 1, keybase1.DeviceTypeV2_PAPER: 2} 1225 1226 type partitionDeviceList []libkb.DeviceWithDeviceNumber 1227 1228 func (p partitionDeviceList) Len() int { 1229 return len(p) 1230 } 1231 1232 func (p partitionDeviceList) Less(a, b int) bool { 1233 if p[a].Type != p[b].Type { 1234 return devtypeSortOrder[p[a].Type] < devtypeSortOrder[p[b].Type] 1235 } 1236 return *p[a].Description < *p[b].Description 1237 } 1238 1239 func (p partitionDeviceList) Swap(a, b int) { 1240 p[a], p[b] = p[b], p[a] 1241 }