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  }