github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/rekey_master.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 service
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"time"
    10  
    11  	gregor "github.com/keybase/client/go/gregor"
    12  	"github.com/keybase/client/go/libkb"
    13  	gregor1 "github.com/keybase/client/go/protocol/gregor1"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    16  	context "golang.org/x/net/context"
    17  )
    18  
    19  const TLFRekeyGregorCategory = "kbfs_tlf_rekey_needed"
    20  
    21  // rekeyMaster is the object that controls all rekey harassment in the keybase service.
    22  // There should only be one such object per service process.
    23  type rekeyMaster struct {
    24  	libkb.Contextified
    25  
    26  	// The rekeyMaster is usually sleeping, but its sleep can be interrupted by various
    27  	// exogenous and internal events. These events are sent into this channel.
    28  	interruptCh chan interruptArg
    29  
    30  	ui            *RekeyUI
    31  	uiRouter      *UIRouter
    32  	sleepUntil    time.Time
    33  	plannedWakeup time.Time
    34  	uiNeeded      bool
    35  	uiVisible     bool
    36  
    37  	// We need to be able to access the gregor full state to see if there are any
    38  	// TLF rekey events. We should only be running if there are (since we want to respect
    39  	// the 3-minute delay on rekey harassment after a new key is added).
    40  	gregor *gregorHandler
    41  }
    42  
    43  type RekeyInterrupt int
    44  
    45  const (
    46  	RekeyInterruptNone       RekeyInterrupt = 0
    47  	RekeyInterruptTimeout    RekeyInterrupt = 1
    48  	RekeyInterruptCreation   RekeyInterrupt = 2
    49  	RekeyInterruptDismissal  RekeyInterrupt = 3
    50  	RekeyInterruptLogout     RekeyInterrupt = 4
    51  	RekeyInterruptLogin      RekeyInterrupt = 5
    52  	RekeyInterruptUIFinished RekeyInterrupt = 6
    53  	RekeyInterruptShowUI     RekeyInterrupt = 7
    54  	RekeyInterruptNewUI      RekeyInterrupt = 8
    55  	RekeyInterruptSync       RekeyInterrupt = 9
    56  	RekeyInterruptSyncForce  RekeyInterrupt = 10
    57  )
    58  
    59  type interruptArg struct {
    60  	retCh          chan struct{}
    61  	rekeyInterrupt RekeyInterrupt
    62  }
    63  
    64  func newRekeyMaster(g *libkb.GlobalContext) *rekeyMaster {
    65  	return &rekeyMaster{
    66  		Contextified: libkb.NewContextified(g),
    67  		interruptCh:  make(chan interruptArg),
    68  	}
    69  }
    70  
    71  func (r *rekeyMaster) Start() {
    72  	go r.mainLoop()
    73  }
    74  
    75  func (r *rekeyMaster) IsAlive() bool {
    76  	return true
    77  }
    78  
    79  func (r *rekeyMaster) Name() string {
    80  	return "rekeyMaster"
    81  }
    82  
    83  func (r *rekeyMaster) Create(ctx context.Context, cli gregor1.IncomingInterface, category string, ibm gregor.Item) (bool, error) {
    84  	switch category {
    85  	case TLFRekeyGregorCategory:
    86  		r.G().Log.Debug("incoming gregor: %+v", ibm)
    87  		return true, r.handleGregorCreation()
    88  	default:
    89  		return true, nil
    90  	}
    91  }
    92  
    93  func (r *rekeyMaster) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string, ibm gregor.Item) (bool, error) {
    94  	switch category {
    95  	case TLFRekeyGregorCategory:
    96  		return true, r.handleGregorDismissal()
    97  	default:
    98  		return true, nil
    99  	}
   100  }
   101  
   102  var _ libkb.GregorInBandMessageHandler = (*rekeyMaster)(nil)
   103  
   104  func (r *rekeyMaster) handleGregorCreation() error {
   105  	r.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptCreation}
   106  	return nil
   107  }
   108  
   109  func (r *rekeyMaster) handleGregorDismissal() error {
   110  	r.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptDismissal}
   111  	return nil
   112  }
   113  
   114  func (r *rekeyMaster) Logout() {
   115  	// Beware deadlocks here! See CORE-3690 for an example. We sometimes
   116  	// block on login state to make an API call. But we don't want
   117  	// LoginState to block on us during a logout call, so send this one
   118  	// async
   119  	go func() {
   120  		r.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptLogout}
   121  	}()
   122  }
   123  
   124  func (r *rekeyMaster) Login() {
   125  	// See comment about Logout() for deadlock avoidance.
   126  	go func() {
   127  		r.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptLogin}
   128  	}()
   129  }
   130  
   131  func (r *rekeyMaster) newUIRegistered() {
   132  	r.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptNewUI}
   133  }
   134  
   135  const (
   136  	rekeyTimeoutBackground      = 24 * time.Hour
   137  	rekeyTimeoutAPIError        = 3 * time.Minute
   138  	rekeyTimeoutLoadMeError     = 3 * time.Minute
   139  	rekeyTimeoutDeviceLoadError = 3 * time.Minute
   140  	rekeyTimeoutActive          = 1 * time.Minute
   141  	rekeyTimeoutUIFinished      = 24 * time.Hour
   142  )
   143  
   144  type rekeyQueryResult struct {
   145  	Status     libkb.AppStatus     `json:"status"`
   146  	ProblemSet keybase1.ProblemSet `json:"problem_set"`
   147  }
   148  
   149  func (r *rekeyQueryResult) GetAppStatus() *libkb.AppStatus {
   150  	return &r.Status
   151  }
   152  
   153  func queryAPIServerForRekeyInfo(g *libkb.GlobalContext) (keybase1.ProblemSet, error) {
   154  
   155  	// Calling with the clear=true boolean means the server will potentially
   156  	// clear all gregor messages as a side-effect of the lookup. Hence the
   157  	// POST rather than GET for this operation.
   158  	args := libkb.HTTPArgs{
   159  		"clear": libkb.B{Val: true},
   160  	}
   161  
   162  	var tmp rekeyQueryResult
   163  	mctx := libkb.NewMetaContextBackground(g)
   164  	err := g.API.PostDecode(mctx, libkb.APIArg{
   165  		Endpoint:    "kbfs/problem_sets",
   166  		SessionType: libkb.APISessionTypeREQUIRED,
   167  		Args:        args,
   168  	}, &tmp)
   169  
   170  	return tmp.ProblemSet, err
   171  }
   172  
   173  func (r *rekeyMaster) continueSleep(ri RekeyInterrupt) (ret time.Duration) {
   174  
   175  	r.G().Log.Debug("+ rekeyMaster#continueSleep")
   176  	defer func() {
   177  		r.G().Log.Debug("- rekeyMaster#continueSleep -> %s", ret)
   178  	}()
   179  
   180  	if r.sleepUntil.IsZero() {
   181  		return ret
   182  	}
   183  
   184  	dur := r.sleepUntil.Sub(r.G().Clock().Now())
   185  
   186  	if dur <= 0 {
   187  		r.G().Log.Debug("| Snooze deadline exceeded (%s ago)", -dur)
   188  		r.sleepUntil = time.Time{}
   189  		return ret
   190  	}
   191  
   192  	if ri == RekeyInterruptLogin {
   193  		r.G().Log.Debug("| resetting sleep until after new login")
   194  		r.sleepUntil = time.Time{}
   195  		return ret
   196  	}
   197  
   198  	r.G().Log.Debug("| Sleeping until %s (%s more)", r.sleepUntil, dur)
   199  	return dur
   200  }
   201  
   202  func (r *rekeyMaster) resumeSleep() time.Duration {
   203  	if r.plannedWakeup.IsZero() {
   204  		return rekeyTimeoutBackground
   205  	}
   206  	if ret := r.plannedWakeup.Sub(r.G().Clock().Now()); ret > 0 {
   207  		return ret
   208  	}
   209  	return rekeyTimeoutActive
   210  }
   211  
   212  func (r *rekeyMaster) runOnce(ri RekeyInterrupt) (ret time.Duration, err error) {
   213  	defer r.G().Trace(fmt.Sprintf("rekeyMaster#runOnce(%d) [%p]", ri, r), &err)()
   214  
   215  	var problemsAndDevices *keybase1.ProblemSetDevices
   216  	var event keybase1.RekeyEvent
   217  
   218  	if ri == RekeyInterruptUIFinished {
   219  		ret = rekeyTimeoutUIFinished
   220  		r.uiVisible = false
   221  		r.sleepUntil = r.G().Clock().Now().Add(ret)
   222  		r.G().Log.Debug("| UI said finished; sleeping %s [%p]", ret, r)
   223  		return ret, nil
   224  	}
   225  
   226  	if ri == RekeyInterruptNewUI && !r.uiNeeded {
   227  		r.G().Log.Debug("| we got a new UI but didn't need it; resuming sleep")
   228  		return r.resumeSleep(), nil
   229  	}
   230  
   231  	if ri != RekeyInterruptSyncForce && ri != RekeyInterruptShowUI {
   232  		if ret = r.continueSleep(ri); ret > 0 {
   233  			r.G().Log.Debug("| Skipping compute and act due to sleep state")
   234  			return ret, nil
   235  		}
   236  	}
   237  
   238  	// compute which folders if any have problems
   239  	ret, problemsAndDevices, event, err = r.computeProblems()
   240  	if err != nil {
   241  		return ret, err
   242  	}
   243  
   244  	// sendRekeyEvent sends a debug message to the UI (useful only in testing)
   245  	event.InterruptType = int(ri)
   246  	err = r.sendRekeyEvent(event)
   247  	if err != nil {
   248  		return ret, err
   249  	}
   250  
   251  	err = r.actOnProblems(problemsAndDevices, event)
   252  	return ret, err
   253  }
   254  
   255  func (r *rekeyMaster) getUI() (ret *RekeyUI, err error) {
   256  	ret, err = r.uiRouter.getOrReuseRekeyUI(r.ui)
   257  	r.ui = ret
   258  	return ret, err
   259  }
   260  
   261  func (r *rekeyMaster) clearUI() (err error) {
   262  	defer r.G().Trace("rekeyMaster#clearUI", &err)()
   263  
   264  	if !r.uiVisible {
   265  		r.G().Log.Debug("| no need to clear the UI; UI wasn't visible")
   266  		return nil
   267  	}
   268  
   269  	var ui *RekeyUI
   270  	ui, err = r.getUI()
   271  
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	if ui == nil {
   277  		r.uiVisible = false
   278  		r.G().Log.Debug("| UI wasn't active, so nothing to do")
   279  		return nil
   280  	}
   281  
   282  	err = ui.Refresh(context.Background(), keybase1.RefreshArg{})
   283  
   284  	if err == nil {
   285  		r.uiVisible = false
   286  	}
   287  
   288  	return err
   289  }
   290  
   291  func (r *rekeyMaster) spawnOrRefreshUI(problemSetDevices keybase1.ProblemSetDevices) (err error) {
   292  	defer r.G().Trace("rekeyMaster#spawnOrRefreshUI", &err)()
   293  
   294  	var ui *RekeyUI
   295  	ui, err = r.getUI()
   296  	if err != nil {
   297  		return err
   298  	}
   299  
   300  	if ui == nil {
   301  		r.G().Log.Info("| Rekey needed, but no active UI; consult logs")
   302  		r.uiNeeded = true
   303  		return nil
   304  	}
   305  
   306  	r.uiNeeded = false
   307  	r.uiVisible = true
   308  
   309  	err = ui.Refresh(context.Background(), keybase1.RefreshArg{ProblemSetDevices: problemSetDevices})
   310  	return err
   311  }
   312  
   313  // sendRekeyEvent sends notification of a rekey event to the UI. It's largely
   314  // used for testing.
   315  func (r *rekeyMaster) sendRekeyEvent(e keybase1.RekeyEvent) (err error) {
   316  	defer r.G().Trace(fmt.Sprintf("rekeyMaster#sendRekeyEvent(%v)", e), &err)()
   317  
   318  	if e.InterruptType == int(RekeyInterruptSync) {
   319  		r.G().Log.Debug("| No need to send a rekey event on a Sync() RPC")
   320  		return nil
   321  	}
   322  
   323  	var ui *RekeyUI
   324  	ui, err = r.getUI()
   325  	if err != nil {
   326  		return err
   327  	}
   328  	if ui == nil {
   329  		r.G().Log.Debug("| no UI; not sending event information")
   330  		return nil
   331  	}
   332  	err = ui.RekeySendEvent(context.Background(), keybase1.RekeySendEventArg{Event: e})
   333  	return err
   334  }
   335  
   336  func (r *rekeyMaster) actOnProblems(problemsAndDevices *keybase1.ProblemSetDevices, event keybase1.RekeyEvent) (err error) {
   337  	defer r.G().Trace(fmt.Sprintf("rekeyMaster#actOnProblems(%v)", problemsAndDevices != nil), &err)()
   338  
   339  	if problemsAndDevices == nil {
   340  		err = r.clearUI()
   341  		return err
   342  	}
   343  
   344  	err = r.spawnOrRefreshUI(*problemsAndDevices)
   345  	return err
   346  }
   347  
   348  func (r *rekeyMaster) hasGregorTLFRekeyMessages() (ret bool, err error) {
   349  	defer r.G().Trace("hasGregorTLFRekeyMessages", &err)()
   350  
   351  	var state gregor1.State
   352  	state, err = r.gregor.getState(context.Background())
   353  	if err != nil {
   354  		return false, err
   355  	}
   356  
   357  	for _, item := range state.Items_ {
   358  		if item.Item_ != nil && string(item.Item_.Category_) == TLFRekeyGregorCategory {
   359  			return true, nil
   360  		}
   361  	}
   362  	return false, nil
   363  }
   364  
   365  func (r *rekeyMaster) computeProblems() (nextWait time.Duration, problemsAndDevices *keybase1.ProblemSetDevices, event keybase1.RekeyEvent, err error) {
   366  	defer r.G().Trace("rekeyMaster#computeProblems", &err)()
   367  
   368  	if !r.G().ActiveDevice.Valid() {
   369  		r.G().Log.Debug("| not logged in")
   370  		nextWait = rekeyTimeoutBackground
   371  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_NOT_LOGGED_IN}, err
   372  	}
   373  
   374  	r.G().Log.Debug("| rekeyMaster#computeProblems: logged in")
   375  
   376  	hasGregor, err := r.hasGregorTLFRekeyMessages()
   377  	if err != nil {
   378  		nextWait = rekeyTimeoutAPIError
   379  		r.G().Log.Debug("| snoozing rekeyMaster for %ds on gregor error", nextWait)
   380  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_API_ERROR}, err
   381  	}
   382  
   383  	if !hasGregor {
   384  		r.G().Log.Debug("| has no gregor TLF rekey messages")
   385  		nextWait = rekeyTimeoutBackground
   386  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_NO_GREGOR_MESSAGES}, err
   387  	}
   388  
   389  	r.G().Log.Debug("| rekeyMaster#computeProblems: has gregor")
   390  
   391  	var problems keybase1.ProblemSet
   392  	problems, err = queryAPIServerForRekeyInfo(r.G())
   393  	if err != nil {
   394  		nextWait = rekeyTimeoutAPIError
   395  		r.G().Log.Debug("| snoozing rekeyMaster for %ds on API error", nextWait)
   396  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_API_ERROR}, err
   397  	}
   398  
   399  	r.G().Log.Debug("| rekeyMaster#computeProblems: queried API server for rekey info")
   400  
   401  	if len(problems.Tlfs) == 0 {
   402  		r.G().Log.Debug("| no problem TLFs found")
   403  
   404  		nextWait = rekeyTimeoutBackground
   405  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_NO_PROBLEMS}, err
   406  	}
   407  
   408  	var me *libkb.User
   409  	me, err = libkb.LoadMe(libkb.NewLoadUserArg(r.G()))
   410  	r.G().Log.Debug("| rekeyMaster#computeProblems: loaded me")
   411  
   412  	if err != nil {
   413  		nextWait = rekeyTimeoutLoadMeError
   414  		r.G().Log.Debug("| snoozing rekeyMaster for %ds on LoadMe error", nextWait)
   415  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_LOAD_ME_ERROR}, err
   416  	}
   417  
   418  	if r.currentDeviceSolvesProblemSet(me, problems) {
   419  		nextWait = rekeyTimeoutBackground
   420  		r.G().Log.Debug("| snoozing rekeyMaster since current device can rekey all")
   421  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_CURRENT_DEVICE_CAN_REKEY}, err
   422  	}
   423  
   424  	r.G().Log.Debug("| rekeyMaster#computeProblems: current device computed")
   425  
   426  	var tmp keybase1.ProblemSetDevices
   427  	tmp, err = newProblemSetDevices(me, problems)
   428  	if err != nil {
   429  		nextWait = rekeyTimeoutDeviceLoadError
   430  		r.G().Log.Debug("| hit error in loading devices")
   431  		return nextWait, nil, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_DEVICE_LOAD_ERROR}, err
   432  	}
   433  
   434  	r.G().Log.Debug("| rekeyMaster#computeProblems: made problem set devices")
   435  
   436  	nextWait = rekeyTimeoutActive
   437  	return nextWait, &tmp, keybase1.RekeyEvent{EventType: keybase1.RekeyEventType_HARASS}, err
   438  }
   439  
   440  // currentDeviceSolvesProblemSet returns true if the current device can fix all
   441  // of the folders in the ProblemSet.
   442  func (r *rekeyMaster) currentDeviceSolvesProblemSet(me *libkb.User, ps keybase1.ProblemSet) (ret bool) {
   443  	r.G().Log.Debug("+ currentDeviceSolvesProblemSet")
   444  	defer func() {
   445  		r.G().Log.Debug("- currentDeviceSolvesProblemSet -> %v\n", ret)
   446  	}()
   447  
   448  	var paperKey libkb.GenericKey
   449  	deviceKey, err := me.GetDeviceSubkey()
   450  	if err != nil {
   451  		r.G().Log.Info("| Problem getting device subkey: %s\n", err)
   452  		return ret
   453  	}
   454  
   455  	m := libkb.NewMetaContextBackground(r.G())
   456  	if d := m.ActiveDevice().ProvisioningKey(m); d != nil {
   457  		paperKey = d.EncryptionKey()
   458  	}
   459  
   460  	// We can continue though, so no need to error out
   461  	if paperKey == nil {
   462  		m.Debug("| No cached paper key")
   463  	}
   464  	if deviceKey != nil {
   465  		r.G().Log.Debug("| currentDeviceSolvesProblemSet: checking device key: %s", deviceKey.GetKID())
   466  	}
   467  	if paperKey != nil {
   468  		r.G().Log.Debug("| currentDeviceSolvesProblemSet: checking paper key: %s", paperKey.GetKID())
   469  	}
   470  
   471  	for _, tlf := range ps.Tlfs {
   472  		if !keysSolveProblemTLF([]libkb.GenericKey{deviceKey, paperKey}, tlf) {
   473  			r.G().Log.Debug("| Doesn't solve problem TLF: %s (%s)\n", tlf.Tlf.Name, tlf.Tlf.Id)
   474  			return ret
   475  		}
   476  	}
   477  	ret = true
   478  	return ret
   479  }
   480  
   481  func (r *rekeyMaster) mainLoop() {
   482  
   483  	// Sleep about ten seconds on startup so as to wait for startup sequence.
   484  	// It's ok if we race here, but it's less work if we don't.
   485  	timeout := 10 * time.Second
   486  
   487  	for {
   488  
   489  		var it RekeyInterrupt
   490  		var interruptArg interruptArg
   491  
   492  		select {
   493  		case interruptArg = <-r.interruptCh:
   494  			it = interruptArg.rekeyInterrupt
   495  		case <-r.G().Clock().After(timeout):
   496  			it = RekeyInterruptTimeout
   497  		}
   498  
   499  		timeout, _ = r.runOnce(it)
   500  		if retCh := interruptArg.retCh; retCh != nil {
   501  			retCh <- struct{}{}
   502  		}
   503  		r.plannedWakeup = r.G().Clock().Now().Add(timeout)
   504  	}
   505  }
   506  
   507  type RekeyHandler2 struct {
   508  	libkb.Contextified
   509  	*BaseHandler
   510  	rm *rekeyMaster
   511  }
   512  
   513  func NewRekeyHandler2(xp rpc.Transporter, g *libkb.GlobalContext, rm *rekeyMaster) *RekeyHandler2 {
   514  	return &RekeyHandler2{
   515  		Contextified: libkb.NewContextified(g),
   516  		BaseHandler:  NewBaseHandler(g, xp),
   517  		rm:           rm,
   518  	}
   519  }
   520  
   521  func (r *RekeyHandler2) ShowPendingRekeyStatus(context.Context, int) error {
   522  	r.rm.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptShowUI}
   523  	return nil
   524  }
   525  
   526  func (r *RekeyHandler2) GetPendingRekeyStatus(_ context.Context, _ int) (ret keybase1.ProblemSetDevices, err error) {
   527  	var me *libkb.User
   528  	me, err = libkb.LoadMe(libkb.NewLoadUserArg(r.G()))
   529  	if err != nil {
   530  		return ret, err
   531  	}
   532  	var problemSet keybase1.ProblemSet
   533  	problemSet, err = queryAPIServerForRekeyInfo(r.G())
   534  	if err != nil {
   535  		return ret, err
   536  	}
   537  	ret, err = newProblemSetDevices(me, problemSet)
   538  	return ret, err
   539  }
   540  
   541  func (r *RekeyHandler2) RekeyStatusFinish(_ context.Context, _ int) (ret keybase1.Outcome, err error) {
   542  	r.rm.interruptCh <- interruptArg{rekeyInterrupt: RekeyInterruptUIFinished}
   543  	ret = keybase1.Outcome_NONE
   544  	return ret, err
   545  }
   546  
   547  func (r *RekeyHandler2) DebugShowRekeyStatus(ctx context.Context, sessionID int) error {
   548  	if r.G().Env.GetRunMode() == libkb.ProductionRunMode {
   549  		return errors.New("DebugShowRekeyStatus is a devel-only RPC")
   550  	}
   551  
   552  	me, err := libkb.LoadMe(libkb.NewLoadUserArg(r.G()))
   553  	if err != nil {
   554  		return err
   555  	}
   556  
   557  	arg := keybase1.RefreshArg{
   558  		SessionID: sessionID,
   559  		ProblemSetDevices: keybase1.ProblemSetDevices{
   560  			ProblemSet: keybase1.ProblemSet{
   561  				User: keybase1.User{
   562  					Uid:      me.GetUID(),
   563  					Username: me.GetName(),
   564  				},
   565  				Tlfs: []keybase1.ProblemTLF{
   566  					{
   567  						Tlf: keybase1.TLF{
   568  							// this is only for debugging
   569  							Name:      "/keybase/private/" + me.GetName(),
   570  							Writers:   []string{me.GetName()},
   571  							Readers:   []string{me.GetName()},
   572  							IsPrivate: true,
   573  						},
   574  					},
   575  				},
   576  			},
   577  		},
   578  	}
   579  
   580  	devices := me.GetComputedKeyFamily().GetAllActiveDevices()
   581  	arg.ProblemSetDevices.Devices = make([]keybase1.Device, len(devices))
   582  	for i, dev := range devices {
   583  		arg.ProblemSetDevices.Devices[i] = *(dev.ProtExport())
   584  	}
   585  
   586  	rekeyUI, err := r.G().UIRouter.GetRekeyUINoSessionID()
   587  	if err != nil {
   588  		return err
   589  	}
   590  	if rekeyUI == nil {
   591  		r.G().Log.Debug("no rekey ui, would have called refresh with this:")
   592  		r.G().Log.Debug("arg: %+v", arg)
   593  		return errors.New("no rekey ui")
   594  	}
   595  
   596  	return rekeyUI.Refresh(ctx, arg)
   597  }
   598  
   599  type unkeyedTLFsQueryResult struct {
   600  	Status libkb.AppStatus `json:"status"`
   601  	TLFs   []keybase1.TLF  `json:"tlfs"`
   602  }
   603  
   604  func (u *unkeyedTLFsQueryResult) GetAppStatus() *libkb.AppStatus {
   605  	return &u.Status
   606  }
   607  
   608  func (r *RekeyHandler2) GetRevokeWarning(ctx context.Context, arg keybase1.GetRevokeWarningArg) (res keybase1.RevokeWarning, err error) {
   609  	var u unkeyedTLFsQueryResult
   610  	actingDevice := arg.ActingDevice
   611  	if actingDevice.IsNil() {
   612  		actingDevice = r.G().Env.GetDeviceID()
   613  	}
   614  	mctx := libkb.NewMetaContext(ctx, r.G())
   615  
   616  	err = r.G().API.GetDecode(mctx, libkb.APIArg{
   617  		Endpoint:    "kbfs/unkeyed_tlfs_from_pair",
   618  		SessionType: libkb.APISessionTypeREQUIRED,
   619  		Args: libkb.HTTPArgs{
   620  			"self_device_id":   libkb.S{Val: string(actingDevice)},
   621  			"target_device_id": libkb.S{Val: string(arg.TargetDevice)},
   622  		},
   623  	}, &u)
   624  	res.EndangeredTLFs = u.TLFs
   625  	return res, err
   626  }
   627  
   628  func (r *RekeyHandler2) RekeySync(_ context.Context, arg keybase1.RekeySyncArg) error {
   629  	ch := make(chan struct{})
   630  	ri := RekeyInterruptSync
   631  	if arg.Force {
   632  		ri = RekeyInterruptSyncForce
   633  	}
   634  	r.rm.interruptCh <- interruptArg{retCh: ch, rekeyInterrupt: ri}
   635  	<-ch
   636  	return nil
   637  }
   638  
   639  var _ keybase1.RekeyInterface = (*RekeyHandler2)(nil)
   640  
   641  func keysSolveProblemTLF(keys []libkb.GenericKey, tlf keybase1.ProblemTLF) bool {
   642  	var ourKIDs []keybase1.KID
   643  	for _, key := range keys {
   644  		if key != nil {
   645  			ourKIDs = append(ourKIDs, key.GetKID())
   646  		}
   647  	}
   648  	for _, theirKID := range tlf.Solution_kids {
   649  		for _, ourKID := range ourKIDs {
   650  			if ourKID.Equal(theirKID) {
   651  				return true
   652  			}
   653  		}
   654  	}
   655  	return false
   656  }
   657  
   658  func newProblemSetDevices(u *libkb.User, pset keybase1.ProblemSet) (keybase1.ProblemSetDevices, error) {
   659  	var set keybase1.ProblemSetDevices
   660  	set.ProblemSet = pset
   661  
   662  	ckf := u.GetComputedKeyFamily()
   663  
   664  	dset := make(map[keybase1.DeviceID]bool)
   665  	for _, f := range pset.Tlfs {
   666  		for _, kid := range f.Solution_kids {
   667  			dev, err := ckf.GetDeviceForKID(kid)
   668  			if err != nil {
   669  				return keybase1.ProblemSetDevices{}, err
   670  			}
   671  			if dset[dev.ID] {
   672  				continue
   673  			}
   674  			dset[dev.ID] = true
   675  			set.Devices = append(set.Devices, *(dev.ProtExport()))
   676  		}
   677  	}
   678  	return set, nil
   679  }