github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/scankeys.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  	"sync"
     9  
    10  	"github.com/keybase/client/go/libkb"
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/keybase/go-crypto/openpgp"
    13  	"github.com/keybase/go-crypto/openpgp/packet"
    14  )
    15  
    16  // ScanKeys finds pgp decryption keys in SKB and also if there is
    17  // one stored on the server.  It satisfies the openpgp.KeyRing
    18  // interface.
    19  //
    20  // It also will find public pgp keys for signature verification.
    21  //
    22  // It is not an engine, but uses an engine and is used by engines,
    23  // so has to be in the engine package.  It is a UIConsumer.
    24  type ScanKeys struct {
    25  	// keys  openpgp.EntityList
    26  	skbs       []*libkb.SKB           // all skb blocks for local keys
    27  	keyOwners  map[uint64]*libkb.User // user objects for owners of keys found, for convenience
    28  	me         *libkb.User
    29  	sync.Mutex // protect keyOwners map
    30  	libkb.MetaContextified
    31  }
    32  
    33  const unlockReason = "PGP Decryption"
    34  
    35  // enforce ScanKeys implements openpgp.KeyRing:
    36  var _ openpgp.KeyRing = &ScanKeys{}
    37  
    38  // NewScanKeys creates a ScanKeys type.  If there is a login
    39  // session, it will load the pgp keys for that user.
    40  func NewScanKeys(m libkb.MetaContext) (sk *ScanKeys, err error) {
    41  	sk = &ScanKeys{
    42  		keyOwners:        make(map[uint64]*libkb.User),
    43  		MetaContextified: libkb.NewMetaContextified(m),
    44  	}
    45  
    46  	defer m.Trace("NewScanKeys", &err)()
    47  
    48  	var loggedIn bool
    49  	loggedIn, err = isLoggedInWithError(m)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	if !loggedIn {
    54  		return sk, nil
    55  	}
    56  
    57  	sk.me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
    58  	if err != nil {
    59  		return nil, fmt.Errorf("loadme error: %s", err)
    60  	}
    61  
    62  	// if user provided, then load their local keys, and their synced secret keys:
    63  	synced, err := sk.me.GetSyncedSecretKeys(m)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("getsyncedsecret err: %s", err)
    66  	}
    67  
    68  	ring, err := m.ActiveDevice().Keyring(m)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	err = sk.coalesceBlocks(m, ring, synced)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return sk, nil
    77  }
    78  
    79  func (s *ScanKeys) Name() string {
    80  	return "ScanKeys"
    81  }
    82  
    83  func (s *ScanKeys) RequiredUIs() []libkb.UIKind {
    84  	return []libkb.UIKind{libkb.SecretUIKind}
    85  }
    86  
    87  func (s *ScanKeys) SubConsumers() []libkb.UIConsumer {
    88  	return []libkb.UIConsumer{
    89  		&PGPKeyfinder{},
    90  	}
    91  }
    92  
    93  // Count returns the number of local keys available.
    94  func (s *ScanKeys) Count() int {
    95  	return len(s.skbs)
    96  }
    97  
    98  // KeysById returns the set of keys that have the given key id.
    99  // It is only called during decryption by openpgp.
   100  func (s *ScanKeys) KeysById(id uint64, fp []byte) []openpgp.Key {
   101  	m := s.M()
   102  	primaries := s.unlockByID(m, id)
   103  	memres := primaries.KeysById(id, fp)
   104  	m.Debug("ScanKeys:KeysById(%016x) => %d keys match in memory", id, len(memres))
   105  	if len(memres) > 0 {
   106  		m.Debug("ScanKeys:KeysById(%016x) => owner == me (%s)", id, s.me.GetName())
   107  		s.Lock()
   108  		s.keyOwners[id] = s.me
   109  		s.Unlock()
   110  		return memres
   111  	}
   112  
   113  	// KeysById is only used for decryption, so getting public keys from
   114  	// API server via s.scan(id) is pointless, so just returning nil.
   115  	return nil
   116  }
   117  
   118  // KeysByIdAndUsage returns the set of public keys with the given
   119  // id that also meet the key usage given by requiredUsage.
   120  //
   121  // The requiredUsage is expressed as the bitwise-OR of
   122  // packet.KeyFlag* values.
   123  //
   124  // It is only called during signature verification so therefore
   125  // requiredUsage will only equal KeyFlagSign, thus only public
   126  // keys are required.  If this ever changes upstream in openpgp,
   127  // this function will panic.
   128  func (s *ScanKeys) KeysByIdUsage(id uint64, fp []byte, requiredUsage byte) []openpgp.Key {
   129  	if requiredUsage != packet.KeyFlagSign {
   130  		panic(fmt.Sprintf("ScanKeys: unexpected requiredUsage flags set: %x", requiredUsage))
   131  	}
   132  
   133  	m := s.M()
   134  
   135  	// check the local keys first.
   136  	primaries := s.publicByID(m, id)
   137  	memres := primaries.KeysByIdUsage(id, fp, requiredUsage)
   138  	m.Debug("ScanKeys#KeysByIdUsage(%016x, %x) => %d keys match in memory", id, requiredUsage, len(memres))
   139  	if len(memres) > 0 {
   140  		m.Debug("ScanKeys#KeysByIdUsage(%016x) => owner == me (%s)", id, s.me.GetName())
   141  		s.Lock()
   142  		s.keyOwners[id] = s.me
   143  		s.Unlock()
   144  		return memres
   145  	}
   146  
   147  	// no match, so now lookup the user on the api server by the key id.
   148  	list, err := s.scan(m, id)
   149  	if err != nil {
   150  		m.Debug("error finding keys for %016x: %s", id, err)
   151  		return nil
   152  	}
   153  	// use the list to find the keys correctly
   154  	m.Debug("ScanKeys#KeysByIdUsage(%d, %x) => %d keys found via api scan", id, requiredUsage, len(list))
   155  	return list.KeysByIdUsage(id, fp, requiredUsage)
   156  }
   157  
   158  // DecryptionKeys returns all private keys that are valid for
   159  // decryption.  It is only used if there is no key id in the
   160  // message.
   161  func (s *ScanKeys) DecryptionKeys() []openpgp.Key {
   162  	m := s.M()
   163  	m.Debug("ScanKeys#DecryptionKeys() => %d keys available", s.Count())
   164  	all := s.unlockAll(m)
   165  	return all.DecryptionKeys()
   166  }
   167  
   168  // KeyOwner returns the owner of the keys found by ScanKeys that were
   169  // used in KeysById or KeysByIdUsage, indexed by keyID.
   170  func (s *ScanKeys) KeyOwner(keyID uint64) *libkb.User {
   171  	s.Lock()
   172  	defer s.Unlock()
   173  
   174  	return s.keyOwners[keyID]
   175  }
   176  
   177  func (s *ScanKeys) KeyOwnerByEntity(entity *openpgp.Entity) *libkb.User {
   178  	s.Lock()
   179  	defer s.Unlock()
   180  
   181  	if entity == nil {
   182  		return nil
   183  	}
   184  	if u, found := s.keyOwners[entity.PrimaryKey.KeyId]; found {
   185  		return u
   186  	}
   187  	for _, subKey := range entity.Subkeys {
   188  		if u, found := s.keyOwners[subKey.PublicKey.KeyId]; found {
   189  			return u
   190  		}
   191  	}
   192  	return nil
   193  }
   194  
   195  // coalesceBlocks puts the synced pgp key block and all the pgp key
   196  // blocks in ring into s.skbs.
   197  func (s *ScanKeys) coalesceBlocks(m libkb.MetaContext, ring *libkb.SKBKeyringFile, synced []*libkb.SKB) (err error) {
   198  	defer m.Trace("ScanKeys#coalesceBlocks", &err)()
   199  
   200  	// We want keys in this order: first local keyring keys that are LKSec, and
   201  	// then server synced keys that are triplesec. In ScanKeys.KeysById, this
   202  	// allows us to prompt for passphrase once and get both passphrase stream
   203  	// cache and triplesec cache the moment first LKSec key is processed by
   204  	// SKB.UnlockSecretKey.
   205  
   206  	// If they were in different order and we got triplesec bundle first, we
   207  	// would prompt for passphrase to get triplesec stream, and then prompt
   208  	// again to get passphrase stream to unlock LKSec bundle, prompting twice
   209  	// in total (assuming someone has both a server-synced bundle and local
   210  	// one).
   211  
   212  	for _, b := range ring.Blocks {
   213  		if !libkb.IsPGPAlgo(b.Type) {
   214  			continue
   215  		}
   216  		// make sure uid set on each block:
   217  		b.SetUID(s.me.GetUID())
   218  		s.skbs = append(s.skbs, b)
   219  	}
   220  
   221  	s.skbs = append(s.skbs, synced...)
   222  
   223  	return nil
   224  }
   225  
   226  // scan finds the user on the api server for the key id.  Then it
   227  // uses PGPKeyfinder to find the public pgp keys for the user.
   228  func (s *ScanKeys) scan(m libkb.MetaContext, id uint64) (openpgp.EntityList, error) {
   229  	// lookup the user on the api server by the key id.
   230  	username, uid, err := s.apiLookup(m, id)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	m.Debug("key id %016x => %s, %s", id, id, username, uid)
   235  	if len(username) == 0 || len(uid) == 0 {
   236  		return nil, libkb.NoKeyError{}
   237  	}
   238  
   239  	// use PGPKeyfinder engine to get the pgp keys for the user
   240  	arg := &PGPKeyfinderArg{Usernames: []string{username}}
   241  	eng := NewPGPKeyfinder(m.G(), arg)
   242  	if err := RunEngine2(m, eng); err != nil {
   243  		return nil, err
   244  	}
   245  	uplus := eng.UsersPlusKeys()
   246  	if len(uplus) != 1 {
   247  		m.Warning("error getting user plus pgp key from %s", username)
   248  		return nil, err
   249  	}
   250  	// user found is the owner of the keys
   251  	m.Debug("scan(%016x) => owner of key = (%s)", id, uplus[0].User.GetName())
   252  	s.Lock()
   253  	s.keyOwners[id] = uplus[0].User
   254  	s.Unlock()
   255  
   256  	// convert the bundles to an openpgp entity list
   257  	// (which implements the openpgp.KeyRing interface)
   258  	var list openpgp.EntityList
   259  	for _, k := range uplus[0].Keys {
   260  		list = append(list, k.Entity)
   261  	}
   262  	return list, nil
   263  }
   264  
   265  // apiLookup gets the username and uid from the api server for the
   266  // key id.
   267  func (s *ScanKeys) apiLookup(m libkb.MetaContext, id uint64) (username string, uid keybase1.UID, err error) {
   268  	return libkb.PGPLookup(m, id)
   269  }
   270  
   271  func (s *ScanKeys) publicByID(m libkb.MetaContext, id uint64) openpgp.EntityList {
   272  	var list openpgp.EntityList
   273  	for _, skb := range s.skbs {
   274  		pubkey, err := skb.GetPubKey()
   275  		if err != nil {
   276  			m.Warning("error getting pub key from skb: %s", err)
   277  			continue
   278  		}
   279  		bundle, ok := pubkey.(*libkb.PGPKeyBundle)
   280  		if !ok {
   281  			continue
   282  		}
   283  		if len(bundle.KeysById(id, nil)) == 0 {
   284  			// no match
   285  			continue
   286  		}
   287  		list = append(list, bundle.Entity)
   288  	}
   289  	return list
   290  }
   291  
   292  func (s *ScanKeys) unlockByID(m libkb.MetaContext, id uint64) openpgp.EntityList {
   293  	var list openpgp.EntityList
   294  	for _, skb := range s.skbs {
   295  		pubkey, err := skb.GetPubKey()
   296  		if err != nil {
   297  			m.Warning("error getting pub key from skb: %s", err)
   298  			continue
   299  		}
   300  		bundle, ok := pubkey.(*libkb.PGPKeyBundle)
   301  		if !ok {
   302  			continue
   303  		}
   304  		if len(bundle.KeysById(id, nil)) == 0 {
   305  			// no match
   306  			continue
   307  		}
   308  
   309  		// some key in the bundle matched, so unlock everything:
   310  		parg := libkb.SecretKeyPromptArg{
   311  			Reason:   unlockReason,
   312  			SecretUI: m.UIs().SecretUI,
   313  		}
   314  		secretStore := libkb.NewSecretStore(m, s.me.GetNormalizedName())
   315  		unlocked, err := skb.PromptAndUnlock(m, parg, secretStore, s.me)
   316  		if err != nil {
   317  			m.Warning("error unlocking key: %s", err)
   318  			continue
   319  		}
   320  		unlockedBundle, ok := unlocked.(*libkb.PGPKeyBundle)
   321  		if !ok {
   322  			m.Warning("could not convert unlocked key to PGPKeyBundle")
   323  			continue
   324  		}
   325  		list = append(list, unlockedBundle.Entity)
   326  	}
   327  	return list
   328  }
   329  
   330  func (s *ScanKeys) unlockAll(m libkb.MetaContext) openpgp.EntityList {
   331  	var list openpgp.EntityList
   332  	for _, skb := range s.skbs {
   333  		parg := libkb.SecretKeyPromptArg{
   334  			Reason:   unlockReason,
   335  			SecretUI: m.UIs().SecretUI,
   336  		}
   337  		secretStore := libkb.NewSecretStore(m, s.me.GetNormalizedName())
   338  		unlocked, err := skb.PromptAndUnlock(m, parg, secretStore, s.me)
   339  		if err != nil {
   340  			m.Warning("error unlocking key: %s", err)
   341  			continue
   342  		}
   343  		unlockedBundle, ok := unlocked.(*libkb.PGPKeyBundle)
   344  		if !ok {
   345  			m.Warning("could not convert unlocked key to PGPKeyBundle")
   346  			continue
   347  		}
   348  		list = append(list, unlockedBundle.Entity)
   349  	}
   350  	return list
   351  }