github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/kex2_provisioner.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  	"fmt"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/kex2"
    11  	"github.com/keybase/client/go/libkb"
    12  	"github.com/keybase/client/go/msgpack"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    15  	jsonw "github.com/keybase/go-jsonw"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  // Kex2Provisioner is an engine.
    20  type Kex2Provisioner struct {
    21  	libkb.Contextified
    22  	secret                kex2.Secret
    23  	secretCh              chan kex2.Secret
    24  	me                    *libkb.User
    25  	signingKey            libkb.GenericKey
    26  	encryptionKey         libkb.NaclDHKeyPair
    27  	pps                   keybase1.PassphraseStream
    28  	provisioneeDeviceName string
    29  	provisioneeDeviceType keybase1.DeviceTypeV2
    30  	mctx                  libkb.MetaContext
    31  	proof                 *jsonw.Wrapper
    32  }
    33  
    34  // Kex2Provisioner implements kex2.Provisioner interface.
    35  var _ kex2.Provisioner = (*Kex2Provisioner)(nil)
    36  
    37  // NewKex2Provisioner creates a Kex2Provisioner engine.
    38  func NewKex2Provisioner(g *libkb.GlobalContext, secret kex2.Secret, pps *libkb.PassphraseStream) *Kex2Provisioner {
    39  	e := &Kex2Provisioner{
    40  		Contextified: libkb.NewContextified(g),
    41  		secret:       secret,
    42  		secretCh:     make(chan kex2.Secret),
    43  	}
    44  	if pps != nil {
    45  		e.pps = pps.Export()
    46  	}
    47  
    48  	return e
    49  }
    50  
    51  // Name is the unique engine name.
    52  func (e *Kex2Provisioner) Name() string {
    53  	return "Kex2Provisioner"
    54  }
    55  
    56  // GetPrereqs returns the engine prereqs.
    57  func (e *Kex2Provisioner) Prereqs() Prereqs {
    58  	return Prereqs{Device: true}
    59  }
    60  
    61  // RequiredUIs returns the required UIs.
    62  func (e *Kex2Provisioner) RequiredUIs() []libkb.UIKind {
    63  	return []libkb.UIKind{
    64  		libkb.SecretUIKind,
    65  		libkb.ProvisionUIKind,
    66  	}
    67  }
    68  
    69  // SubConsumers returns the other UI consumers for this engine.
    70  func (e *Kex2Provisioner) SubConsumers() []libkb.UIConsumer {
    71  	return nil
    72  }
    73  
    74  // Run starts the provisioner engine.
    75  func (e *Kex2Provisioner) Run(m libkb.MetaContext) error {
    76  	// The guard is acquired later, after the potentially long pause by the user.
    77  	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner")
    78  
    79  	// before starting provisioning, need to load some information:
    80  	if err := e.loadMe(); err != nil {
    81  		return err
    82  	}
    83  	if err := m.ActiveDevice().ClearPassphraseStreamCacheIfOutdated(m); err != nil {
    84  		return err
    85  	}
    86  	if err := e.loadSecretKeys(m); err != nil {
    87  		return err
    88  	}
    89  
    90  	// get current passphrase stream if necessary:
    91  	if e.pps.PassphraseStream == nil {
    92  		m.Debug("kex2 provisioner needs passphrase stream, getting it via GetPassphraseStreamStored")
    93  		pps, err := libkb.GetPassphraseStreamStored(m)
    94  		if err != nil {
    95  			return err
    96  		}
    97  		e.pps = pps.Export()
    98  	}
    99  
   100  	// Go's context.Context needed by some kex2 callback functions
   101  	m = m.EnsureCtx()
   102  	e.mctx = m
   103  
   104  	deviceID := m.G().Env.GetDeviceID()
   105  
   106  	// all set:  start provisioner
   107  	karg := kex2.KexBaseArg{
   108  		Ctx:           m.Ctx(),
   109  		LogCtx:        newKex2LogContext(m.G()),
   110  		Mr:            libkb.NewKexRouter(m),
   111  		DeviceID:      deviceID,
   112  		Secret:        e.secret,
   113  		SecretChannel: e.secretCh,
   114  		Timeout:       60 * time.Minute,
   115  	}
   116  	parg := kex2.ProvisionerArg{
   117  		KexBaseArg:   karg,
   118  		Provisioner:  e,
   119  		HelloTimeout: 15 * time.Second,
   120  	}
   121  	if err := kex2.RunProvisioner(parg); err != nil {
   122  		return err
   123  	}
   124  	m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner")
   125  
   126  	// successfully provisioned the other device
   127  	sarg := keybase1.ProvisionerSuccessArg{
   128  		DeviceName: e.provisioneeDeviceName,
   129  		DeviceType: e.provisioneeDeviceType,
   130  	}
   131  	return m.UIs().ProvisionUI.ProvisionerSuccess(context.Background(), sarg)
   132  }
   133  
   134  func (e *Kex2Provisioner) loadSecretKeys(m libkb.MetaContext) (err error) {
   135  	// get signing key (including secret key)
   136  	ska1 := libkb.SecretKeyArg{
   137  		Me:      e.me,
   138  		KeyType: libkb.DeviceSigningKeyType,
   139  	}
   140  	e.signingKey, err = m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska1, "new device install"))
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	// get encryption key (including secret key)
   146  	ska2 := libkb.SecretKeyArg{
   147  		Me:      e.me,
   148  		KeyType: libkb.DeviceEncryptionKeyType,
   149  	}
   150  	encryptionKeyGeneric, err := e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska2, "new device install"))
   151  	if err != nil {
   152  		return err
   153  	}
   154  	var ok bool
   155  	e.encryptionKey, ok = encryptionKeyGeneric.(libkb.NaclDHKeyPair)
   156  	if !ok {
   157  		return fmt.Errorf("Unexpected encryption key type")
   158  	}
   159  	return nil
   160  }
   161  
   162  // AddSecret inserts a received secret into the provisioner's
   163  // secret channel.
   164  func (e *Kex2Provisioner) AddSecret(s kex2.Secret) {
   165  	e.secretCh <- s
   166  }
   167  
   168  // GetLogFactory implements GetLogFactory in kex2.Provisioner.
   169  func (e *Kex2Provisioner) GetLogFactory() rpc.LogFactory {
   170  	return rpc.NewSimpleLogFactory(e.G().Log, nil)
   171  }
   172  
   173  // GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisioner.
   174  func (e *Kex2Provisioner) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage {
   175  	return e.G().RemoteNetworkInstrumenterStorage
   176  }
   177  
   178  // GetHelloArg implements GetHelloArg in kex2.Provisioner.
   179  func (e *Kex2Provisioner) GetHelloArg() (arg keybase1.HelloArg, err error) {
   180  
   181  	// Pull the metaContext out of the this object, since we can't pass it through the
   182  	// kex2/provisioner interface
   183  	m := e.mctx
   184  
   185  	defer m.Trace("Kex2Provisioner#GetHelloArg()", &err)()
   186  
   187  	_ = m.UIs().ProvisionUI.DisplaySecretExchanged(context.Background(), 0)
   188  
   189  	// get a session token that device Y can use
   190  	mctx := libkb.NewMetaContextBackground(e.G())
   191  	tokener, err := libkb.NewSessionTokener(mctx)
   192  	if err != nil {
   193  		return arg, err
   194  	}
   195  	token, csrf := tokener.Tokens()
   196  
   197  	// generate a skeleton key proof
   198  	sigBody, err := e.skeletonProof(m)
   199  	if err != nil {
   200  		return arg, err
   201  	}
   202  
   203  	// return the HelloArg
   204  	arg = keybase1.HelloArg{
   205  		Uid:     e.me.GetUID(),
   206  		Pps:     e.pps,
   207  		Token:   keybase1.SessionToken(token),
   208  		Csrf:    keybase1.CsrfToken(csrf),
   209  		SigBody: sigBody,
   210  	}
   211  	return arg, nil
   212  }
   213  
   214  // GetHello2Arg implements GetHello2Arg in kex2.Provisioner.
   215  func (e *Kex2Provisioner) GetHello2Arg() (arg2 keybase1.Hello2Arg, err error) {
   216  	// Pull the metaContext out of the this object, since we can't pass it through the
   217  	// kex2/provisioner interface
   218  	m := e.mctx
   219  
   220  	defer m.Trace("Kex2Provisioner#GetHello2Arg", &err)()
   221  
   222  	var arg1 keybase1.HelloArg
   223  	arg1, err = e.GetHelloArg()
   224  	if err != nil {
   225  		return arg2, err
   226  	}
   227  
   228  	arg2 = keybase1.Hello2Arg{
   229  		Uid:     arg1.Uid,
   230  		Token:   arg1.Token,
   231  		Csrf:    arg1.Csrf,
   232  		SigBody: arg1.SigBody,
   233  	}
   234  	return arg2, nil
   235  }
   236  
   237  // CounterSign implements CounterSign in kex2.Provisioner.
   238  func (e *Kex2Provisioner) CounterSign(input keybase1.HelloRes) (sig []byte, err error) {
   239  	m := e.mctx
   240  	defer m.Trace("Kex2Provisioner#CounterSign", &err)()
   241  
   242  	jw, err := jsonw.Unmarshal([]byte(input))
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	// check the reverse signature and put the values from the provisionee into
   248  	// e.proof
   249  	if err = e.checkReverseSig(jw); err != nil {
   250  		m.Debug("provisioner failed to verify reverse sig: %s", err)
   251  		return nil, err
   252  	}
   253  	m.Debug("provisioner verified reverse sig")
   254  
   255  	// remember some device information for ProvisionUI.ProvisionerSuccess()
   256  	if err = e.rememberDeviceInfo(e.proof); err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	// sign the whole thing with provisioner's signing key
   261  	s, _, _, err := libkb.SignJSON(e.proof, e.signingKey)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	return []byte(s), nil
   267  }
   268  
   269  // CounterSign2 implements CounterSign in kex2.Provisioner.
   270  func (e *Kex2Provisioner) CounterSign2(input keybase1.Hello2Res) (output keybase1.DidCounterSign2Arg, err error) {
   271  
   272  	m := e.mctx
   273  
   274  	defer m.Trace("Kex2Provisioner#CounterSign2", &err)()
   275  	var key libkb.GenericKey
   276  	key, err = libkb.ImportKeypairFromKID(input.EncryptionKey)
   277  	if err != nil {
   278  		return output, err
   279  	}
   280  
   281  	output.Sig, err = e.CounterSign(input.SigPayload)
   282  	if err != nil {
   283  		return output, err
   284  	}
   285  
   286  	var ppsPacked []byte
   287  	ppsPacked, err = msgpack.Encode(e.pps)
   288  	if err != nil {
   289  		return output, err
   290  	}
   291  	output.PpsEncrypted, err = key.EncryptToString(ppsPacked, nil)
   292  
   293  	// Sync the PUK, if the pukring is nil, we don't have a PUK and have
   294  	// nothing to box. We also can't make a userEKBox which is signed by the
   295  	// PUK.
   296  	pukring, err := e.syncPUK(m)
   297  	if err != nil || pukring == nil {
   298  		return output, err
   299  	}
   300  
   301  	output.PukBox, err = e.makePukBox(m, pukring, key)
   302  	if err != nil {
   303  		return output, err
   304  	}
   305  
   306  	userEKBoxStorage := m.G().GetUserEKBoxStorage()
   307  	if input.DeviceEkKID.Exists() && userEKBoxStorage != nil {
   308  		// If we error out here the provisionee will create it's own keys later
   309  		// but we shouldn't fail kex.
   310  		userEKBox, ekErr := makeUserEKBoxForProvisionee(m, input.DeviceEkKID)
   311  		if ekErr != nil {
   312  			userEKBox = nil
   313  			m.Debug("Unable to makeUserEKBox %v", ekErr)
   314  		}
   315  		output.UserEkBox = userEKBox
   316  	} else {
   317  		m.Debug("Skipping userEKBox generation empty KID or storage. KID: %v, storage: %v", input.DeviceEkKID, userEKBoxStorage)
   318  	}
   319  
   320  	return output, nil
   321  }
   322  
   323  // skeletonProof generates a partial key proof structure that device Y can
   324  // fill in. When verifying the reverse signature we fill in the values from Y
   325  // to check the reverse signature
   326  func (e *Kex2Provisioner) skeletonProof(m libkb.MetaContext) (sigBody string, err error) {
   327  
   328  	// Set the local sigchain guard to tell background tasks
   329  	// to stay off the sigchain while we do this.
   330  	// This is released at the end of Kex2Provisioner#Run
   331  	e.G().LocalSigchainGuard().Set(context.TODO(), "Kex2Provisioner")
   332  
   333  	// reload the self user to make sure it is fresh
   334  	// (this fixes TestProvisionWithRevoke [CORE-5631, CORE-5636])
   335  	if err := e.loadMe(); err != nil {
   336  		return "", err
   337  	}
   338  
   339  	delg := libkb.Delegator{
   340  		ExistingKey:    e.signingKey,
   341  		Me:             e.me,
   342  		DelegationType: libkb.DelegationTypeSibkey,
   343  		Expire:         libkb.NaclEdDSAExpireIn,
   344  		Contextified:   libkb.NewContextified(e.G()),
   345  	}
   346  
   347  	e.proof, err = libkb.KeyProof(m, delg)
   348  	if err != nil {
   349  		return "", err
   350  	}
   351  	body, err := e.proof.Marshal()
   352  	if err != nil {
   353  		return "", err
   354  	}
   355  	return string(body), nil
   356  }
   357  
   358  // checkReverseSig verifies that the reverse sig in jw is valid and matches
   359  // e.proof. The provisionee is only allowed to pass the following fields to the
   360  // provisioner:
   361  // body.device
   362  // body.sibkey.kid
   363  // The values at these paths in the json reserialized and are inserted into the
   364  // skeleton proof that we initially passed to the provisionee so we can ensure
   365  // no other values were added when verifying the signature.
   366  func (e *Kex2Provisioner) checkReverseSig(jw *jsonw.Wrapper) error {
   367  	kid, err := jw.AtPath("body.sibkey.kid").GetString()
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	keypair, err := libkb.ImportKeypairFromKID(keybase1.KIDFromString(kid))
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	revsig, err := jw.AtPath("body.sibkey.reverse_sig").GetString()
   378  	if err != nil {
   379  		return err
   380  	}
   381  
   382  	// set reverse_sig to nil to verify it:
   383  	err = e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewNil())
   384  	if err != nil {
   385  		return err
   386  	}
   387  
   388  	// Copy known fields that provisionee set into e.proof
   389  	deviceWrapper := jw.AtPath("body.device")
   390  	// NOTE the time value is dropped during Export, value here is arbitrary.
   391  	device, err := libkb.ParseDevice(deviceWrapper, time.Now())
   392  	if err != nil {
   393  		return err
   394  	}
   395  	dw, err := device.Export(libkb.LinkType(libkb.DelegationTypeSibkey))
   396  	if err != nil {
   397  		return err
   398  	}
   399  	err = e.proof.SetValueAtPath("body.device", dw)
   400  	if err != nil {
   401  		return err
   402  	}
   403  	err = e.proof.SetValueAtPath("body.sibkey.kid", jsonw.NewString(kid))
   404  	if err != nil {
   405  		return err
   406  	}
   407  
   408  	msg, err := e.proof.Marshal()
   409  	if err != nil {
   410  		return err
   411  	}
   412  	_, err = keypair.VerifyString(e.G().Log, revsig, msg)
   413  	if err != nil {
   414  		return err
   415  	}
   416  
   417  	// put reverse_sig back in
   418  	return e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewString(revsig))
   419  }
   420  
   421  // rememberDeviceInfo saves the device name and type in
   422  // Kex2Provisioner for later use.
   423  func (e *Kex2Provisioner) rememberDeviceInfo(jw *jsonw.Wrapper) error {
   424  	name, err := jw.AtPath("body.device.name").GetString()
   425  	if err != nil {
   426  		return err
   427  	}
   428  	e.provisioneeDeviceName = name
   429  
   430  	dtype, err := jw.AtPath("body.device.type").GetString()
   431  	if err != nil {
   432  		return err
   433  	}
   434  	e.provisioneeDeviceType, err = keybase1.StringToDeviceTypeV2(dtype)
   435  
   436  	return err
   437  }
   438  
   439  // Returns nil if there are no per-user-keys.
   440  func (e *Kex2Provisioner) syncPUK(m libkb.MetaContext) (*libkb.PerUserKeyring, error) {
   441  	pukring, err := e.G().GetPerUserKeyring(m.Ctx())
   442  	if err != nil {
   443  		return nil, err
   444  	}
   445  	if err = pukring.Sync(m); err != nil {
   446  		return nil, err
   447  	}
   448  	if !pukring.HasAnyKeys() {
   449  		return nil, nil
   450  	}
   451  	return pukring, nil
   452  }
   453  
   454  func (e *Kex2Provisioner) makePukBox(m libkb.MetaContext, pukring *libkb.PerUserKeyring, receiverKeyGeneric libkb.GenericKey) (*keybase1.PerUserKeyBox, error) {
   455  	receiverKey, ok := receiverKeyGeneric.(libkb.NaclDHKeyPair)
   456  	if !ok {
   457  		return nil, fmt.Errorf("Unexpected receiver key type")
   458  	}
   459  
   460  	pukBox, err := pukring.PrepareBoxForNewDevice(m,
   461  		receiverKey,     // receiver key: provisionee enc
   462  		e.encryptionKey) // sender key: this device enc
   463  	return &pukBox, err
   464  }
   465  
   466  func (e *Kex2Provisioner) loadMe() error {
   467  	var err error
   468  	e.me, err = libkb.LoadMe(libkb.NewLoadUserArg(e.G()))
   469  	return err
   470  }