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) {}