github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/saltpack_user_keyfinder.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  
     9  	"github.com/keybase/client/go/libkb"
    10  
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  // SaltpackUserKeyfinder is an engine to find Per User Keys (PUK). Users can also be loaded by assertions, possibly tracking them if necessary.
    15  // This engine does not find per team keys, which capability is implemented by SaltpackRecipientKeyfinder in the saltpackKeyHelpers package.
    16  type SaltpackUserKeyfinder struct {
    17  	Arg                           libkb.SaltpackRecipientKeyfinderArg
    18  	RecipientEntityKeyMap         map[keybase1.UserOrTeamID]([]keybase1.KID)
    19  	RecipientDeviceAndPaperKeyMap map[keybase1.UID]([]keybase1.KID)
    20  	UsingSBS                      bool
    21  	SBSAssertion                  string
    22  }
    23  
    24  var _ libkb.Engine2 = (*SaltpackUserKeyfinder)(nil)
    25  var _ libkb.SaltpackRecipientKeyfinderEngineInterface = (*SaltpackUserKeyfinder)(nil)
    26  
    27  // NewSaltpackUserKeyfinderAsInterface creates a SaltpackUserKeyfinder engine.
    28  func NewSaltpackUserKeyfinderAsInterface(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface {
    29  	return NewSaltpackUserKeyfinder(arg)
    30  }
    31  
    32  func NewSaltpackUserKeyfinder(arg libkb.SaltpackRecipientKeyfinderArg) *SaltpackUserKeyfinder {
    33  	return &SaltpackUserKeyfinder{
    34  		Arg:                           arg,
    35  		RecipientEntityKeyMap:         make(map[keybase1.UserOrTeamID]([]keybase1.KID)),
    36  		RecipientDeviceAndPaperKeyMap: make(map[keybase1.UID]([]keybase1.KID)),
    37  	}
    38  }
    39  
    40  // Name is the unique engine name.
    41  func (e *SaltpackUserKeyfinder) Name() string {
    42  	return "SaltpackUserKeyfinder"
    43  }
    44  
    45  // Prereqs returns the engine prereqs.
    46  func (e *SaltpackUserKeyfinder) Prereqs() Prereqs {
    47  	return Prereqs{}
    48  }
    49  
    50  // RequiredUIs returns the required UIs.
    51  func (e *SaltpackUserKeyfinder) RequiredUIs() []libkb.UIKind {
    52  	return []libkb.UIKind{}
    53  }
    54  
    55  // SubConsumers returns the other UI consumers for this engine.
    56  func (e *SaltpackUserKeyfinder) SubConsumers() []libkb.UIConsumer {
    57  	return []libkb.UIConsumer{}
    58  }
    59  
    60  func (e *SaltpackUserKeyfinder) GetPublicKIDs() []keybase1.KID {
    61  	var r []keybase1.KID
    62  	for _, keys := range e.RecipientDeviceAndPaperKeyMap {
    63  		r = append(r, keys...)
    64  	}
    65  	for _, keys := range e.RecipientEntityKeyMap {
    66  		r = append(r, keys...)
    67  	}
    68  
    69  	return r
    70  }
    71  
    72  func (e *SaltpackUserKeyfinder) GetSymmetricKeys() []libkb.SaltpackReceiverSymmetricKey {
    73  	return []libkb.SaltpackReceiverSymmetricKey{}
    74  }
    75  
    76  func (e *SaltpackUserKeyfinder) UsedUnresolvedSBSAssertion() (bool, string) {
    77  	return e.UsingSBS, e.SBSAssertion
    78  }
    79  
    80  func (e *SaltpackUserKeyfinder) Run(m libkb.MetaContext) (err error) {
    81  	defer m.Trace("SaltpackUserKeyfinder#Run", &err)()
    82  
    83  	if len(e.Arg.TeamRecipients) != 0 {
    84  		m.Debug("tried to use SaltpackUserKeyfinder for a team. This should never happen")
    85  		return fmt.Errorf("cannot find keys for teams")
    86  	}
    87  
    88  	err = e.AddOwnKeysIfNeeded(m)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	err = e.identifyAndAddRecipients(m)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  func (e *SaltpackUserKeyfinder) AddOwnKeysIfNeeded(m libkb.MetaContext) error {
   101  	if e.Arg.NoSelfEncrypt {
   102  		return nil
   103  	}
   104  	if !m.ActiveDevice().Valid() {
   105  		return libkb.NewLoginRequiredError("need to be logged in or use --no-self-encrypt")
   106  	}
   107  	arg := libkb.NewLoadUserArgWithMetaContext(m).WithUID(m.ActiveDevice().UID()).WithForcePoll(!e.Arg.NoForcePoll)
   108  	upak, _, err := m.G().GetUPAKLoader().LoadV2(arg)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	return e.AddUserRecipient(m, &upak.Current)
   113  }
   114  
   115  // identifyAndAddRecipients adds the KID corresponding to each recipient to the recipientMap
   116  func (e *SaltpackUserKeyfinder) identifyAndAddRecipients(m libkb.MetaContext) error {
   117  	for _, u := range e.Arg.Recipients {
   118  		// TODO make these lookups in parallel (maybe using sync.WaitGroup)
   119  		upk, err := e.IdentifyUser(m, u) // For existing users
   120  		switch {
   121  		case err == nil:
   122  			// nothing to do here
   123  		case libkb.IsIdentifyProofError(err):
   124  			return fmt.Errorf("Cannot encrypt for %v as their account has changed since you last followed them (it might have been compromised!): please review their identity (with `keybase follow %v`) and then try again (err = %v)", u, u, err)
   125  		case libkb.IsNotFoundError(err) || libkb.IsResolutionError(err):
   126  			return fmt.Errorf("Cannot find keys for %v: it is not an assertion for a registered user (err = %v)", u, err)
   127  		default:
   128  			return fmt.Errorf("Error while adding keys for %v: %v", u, err)
   129  		}
   130  		err = e.AddUserRecipient(m, upk)
   131  		if err != nil {
   132  			return err
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  func (e *SaltpackUserKeyfinder) IdentifyUser(m libkb.MetaContext, user string) (upk *keybase1.UserPlusKeysV2, err error) {
   139  
   140  	Arg := keybase1.Identify2Arg{
   141  		UserAssertion: user,
   142  		Reason: keybase1.IdentifyReason{
   143  			Type: keybase1.IdentifyReasonType_ENCRYPT,
   144  		},
   145  		AlwaysBlock:      true,
   146  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_SALTPACK,
   147  	}
   148  	eng := NewResolveThenIdentify2(m.G(), &Arg)
   149  	if err := RunEngine2(m, eng); err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	engRes, err := eng.Result(m)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	return &engRes.Upk.Current, nil
   159  }
   160  
   161  func (e *SaltpackUserKeyfinder) hasRecipientDeviceOrPaperKeys(id keybase1.UID) bool {
   162  	_, ok := e.RecipientDeviceAndPaperKeyMap[id]
   163  	return ok
   164  }
   165  
   166  func (e *SaltpackUserKeyfinder) hasRecipientEntityKeys(id keybase1.UserOrTeamID) bool {
   167  	_, ok := e.RecipientEntityKeyMap[id]
   168  	return ok
   169  }
   170  
   171  func (e *SaltpackUserKeyfinder) AddUserRecipient(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
   172  	err := e.AddDeviceAndPaperKeys(m, upk)
   173  	err2 := e.AddPUK(m, upk)
   174  
   175  	// If we managed to add at least one key for upk, we are happy.
   176  	if (!(e.Arg.UseDeviceKeys || e.Arg.UsePaperKeys) || err != nil) && (!e.Arg.UseEntityKeys || err2 != nil) {
   177  		return libkb.PickFirstError(err, err2)
   178  	}
   179  	return nil
   180  }
   181  
   182  func (e *SaltpackUserKeyfinder) isPaperEncryptionKey(key *keybase1.PublicKeyV2NaCl, deviceKeys *(map[keybase1.KID]keybase1.PublicKeyV2NaCl)) bool {
   183  	return libkb.KIDIsDeviceEncrypt(key.Base.Kid) && key.Parent != nil && (*deviceKeys)[*key.Parent].DeviceType == keybase1.DeviceTypeV2_PAPER
   184  }
   185  
   186  // AddPUK returns no error if it adds at least one key (or no paper keys and device keys were requested), otherwise it returns a libkb.NoNaClEncryptionKeyError
   187  func (e *SaltpackUserKeyfinder) AddDeviceAndPaperKeys(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
   188  	if !e.Arg.UsePaperKeys && !e.Arg.UseDeviceKeys {
   189  		// No need to add anything
   190  		return nil
   191  	}
   192  
   193  	if e.hasRecipientDeviceOrPaperKeys(upk.Uid) {
   194  		// This user's keys were already added
   195  		return nil
   196  	}
   197  
   198  	var keys []keybase1.KID
   199  
   200  	hasPaperKey := false
   201  	hasDeviceKey := false
   202  	hasPUK := len(upk.PerUserKeys) > 0
   203  
   204  	for KID, key := range upk.DeviceKeys {
   205  		// Note: for Nacl encryption keys, the DeviceType field is not set, so we need to look at the "parent" signing key
   206  		if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
   207  			hasPaperKey = true
   208  			if e.Arg.UsePaperKeys {
   209  				keys = append(keys, KID)
   210  				m.Debug("adding user %v's paper key", upk.Username)
   211  			}
   212  		}
   213  
   214  		if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
   215  			hasDeviceKey = true
   216  			if e.Arg.UseDeviceKeys {
   217  				keys = append(keys, KID)
   218  				m.Debug("adding user %v's device key", upk.Username)
   219  			}
   220  		}
   221  	}
   222  
   223  	e.RecipientDeviceAndPaperKeyMap[upk.Uid] = keys
   224  
   225  	if len(keys) == 0 {
   226  		m.Debug("did not add any device or paper keys for %v", upk.Username)
   227  		return libkb.NoNaClEncryptionKeyError{
   228  			Username:     upk.Username,
   229  			HasPGPKey:    len(upk.PGPKeys) > 0,
   230  			HasPUK:       hasPUK,
   231  			HasDeviceKey: hasDeviceKey,
   232  			HasPaperKey:  hasPaperKey,
   233  		}
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  // AddPUK returns no error unless the user has no PUK, in which case it returns a libkb.NoNaClEncryptionKeyError
   240  func (e *SaltpackUserKeyfinder) AddPUK(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
   241  	if !e.Arg.UseEntityKeys {
   242  		// No need to add anything
   243  		return nil
   244  	}
   245  
   246  	if e.hasRecipientEntityKeys(upk.Uid.AsUserOrTeam()) {
   247  		// This user's keys were already added
   248  		return nil
   249  	}
   250  
   251  	hasPUK := len(upk.PerUserKeys) > 0
   252  
   253  	if !hasPUK {
   254  		hasPaperKey := false
   255  		hasDeviceKey := false
   256  		for KID, key := range upk.DeviceKeys {
   257  			if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
   258  				hasPaperKey = true
   259  			}
   260  			if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
   261  				hasDeviceKey = true
   262  			}
   263  		}
   264  
   265  		m.Debug("did not add any per user keys for %v", upk.Username)
   266  		return libkb.NoNaClEncryptionKeyError{
   267  			Username:     upk.Username,
   268  			HasPGPKey:    len(upk.PGPKeys) > 0,
   269  			HasPUK:       hasPUK,
   270  			HasDeviceKey: hasDeviceKey,
   271  			HasPaperKey:  hasPaperKey,
   272  		}
   273  	}
   274  
   275  	// We ensured above that the user has a PUK, so the loop below will be executed at least once
   276  	maxGen := -1
   277  	var lk keybase1.KID
   278  	for _, k := range upk.PerUserKeys {
   279  		if k.Gen > maxGen {
   280  			maxGen = k.Gen
   281  			lk = k.EncKID
   282  		}
   283  	}
   284  	if lk == "" {
   285  		panic("This should never happen, user has a PUK with a nil KID")
   286  	}
   287  
   288  	m.Debug("adding user %v's latest per user key", upk.Username)
   289  	e.RecipientEntityKeyMap[upk.Uid.AsUserOrTeam()] = []keybase1.KID{lk}
   290  
   291  	return nil
   292  }