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

     1  package chat
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/keybase/client/go/chat/globals"
     8  	"github.com/keybase/client/go/chat/types"
     9  	"github.com/keybase/client/go/chat/utils"
    10  	"github.com/keybase/client/go/libkb"
    11  	"github.com/keybase/client/go/protocol/chat1"
    12  	"github.com/keybase/client/go/protocol/gregor1"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"golang.org/x/net/context"
    15  )
    16  
    17  type encItem struct {
    18  	key types.CryptKey
    19  	ni  types.NameInfo
    20  }
    21  
    22  // KeyFinder remembers results from previous calls to CryptKeys().
    23  type KeyFinderImpl struct {
    24  	globals.Contextified
    25  	utils.DebugLabeler
    26  	sync.Mutex
    27  
    28  	keys    map[string]*types.NameInfo
    29  	decKeys map[string]types.CryptKey
    30  	encKeys map[string]encItem
    31  }
    32  
    33  // NewKeyFinder creates a KeyFinder.
    34  func NewKeyFinder(g *globals.Context) types.KeyFinder {
    35  	return &KeyFinderImpl{
    36  		Contextified: globals.NewContextified(g),
    37  		DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "KeyFinder", false),
    38  		keys:         make(map[string]*types.NameInfo),
    39  		decKeys:      make(map[string]types.CryptKey),
    40  		encKeys:      make(map[string]encItem),
    41  	}
    42  }
    43  
    44  func (k *KeyFinderImpl) Reset() {
    45  	k.keys = make(map[string]*types.NameInfo)
    46  	k.decKeys = make(map[string]types.CryptKey)
    47  	k.encKeys = make(map[string]encItem)
    48  }
    49  
    50  func (k *KeyFinderImpl) encCacheKey(name string, tlfID chat1.TLFID, membersType chat1.ConversationMembersType,
    51  	public bool, botUID *gregor1.UID) string {
    52  	return fmt.Sprintf("_enc:%s|%s|%v|%v|%v", name, tlfID, membersType, public, botUID)
    53  }
    54  
    55  func (k *KeyFinderImpl) decCacheKey(name string, tlfID chat1.TLFID, membersType chat1.ConversationMembersType,
    56  	generation int, public bool, kbfsEncrypted bool, botUID *gregor1.UID) string {
    57  	return fmt.Sprintf("_dec:%s|%s|%v|%v|%v|%d|%v", name, tlfID, membersType, public,
    58  		kbfsEncrypted, generation, botUID)
    59  }
    60  
    61  func (k *KeyFinderImpl) createNameInfoSource(ctx context.Context,
    62  	membersType chat1.ConversationMembersType) types.NameInfoSource {
    63  	return CreateNameInfoSource(ctx, k.G(), membersType)
    64  }
    65  
    66  func (k *KeyFinderImpl) lookupEncKey(key string) (encItem, bool) {
    67  	k.Lock()
    68  	defer k.Unlock()
    69  	existing, ok := k.encKeys[key]
    70  	return existing, ok
    71  }
    72  
    73  func (k *KeyFinderImpl) writeEncKey(key string, v encItem) {
    74  	k.Lock()
    75  	defer k.Unlock()
    76  	k.encKeys[key] = v
    77  }
    78  
    79  func (k *KeyFinderImpl) lookupDecKey(key string) (types.CryptKey, bool) {
    80  	k.Lock()
    81  	defer k.Unlock()
    82  	existing, ok := k.decKeys[key]
    83  	return existing, ok
    84  }
    85  
    86  func (k *KeyFinderImpl) writeDecKey(key string, v types.CryptKey) {
    87  	k.Lock()
    88  	defer k.Unlock()
    89  	k.decKeys[key] = v
    90  }
    91  
    92  // FindForEncryption finds keys up-to-date enough for encrypting.
    93  // Ignores tlfName or teamID based on membersType.
    94  func (k *KeyFinderImpl) FindForEncryption(ctx context.Context, tlfName string, tlfID chat1.TLFID,
    95  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (res types.CryptKey, ni types.NameInfo, err error) {
    96  
    97  	ckey := k.encCacheKey(tlfName, tlfID, membersType, public, botUID)
    98  	existing, ok := k.lookupEncKey(ckey)
    99  	if ok {
   100  		return existing.key, existing.ni, nil
   101  	}
   102  	defer func() {
   103  		if err == nil {
   104  			k.writeEncKey(ckey, encItem{
   105  				key: res,
   106  				ni:  ni,
   107  			})
   108  		}
   109  	}()
   110  
   111  	return k.createNameInfoSource(ctx, membersType).EncryptionKey(ctx, tlfName, tlfID,
   112  		membersType, public, botUID)
   113  }
   114  
   115  // FindForDecryption ignores tlfName or teamID based on membersType.
   116  func (k *KeyFinderImpl) FindForDecryption(ctx context.Context,
   117  	tlfName string, tlfID chat1.TLFID,
   118  	membersType chat1.ConversationMembersType, public bool,
   119  	keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (res types.CryptKey, err error) {
   120  	ckey := k.decCacheKey(tlfName, tlfID, membersType, keyGeneration,
   121  		public, kbfsEncrypted, botUID)
   122  	existing, ok := k.lookupDecKey(ckey)
   123  	if ok {
   124  		return existing, nil
   125  	}
   126  	defer func() {
   127  		if err == nil {
   128  			k.writeDecKey(ckey, res)
   129  		}
   130  	}()
   131  	return k.createNameInfoSource(ctx, membersType).DecryptionKey(ctx, tlfName, tlfID,
   132  		membersType, public, keyGeneration, kbfsEncrypted, botUID)
   133  }
   134  
   135  func (k *KeyFinderImpl) EphemeralKeyForEncryption(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   136  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (ek types.EphemeralCryptKey, err error) {
   137  	return k.createNameInfoSource(mctx.Ctx(), membersType).EphemeralEncryptionKey(
   138  		mctx, tlfName, tlfID, membersType, public, botUID)
   139  }
   140  
   141  func (k *KeyFinderImpl) EphemeralKeyForDecryption(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   142  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID,
   143  	generation keybase1.EkGeneration, contentCtime *gregor1.Time) (types.EphemeralCryptKey, error) {
   144  	return k.createNameInfoSource(mctx.Ctx(), membersType).EphemeralDecryptionKey(
   145  		mctx, tlfName, tlfID, membersType, public, botUID, generation, contentCtime)
   146  }
   147  
   148  func (k *KeyFinderImpl) ShouldPairwiseMAC(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   149  	membersType chat1.ConversationMembersType, public bool) (bool, []keybase1.KID, error) {
   150  	return k.createNameInfoSource(ctx, membersType).ShouldPairwiseMAC(ctx, tlfName, tlfID, membersType, public)
   151  }
   152  
   153  type KeyFinderMock struct {
   154  	cryptKeys []keybase1.CryptKey
   155  }
   156  
   157  var _ types.KeyFinder = (*KeyFinderMock)(nil)
   158  
   159  func NewKeyFinderMock(cryptKeys []keybase1.CryptKey) types.KeyFinder {
   160  	return &KeyFinderMock{cryptKeys}
   161  }
   162  
   163  func (k *KeyFinderMock) Reset() {}
   164  
   165  func (k *KeyFinderMock) FindForEncryption(ctx context.Context,
   166  	tlfName string, teamID chat1.TLFID,
   167  	membersType chat1.ConversationMembersType, public bool,
   168  	botUID *gregor1.UID) (res types.CryptKey, ni types.NameInfo, err error) {
   169  	if botUID != nil {
   170  		return res, ni, fmt.Errorf("bot keys not supported in KeyFinderMock")
   171  	}
   172  	return k.cryptKeys[len(k.cryptKeys)-1], ni, nil
   173  }
   174  
   175  func (k *KeyFinderMock) FindForDecryption(ctx context.Context,
   176  	tlfName string, teamID chat1.TLFID,
   177  	membersType chat1.ConversationMembersType, public bool,
   178  	keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (res types.CryptKey, err error) {
   179  	if botUID != nil {
   180  		return res, fmt.Errorf("TeambotKeys not supported in KeyFinderMock")
   181  	}
   182  	for _, key := range k.cryptKeys {
   183  		if key.Generation() == keyGeneration {
   184  			return key, nil
   185  		}
   186  	}
   187  	return res, NewDecryptionKeyNotFoundError(keyGeneration, public, kbfsEncrypted)
   188  }
   189  
   190  func (k *KeyFinderMock) EphemeralKeyForEncryption(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   191  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (types.EphemeralCryptKey, error) {
   192  	panic("unimplemented")
   193  }
   194  
   195  func (k *KeyFinderMock) EphemeralKeyForDecryption(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   196  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID,
   197  	generation keybase1.EkGeneration, contentCtime *gregor1.Time) (types.EphemeralCryptKey, error) {
   198  	panic("unimplemented")
   199  }
   200  
   201  func (k *KeyFinderMock) ShouldPairwiseMAC(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   202  	membersType chat1.ConversationMembersType, public bool) (bool, []keybase1.KID, error) {
   203  	panic("unimplemented")
   204  }
   205  
   206  func (k *KeyFinderMock) SetNameInfoSourceOverride(ni types.NameInfoSource) {}