github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/selfprovision.go (about)

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