github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/teams.go (about) 1 package chat 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 lru "github.com/hashicorp/golang-lru" 9 "github.com/keybase/client/go/chat/globals" 10 "github.com/keybase/client/go/chat/types" 11 "github.com/keybase/client/go/chat/utils" 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/protocol/chat1" 14 "github.com/keybase/client/go/protocol/gregor1" 15 "github.com/keybase/client/go/protocol/keybase1" 16 "github.com/keybase/client/go/teambot" 17 "github.com/keybase/client/go/teams" 18 context "golang.org/x/net/context" 19 ) 20 21 func getTeamCryptKey(mctx libkb.MetaContext, team *teams.Team, generation keybase1.PerTeamKeyGeneration, 22 public, kbfsEncrypted bool, botUID *gregor1.UID, forEncryption bool) (res types.CryptKey, err error) { 23 if public { 24 return publicCryptKey, nil 25 } 26 27 if teambot.CurrentUserIsBot(mctx, botUID) { 28 if kbfsEncrypted { 29 return res, fmt.Errorf("TeambotKeys not supported by KBFS") 30 } 31 keyer := mctx.G().GetTeambotBotKeyer() 32 if forEncryption { 33 return keyer.GetLatestTeambotKey(mctx, team.ID, keybase1.TeamApplication_CHAT) 34 } 35 return keyer.GetTeambotKeyAtGeneration(mctx, team.ID, keybase1.TeamApplication_CHAT, keybase1.TeambotKeyGeneration(generation)) 36 } 37 38 if kbfsEncrypted { 39 if botUID != nil { 40 return res, fmt.Errorf("TeambotKeys not supported by KBFS") 41 } 42 kbfsKeys := team.KBFSCryptKeys(mctx.Ctx(), keybase1.TeamApplication_CHAT) 43 for _, key := range kbfsKeys { 44 if key.Generation() == int(generation) { 45 return key, nil 46 } 47 } 48 return res, NewDecryptionKeyNotFoundError(int(generation), kbfsEncrypted, public) 49 } 50 51 appKey, err := team.ChatKeyAtGeneration(mctx.Ctx(), generation) 52 if err != nil { 53 return res, err 54 } 55 56 // Need to convert this key in to a TeambotKey 57 if botUID != nil { 58 res, _, err = mctx.G().GetTeambotMemberKeyer().GetOrCreateTeambotKey( 59 mctx, team.ID, *botUID, appKey) 60 return res, err 61 } 62 return appKey, nil 63 } 64 65 func encryptionKeyViaFTL(m libkb.MetaContext, name string, tlfID chat1.TLFID, 66 membersType chat1.ConversationMembersType) (res types.CryptKey, ni types.NameInfo, err error) { 67 ftlRes, err := getKeyViaFTL(m, name, tlfID, membersType, 0) 68 if err != nil { 69 return res, ni, err 70 } 71 ni = types.NameInfo{ 72 ID: tlfID, 73 CanonicalName: ftlRes.Name.String(), 74 } 75 return ftlRes.ApplicationKeys[0], ni, nil 76 } 77 78 func decryptionKeyViaFTL(m libkb.MetaContext, tlfID chat1.TLFID, membersType chat1.ConversationMembersType, 79 keyGeneration int) (res types.CryptKey, err error) { 80 81 // We don't pass a `name` during decryption. 82 ftlRes, err := getKeyViaFTL(m, "" /*name*/, tlfID, membersType, keyGeneration) 83 if err != nil { 84 return nil, err 85 } 86 return ftlRes.ApplicationKeys[0], nil 87 } 88 89 func getKeyViaFTL(mctx libkb.MetaContext, name string, tlfID chat1.TLFID, 90 membersType chat1.ConversationMembersType, keyGeneration int) (res keybase1.FastTeamLoadRes, err error) { 91 defer mctx.Trace(fmt.Sprintf("getKeyViaFTL(%s,%v,%d)", name, tlfID, keyGeneration), &err)() 92 var teamID keybase1.TeamID 93 switch membersType { 94 case chat1.ConversationMembersType_TEAM, 95 chat1.ConversationMembersType_IMPTEAMNATIVE: 96 teamID, err = keybase1.TeamIDFromString(tlfID.String()) 97 if err != nil { 98 return res, err 99 } 100 case chat1.ConversationMembersType_IMPTEAMUPGRADE: 101 teamID, err = tlfIDToTeamID.Lookup(mctx, tlfID, mctx.G().API) 102 if err != nil { 103 return res, err 104 } 105 } 106 107 // The `name` parameter is optional since subteams can be renamed and 108 // messages with the old name must be successfully decrypted. 109 var teamNamePtr *keybase1.TeamName 110 if name != "" { 111 teamName, err := keybase1.TeamNameFromString(name) 112 if err != nil { 113 return res, err 114 } 115 teamNamePtr = &teamName 116 } 117 arg := keybase1.FastTeamLoadArg{ 118 ID: teamID, 119 Public: false, 120 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 121 AssertTeamName: teamNamePtr, 122 } 123 124 if keyGeneration > 0 { 125 arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(keyGeneration)} 126 } else { 127 arg.NeedLatestKey = true 128 } 129 130 res, err = mctx.G().GetFastTeamLoader().Load(mctx, arg) 131 if err != nil { 132 return res, err 133 } 134 135 n := len(res.ApplicationKeys) 136 if n != 1 { 137 return res, NewFTLError(fmt.Sprintf("wrong number of keys back from FTL; wanted 1, but got %d", n)) 138 } 139 140 if keyGeneration > 0 && res.ApplicationKeys[0].KeyGeneration != keybase1.PerTeamKeyGeneration(keyGeneration) { 141 return res, NewFTLError(fmt.Sprintf("wrong generation back from FTL; wanted %d but got %d", keyGeneration, res.ApplicationKeys[0].KeyGeneration)) 142 } 143 144 if res.ApplicationKeys[0].Application != keybase1.TeamApplication_CHAT { 145 return res, NewFTLError(fmt.Sprintf("wrong application; wanted %d but got %d", keybase1.TeamApplication_CHAT, res.ApplicationKeys[0].Application)) 146 } 147 148 return res, nil 149 } 150 151 func loadTeamForDecryption(mctx libkb.MetaContext, loader *TeamLoader, name string, teamID chat1.TLFID, 152 membersType chat1.ConversationMembersType, public bool, 153 keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (*teams.Team, error) { 154 155 var refreshers keybase1.TeamRefreshers 156 if !public && !teambot.CurrentUserIsBot(mctx, botUID) { 157 // Only need keys for private teams. 158 if !kbfsEncrypted { 159 refreshers.NeedApplicationsAtGenerations = map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{ 160 keybase1.PerTeamKeyGeneration(keyGeneration): {keybase1.TeamApplication_CHAT}, 161 } 162 } else { 163 refreshers.NeedKBFSKeyGeneration = keybase1.TeamKBFSKeyRefresher{ 164 Generation: keyGeneration, 165 AppType: keybase1.TeamApplication_CHAT, 166 } 167 } 168 } 169 team, err := loader.loadTeam(mctx.Ctx(), teamID, name, membersType, public, 170 func(teamID keybase1.TeamID) keybase1.LoadTeamArg { 171 return keybase1.LoadTeamArg{ 172 ID: teamID, 173 Public: public, 174 Refreshers: refreshers, 175 StaleOK: true, 176 } 177 }) 178 if err != nil { 179 return nil, err 180 } 181 return team, nil 182 } 183 184 type TeamLoader struct { 185 libkb.Contextified 186 utils.DebugLabeler 187 } 188 189 func NewTeamLoader(g *libkb.GlobalContext) *TeamLoader { 190 return &TeamLoader{ 191 Contextified: libkb.NewContextified(g), 192 DebugLabeler: utils.NewDebugLabeler(g, "TeamLoader", false), 193 } 194 } 195 196 func (t *TeamLoader) validKBFSTLFID(tlfID chat1.TLFID, team *teams.Team) bool { 197 tlfIDs := team.KBFSTLFIDs() 198 for _, id := range tlfIDs { 199 if tlfID.EqString(id) { 200 return true 201 } 202 } 203 return false 204 } 205 206 func (t *TeamLoader) validateImpTeamname(ctx context.Context, tlfName string, public bool, 207 team *teams.Team) error { 208 impTeamName, err := team.ImplicitTeamDisplayNameNoConflicts(ctx) 209 if err != nil { 210 return err 211 } 212 if impTeamName.String() != tlfName { 213 // Try resolving both the tlf name, and the team we loaded 214 resImpName, err := teams.ResolveImplicitTeamDisplayName(ctx, t.G(), impTeamName.String(), public) 215 if err != nil { 216 return err 217 } 218 resTlfName, err := teams.ResolveImplicitTeamDisplayName(ctx, t.G(), tlfName, public) 219 if err != nil { 220 return err 221 } 222 if resImpName.String() != resTlfName.String() { 223 return ImpteamBadteamError{ 224 Msg: fmt.Sprintf("mismatch TLF name to implicit team name: %s != %s (%s != %s)", 225 impTeamName, tlfName, resImpName, resTlfName), 226 } 227 } 228 } 229 return nil 230 } 231 232 func (t *TeamLoader) loadTeam(ctx context.Context, tlfID chat1.TLFID, 233 tlfName string, membersType chat1.ConversationMembersType, public bool, 234 loadTeamArgOverride func(keybase1.TeamID) keybase1.LoadTeamArg) (team *teams.Team, err error) { 235 mctx := libkb.NewMetaContext(ctx, t.G()) 236 defer t.Trace(ctx, &err, "loadTeam(%s,%s,%v)", tlfName, tlfID, membersType)() 237 238 // Set up load team argument construction, possibly controlled by the caller 239 ltarg := func(teamID keybase1.TeamID) keybase1.LoadTeamArg { 240 return keybase1.LoadTeamArg{ 241 ID: teamID, 242 Public: public, 243 } 244 } 245 if loadTeamArgOverride != nil { 246 ltarg = loadTeamArgOverride 247 } 248 249 switch membersType { 250 case chat1.ConversationMembersType_TEAM, 251 chat1.ConversationMembersType_IMPTEAMNATIVE: 252 teamID, err := keybase1.TeamIDFromString(tlfID.String()) 253 if err != nil { 254 return nil, err 255 } 256 return teams.Load(ctx, t.G(), ltarg(teamID)) 257 case chat1.ConversationMembersType_IMPTEAMUPGRADE: 258 teamID, err := tlfIDToTeamID.Lookup(mctx, tlfID, t.G().API) 259 if err != nil { 260 return nil, err 261 } 262 loadAttempt := func(repoll bool) error { 263 arg := ltarg(teamID) 264 arg.ForceRepoll = arg.ForceRepoll || repoll 265 team, err = teams.Load(ctx, t.G(), arg) 266 if err != nil { 267 return err 268 } 269 if !t.validKBFSTLFID(tlfID, team) { 270 return ImpteamBadteamError{ 271 Msg: fmt.Sprintf("TLF ID not found in team: %s", tlfID), 272 } 273 } 274 return nil 275 } 276 if err = loadAttempt(false); err != nil { 277 mctx.Debug("loadTeam: failed to load the team: err: %s", err) 278 if IsOfflineError(err) == OfflineErrorKindOnline { 279 // try again on bad team, might have had an old team cached 280 mctx.Debug("loadTeam: non-offline error, trying again: %s", err) 281 if err = loadAttempt(true); err != nil { 282 return nil, err 283 } 284 } else { 285 // generic error we bail out 286 return nil, err 287 } 288 } 289 // In upgraded implicit teams, make sure to check that tlfName matches 290 // team display name. 291 if err := t.validateImpTeamname(ctx, tlfName, public, team); err != nil { 292 return nil, err 293 } 294 return team, nil 295 } 296 return nil, fmt.Errorf("invalid impteam members type: %v", membersType) 297 } 298 299 type TeamsNameInfoSource struct { 300 globals.Contextified 301 utils.DebugLabeler 302 303 loader *TeamLoader 304 } 305 306 func NewTeamsNameInfoSource(g *globals.Context) *TeamsNameInfoSource { 307 return &TeamsNameInfoSource{ 308 Contextified: globals.NewContextified(g), 309 DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "TeamsNameInfoSource", false), 310 loader: NewTeamLoader(g.ExternalG()), 311 } 312 } 313 314 func (t *TeamsNameInfoSource) LookupID(ctx context.Context, name string, public bool) (res types.NameInfo, err error) { 315 defer t.Trace(ctx, &err, fmt.Sprintf("LookupID(%s)", name))() 316 317 teamName, err := keybase1.TeamNameFromString(name) 318 if err != nil { 319 return res, err 320 } 321 id, err := teams.ResolveNameToID(ctx, t.G().ExternalG(), teamName) 322 if err != nil { 323 return res, err 324 } 325 tlfID, err := chat1.TeamIDToTLFID(id) 326 if err != nil { 327 return res, err 328 } 329 return types.NameInfo{ 330 ID: tlfID, 331 CanonicalName: teamName.String(), 332 }, nil 333 } 334 335 func (t *TeamsNameInfoSource) LookupName(ctx context.Context, tlfID chat1.TLFID, public bool, 336 unverifiedTLFName string) (res types.NameInfo, err error) { 337 defer t.Trace(ctx, &err, fmt.Sprintf("LookupName(%s)", tlfID))() 338 teamID, err := keybase1.TeamIDFromString(tlfID.String()) 339 if err != nil { 340 return res, err 341 } 342 m := libkb.NewMetaContext(ctx, t.G().ExternalG()) 343 loadRes, err := m.G().GetFastTeamLoader().Load(m, keybase1.FastTeamLoadArg{ 344 ID: teamID, 345 Public: teamID.IsPublic(), 346 }) 347 if err != nil { 348 return res, err 349 } 350 return types.NameInfo{ 351 ID: tlfID, 352 CanonicalName: loadRes.Name.String(), 353 }, nil 354 } 355 356 func (t *TeamsNameInfoSource) TeamBotSettings(ctx context.Context, tlfName string, tlfID chat1.TLFID, 357 membersType chat1.ConversationMembersType, public bool) (map[keybase1.UserVersion]keybase1.TeamBotSettings, error) { 358 team, err := NewTeamLoader(t.G().ExternalG()).loadTeam(ctx, tlfID, tlfName, membersType, public, nil) 359 if err != nil { 360 return nil, err 361 } 362 return team.TeamBotSettings() 363 } 364 365 func (t *TeamsNameInfoSource) AllCryptKeys(ctx context.Context, name string, public bool) (res types.AllCryptKeys, err error) { 366 defer t.Trace(ctx, &err, "AllCryptKeys")() 367 return res, errors.New("unable to list all crypt keys on teams name info source") 368 } 369 370 func (t *TeamsNameInfoSource) EncryptionKey(ctx context.Context, name string, teamID chat1.TLFID, 371 membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (res types.CryptKey, ni types.NameInfo, err error) { 372 defer t.Trace(ctx, &err, 373 fmt.Sprintf("EncryptionKey(%s,%s,%v,%v)", name, teamID, public, botUID))() 374 375 mctx := libkb.NewMetaContext(ctx, t.G().ExternalG()) 376 if botUID == nil && !public { 377 return encryptionKeyViaFTL(mctx, name, teamID, membersType) 378 } 379 380 team, err := t.loader.loadTeam(ctx, teamID, name, membersType, public, nil) 381 if err != nil { 382 return res, ni, err 383 } 384 if res, err = getTeamCryptKey(mctx, team, team.Generation(), public, false, /* kbfsEncrypted */ 385 botUID, true /* forEncryption */); err != nil { 386 return res, ni, err 387 } 388 389 tlfID, err := chat1.TeamIDToTLFID(team.ID) 390 if err != nil { 391 return res, ni, err 392 } 393 return res, types.NameInfo{ 394 ID: tlfID, 395 CanonicalName: team.Name().String(), 396 }, nil 397 } 398 399 func (t *TeamsNameInfoSource) DecryptionKey(ctx context.Context, name string, teamID chat1.TLFID, 400 membersType chat1.ConversationMembersType, public bool, 401 keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (res types.CryptKey, err error) { 402 defer t.Trace(ctx, &err, 403 fmt.Sprintf("DecryptionKey(%s,%s,%v,%d,%v,%v)", name, teamID, public, 404 keyGeneration, kbfsEncrypted, botUID))() 405 406 mctx := libkb.NewMetaContext(ctx, t.G().ExternalG()) 407 if botUID == nil && !kbfsEncrypted && !public { 408 return decryptionKeyViaFTL(mctx, teamID, membersType, keyGeneration) 409 } 410 411 team, err := loadTeamForDecryption(mctx, t.loader, name, teamID, membersType, public, 412 keyGeneration, kbfsEncrypted, botUID) 413 if err != nil { 414 return res, err 415 } 416 return getTeamCryptKey(mctx, team, keybase1.PerTeamKeyGeneration(keyGeneration), public, 417 kbfsEncrypted, botUID, false /* forEncryption */) 418 } 419 420 func (t *TeamsNameInfoSource) EphemeralEncryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID, 421 membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (ek types.EphemeralCryptKey, err error) { 422 if public { 423 return ek, NewPublicTeamEphemeralKeyError() 424 } 425 426 teamID, err := keybase1.TeamIDFromString(tlfID.String()) 427 if err != nil { 428 return ek, err 429 } 430 if botUID != nil { 431 ek, _, err = t.G().GetEKLib().GetOrCreateLatestTeambotEK(mctx, teamID, *botUID) 432 } else { 433 ek, _, err = t.G().GetEKLib().GetOrCreateLatestTeamEK(mctx, teamID) 434 } 435 return ek, err 436 } 437 438 func (t *TeamsNameInfoSource) EphemeralDecryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID, 439 membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID, 440 generation keybase1.EkGeneration, contentCtime *gregor1.Time) (ek types.EphemeralCryptKey, err error) { 441 if public { 442 return ek, NewPublicTeamEphemeralKeyError() 443 } 444 445 teamID, err := keybase1.TeamIDFromString(tlfID.String()) 446 if err != nil { 447 return ek, err 448 } 449 if botUID != nil { 450 return t.G().GetEKLib().GetTeambotEK(mctx, teamID, *botUID, generation, contentCtime) 451 } 452 return t.G().GetEKLib().GetTeamEK(mctx, teamID, generation, contentCtime) 453 } 454 455 func (t *TeamsNameInfoSource) ShouldPairwiseMAC(ctx context.Context, tlfName string, tlfID chat1.TLFID, 456 membersType chat1.ConversationMembersType, public bool) (bool, []keybase1.KID, error) { 457 return shouldPairwiseMAC(ctx, t.G(), t.loader, tlfName, tlfID, membersType, public) 458 } 459 460 func batchLoadEncryptionKIDs(ctx context.Context, g *libkb.GlobalContext, uvs []keybase1.UserVersion) (ret []keybase1.KID, err error) { 461 462 getArg := func(i int) *libkb.LoadUserArg { 463 if i >= len(uvs) { 464 return nil 465 } 466 tmp := libkb.NewLoadUserByUIDArg(ctx, g, uvs[i].Uid).WithPublicKeyOptional() 467 return &tmp 468 } 469 470 processResult := func(i int, upak *keybase1.UserPlusKeysV2AllIncarnations) error { 471 if upak == nil { 472 return nil 473 } 474 for _, key := range upak.Current.DeviceKeys { 475 // Include only unrevoked encryption keys. 476 if !key.Base.IsSibkey && key.Base.Revocation == nil { 477 ret = append(ret, key.Base.Kid) 478 } 479 } 480 return nil 481 } 482 483 err = g.GetUPAKLoader().Batcher(ctx, getArg, processResult, 0) 484 return ret, err 485 } 486 487 func shouldPairwiseMAC(ctx context.Context, g *globals.Context, loader *TeamLoader, tlfName string, 488 tlfID chat1.TLFID, membersType chat1.ConversationMembersType, public bool) (should bool, kids []keybase1.KID, err error) { 489 490 if public { 491 return false, nil, nil 492 } 493 494 defer g.CTrace(ctx, fmt.Sprintf("shouldPairwiseMAC teamID %s", tlfID.String()), &err)() 495 496 team, err := loader.loadTeam(ctx, tlfID, tlfName, membersType, public, nil) 497 if err != nil { 498 return false, nil, err 499 } 500 members, err := team.Members() 501 if err != nil { 502 return false, nil, err 503 } 504 memberUVs := members.AllUserVersions() 505 506 // For performance reasons, we don't try to pairwise MAC any messages in 507 // large teams. 508 if len(memberUVs) > libkb.MaxTeamMembersForPairwiseMAC { 509 return false, nil, nil 510 } 511 512 unrevokedKIDs, err := batchLoadEncryptionKIDs(ctx, g.GlobalContext, memberUVs) 513 if err != nil { 514 return false, nil, err 515 } 516 517 if len(unrevokedKIDs) > 10*libkb.MaxTeamMembersForPairwiseMAC { 518 // If someone on the team has a ton of devices, it could break our "100 519 // members" heuristic and lead to bad performance. We don't want to 520 // silently fall back to the non-repudiable mode, because that would 521 // create an opening for downgrade attacks, and we'd need to document 522 // this exception everywhere we talk about repudiability. But if this 523 // turns out to be a performance issue in practice, we might want to 524 // add some workaround. (For example, we could choose to omit 525 // recipients with an unreasonable number of devices.) 526 g.Log.CWarningf(ctx, "unreasonable number of devices (%d) in recipients list", len(unrevokedKIDs)) 527 } 528 return true, unrevokedKIDs, nil 529 } 530 531 type ImplicitTeamsNameInfoSource struct { 532 globals.Contextified 533 utils.DebugLabeler 534 *NameIdentifier 535 536 loader *TeamLoader 537 membersType chat1.ConversationMembersType 538 } 539 540 func NewImplicitTeamsNameInfoSource(g *globals.Context, membersType chat1.ConversationMembersType) *ImplicitTeamsNameInfoSource { 541 return &ImplicitTeamsNameInfoSource{ 542 Contextified: globals.NewContextified(g), 543 DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "ImplicitTeamsNameInfoSource", false), 544 NameIdentifier: NewNameIdentifier(g), 545 loader: NewTeamLoader(g.ExternalG()), 546 membersType: membersType, 547 } 548 } 549 550 func (t *ImplicitTeamsNameInfoSource) lookupUpgraded() bool { 551 return t.membersType == chat1.ConversationMembersType_IMPTEAMUPGRADE 552 } 553 554 // Identify participants of a conv. 555 // Returns as if all IDs succeeded if ctx is in TLFIdentifyBehavior_CHAT_GUI mode. 556 func (t *ImplicitTeamsNameInfoSource) identify(ctx context.Context, team *teams.Team, impTeamName keybase1.ImplicitTeamDisplayName) (err error) { 557 var names []string 558 names = append(names, impTeamName.Writers.KeybaseUsers...) 559 names = append(names, impTeamName.Readers.KeybaseUsers...) 560 561 // identify the members in the conversation 562 identBehavior, _, ok := globals.CtxIdentifyMode(ctx) 563 defer t.Trace(ctx, &err, fmt.Sprintf("identify(%s, %v)", impTeamName.String(), identBehavior))() 564 if !ok { 565 return errors.New("invalid context with no chat metadata") 566 } 567 cb := make(chan struct{}) 568 go func(ctx context.Context) { 569 var idFails []keybase1.TLFIdentifyFailure 570 idFails, err = t.Identify(ctx, names, true, 571 func() keybase1.TLFID { 572 return keybase1.TLFID(team.ID.String()) 573 }, 574 func() keybase1.CanonicalTlfName { 575 return keybase1.CanonicalTlfName(impTeamName.String()) 576 }) 577 if err != nil || len(idFails) > 0 { 578 t.Debug(ctx, "identify failed err=%v fails=%+v", err, idFails) 579 } 580 // ignore idFails 581 close(cb) 582 }(globals.BackgroundChatCtx(ctx, t.G())) 583 switch identBehavior { 584 case keybase1.TLFIdentifyBehavior_CHAT_GUI: 585 // For GUI mode, let the full IDs roll in the background. We will be sending up 586 // tracker breaks to the UI out of band with whatever chat operation has invoked us here. 587 588 // CORE-10522 peg breaks will need to block sending for non-gui mode too. 589 // Peg breaks could count as track breaks for real Identify. 590 // That could nicely cover other applications besides chat. But the UI 591 // for fixing track breaks doesn't quite make sense for peg breaks. 592 593 // But check reset-pegs on the critical path. 594 for _, uv := range team.AllUserVersions(ctx) { 595 err = t.G().Pegboard.CheckUV(t.MetaContext(ctx), uv) 596 if err != nil { 597 // Turn peg failures into identify failures 598 t.Debug(ctx, "pegboard rejected %v: %v", uv, err) 599 return fmt.Errorf("A user may have reset: %v", err) 600 } 601 } 602 return nil 603 default: 604 <-cb 605 return err 606 } 607 } 608 609 func (t *ImplicitTeamsNameInfoSource) transformTeamDoesNotExist(ctx context.Context, err error, name string) error { 610 switch err.(type) { 611 case nil: 612 return nil 613 case teams.TeamDoesNotExistError: 614 return NewUnknownTLFNameError(name) 615 default: 616 t.Debug(ctx, "Lookup: error looking up the team: %v", err) 617 return err 618 } 619 } 620 621 func (t *ImplicitTeamsNameInfoSource) LookupID(ctx context.Context, name string, public bool) (res types.NameInfo, err error) { 622 defer t.Trace(ctx, &err, fmt.Sprintf("LookupID(%s)", name))() 623 // check if name is prefixed 624 if strings.HasPrefix(name, keybase1.ImplicitTeamPrefix) { 625 return t.lookupInternalName(ctx, name, public) 626 } 627 628 // This is on the critical path of sends, so don't force a repoll. 629 team, _, impTeamName, err := teams.LookupImplicitTeam(ctx, t.G().ExternalG(), name, public, teams.ImplicitTeamOptions{NoForceRepoll: true}) 630 if err != nil { 631 return res, t.transformTeamDoesNotExist(ctx, err, name) 632 } 633 if !team.ID.IsRootTeam() { 634 panic(fmt.Sprintf("implicit team found via LookupImplicitTeam not root team: %s", team.ID)) 635 } 636 637 var tlfID chat1.TLFID 638 if t.lookupUpgraded() { 639 tlfIDs := team.KBFSTLFIDs() 640 if len(tlfIDs) > 0 { 641 // We pull the first TLF ID here for this lookup since it has the highest chance of being 642 // correct. The upgrade wrote a bunch of TLF IDs in over the last months, but it is possible 643 // that KBFS can add more. All the upgrade TLFs should be ahead of them though, since if the 644 // upgrade process encounters a team with a TLF ID already in there, it will abort if they 645 // don't match. 646 tlfID = tlfIDs[0].ToBytes() 647 } 648 } else { 649 tlfID, err = chat1.TeamIDToTLFID(team.ID) 650 if err != nil { 651 return res, err 652 } 653 } 654 res = types.NameInfo{ 655 ID: tlfID, 656 CanonicalName: impTeamName.String(), 657 } 658 return res, nil 659 } 660 661 func (t *ImplicitTeamsNameInfoSource) LookupName(ctx context.Context, tlfID chat1.TLFID, public bool, 662 unverifiedTLFName string) (res types.NameInfo, err error) { 663 defer t.Trace(ctx, &err, fmt.Sprintf("LookupName(%s)", tlfID))() 664 team, err := t.loader.loadTeam(ctx, tlfID, unverifiedTLFName, t.membersType, public, nil) 665 if err != nil { 666 return res, err 667 } 668 impTeamName, err := team.ImplicitTeamDisplayNameNoConflicts(ctx) 669 if err != nil { 670 return res, err 671 } 672 t.Debug(ctx, "LookupName: got name: %s", impTeamName.String()) 673 members, err := team.Members() 674 if err != nil { 675 return res, err 676 } 677 var verifiedMembers []gregor1.UID 678 for _, member := range members.AllUIDs() { 679 verifiedMembers = append(verifiedMembers, gregor1.UID(member.ToBytes())) 680 } 681 return types.NameInfo{ 682 ID: tlfID, 683 CanonicalName: impTeamName.String(), 684 VerifiedMembers: verifiedMembers, 685 }, nil 686 } 687 688 func (t *ImplicitTeamsNameInfoSource) TeamBotSettings(ctx context.Context, tlfName string, tlfID chat1.TLFID, 689 membersType chat1.ConversationMembersType, public bool) (map[keybase1.UserVersion]keybase1.TeamBotSettings, error) { 690 team, err := NewTeamLoader(t.G().ExternalG()).loadTeam(ctx, tlfID, tlfName, membersType, public, nil) 691 if err != nil { 692 return nil, err 693 } 694 return team.TeamBotSettings() 695 } 696 697 func (t *ImplicitTeamsNameInfoSource) AllCryptKeys(ctx context.Context, name string, public bool) (res types.AllCryptKeys, err error) { 698 defer t.Trace(ctx, &err, "AllCryptKeys")() 699 return res, errors.New("unable to list all crypt keys in implicit team name info source") 700 } 701 702 func (t *ImplicitTeamsNameInfoSource) EncryptionKey(ctx context.Context, name string, teamID chat1.TLFID, 703 membersType chat1.ConversationMembersType, public bool, 704 botUID *gregor1.UID) (res types.CryptKey, ni types.NameInfo, err error) { 705 defer t.Trace(ctx, &err, 706 fmt.Sprintf("EncryptionKey(%s,%s,%v,%v)", name, teamID, public, botUID))() 707 708 team, err := t.loader.loadTeam(ctx, teamID, name, membersType, public, nil) 709 if err != nil { 710 return res, ni, err 711 } 712 impTeamName, err := team.ImplicitTeamDisplayNameNoConflicts(ctx) 713 if err != nil { 714 return res, ni, err 715 } 716 if err := t.identify(ctx, team, impTeamName); err != nil { 717 return res, ni, err 718 } 719 720 mctx := libkb.NewMetaContext(ctx, t.G().ExternalG()) 721 if res, err = getTeamCryptKey(mctx, team, team.Generation(), public, false, /* kbfsEncrypted */ 722 botUID, true /* forEncryption */); err != nil { 723 return res, ni, err 724 } 725 return res, types.NameInfo{ 726 ID: teamID, 727 CanonicalName: impTeamName.String(), 728 }, nil 729 } 730 731 func (t *ImplicitTeamsNameInfoSource) DecryptionKey(ctx context.Context, name string, teamID chat1.TLFID, 732 membersType chat1.ConversationMembersType, public bool, 733 keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (res types.CryptKey, err error) { 734 defer t.Trace(ctx, &err, 735 fmt.Sprintf("DecryptionKey(%s,%s,%v,%d,%v,%v)", name, teamID, public, keyGeneration, kbfsEncrypted, botUID))() 736 mctx := libkb.NewMetaContext(ctx, t.G().ExternalG()) 737 738 if botUID == nil && !kbfsEncrypted && !public { 739 return decryptionKeyViaFTL(mctx, teamID, membersType, keyGeneration) 740 } 741 742 team, err := loadTeamForDecryption(mctx, t.loader, name, teamID, membersType, public, 743 keyGeneration, kbfsEncrypted, botUID) 744 if err != nil { 745 return res, err 746 } 747 impTeamName, err := team.ImplicitTeamDisplayNameNoConflicts(ctx) 748 if err != nil { 749 return res, err 750 } 751 if err := t.identify(ctx, team, impTeamName); err != nil { 752 return res, err 753 } 754 return getTeamCryptKey(mctx, team, keybase1.PerTeamKeyGeneration(keyGeneration), public, 755 kbfsEncrypted, botUID, false /* forEncryption */) 756 } 757 758 func (t *ImplicitTeamsNameInfoSource) ephemeralLoadAndIdentify(ctx context.Context, encrypting bool, tlfName string, tlfID chat1.TLFID, 759 membersType chat1.ConversationMembersType, public bool) (teamID keybase1.TeamID, err error) { 760 if public { 761 return teamID, NewPublicTeamEphemeralKeyError() 762 } 763 team, err := t.loader.loadTeam(ctx, tlfID, tlfName, membersType, public, nil) 764 if err != nil { 765 return teamID, err 766 } 767 impTeamName, err := team.ImplicitTeamDisplayNameNoConflicts(ctx) 768 if err != nil { 769 return teamID, err 770 } 771 if err := t.identify(ctx, team, impTeamName); err != nil { 772 return teamID, err 773 } 774 return team.ID, nil 775 } 776 777 func (t *ImplicitTeamsNameInfoSource) EphemeralEncryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID, 778 membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (ek types.EphemeralCryptKey, err error) { 779 teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), true, tlfName, tlfID, membersType, public) 780 if err != nil { 781 return ek, err 782 } 783 if botUID != nil { 784 ek, _, err = t.G().GetEKLib().GetOrCreateLatestTeambotEK(mctx, teamID, *botUID) 785 } else { 786 ek, _, err = t.G().GetEKLib().GetOrCreateLatestTeamEK(mctx, teamID) 787 } 788 return ek, err 789 } 790 791 func (t *ImplicitTeamsNameInfoSource) EphemeralDecryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID, 792 membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID, 793 generation keybase1.EkGeneration, contentCtime *gregor1.Time) (teamEK types.EphemeralCryptKey, err error) { 794 teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), false, tlfName, tlfID, membersType, public) 795 if err != nil { 796 return teamEK, err 797 } 798 if botUID != nil { 799 return t.G().GetEKLib().GetTeambotEK(mctx, teamID, *botUID, generation, contentCtime) 800 } 801 return t.G().GetEKLib().GetTeamEK(mctx, teamID, generation, contentCtime) 802 } 803 804 func (t *ImplicitTeamsNameInfoSource) ShouldPairwiseMAC(ctx context.Context, tlfName string, tlfID chat1.TLFID, 805 membersType chat1.ConversationMembersType, public bool) (bool, []keybase1.KID, error) { 806 return shouldPairwiseMAC(ctx, t.G(), t.loader, tlfName, tlfID, membersType, public) 807 } 808 809 func (t *ImplicitTeamsNameInfoSource) lookupInternalName(ctx context.Context, name string, public bool) (res types.NameInfo, err error) { 810 team, err := teams.Load(ctx, t.G().ExternalG(), keybase1.LoadTeamArg{ 811 Name: name, 812 Public: public, 813 }) 814 if err != nil { 815 return res, err 816 } 817 res.CanonicalName = team.Name().String() 818 if res.ID, err = chat1.TeamIDToTLFID(team.ID); err != nil { 819 return res, err 820 } 821 return res, nil 822 } 823 824 type tlfIDToTeamIDMap struct { 825 storage *lru.Cache 826 } 827 828 func newTlfIDToTeamIDMap() *tlfIDToTeamIDMap { 829 s, _ := lru.New(10000) 830 return &tlfIDToTeamIDMap{ 831 storage: s, 832 } 833 } 834 835 func TLFIDToTeamID(tlfID chat1.TLFID) (keybase1.TeamID, error) { 836 return keybase1.TeamIDFromString(tlfID.String()) 837 } 838 839 // Lookup gives the server trust mapping between tlfID and teamID 840 func (t *tlfIDToTeamIDMap) Lookup(mctx libkb.MetaContext, tlfID chat1.TLFID, api libkb.API) (res keybase1.TeamID, err error) { 841 if iTeamID, ok := t.storage.Get(tlfID.String()); ok { 842 return iTeamID.(keybase1.TeamID), nil 843 } 844 arg := libkb.NewAPIArg("team/id") 845 arg.Args = libkb.NewHTTPArgs() 846 arg.Args.Add("tlf_id", libkb.S{Val: tlfID.String()}) 847 arg.SessionType = libkb.APISessionTypeREQUIRED 848 apiRes, err := api.Get(mctx, arg) 849 if err != nil { 850 return res, err 851 } 852 st, err := apiRes.Body.AtKey("team_id").GetString() 853 if err != nil { 854 return res, err 855 } 856 teamID, err := keybase1.TeamIDFromString(st) 857 if err != nil { 858 return res, err 859 } 860 t.storage.Add(tlfID.String(), teamID) 861 return teamID, nil 862 } 863 864 func (t *tlfIDToTeamIDMap) LookupTLFID(mctx libkb.MetaContext, teamID keybase1.TeamID, api libkb.API) (res chat1.TLFID, err error) { 865 if iTLFID, ok := t.storage.Get(teamID.String()); ok { 866 return iTLFID.(chat1.TLFID), nil 867 } 868 arg := libkb.NewAPIArg("team/tlfid") 869 arg.Args = libkb.NewHTTPArgs() 870 arg.Args.Add("team_id", libkb.S{Val: teamID.String()}) 871 arg.SessionType = libkb.APISessionTypeREQUIRED 872 apiRes, err := api.Get(mctx, arg) 873 if err != nil { 874 return res, err 875 } 876 st, err := apiRes.Body.AtKey("tlf_id").GetString() 877 if err != nil { 878 return res, err 879 } 880 tlfID, err := chat1.MakeTLFID(st) 881 if err != nil { 882 return res, err 883 } 884 t.storage.Add(teamID.String(), tlfID) 885 return tlfID, nil 886 } 887 888 var tlfIDToTeamID = newTlfIDToTeamIDMap()