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

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/keybase/client/go/libkb"
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  type PaperProvisionEngine struct {
    15  	libkb.Contextified
    16  	Username       string
    17  	DeviceName     string
    18  	PaperKey       string
    19  	result         error
    20  	lks            *libkb.LKSec
    21  	User           *libkb.User
    22  	perUserKeyring *libkb.PerUserKeyring
    23  
    24  	deviceWrapEng *DeviceWrap
    25  }
    26  
    27  func NewPaperProvisionEngine(g *libkb.GlobalContext, username, deviceName,
    28  	paperKey string) *PaperProvisionEngine {
    29  	return &PaperProvisionEngine{
    30  		Contextified: libkb.NewContextified(g),
    31  		Username:     username,
    32  		DeviceName:   deviceName,
    33  		PaperKey:     paperKey,
    34  	}
    35  }
    36  
    37  func (e *PaperProvisionEngine) Name() string {
    38  	return "PaperProvision"
    39  }
    40  
    41  func (e *PaperProvisionEngine) Prereqs() Prereqs {
    42  	return Prereqs{}
    43  }
    44  
    45  func (e *PaperProvisionEngine) RequiredUIs() []libkb.UIKind {
    46  	return []libkb.UIKind{
    47  		libkb.ProvisionUIKind,
    48  		libkb.LogUIKind,
    49  		libkb.SecretUIKind,
    50  		libkb.LoginUIKind,
    51  	}
    52  }
    53  
    54  func (e *PaperProvisionEngine) Run(m libkb.MetaContext) (err error) {
    55  	defer m.Trace("PaperProvisionEngine#Run", &err)()
    56  
    57  	// clear out any existing session
    58  	err = m.LogoutKeepSecrets()
    59  	if err != nil {
    60  		m.Debug("error on logout: %+v", err)
    61  	}
    62  
    63  	m = m.WithNewProvisionalLoginContext()
    64  
    65  	// From this point on, if there's an error, we abort the
    66  	// transaction.
    67  	defer func() {
    68  		if err == nil {
    69  			m = m.CommitProvisionalLogin()
    70  		}
    71  	}()
    72  
    73  	// run the LoginLoadUser sub-engine to load a user
    74  	ueng := newLoginLoadUser(e.G(), e.Username)
    75  	if err = RunEngine2(m, ueng); err != nil {
    76  		return err
    77  	}
    78  
    79  	// make sure the user isn't already provisioned (can
    80  	// get here if usernameOrEmail is an email address
    81  	// for an already provisioned on this device user).
    82  	if ueng.User().HasCurrentDeviceInCurrentInstall() {
    83  		return libkb.DeviceAlreadyProvisionedError{}
    84  	}
    85  	e.User = ueng.User()
    86  
    87  	// Transform the paper key phrase into a key pair
    88  	bkarg := &PaperKeyGenArg{
    89  		Passphrase: libkb.PaperKeyPhrase(e.PaperKey),
    90  		SkipPush:   true,
    91  	}
    92  	bkeng := NewPaperKeyGen(e.G(), bkarg)
    93  	if err := RunEngine2(m, bkeng); err != nil {
    94  		return err
    95  	}
    96  
    97  	keys := bkeng.DeviceWithKeys()
    98  
    99  	// Make sure the key matches the logged in user
   100  	// use the KID to find the uid
   101  	uid, err := keys.Populate(m)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	if uid.NotEqual(e.User.GetUID()) {
   107  		e.G().Log.Debug("paper key entered was for a different user")
   108  		return fmt.Errorf("paper key valid, but for %s, not %s", uid, e.User.GetUID())
   109  	}
   110  
   111  	e.perUserKeyring, err = libkb.NewPerUserKeyring(e.G(), e.User.GetUID())
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	// Make new device keys and sign them with this paper key
   117  	if err = e.paper(m, keys); err != nil {
   118  		return err
   119  	}
   120  
   121  	// Finish provisoning by calling SwitchConfigAndActiveDevice. we
   122  	// can't undo that, so do not error out after that.
   123  	if err := e.deviceWrapEng.SwitchConfigAndActiveDevice(m); err != nil {
   124  		return err
   125  	}
   126  
   127  	e.sendNotification(m)
   128  	return nil
   129  
   130  }
   131  
   132  // copied more or less from loginProvision.paper()
   133  func (e *PaperProvisionEngine) paper(m libkb.MetaContext, keys *libkb.DeviceWithKeys) error {
   134  	// After obtaining login session, this will be called before the login state is released.
   135  	// It signs this new device with the paper key.
   136  	u := e.User
   137  	nn := u.GetNormalizedName()
   138  	uv := u.ToUserVersion()
   139  
   140  	// Set the active device to be a special paper key active device, which keeps
   141  	// a cached copy around for DeviceKeyGen, which requires it to be in memory.
   142  	// It also will establish a NIST so that API calls can proceed on behalf of the user.
   143  	m = m.WithProvisioningKeyActiveDevice(keys, uv)
   144  	if err := m.LoginContext().SetUsernameUserVersion(nn, uv); err != nil {
   145  		return err
   146  	}
   147  
   148  	// need lksec to store device keys locally
   149  	if err := e.fetchLKS(m, keys.EncryptionKey()); err != nil {
   150  		return err
   151  	}
   152  
   153  	if err := e.makeDeviceKeysWithSigner(m, keys.SigningKey()); err != nil {
   154  		return err
   155  	}
   156  
   157  	// Cache the paper keys globally now that we're logged in
   158  	m = m.WithGlobalActiveDevice()
   159  	m.ActiveDevice().CacheProvisioningKey(m, keys)
   160  
   161  	return nil
   162  }
   163  
   164  func (e *PaperProvisionEngine) sendNotification(m libkb.MetaContext) {
   165  	e.G().NotifyRouter.HandleLogin(m.Ctx(), string(e.G().Env.GetUsername()))
   166  }
   167  
   168  func (e *PaperProvisionEngine) SubConsumers() []libkb.UIConsumer {
   169  	return []libkb.UIConsumer{
   170  		&loginLoadUser{},
   171  	}
   172  }
   173  
   174  func (e *PaperProvisionEngine) Result() error {
   175  	return e.result
   176  }
   177  
   178  // copied from loginProvision
   179  func (e *PaperProvisionEngine) fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) error {
   180  	gen, clientLKS, err := fetchLKS(m, encKey)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	e.lks = libkb.NewLKSecWithClientHalf(clientLKS, gen, e.User.GetUID())
   185  	return nil
   186  }
   187  
   188  // copied from loginProvision
   189  // makeDeviceKeysWithSigner creates device keys given a signing
   190  // key.
   191  func (e *PaperProvisionEngine) makeDeviceKeysWithSigner(m libkb.MetaContext, signer libkb.GenericKey) error {
   192  	args, err := e.makeDeviceWrapArgs(m)
   193  	if err != nil {
   194  		return err
   195  	}
   196  	args.Signer = signer
   197  	args.IsEldest = false // just to be explicit
   198  	args.EldestKID = e.User.GetEldestKID()
   199  
   200  	return e.makeDeviceKeys(m, args)
   201  }
   202  
   203  // copied from loginProvision
   204  // makeDeviceWrapArgs creates a base set of args for DeviceWrap.
   205  // It ensures that LKSec is created.  It also gets a new device
   206  // name for this device.
   207  func (e *PaperProvisionEngine) makeDeviceWrapArgs(m libkb.MetaContext) (*DeviceWrapArgs, error) {
   208  	if err := e.ensureLKSec(m); err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	return &DeviceWrapArgs{
   213  		Me:             e.User,
   214  		DeviceName:     e.DeviceName,
   215  		DeviceType:     keybase1.DeviceTypeV2_DESKTOP,
   216  		Lks:            e.lks,
   217  		PerUserKeyring: e.perUserKeyring,
   218  	}, nil
   219  }
   220  
   221  // Copied from loginProvision. makeDeviceKeys uses DeviceWrap to
   222  // generate device keys and sets active device.
   223  func (e *PaperProvisionEngine) makeDeviceKeys(m libkb.MetaContext, args *DeviceWrapArgs) error {
   224  	e.deviceWrapEng = NewDeviceWrap(m.G(), args)
   225  	return RunEngine2(m, e.deviceWrapEng)
   226  }
   227  
   228  // copied from loginProvision
   229  // ensureLKSec ensures we have LKSec for saving device keys.
   230  func (e *PaperProvisionEngine) ensureLKSec(m libkb.MetaContext) error {
   231  	if e.lks != nil {
   232  		return nil
   233  	}
   234  
   235  	pps, err := e.ppStream(m)
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	e.lks = libkb.NewLKSec(pps, e.User.GetUID())
   241  	return nil
   242  }
   243  
   244  // copied from loginProvision
   245  // ppStream gets the passphrase stream from the cache
   246  func (e *PaperProvisionEngine) ppStream(m libkb.MetaContext) (*libkb.PassphraseStream, error) {
   247  	if m.LoginContext() == nil {
   248  		return nil, errors.New("loginProvision: ppStream() -> nil ctx.LoginContext")
   249  	}
   250  	cached := m.LoginContext().PassphraseStreamCache()
   251  	if cached == nil {
   252  		return nil, errors.New("loginProvision: ppStream() -> nil PassphraseStreamCache")
   253  	}
   254  	return cached.PassphraseStream(), nil
   255  }