github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/kex2_provisionee.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  	"encoding/base64"
     8  	"errors"
     9  	"fmt"
    10  	"net/url"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/kbcrypto"
    14  	"github.com/keybase/client/go/kex2"
    15  	"github.com/keybase/client/go/libkb"
    16  	"github.com/keybase/client/go/logger"
    17  	"github.com/keybase/client/go/msgpack"
    18  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    19  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    20  	jsonw "github.com/keybase/go-jsonw"
    21  	"golang.org/x/net/context"
    22  )
    23  
    24  // Kex2Provisionee is an engine.
    25  type Kex2Provisionee struct {
    26  	libkb.Contextified
    27  	device       *libkb.Device
    28  	secret       kex2.Secret
    29  	secretCh     chan kex2.Secret
    30  	eddsa        libkb.NaclKeyPair
    31  	dh           libkb.NaclKeyPair
    32  	uid          keybase1.UID
    33  	username     string
    34  	sessionToken keybase1.SessionToken
    35  	csrfToken    keybase1.CsrfToken
    36  	pps          keybase1.PassphraseStream
    37  	lks          *libkb.LKSec
    38  	kex2Cancel   func()
    39  	mctx         libkb.MetaContext
    40  	salt         []byte
    41  	ekReboxer    *ephemeralKeyReboxer
    42  	expectedUID  keybase1.UID
    43  }
    44  
    45  // Kex2Provisionee implements kex2.Provisionee, libkb.UserBasic,
    46  // and libkb.APITokener interfaces.
    47  var _ kex2.Provisionee = (*Kex2Provisionee)(nil)
    48  var _ libkb.UserBasic = (*Kex2Provisionee)(nil)
    49  var _ libkb.APITokener = (*Kex2Provisionee)(nil)
    50  
    51  // NewKex2Provisionee creates a Kex2Provisionee engine.
    52  func NewKex2Provisionee(g *libkb.GlobalContext, device *libkb.Device, secret kex2.Secret,
    53  	expectedUID keybase1.UID, salt []byte) *Kex2Provisionee {
    54  	return &Kex2Provisionee{
    55  		Contextified: libkb.NewContextified(g),
    56  		device:       device,
    57  		secret:       secret,
    58  		secretCh:     make(chan kex2.Secret),
    59  		salt:         salt,
    60  		expectedUID:  expectedUID,
    61  	}
    62  }
    63  
    64  // Name is the unique engine name.
    65  func (e *Kex2Provisionee) Name() string {
    66  	return "Kex2Provisionee"
    67  }
    68  
    69  // GetPrereqs returns the engine prereqs.
    70  func (e *Kex2Provisionee) Prereqs() Prereqs {
    71  	return Prereqs{}
    72  }
    73  
    74  func (e *Kex2Provisionee) GetLKSec() *libkb.LKSec {
    75  	return e.lks
    76  }
    77  
    78  // RequiredUIs returns the required UIs.
    79  func (e *Kex2Provisionee) RequiredUIs() []libkb.UIKind {
    80  	return []libkb.UIKind{
    81  		libkb.ProvisionUIKind,
    82  	}
    83  }
    84  
    85  // SubConsumers returns the other UI consumers for this engine.
    86  func (e *Kex2Provisionee) SubConsumers() []libkb.UIConsumer {
    87  	return nil
    88  }
    89  
    90  type kex2LogContext struct {
    91  	log logger.Logger
    92  }
    93  
    94  func (k kex2LogContext) Debug(format string, args ...interface{}) {
    95  	k.log.Debug(format, args...)
    96  }
    97  
    98  func newKex2LogContext(g *libkb.GlobalContext) kex2LogContext {
    99  	return kex2LogContext{g.Log}
   100  }
   101  
   102  // Run starts the engine.
   103  func (e *Kex2Provisionee) Run(m libkb.MetaContext) error {
   104  	m.G().LocalSigchainGuard().Set(m.Ctx(), "Kex2Provisionee")
   105  	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisionee")
   106  
   107  	// check device struct:
   108  	if e.device.Type == keybase1.DeviceTypeV2_NONE {
   109  		return errors.New("provisionee device requires Type to be set")
   110  	}
   111  	if e.device.ID.IsNil() {
   112  		return errors.New("provisionee device requires ID to be set")
   113  	}
   114  
   115  	if m.LoginContext() == nil {
   116  		return errors.New("Kex2Provisionee needs LoginContext set in engine.Context")
   117  	}
   118  
   119  	if len(e.secret) == 0 {
   120  		panic("empty secret")
   121  	}
   122  
   123  	m, e.kex2Cancel = m.WithContextCancel()
   124  	defer e.kex2Cancel()
   125  
   126  	// The MetaContext m is needed in some of the kex2 functions. Make sure to do that
   127  	// after we've added a cancelation above.
   128  	e.mctx = m
   129  
   130  	karg := kex2.KexBaseArg{
   131  		Ctx:           m.Ctx(),
   132  		LogCtx:        newKex2LogContext(m.G()),
   133  		Mr:            libkb.NewKexRouter(m),
   134  		DeviceID:      e.device.ID,
   135  		Secret:        e.secret,
   136  		SecretChannel: e.secretCh,
   137  		Timeout:       60 * time.Minute,
   138  	}
   139  	parg := kex2.ProvisioneeArg{
   140  		KexBaseArg:  karg,
   141  		Provisionee: e,
   142  	}
   143  	return kex2.RunProvisionee(parg)
   144  }
   145  
   146  // Cancel cancels the kex2 run if it is running.
   147  func (e *Kex2Provisionee) Cancel() {
   148  	if e.kex2Cancel == nil {
   149  		return
   150  	}
   151  	e.kex2Cancel()
   152  }
   153  
   154  // AddSecret inserts a received secret into the provisionee's
   155  // secret channel.
   156  func (e *Kex2Provisionee) AddSecret(s kex2.Secret) {
   157  	e.secretCh <- s
   158  }
   159  
   160  // GetLogFactory implements GetLogFactory in kex2.Provisionee.
   161  func (e *Kex2Provisionee) GetLogFactory() rpc.LogFactory {
   162  	return rpc.NewSimpleLogFactory(e.G().Log, nil)
   163  }
   164  
   165  // GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisionee.
   166  func (e *Kex2Provisionee) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage {
   167  	return e.G().RemoteNetworkInstrumenterStorage
   168  }
   169  
   170  // HandleHello implements HandleHello in kex2.Provisionee.
   171  func (e *Kex2Provisionee) HandleHello(_ context.Context, harg keybase1.HelloArg) (res keybase1.HelloRes, err error) {
   172  	m := e.mctx
   173  	defer m.Trace("Kex2Provisionee#HandleHello", &err)()
   174  	e.pps = harg.Pps
   175  	res, err = e.handleHello(m, harg.Uid, harg.Token, harg.Csrf, harg.SigBody)
   176  	return res, err
   177  }
   178  
   179  func (e *Kex2Provisionee) handleHello(m libkb.MetaContext, uid keybase1.UID, token keybase1.SessionToken, csrf keybase1.CsrfToken, sigBody string) (res keybase1.HelloRes, err error) {
   180  
   181  	// save parts of the hello arg for later:
   182  	e.uid = uid
   183  	e.sessionToken = token
   184  	e.csrfToken = csrf
   185  
   186  	jw, err := jsonw.Unmarshal([]byte(sigBody))
   187  	if err != nil {
   188  		return res, err
   189  	}
   190  
   191  	// need the username later:
   192  	e.username, err = jw.AtPath("body.key.username").GetString()
   193  	if err != nil {
   194  		return res, err
   195  	}
   196  
   197  	if e.uid != e.expectedUID {
   198  		m.Debug("Unexpected UID in handleHello: wanted %s, got: %s", e.expectedUID, e.uid)
   199  		m.Debug("Username from the signature is: %q", e.username)
   200  		return res, fmt.Errorf("Provisioner is a different user than we wanted.")
   201  	}
   202  
   203  	e.eddsa, err = libkb.GenerateNaclSigningKeyPair()
   204  	if err != nil {
   205  		return res, err
   206  	}
   207  
   208  	e.dh, err = libkb.GenerateNaclDHKeyPair()
   209  	if err != nil {
   210  		return res, err
   211  	}
   212  
   213  	e.ekReboxer = newEphemeralKeyReboxer()
   214  
   215  	if err = e.addDeviceSibkey(m, jw); err != nil {
   216  		return res, err
   217  	}
   218  
   219  	if err = e.reverseSig(jw); err != nil {
   220  		return res, err
   221  	}
   222  
   223  	out, err := jw.Marshal()
   224  	if err != nil {
   225  		return res, err
   226  	}
   227  
   228  	return keybase1.HelloRes(out), err
   229  }
   230  
   231  // HandleHello2 implements HandleHello2 in kex2.Provisionee.
   232  func (e *Kex2Provisionee) HandleHello2(_ context.Context, harg keybase1.Hello2Arg) (res keybase1.Hello2Res, err error) {
   233  	m := e.mctx
   234  	defer m.Trace("Kex2Provisionee#HandleHello2()", &err)()
   235  	var res1 keybase1.HelloRes
   236  	res1, err = e.handleHello(m, harg.Uid, harg.Token, harg.Csrf, harg.SigBody)
   237  	if err != nil {
   238  		return res, err
   239  	}
   240  	res.SigPayload = res1
   241  	res.EncryptionKey = e.dh.GetKID()
   242  	res.DeviceEkKID, err = e.ekReboxer.getDeviceEKKID(m)
   243  	if err != nil {
   244  		return res, err
   245  	}
   246  	return res, err
   247  }
   248  
   249  func (e *Kex2Provisionee) HandleDidCounterSign2(_ context.Context, arg keybase1.DidCounterSign2Arg) (err error) {
   250  	mctx := e.mctx
   251  	defer mctx.Trace("Kex2Provisionee#HandleDidCounterSign2()", &err)()
   252  	var ppsBytes []byte
   253  	ppsBytes, _, err = e.dh.DecryptFromString(arg.PpsEncrypted)
   254  	if err != nil {
   255  		mctx.Debug("| Failed to decrypt pps: %s", err)
   256  		return err
   257  	}
   258  	err = msgpack.Decode(&e.pps, ppsBytes)
   259  	if err != nil {
   260  		mctx.Debug("| Failed to unpack pps: %s", err)
   261  		return err
   262  	}
   263  	return e.handleDidCounterSign(mctx, arg.Sig, arg.PukBox, arg.UserEkBox)
   264  }
   265  
   266  // HandleDidCounterSign implements HandleDidCounterSign in
   267  // kex2.Provisionee interface.
   268  func (e *Kex2Provisionee) HandleDidCounterSign(_ context.Context, sig []byte) (err error) {
   269  	return e.handleDidCounterSign(e.mctx, sig, nil, nil)
   270  }
   271  
   272  func (e *Kex2Provisionee) handleDidCounterSign(m libkb.MetaContext, sig []byte, perUserKeyBox *keybase1.PerUserKeyBox, userEKBox *keybase1.UserEkBoxed) (err error) {
   273  
   274  	defer m.Trace("Kex2Provisionee#handleDidCounterSign()", &err)()
   275  
   276  	// load self user (to load merkle root)
   277  	m.Debug("| running for username %s", e.username)
   278  	loadArg := libkb.NewLoadUserArgWithMetaContext(m).WithName(e.username)
   279  	var me *libkb.User
   280  	me, err = libkb.LoadUser(loadArg)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	uv := me.ToUserVersion()
   285  	if !uv.Uid.Equal(e.uid) {
   286  		return fmt.Errorf("Wrong user for key exchange: %v != %v", uv.Uid, e.uid)
   287  	}
   288  
   289  	// decode sig
   290  	decSig, err := e.decodeSig(sig)
   291  	if err != nil {
   292  		return err
   293  	}
   294  
   295  	// make a keyproof for the dh key, signed w/ e.eddsa
   296  	dhSig, dhSigID, err := e.dhKeyProof(m, e.dh, decSig.eldestKID, decSig.seqno, decSig.linkID)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	// create the key args for eddsa, dh keys
   302  	eddsaArgs, err := makeKeyArgs(decSig.sigID, sig, libkb.DelegationTypeSibkey, e.eddsa, decSig.eldestKID, decSig.signingKID)
   303  	if err != nil {
   304  		return err
   305  	}
   306  	dhArgs, err := makeKeyArgs(dhSigID, []byte(dhSig), libkb.DelegationTypeSubkey, e.dh, decSig.eldestKID, e.eddsa.GetKID())
   307  	if err != nil {
   308  		return err
   309  	}
   310  
   311  	// logged in, so update our temporary session to say so
   312  	if err = e.updateTemporarySession(m, uv); err != nil {
   313  		return err
   314  	}
   315  
   316  	// push the LKS server half
   317  	if err = e.pushLKSServerHalf(m); err != nil {
   318  		return err
   319  	}
   320  
   321  	// save device keys locally
   322  	if err = e.saveKeys(m); err != nil {
   323  		return err
   324  	}
   325  
   326  	if err := retryOnEphemeralRace(m, func(m libkb.MetaContext) error {
   327  		// Finish the ephemeral key generation -- create a deviceEKStatement and
   328  		// prepare the boxMetadata for posting if we received a valid userEKBox
   329  		reboxArg, err := e.ekReboxer.getReboxArg(m, userEKBox, e.device.ID, e.eddsa)
   330  		if err != nil {
   331  			return err
   332  		}
   333  
   334  		// post the key sigs to the api server
   335  		if err = e.postSigs(eddsaArgs, dhArgs, perUserKeyBox, reboxArg); err != nil {
   336  			return err
   337  		}
   338  		return nil
   339  	}); err != nil {
   340  		return err
   341  	}
   342  
   343  	// update the global active device, and also store the device keys in memory under ActiveDevice
   344  	if err = e.saveConfig(m, uv); err != nil {
   345  		return err
   346  	}
   347  
   348  	// Store the ephemeralkeys, if any. If this fails after we have
   349  	// posted the client will not have access to the userEK it was
   350  	// just reboxed for unfortunately. Without any EKs, the normal
   351  	// generation machinery will take over and they will make a new
   352  	// userEK.
   353  	if err := e.ekReboxer.storeEKs(m); err != nil {
   354  		// Swallow the error - provisioning has already happened and
   355  		// we've already save the config, there's no going back.
   356  		m.Debug("Unable to store EKs: %s", err)
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  // updateTemporarySession commits the session token and csrf token to our temporary session,
   363  // stored in our provisional login context. We'll need that to post successfully.
   364  func (e *Kex2Provisionee) updateTemporarySession(m libkb.MetaContext, uv keybase1.UserVersion) (err error) {
   365  	defer m.Trace("Kex2Provisionee#updateTemporarySession", &err)()
   366  	m.Debug("login context: %T %+v", m.LoginContext(), m.LoginContext())
   367  	return m.LoginContext().SaveState(string(e.sessionToken), string(e.csrfToken), libkb.NewNormalizedUsername(e.username), uv, e.device.ID)
   368  }
   369  
   370  type decodedSig struct {
   371  	sigID      keybase1.SigID
   372  	linkID     libkb.LinkID
   373  	seqno      int
   374  	eldestKID  keybase1.KID
   375  	signingKID keybase1.KID
   376  }
   377  
   378  func (e *Kex2Provisionee) decodeSig(sig []byte) (*decodedSig, error) {
   379  	body, err := base64.StdEncoding.DecodeString(string(sig))
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  	naclSig, err := kbcrypto.DecodeNaclSigInfoPacket(body)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	jw, err := jsonw.Unmarshal(naclSig.Payload)
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  	res := decodedSig{
   392  		sigID:  kbcrypto.ComputeSigIDFromSigBody(body).ToSigIDLegacy(),
   393  		linkID: libkb.ComputeLinkID(naclSig.Payload),
   394  	}
   395  	res.seqno, err = jw.AtKey("seqno").GetInt()
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	seldestKID, err := jw.AtPath("body.key.eldest_kid").GetString()
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	res.eldestKID = keybase1.KIDFromString(seldestKID)
   404  	ssigningKID, err := jw.AtPath("body.key.kid").GetString()
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  	res.signingKID = keybase1.KIDFromString(ssigningKID)
   409  
   410  	return &res, nil
   411  }
   412  
   413  // GetName implements libkb.UserBasic interface.
   414  func (e *Kex2Provisionee) GetName() string {
   415  	return e.username
   416  }
   417  
   418  // GetUID implements libkb.UserBasic interface.
   419  func (e *Kex2Provisionee) GetUID() keybase1.UID {
   420  	return e.uid
   421  }
   422  
   423  // Tokens implements the APITokener interface. This is the only implementer, but it's
   424  // a pretty unusual case --- the provisioned device is giving us, the provisionee,
   425  // a session and CSRF token to use for the server.
   426  func (e *Kex2Provisionee) Tokens() (token, csrf string) {
   427  	return string(e.sessionToken), string(e.csrfToken)
   428  }
   429  
   430  // Device returns the new device struct.
   431  func (e *Kex2Provisionee) Device() *libkb.Device {
   432  	return e.device
   433  }
   434  
   435  func (e *Kex2Provisionee) addDeviceSibkey(m libkb.MetaContext, jw *jsonw.Wrapper) error {
   436  	if e.device.Description == nil {
   437  		// should not get in here with change to login_provision.go
   438  		// deviceWithType that is prompting for device name before
   439  		// starting this engine, but leaving the code here just
   440  		// as a safety measure.
   441  
   442  		m.Debug("kex2 provisionee: device name (e.device.Description) is nil. It should be set by caller.")
   443  		m.Debug("kex2 provisionee: proceeding to prompt user for device name, but figure out how this happened...")
   444  
   445  		// need user to get existing device names
   446  		loadArg := libkb.NewLoadUserArgWithMetaContext(m).WithName(e.username)
   447  		user, err := libkb.LoadUser(loadArg)
   448  		if err != nil {
   449  			return err
   450  		}
   451  		existingDevices, err := user.DeviceNames()
   452  		if err != nil {
   453  			m.Debug("proceeding despite error getting existing device names: %s", err)
   454  		}
   455  
   456  		e.G().Log.Debug("kex2 provisionee: prompting for device name")
   457  		arg := keybase1.PromptNewDeviceNameArg{
   458  			ExistingDevices: existingDevices,
   459  		}
   460  		name, err := m.UIs().ProvisionUI.PromptNewDeviceName(m.Ctx(), arg)
   461  		if err != nil {
   462  			return err
   463  		}
   464  		e.device.Description = &name
   465  		m.Debug("kex2 provisionee: got device name: %q", name)
   466  	}
   467  
   468  	s := libkb.DeviceStatusActive
   469  	e.device.Status = &s
   470  	e.device.Kid = e.eddsa.GetKID()
   471  	dw, err := e.device.Export(libkb.LinkType(libkb.DelegationTypeSibkey))
   472  	if err != nil {
   473  		return err
   474  	}
   475  	err = jw.SetValueAtPath("body.device", dw)
   476  	if err != nil {
   477  		return err
   478  	}
   479  
   480  	return jw.SetValueAtPath("body.sibkey.kid", jsonw.NewString(e.eddsa.GetKID().String()))
   481  }
   482  
   483  func (e *Kex2Provisionee) reverseSig(jw *jsonw.Wrapper) error {
   484  	// need to set reverse_sig to nil before making reverse sig:
   485  	if err := jw.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewNil()); err != nil {
   486  		return err
   487  	}
   488  
   489  	sig, _, _, err := libkb.SignJSON(jw, e.eddsa)
   490  	if err != nil {
   491  		return err
   492  	}
   493  
   494  	// put the signature in reverse_sig
   495  	return jw.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewString(sig))
   496  }
   497  
   498  // postSigs takes the HTTP args for the signing key and encrypt
   499  // key and posts them to the api server.
   500  func (e *Kex2Provisionee) postSigs(signingArgs, encryptArgs *libkb.HTTPArgs,
   501  	perUserKeyBox *keybase1.PerUserKeyBox, reboxArg *keybase1.UserEkReboxArg) error {
   502  	payload := make(libkb.JSONPayload)
   503  	payload["sigs"] = []map[string]string{firstValues(signingArgs.ToValues()), firstValues(encryptArgs.ToValues())}
   504  
   505  	// Post the per-user-secret encrypted for the provisionee device by the provisioner.
   506  	if perUserKeyBox != nil {
   507  		libkb.AddPerUserKeyServerArg(payload, perUserKeyBox.Generation, []keybase1.PerUserKeyBox{*perUserKeyBox}, nil)
   508  	}
   509  
   510  	libkb.AddUserEKReBoxServerArg(payload, reboxArg)
   511  
   512  	mctx := e.mctx.WithAPITokener(e)
   513  	arg := libkb.APIArg{
   514  		Endpoint:    "key/multi",
   515  		SessionType: libkb.APISessionTypeREQUIRED,
   516  		JSONPayload: payload,
   517  	}
   518  	// MerkleCheckPostedUserSig was not added here. Changing kex2 is risky and there's no obvious attack.
   519  
   520  	_, err := e.G().API.PostJSON(mctx, arg)
   521  	return err
   522  }
   523  
   524  func makeKeyArgs(sigID keybase1.SigID, sig []byte, delType libkb.DelegationType, key libkb.GenericKey, eldestKID, signingKID keybase1.KID) (*libkb.HTTPArgs, error) {
   525  	pub, err := key.Encode()
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  	args := libkb.HTTPArgs{
   530  		"sig_id_base":     libkb.S{Val: sigID.StripSuffix().String()},
   531  		"sig_id_short":    libkb.S{Val: sigID.ToShortID()},
   532  		"sig":             libkb.S{Val: string(sig)},
   533  		"type":            libkb.S{Val: string(delType)},
   534  		"is_remote_proof": libkb.B{Val: false},
   535  		"public_key":      libkb.S{Val: pub},
   536  		"eldest_kid":      libkb.S{Val: eldestKID.String()},
   537  		"signing_kid":     libkb.S{Val: signingKID.String()},
   538  	}
   539  	return &args, nil
   540  }
   541  
   542  func (e *Kex2Provisionee) dhKeyProof(m libkb.MetaContext, dh libkb.GenericKey, eldestKID keybase1.KID, seqno int, linkID libkb.LinkID) (sig string, sigID keybase1.SigID, err error) {
   543  	delg := libkb.Delegator{
   544  		ExistingKey:    e.eddsa,
   545  		NewKey:         dh,
   546  		DelegationType: libkb.DelegationTypeSubkey,
   547  		Expire:         libkb.NaclDHExpireIn,
   548  		EldestKID:      eldestKID,
   549  		Device:         e.device,
   550  		Seqno:          keybase1.Seqno(seqno) + 1,
   551  		PrevLinkID:     linkID,
   552  		SigningUser:    e,
   553  		Contextified:   libkb.NewContextified(e.G()),
   554  	}
   555  
   556  	jw, err := libkb.KeyProof(m, delg)
   557  	if err != nil {
   558  		return "", "", err
   559  	}
   560  
   561  	e.G().Log.Debug("dh key proof: %s", jw.MarshalPretty())
   562  
   563  	dhSig, dhSigID, _, err := libkb.SignJSON(jw, e.eddsa)
   564  	if err != nil {
   565  		return "", "", err
   566  	}
   567  
   568  	return dhSig, dhSigID.ToSigIDLegacy(), nil
   569  
   570  }
   571  
   572  func (e *Kex2Provisionee) pushLKSServerHalf(m libkb.MetaContext) (err error) {
   573  	defer m.Trace("Kex2Provisionee#pushLKSServerHalf", &err)()
   574  
   575  	// make new lks
   576  	ppstream := libkb.NewPassphraseStream(e.pps.PassphraseStream)
   577  	ppstream.SetGeneration(libkb.PassphraseGeneration(e.pps.Generation))
   578  	e.lks = libkb.NewLKSec(ppstream, e.uid)
   579  	err = e.lks.GenerateServerHalf()
   580  	if err != nil {
   581  		return err
   582  	}
   583  
   584  	// make client half recovery
   585  	chrKID := e.dh.GetKID()
   586  	chrText, err := e.lks.EncryptClientHalfRecovery(e.dh)
   587  	if err != nil {
   588  		return err
   589  	}
   590  
   591  	err = libkb.PostDeviceLKS(m.WithAPITokener(e), e.device.ID, e.device.Type, e.lks.GetServerHalf(), e.lks.Generation(), chrText, chrKID)
   592  	if err != nil {
   593  		return err
   594  	}
   595  
   596  	// Sync the LKS stuff back from the server, so that subsequent
   597  	// attempts to use public key login will work.
   598  	if err = m.LoginContext().RunSecretSyncer(m, e.uid); err != nil {
   599  		return err
   600  	}
   601  
   602  	// Cache the passphrase stream.  Note that we don't have the triplesec
   603  	// portion of the stream cache, and that the only bytes in ppstream
   604  	// are the lksec portion (no pwhash, eddsa, dh).  Currently passes
   605  	// all tests with this situation and code that uses those portions
   606  	// looks to be ok.
   607  	m.LoginContext().CreateStreamCache(nil, ppstream)
   608  
   609  	return nil
   610  }
   611  
   612  // saveKeys writes the device keys to LKSec.
   613  func (e *Kex2Provisionee) saveKeys(m libkb.MetaContext) error {
   614  	_, err := libkb.WriteLksSKBToKeyring(m, e.eddsa, e.lks)
   615  	if err != nil {
   616  		return err
   617  	}
   618  	_, err = libkb.WriteLksSKBToKeyring(m, e.dh, e.lks)
   619  	if err != nil {
   620  		return err
   621  	}
   622  	return nil
   623  }
   624  
   625  // cacheKeys caches the device keys in the Account object.
   626  func (e *Kex2Provisionee) saveConfig(m libkb.MetaContext, uv keybase1.UserVersion) (err error) {
   627  	defer m.Trace("Kex2Provisionee#saveConfig", &err)()
   628  	if e.eddsa == nil {
   629  		return errors.New("cacheKeys called, but eddsa key is nil")
   630  	}
   631  	if e.dh == nil {
   632  		return errors.New("cacheKeys called, but dh key is nil")
   633  	}
   634  
   635  	var deviceName string
   636  	if e.device.Description != nil {
   637  		deviceName = *e.device.Description
   638  	}
   639  
   640  	return m.SwitchUserNewConfigActiveDevice(uv, libkb.NewNormalizedUsername(e.username), e.salt, e.device.ID, e.eddsa, e.dh, deviceName, libkb.KeychainModeOS)
   641  }
   642  
   643  func (e *Kex2Provisionee) SigningKey() (libkb.GenericKey, error) {
   644  	if e.eddsa == nil {
   645  		return nil, errors.New("provisionee missing signing key")
   646  	}
   647  	return e.eddsa, nil
   648  }
   649  
   650  func (e *Kex2Provisionee) EncryptionKey() (libkb.NaclDHKeyPair, error) {
   651  	if e.dh == nil {
   652  		return libkb.NaclDHKeyPair{}, errors.New("provisionee missing encryption key")
   653  	}
   654  	ret, ok := e.dh.(libkb.NaclDHKeyPair)
   655  	if !ok {
   656  		return libkb.NaclDHKeyPair{}, fmt.Errorf("provisionee encryption key unexpected type %T", e.dh)
   657  	}
   658  	return ret, nil
   659  }
   660  
   661  func firstValues(vals url.Values) map[string]string {
   662  	res := make(map[string]string)
   663  	for k, v := range vals {
   664  		res[k] = v[0]
   665  	}
   666  	return res
   667  }