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()