github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/puk_upkeep.go (about)

     1  // Copyright 2017 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  // PerUserKeyUpkeep rolls the user's per-user-key if the last PUK
     5  // was added by a now-revoked device.
     6  // Does not add a first per-user-key. Use PerUserKeyUpgrade for that.
     7  // This engine makes up for the fact that after a self-deprovision
     8  // the latest PUK for a user was generated on the very machine they
     9  // wanted to deprovision.
    10  // This will not notice if a device revoked another device but neglected
    11  // to roll the PUK. No clients should do that.
    12  package engine
    13  
    14  import (
    15  	"errors"
    16  
    17  	"github.com/keybase/client/go/libkb"
    18  	"github.com/keybase/client/go/protocol/keybase1"
    19  )
    20  
    21  // PerUserKeyUpkeep is an engine.
    22  type PerUserKeyUpkeep struct {
    23  	libkb.Contextified
    24  	args       *PerUserKeyUpkeepArgs
    25  	DidRollKey bool
    26  }
    27  
    28  type PerUserKeyUpkeepArgs struct{}
    29  
    30  // NewPerUserKeyUpkeep creates a PerUserKeyUpkeep engine.
    31  func NewPerUserKeyUpkeep(g *libkb.GlobalContext, args *PerUserKeyUpkeepArgs) *PerUserKeyUpkeep {
    32  	return &PerUserKeyUpkeep{
    33  		args:         args,
    34  		Contextified: libkb.NewContextified(g),
    35  	}
    36  }
    37  
    38  // Name is the unique engine name.
    39  func (e *PerUserKeyUpkeep) Name() string {
    40  	return "PerUserKeyUpkeep"
    41  }
    42  
    43  // GetPrereqs returns the engine prereqs.
    44  func (e *PerUserKeyUpkeep) Prereqs() Prereqs {
    45  	return Prereqs{
    46  		Device: true,
    47  	}
    48  }
    49  
    50  // RequiredUIs returns the required UIs.
    51  func (e *PerUserKeyUpkeep) RequiredUIs() []libkb.UIKind {
    52  	return []libkb.UIKind{}
    53  }
    54  
    55  // SubConsumers returns the other UI consumers for this engine.
    56  func (e *PerUserKeyUpkeep) SubConsumers() []libkb.UIConsumer {
    57  	return []libkb.UIConsumer{}
    58  }
    59  
    60  // Run starts the engine.
    61  func (e *PerUserKeyUpkeep) Run(m libkb.MetaContext) (err error) {
    62  	defer m.Trace("PerUserKeyUpkeep", &err)()
    63  	return e.inner(m)
    64  }
    65  
    66  func (e *PerUserKeyUpkeep) inner(m libkb.MetaContext) error {
    67  	m.Debug("PerUserKeyUpkeep load self")
    68  
    69  	uid := e.G().GetMyUID()
    70  	if uid.IsNil() {
    71  		return libkb.NoUIDError{}
    72  	}
    73  
    74  	loadArg := libkb.NewLoadUserArgWithMetaContext(m).
    75  		WithUID(uid).
    76  		WithSelf(true).
    77  		WithPublicKeyOptional()
    78  	upak, me, err := m.G().GetUPAKLoader().LoadV2(loadArg)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	// `me` could be nil.
    83  
    84  	var shouldRollKey bool
    85  	shouldRollKey, err = e.shouldRollKey(m, uid, &upak.Current)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	if !shouldRollKey {
    90  		m.Debug("PerUserKeyUpkeep skipping")
    91  		return nil
    92  	}
    93  
    94  	// Roll the key
    95  	m.Debug("PerUserKeyUpkeep rolling key")
    96  	arg := &PerUserKeyRollArgs{
    97  		Me: me,
    98  	}
    99  	eng := NewPerUserKeyRoll(m.G(), arg)
   100  	err = RunEngine2(m, eng)
   101  	e.DidRollKey = eng.DidNewKey
   102  	return err
   103  }
   104  
   105  // Whether we should roll the per-user-key.
   106  func (e *PerUserKeyUpkeep) shouldRollKey(m libkb.MetaContext, uid keybase1.UID, upak *keybase1.UserPlusKeysV2) (bool, error) {
   107  
   108  	if len(upak.PerUserKeys) == 0 {
   109  		m.Debug("PerUserKeyUpkeep has no per-user-key")
   110  		return false, nil
   111  	}
   112  	m.Debug("PerUserKeyUpkeep has %v per-user-keys", len(upak.PerUserKeys))
   113  
   114  	lastPuk := upak.PerUserKeys[len(upak.PerUserKeys)-1]
   115  	if !lastPuk.SignedByKID.IsValid() {
   116  		return false, errors.New("latest per-user-key had invalid signed-by KID")
   117  	}
   118  	m.Debug("PerUserKeyUpkeep last key signed by KID: %v", lastPuk.SignedByKID.String())
   119  	return !e.keyIsActiveSibkey(m, lastPuk.SignedByKID, upak), nil
   120  }
   121  
   122  func (e *PerUserKeyUpkeep) keyIsActiveSibkey(m libkb.MetaContext, kid keybase1.KID, upak *keybase1.UserPlusKeysV2) bool {
   123  	for _, dkey := range upak.DeviceKeys {
   124  		active := dkey.Base.Revocation == nil
   125  		if active && dkey.Base.IsSibkey && dkey.Base.Kid.Equal(kid) {
   126  			return true
   127  		}
   128  	}
   129  	return false
   130  }