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

     1  package teams
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  	"github.com/keybase/client/go/libkb"
    10  	"github.com/keybase/client/go/protocol/keybase1"
    11  	"github.com/keybase/client/go/teams/hidden"
    12  	jsonw "github.com/keybase/go-jsonw"
    13  )
    14  
    15  // Create a new user/version pair.
    16  func NewUserVersion(uid keybase1.UID, eldestSeqno keybase1.Seqno) keybase1.UserVersion {
    17  	return keybase1.NewUserVersion(uid, eldestSeqno)
    18  }
    19  
    20  const TeamSigChainPlayerSupportedLinkVersion = 2
    21  
    22  // Accessor wrapper for keybase1.TeamSigChainState
    23  type TeamSigChainState struct {
    24  	inner  keybase1.TeamSigChainState
    25  	hidden *keybase1.HiddenTeamChain
    26  }
    27  
    28  func newTeamSigChainState(t Teamer) TeamSigChainState {
    29  	ret := TeamSigChainState{hidden: t.HiddenChain()}
    30  	if t.MainChain() != nil {
    31  		ret.inner = t.MainChain().Chain
    32  	}
    33  	return ret
    34  }
    35  
    36  func (t TeamSigChainState) DeepCopy() TeamSigChainState {
    37  	ret := TeamSigChainState{
    38  		inner: t.inner.DeepCopy(),
    39  	}
    40  	if t.hidden != nil {
    41  		tmp := t.hidden.DeepCopy()
    42  		ret.hidden = &tmp
    43  	}
    44  	return ret
    45  }
    46  
    47  func (t TeamSigChainState) DeepCopyToPtr() *TeamSigChainState {
    48  	t2 := t.DeepCopy()
    49  	return &t2
    50  }
    51  
    52  func (t TeamSigChainState) GetID() keybase1.TeamID {
    53  	return t.inner.Id
    54  }
    55  
    56  func (t TeamSigChainState) IsSubteam() bool {
    57  	return t.inner.ParentID != nil
    58  }
    59  
    60  func (t TeamSigChainState) IsImplicit() bool {
    61  	return t.inner.Implicit
    62  }
    63  
    64  func (t TeamSigChainState) IsPublic() bool {
    65  	return t.inner.Public
    66  }
    67  
    68  func (t TeamSigChainState) IsOpen() bool {
    69  	return t.inner.Open
    70  }
    71  
    72  func (t TeamSigChainState) LatestLastNamePart() keybase1.TeamNamePart {
    73  	return t.inner.NameLog[len(t.inner.NameLog)-1].LastPart
    74  }
    75  
    76  // Only non-nil if this is a subteam.
    77  func (t TeamSigChainState) GetParentID() *keybase1.TeamID {
    78  	return t.inner.ParentID
    79  }
    80  
    81  func (t TeamSigChainState) GetLatestSeqno() keybase1.Seqno {
    82  	return t.inner.LastSeqno
    83  }
    84  
    85  func (t TeamSigChainState) GetLatestHiddenSeqno() keybase1.Seqno {
    86  	if t.hidden == nil {
    87  		return keybase1.Seqno(0)
    88  	}
    89  	return t.hidden.Last
    90  }
    91  
    92  func (t TeamSigChainState) GetLatestLinkID() keybase1.LinkID {
    93  	return t.inner.LastLinkID
    94  }
    95  
    96  func (t TeamSigChainState) GetLatestHighSeqno() keybase1.Seqno {
    97  	return t.inner.LastHighSeqno
    98  }
    99  
   100  func (t TeamSigChainState) GetLatestHighLinkID() keybase1.LinkID {
   101  	return t.inner.LastHighLinkID
   102  }
   103  
   104  func (t TeamSigChainState) GetLatestLibkbLinkID() (libkb.LinkID, error) {
   105  	return libkb.ImportLinkID(t.GetLatestLinkID())
   106  }
   107  
   108  func (t TeamSigChainState) GetLinkIDBySeqno(seqno keybase1.Seqno) (keybase1.LinkID, error) {
   109  	l1, ok := t.inner.LinkIDs[seqno]
   110  	if !ok {
   111  		return l1, fmt.Errorf("seqno %v not in chain", seqno)
   112  	}
   113  	return l1, nil
   114  }
   115  
   116  func (t TeamSigChainState) GetLibkbLinkIDBySeqno(seqno keybase1.Seqno) (l2 libkb.LinkID, err error) {
   117  	l1, err := t.GetLinkIDBySeqno(seqno)
   118  	if err != nil {
   119  		return l2, err
   120  	}
   121  	return libkb.ImportLinkID(l1)
   122  }
   123  
   124  func (t TeamSigChainState) GetLatestGeneration() keybase1.PerTeamKeyGeneration {
   125  	ret := t.inner.MaxPerTeamKeyGeneration
   126  	if h := t.hidden.MaxReaderPerTeamKeyGeneration(); h > ret {
   127  		ret = h
   128  	}
   129  	return ret
   130  }
   131  
   132  func (t TeamSigChainState) GetLatestKBFSGeneration(appType keybase1.TeamApplication) (int, error) {
   133  	info, ok := t.inner.TlfLegacyUpgrade[appType]
   134  	if !ok {
   135  		return 0, errors.New("no KBFS keys available")
   136  	}
   137  	return info.LegacyGeneration, nil
   138  }
   139  
   140  func (t TeamSigChainState) makeHiddenRatchet(mctx libkb.MetaContext) (ret *hidden.Ratchet, err error) {
   141  	return hidden.MakeRatchet(mctx, t.hidden)
   142  }
   143  
   144  func (t TeamSigChainState) GetUserRole(user keybase1.UserVersion) (keybase1.TeamRole, error) {
   145  	return t.getUserRole(user), nil
   146  }
   147  
   148  // Get the user's role right after link at seqno was processed.
   149  func (t TeamSigChainState) GetUserRoleAtSeqno(user keybase1.UserVersion, seqno keybase1.Seqno) (keybase1.TeamRole, error) {
   150  	role := keybase1.TeamRole_NONE
   151  	if seqno <= 0 {
   152  		return role, fmt.Errorf("seqno %v is less than 1", seqno)
   153  	}
   154  	for _, point := range t.inner.UserLog[user] {
   155  		if point.SigMeta.SigChainLocation.Seqno > seqno {
   156  			return role, nil
   157  		}
   158  		role = point.Role
   159  	}
   160  	return role, nil
   161  }
   162  
   163  func (t TeamSigChainState) MemberCtime(user keybase1.UserVersion) *keybase1.Time {
   164  	points := t.inner.UserLog[user]
   165  	if len(points) == 0 {
   166  		return nil
   167  	}
   168  	// see if the user ever left the team so we return their most recent join
   169  	// time.
   170  	for i := len(points) - 1; i > 0; i-- {
   171  		// if we left the team at some point, return our later join time
   172  		if points[i].Role == keybase1.TeamRole_NONE {
   173  			if i < len(points)-1 && points[i+1].Role != keybase1.TeamRole_NONE {
   174  				return &points[i+1].SigMeta.Time
   175  			}
   176  		}
   177  	}
   178  	// we never left the team, give our original join time.
   179  	return &points[0].SigMeta.Time
   180  }
   181  
   182  func (t TeamSigChainState) GetUserLogPoint(user keybase1.UserVersion) *keybase1.UserLogPoint {
   183  	points := t.inner.UserLog[user]
   184  	if len(points) == 0 {
   185  		return nil
   186  	}
   187  	tmp := points[len(points)-1].DeepCopy()
   188  	return &tmp
   189  }
   190  
   191  func (t TeamSigChainState) GetAdminUserLogPoint(user keybase1.UserVersion) *keybase1.UserLogPoint {
   192  	ret := t.GetUserLogPoint(user)
   193  	if ret == nil {
   194  		return nil
   195  	}
   196  	if ret.Role != keybase1.TeamRole_ADMIN && ret.Role != keybase1.TeamRole_OWNER {
   197  		return nil
   198  	}
   199  	return ret
   200  }
   201  
   202  func (t TeamSigChainState) getUserRole(user keybase1.UserVersion) keybase1.TeamRole {
   203  	return t.inner.UserRole(user)
   204  }
   205  
   206  func (t TeamSigChainState) GetUserLastJoinTime(user keybase1.UserVersion) (time keybase1.Time, err error) {
   207  	return t.inner.GetUserLastJoinTime(user)
   208  }
   209  
   210  // GetUserLastRoleChangeTime returns the time of the last role change for user
   211  // in team. If the user left the team as a last change, the time of such leave
   212  // event is returned. If the user was never in the team, then this function
   213  // returns time=0 and wasMember=false.
   214  func (t TeamSigChainState) GetUserLastRoleChangeTime(user keybase1.UserVersion) (time keybase1.Time, wasMember bool) {
   215  	return t.inner.GetUserLastRoleChangeTime(user)
   216  }
   217  
   218  // NewStyle invites are completed in the `used_invites` field in the change
   219  // membership link, can optionally specify an expiration time, and a maximum
   220  // number of uses (potentially infinite).
   221  func IsNewStyleInvite(invite keybase1.TeamInvite) (bool, error) {
   222  	return invite.MaxUses != nil, nil
   223  }
   224  
   225  // assertBecameAdminAt asserts that the user (uv) became admin at the SigChainLocation given.
   226  // Figure out when this admin permission was revoked, if at all. If the promotion event
   227  // wasn't found as specified, then return an AdminPermissionError. In addition, we return
   228  // a bookend object, in the success case, to convey when the adminship was downgraded, if at all.
   229  func (t TeamSigChainState) assertBecameAdminAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (ret proofTermBookends, err error) {
   230  	points := t.inner.UserLog[uv]
   231  	linkMap := t.inner.LinkIDs
   232  	for i := len(points) - 1; i >= 0; i-- {
   233  		point := points[i]
   234  		if point.SigMeta.SigChainLocation.Eq(scl) {
   235  			if !point.Role.IsAdminOrAbove() {
   236  				return ret, NewAdminPermissionError(t.GetID(), uv, "not admin permission")
   237  			}
   238  			ret.left = newProofTerm(t.GetID().AsUserOrTeam(), point.SigMeta, linkMap)
   239  			r := findRoleDowngrade(points[(i+1):], keybase1.TeamRole_ADMIN)
   240  			if r != nil {
   241  				tmp := newProofTerm(t.GetID().AsUserOrTeam(), *r, linkMap)
   242  				ret.right = &tmp
   243  			}
   244  			return ret, nil
   245  		}
   246  	}
   247  	return ret, NewAdminPermissionError(t.GetID(), uv, "not found")
   248  }
   249  
   250  // Find a point where the role is taken away.
   251  func findRoleDowngrade(points []keybase1.UserLogPoint, role keybase1.TeamRole) *keybase1.SignatureMetadata {
   252  	for _, p := range points {
   253  		if !p.Role.IsOrAbove(role) {
   254  			return &p.SigMeta
   255  		}
   256  	}
   257  	return nil
   258  }
   259  
   260  // AssertWasRoleOrAboveAt asserts that user `uv` had `role` or above on the
   261  // team just after the given SigChainLocation `scl`.
   262  // We start at the point given, go backwards until we find a promotion,
   263  // then go forwards to make sure there wasn't a demotion before the specified time.
   264  // If there was, return a PermissionError. If no adminship was found at all, return a PermissionError.
   265  func (t TeamSigChainState) AssertWasRoleOrAboveAt(uv keybase1.UserVersion,
   266  	role keybase1.TeamRole, scl keybase1.SigChainLocation) (err error) {
   267  	mkErr := func(format string, args ...interface{}) error {
   268  		msg := fmt.Sprintf(format, args...)
   269  		if role.IsOrAbove(keybase1.TeamRole_ADMIN) {
   270  			return NewAdminPermissionError(t.GetID(), uv, msg)
   271  		}
   272  		return NewPermissionError(t.GetID(), uv, msg)
   273  	}
   274  	if scl.Seqno < keybase1.Seqno(0) {
   275  		return mkErr("negative seqno: %v", scl.Seqno)
   276  	}
   277  	points := t.inner.UserLog[uv]
   278  	for i := len(points) - 1; i >= 0; i-- {
   279  		point := points[i]
   280  		if err := point.SigMeta.SigChainLocation.Comparable(scl); err != nil {
   281  			return mkErr(err.Error())
   282  		}
   283  		if point.SigMeta.SigChainLocation.LessThanOrEqualTo(scl) && point.Role.IsOrAbove(role) {
   284  			// OK great, we found a point with the role in the log that's less than or equal to the given one.
   285  			// But now we reverse and go forward, and check that it wasn't revoked or downgraded.
   286  			// If so, that's a problem!
   287  			if right := findRoleDowngrade(points[(i+1):], role); right != nil && right.SigChainLocation.LessThanOrEqualTo(scl) {
   288  				return mkErr("%v permission was downgraded too soon!", role)
   289  			}
   290  			return nil
   291  		}
   292  	}
   293  	return mkErr("%v role point not found", role)
   294  }
   295  
   296  func (t TeamSigChainState) AssertWasWriterAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (err error) {
   297  	return t.AssertWasRoleOrAboveAt(uv, keybase1.TeamRole_WRITER, scl)
   298  }
   299  
   300  func (t TeamSigChainState) AssertWasAdminAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (err error) {
   301  	return t.AssertWasRoleOrAboveAt(uv, keybase1.TeamRole_ADMIN, scl)
   302  }
   303  
   304  func (t TeamSigChainState) GetUsersWithRole(role keybase1.TeamRole) (res []keybase1.UserVersion, err error) {
   305  	if role == keybase1.TeamRole_NONE {
   306  		return nil, errors.New("cannot get users with NONE role")
   307  	}
   308  	for uv := range t.inner.UserLog {
   309  		if t.getUserRole(uv) == role {
   310  			res = append(res, uv)
   311  		}
   312  	}
   313  	return res, nil
   314  }
   315  
   316  func (t TeamSigChainState) GetUsersWithRoleOrAbove(role keybase1.TeamRole) (res []keybase1.UserVersion, err error) {
   317  	if role == keybase1.TeamRole_NONE {
   318  		return nil, errors.New("cannot get users with NONE role")
   319  	}
   320  	for uv := range t.inner.UserLog {
   321  		if t.getUserRole(uv).IsOrAbove(role) {
   322  			res = append(res, uv)
   323  		}
   324  	}
   325  	return res, nil
   326  }
   327  
   328  func (t TeamSigChainState) GetLatestUVWithUID(uid keybase1.UID) (res keybase1.UserVersion, err error) {
   329  	found := false
   330  	for uv := range t.inner.UserLog {
   331  		if uv.Uid == uid && t.getUserRole(uv) != keybase1.TeamRole_NONE && (!found || res.EldestSeqno < uv.EldestSeqno) {
   332  			res = uv
   333  			found = true
   334  		}
   335  	}
   336  
   337  	if !found {
   338  		return res, errors.New("did not find user with given uid")
   339  	}
   340  	return res.DeepCopy(), nil
   341  }
   342  
   343  func (t TeamSigChainState) GetAllUVsWithUID(uid keybase1.UID) (res []keybase1.UserVersion) {
   344  	for uv := range t.inner.UserLog {
   345  		if uv.Uid == uid && t.getUserRole(uv) != keybase1.TeamRole_NONE {
   346  			res = append(res, uv)
   347  		}
   348  	}
   349  	return res
   350  }
   351  
   352  func (t TeamSigChainState) GetAllUVs() (res []keybase1.UserVersion) {
   353  	return t.inner.GetAllUVs()
   354  }
   355  
   356  func (t TeamSigChainState) GetLatestPerTeamKey(mctx libkb.MetaContext) (res keybase1.PerTeamKey, err error) {
   357  	res, _, err = t.getLatestPerTeamKeyWithMerkleSeqno(mctx)
   358  	return res, err
   359  }
   360  
   361  func (t TeamSigChainState) getLatestPerTeamKeyWithMerkleSeqno(mctx libkb.MetaContext) (res keybase1.PerTeamKey, mr keybase1.MerkleRootV2, err error) {
   362  	var hk *keybase1.PerTeamKey
   363  	if t.hidden != nil {
   364  		hk = t.hidden.MaxReaderPerTeamKey()
   365  	}
   366  	var ok bool
   367  	res, ok = t.inner.PerTeamKeys[t.inner.MaxPerTeamKeyGeneration]
   368  
   369  	if hk == nil && ok {
   370  		return res, t.inner.MerkleRoots[res.Seqno], nil
   371  	}
   372  	if !ok && hk != nil {
   373  		return *hk, t.hidden.MerkleRoots[hk.Seqno], nil
   374  	}
   375  	if !ok && hk == nil {
   376  		// if this happens it's a programming error
   377  		mctx.Debug("PTK not found error debug dump: inner %+v", t.inner)
   378  		if t.hidden != nil {
   379  			mctx.Debug("PTK not found error debug dump: hidden: %+v", *t.hidden)
   380  		}
   381  		return res, mr, fmt.Errorf("per-team-key not found for latest generation %d", t.inner.MaxPerTeamKeyGeneration)
   382  	}
   383  	if hk.Gen > res.Gen {
   384  		return *hk, t.hidden.MerkleRoots[hk.Seqno], nil
   385  	}
   386  	return res, t.inner.MerkleRoots[res.Seqno], nil
   387  }
   388  
   389  // checkNewPTK takes an existing state (t) and checks that a new PTK found (ptk) doesn't clash
   390  // against any PTKs already in the state.
   391  func (t *TeamSigChainState) checkNewPTK(ptk SCPerTeamKey) error {
   392  	// If there was no prior state, then the new PTK is OK
   393  	if t == nil {
   394  		return nil
   395  	}
   396  	gen := ptk.Generation
   397  	chk := func(existing keybase1.PerTeamKey, found bool) error {
   398  		if !found {
   399  			return nil
   400  		}
   401  		if !existing.SigKID.Equal(ptk.SigKID) || !existing.EncKID.Equal(ptk.EncKID) {
   402  			return fmt.Errorf("PTK clash at generation %d", gen)
   403  		}
   404  		return nil
   405  	}
   406  	if t.hidden != nil {
   407  		tmp, found := t.hidden.GetReaderPerTeamKeyAtGeneration(gen)
   408  		if err := chk(tmp, found); err != nil {
   409  			return err
   410  		}
   411  	}
   412  	tmp, found := t.inner.PerTeamKeys[gen]
   413  	if err := chk(tmp, found); err != nil {
   414  		return err
   415  	}
   416  	return nil
   417  }
   418  
   419  func (t *TeamSigChainState) GetLatestPerTeamKeyCTime() keybase1.UnixTime {
   420  	return t.inner.PerTeamKeyCTime
   421  }
   422  
   423  func (t TeamSigChainState) GetPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) (keybase1.PerTeamKey, error) {
   424  	res, ok := t.inner.PerTeamKeys[gen]
   425  	if ok {
   426  		return res, nil
   427  	}
   428  	res, ok = t.hidden.GetReaderPerTeamKeyAtGeneration(gen)
   429  	if ok {
   430  		return res, nil
   431  	}
   432  	return keybase1.PerTeamKey{}, libkb.NotFoundError{Msg: fmt.Sprintf("per-team-key not found for generation %d", gen)}
   433  }
   434  
   435  func (t TeamSigChainState) HasAnyStubbedLinks() bool {
   436  	for _, v := range t.inner.StubbedLinks {
   437  		if v {
   438  			return true
   439  		}
   440  	}
   441  	return false
   442  }
   443  
   444  // Whether the link has been processed and is not stubbed.
   445  func (t TeamSigChainState) IsLinkFilled(seqno keybase1.Seqno) bool {
   446  	if seqno > t.inner.LastSeqno {
   447  		return false
   448  	}
   449  	if seqno < 0 {
   450  		return false
   451  	}
   452  	return !t.inner.StubbedLinks[seqno]
   453  }
   454  
   455  func (t TeamSigChainState) HasStubbedSeqno(seqno keybase1.Seqno) bool {
   456  	return t.inner.StubbedLinks[seqno]
   457  }
   458  
   459  func (t TeamSigChainState) GetSubteamName(id keybase1.TeamID) (*keybase1.TeamName, error) {
   460  	lastPoint := t.getLastSubteamPoint(id)
   461  	if lastPoint != nil {
   462  		return &lastPoint.Name, nil
   463  	}
   464  	return nil, fmt.Errorf("subteam not found: %v", id.String())
   465  }
   466  
   467  // Inform the UserLog and Bots of a user's role.
   468  // Mutates the UserLog and Bots.
   469  // Must be called with seqno's and events in correct order.
   470  func (t *TeamSigChainState) inform(u keybase1.UserVersion, role keybase1.TeamRole, sigMeta keybase1.SignatureMetadata) {
   471  	t.inner.UserLog[u] = append(t.inner.UserLog[u], keybase1.UserLogPoint{
   472  		Role:    role,
   473  		SigMeta: sigMeta,
   474  	})
   475  	// Clear an entry in Bots if any
   476  	if !role.IsRestrictedBot() {
   477  		delete(t.inner.Bots, u)
   478  	}
   479  }
   480  
   481  func (t *TeamSigChainState) informNewInvite(i keybase1.TeamInvite, teamSigMeta keybase1.TeamSignatureMetadata) {
   482  	t.inner.InviteMetadatas[i.Id] = keybase1.NewTeamInviteMetadata(i, teamSigMeta)
   483  }
   484  
   485  func (t *TeamSigChainState) informCanceledInvite(i keybase1.TeamInviteID,
   486  	cancelTeamSigMeta keybase1.TeamSignatureMetadata) {
   487  	inviteMD, ok := t.inner.InviteMetadatas[i]
   488  	if !ok {
   489  		return
   490  	}
   491  	inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithCancelled(keybase1.TeamInviteMetadataCancel{
   492  		TeamSigMeta: cancelTeamSigMeta,
   493  	})
   494  	t.inner.InviteMetadatas[i] = inviteMD
   495  }
   496  
   497  func (t *TeamSigChainState) informCompletedInvite(i keybase1.TeamInviteID,
   498  	completeTeamSigMeta keybase1.TeamSignatureMetadata) {
   499  	inviteMD, ok := t.inner.InviteMetadatas[i]
   500  	if !ok {
   501  		return
   502  	}
   503  	inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithCompleted(keybase1.TeamInviteMetadataCompleted{
   504  		TeamSigMeta: completeTeamSigMeta,
   505  	})
   506  	t.inner.InviteMetadatas[i] = inviteMD
   507  }
   508  
   509  func (t *TeamSigChainState) getLastSubteamPoint(id keybase1.TeamID) *keybase1.SubteamLogPoint {
   510  	if len(t.inner.SubteamLog[id]) > 0 {
   511  		return &t.inner.SubteamLog[id][len(t.inner.SubteamLog[id])-1]
   512  	}
   513  	return nil
   514  }
   515  
   516  // Inform the SubteamLog of a subteam name change.
   517  // Links must be added in order by seqno for each subteam (asserted here).
   518  // Links for different subteams can interleave.
   519  // Mutates the SubteamLog.
   520  // Name collisions are allowed here. They are allowed because you might
   521  // be removed a subteam and then its future name changes and deletion would be stubbed.
   522  // See ListSubteams for details.
   523  func (t *TeamSigChainState) informSubteam(id keybase1.TeamID, name keybase1.TeamName, seqno keybase1.Seqno) error {
   524  	lastPoint := t.getLastSubteamPoint(id)
   525  	if lastPoint != nil && lastPoint.Seqno.Eq(seqno) {
   526  		return fmt.Errorf("re-entry into subteam log for seqno: %v", seqno)
   527  	}
   528  	if lastPoint != nil && seqno < lastPoint.Seqno {
   529  		return fmt.Errorf("cannot add to subteam log out of order: %v < %v", seqno, lastPoint.Seqno)
   530  	}
   531  	if lastPoint != nil && lastPoint.Name.IsNil() {
   532  		return fmt.Errorf("cannot process subteam rename because %v was deleted at seqno %v", id, lastPoint.Seqno)
   533  	}
   534  	t.inner.SubteamLog[id] = append(t.inner.SubteamLog[id], keybase1.SubteamLogPoint{
   535  		Name:  name,
   536  		Seqno: seqno,
   537  	})
   538  	return nil
   539  }
   540  
   541  // Inform the SubteamLog of a subteam deletion.
   542  // Links must be added in order by seqno for each subteam (asserted here).
   543  // Links for different subteams can interleave.
   544  // Mutates the SubteamLog.
   545  func (t *TeamSigChainState) informSubteamDelete(id keybase1.TeamID, seqno keybase1.Seqno) error {
   546  	lastPoint := t.getLastSubteamPoint(id)
   547  	if lastPoint != nil && lastPoint.Seqno.Eq(seqno) {
   548  		return fmt.Errorf("re-entry into subteam log for seqno: %v", seqno)
   549  	}
   550  	if lastPoint != nil && seqno < lastPoint.Seqno {
   551  		return fmt.Errorf("cannot add to subteam log out of order: %v < %v", seqno, lastPoint.Seqno)
   552  	}
   553  	// Don't check for deleting the same team twice. Just allow it.
   554  	t.inner.SubteamLog[id] = append(t.inner.SubteamLog[id], keybase1.SubteamLogPoint{
   555  		Seqno: seqno,
   556  	})
   557  	return nil
   558  }
   559  
   560  // Only call this on a Team that has been loaded with NeedAdmin.
   561  // Otherwise, you might get incoherent answers due to links that
   562  // were stubbed over the life of the cached object.
   563  //
   564  // For subteams that you were removed from, this list may
   565  // still include them because your removal was stubbed.
   566  // The list will not contain duplicate names.
   567  // Since this should only be called when you are an admin,
   568  // none of that should really come up, but it's here just to be less fragile.
   569  func (t *TeamSigChainState) ListSubteams() (res []keybase1.TeamIDAndName) {
   570  	return t.inner.ListSubteams()
   571  }
   572  
   573  // Check that a subteam rename occurred just so.
   574  // That the subteam `subteamID` got a new name `newName` at exactly `seqno` in this,
   575  // the parent, chain.
   576  // Note this only checks against the last part of `newName` because mid-team renames are such a pain.
   577  // This is currently linear in the number of times that subteam has been renamed.
   578  // It should be easy to add an index if need be.
   579  func (t *TeamSigChainState) SubteamRenameOccurred(
   580  	subteamID keybase1.TeamID, newName keybase1.TeamName, seqno keybase1.Seqno) error {
   581  
   582  	points := t.inner.SubteamLog[subteamID]
   583  	if len(points) == 0 {
   584  		return fmt.Errorf("subteam %v has no name log", subteamID)
   585  	}
   586  	for _, point := range points {
   587  		if point.Seqno == seqno {
   588  			if point.Name.LastPart().Eq(newName.LastPart()) {
   589  				// found it!
   590  				return nil
   591  			}
   592  		}
   593  		if point.Seqno > seqno {
   594  			break
   595  		}
   596  	}
   597  	return fmt.Errorf("subteam %v did not have rename entry in log: %v %v",
   598  		subteamID, newName, seqno)
   599  }
   600  
   601  func (t *TeamSigChainState) ActiveInvites() (ret []keybase1.TeamInviteMetadata) {
   602  	for _, md := range t.inner.InviteMetadatas {
   603  		if code, err := md.Status.Code(); err == nil &&
   604  			code == keybase1.TeamInviteMetadataStatusCode_ACTIVE {
   605  			ret = append(ret, md)
   606  		}
   607  	}
   608  	return ret
   609  }
   610  
   611  func (t *TeamSigChainState) NumActiveInvites() int {
   612  	return len(t.ActiveInvites())
   613  }
   614  
   615  func (t *TeamSigChainState) HasActiveInvite(name keybase1.TeamInviteName, typ keybase1.TeamInviteType) (bool, error) {
   616  	i, err := t.FindActiveInvite(name, typ)
   617  	if err != nil {
   618  		if _, ok := err.(libkb.NotFoundError); ok {
   619  			return false, nil
   620  		}
   621  		return false, err
   622  	}
   623  	if i != nil {
   624  		return true, nil
   625  	}
   626  	return false, nil
   627  }
   628  
   629  func (t *TeamSigChainState) FindActiveInvite(name keybase1.TeamInviteName, typ keybase1.TeamInviteType) (*keybase1.TeamInvite, error) {
   630  	for _, inviteMD := range t.ActiveInvites() {
   631  		if inviteMD.Invite.Name == name && inviteMD.Invite.Type.Eq(typ) {
   632  			return &inviteMD.Invite, nil
   633  		}
   634  	}
   635  	return nil, libkb.NotFoundError{}
   636  }
   637  
   638  func (t *TeamSigChainState) FindActiveInviteString(mctx libkb.MetaContext, name string, typ string) (ret keybase1.TeamInvite, err error) {
   639  	itype, err := TeamInviteTypeFromString(mctx, typ)
   640  	if err != nil {
   641  		return ret, err
   642  	}
   643  	invPtr, err := t.FindActiveInvite(keybase1.TeamInviteName(name), itype)
   644  	switch {
   645  	case err != nil:
   646  		return ret, err
   647  	case invPtr == nil:
   648  		return ret, libkb.NotFoundError{}
   649  	}
   650  	return *invPtr, nil
   651  }
   652  
   653  // FindActiveInviteMDByID returns potentially expired invites that have not been
   654  // explicitly cancelled, since the sigchain player is agnostic to the concept
   655  // of time. We treat invite expiration times as advisory for admin clients
   656  // completing invites, but do not check them in the sigchain player.
   657  func (t *TeamSigChainState) FindActiveInviteMDByID(
   658  	id keybase1.TeamInviteID) (inviteMD keybase1.TeamInviteMetadata, found bool) {
   659  	res, found := t.inner.InviteMetadatas[id]
   660  	if !found {
   661  		return inviteMD, false
   662  	}
   663  	code, err := res.Status.Code()
   664  	if err != nil {
   665  		return inviteMD, false
   666  	}
   667  	if code != keybase1.TeamInviteMetadataStatusCode_ACTIVE {
   668  		return inviteMD, false
   669  	}
   670  	return res, true
   671  }
   672  
   673  func (t *TeamSigChainState) IsInviteObsolete(id keybase1.TeamInviteID) bool {
   674  	inviteMD, found := t.inner.InviteMetadatas[id]
   675  	if !found {
   676  		return false
   677  	}
   678  	code, err := inviteMD.Status.Code()
   679  	if err != nil {
   680  		return false
   681  	}
   682  	return code == keybase1.TeamInviteMetadataStatusCode_OBSOLETE
   683  }
   684  
   685  // FindActiveKeybaseInvite finds and returns a Keybase-type
   686  // invite for given UID. Ordering here is not guaranteed, caller
   687  // shouldn't assume that returned invite will be the oldest/newest one
   688  // for the UID.
   689  func (t *TeamSigChainState) FindActiveKeybaseInvite(uid keybase1.UID) (keybase1.TeamInvite, keybase1.UserVersion, bool) {
   690  	for _, inviteMD := range t.ActiveInvites() {
   691  		if inviteUv, err := inviteMD.Invite.KeybaseUserVersion(); err == nil {
   692  			if inviteUv.Uid.Equal(uid) {
   693  				return inviteMD.Invite, inviteUv, true
   694  			}
   695  		}
   696  	}
   697  	return keybase1.TeamInvite{}, keybase1.UserVersion{}, false
   698  }
   699  
   700  func (t *TeamSigChainState) GetMerkleRoots() map[keybase1.Seqno]keybase1.MerkleRootV2 {
   701  	return t.inner.MerkleRoots
   702  }
   703  
   704  func (t TeamSigChainState) TeamBotSettings() map[keybase1.UserVersion]keybase1.TeamBotSettings {
   705  	return t.inner.Bots
   706  }
   707  
   708  // --------------------------------------------------
   709  
   710  // AppendChainLink process a chain link.
   711  // It must have already been partially verified by TeamLoader.
   712  // `reader` is the user who is processing the chain.
   713  // `state` is moved into this function. There must exist no live references into it from now on.
   714  // If `state` is nil this is the first link of the chain.
   715  // `signer` may be nil iff link is stubbed.
   716  func AppendChainLink(ctx context.Context, g *libkb.GlobalContext, reader keybase1.UserVersion, state *TeamSigChainState,
   717  	link *ChainLinkUnpacked, signer *SignerX) (res TeamSigChainState, err error) {
   718  	t := &teamSigchainPlayer{
   719  		Contextified: libkb.NewContextified(g),
   720  		reader:       reader,
   721  	}
   722  	var latestSeqno keybase1.Seqno
   723  	if state != nil {
   724  		latestSeqno = state.GetLatestSeqno()
   725  	}
   726  	res, err = t.appendChainLinkHelper(libkb.NewMetaContext(ctx, g), state, link, signer)
   727  	if err != nil {
   728  		return TeamSigChainState{}, NewAppendLinkError(link, latestSeqno, err)
   729  	}
   730  	return res, err
   731  }
   732  
   733  // InflateLink adds the full inner link for a link that has already been added in stubbed form.
   734  // `state` is moved into this function. There must exist no live references into it from now on.
   735  func InflateLink(ctx context.Context, g *libkb.GlobalContext, reader keybase1.UserVersion, state TeamSigChainState,
   736  	link *ChainLinkUnpacked, signer SignerX) (res TeamSigChainState, err error) {
   737  	if link.isStubbed() {
   738  		return TeamSigChainState{}, NewStubbedError(link)
   739  	}
   740  	if link.Seqno() > state.GetLatestSeqno() {
   741  		return TeamSigChainState{}, NewInflateErrorWithNote(link,
   742  			fmt.Sprintf("seqno off the chain %v > %v", link.Seqno(), state.GetLatestSeqno()))
   743  	}
   744  
   745  	// Check the that the link id matches our stubbed record.
   746  	seenLinkID, err := state.GetLibkbLinkIDBySeqno(link.Seqno())
   747  	if err != nil {
   748  		return TeamSigChainState{}, err
   749  	}
   750  	if !seenLinkID.Eq(link.LinkID()) {
   751  		return TeamSigChainState{}, NewInflateErrorWithNote(link,
   752  			fmt.Sprintf("link id mismatch: %v != %v", link.LinkID().String(), seenLinkID.String()))
   753  	}
   754  
   755  	// Check that the link has not already been inflated.
   756  	if _, ok := state.inner.StubbedLinks[link.Seqno()]; !ok {
   757  		return TeamSigChainState{}, NewInflateErrorWithNote(link, "already inflated")
   758  	}
   759  
   760  	t := &teamSigchainPlayer{
   761  		Contextified: libkb.NewContextified(g),
   762  		reader:       reader,
   763  	}
   764  	iRes, err := t.addInnerLink(libkb.NewMetaContext(ctx, g), &state, link, signer, true)
   765  	if err != nil {
   766  		return TeamSigChainState{}, err
   767  	}
   768  
   769  	delete(iRes.newState.inner.StubbedLinks, link.Seqno())
   770  
   771  	return iRes.newState, nil
   772  
   773  }
   774  
   775  // Helper struct for playing sigchains.
   776  type teamSigchainPlayer struct {
   777  	libkb.Contextified
   778  	reader keybase1.UserVersion // the user processing the chain
   779  }
   780  
   781  // Add a chain link to the end.
   782  // `prevState` is moved into this function. There must exist no live references into it from now on.
   783  // `signer` may be nil iff link is stubbed.
   784  // If `prevState` is nil this is the first chain link.
   785  func (t *teamSigchainPlayer) appendChainLinkHelper(
   786  	mctx libkb.MetaContext, prevState *TeamSigChainState, link *ChainLinkUnpacked, signer *SignerX) (
   787  	res TeamSigChainState, err error) {
   788  
   789  	err = t.checkOuterLink(mctx.Ctx(), prevState, link)
   790  	if err != nil {
   791  		return res, fmt.Errorf("team sigchain outer link: %s", err)
   792  	}
   793  
   794  	var newState *TeamSigChainState
   795  	if link.isStubbed() {
   796  		if prevState == nil {
   797  			return res, NewStubbedErrorWithNote(link, "first link cannot be stubbed")
   798  		}
   799  		newState2 := prevState.DeepCopy()
   800  		newState = &newState2
   801  	} else {
   802  		if signer == nil || !signer.signer.Uid.Exists() {
   803  			return res, NewInvalidLink(link, "signing user not provided for team link")
   804  		}
   805  		iRes, err := t.addInnerLink(mctx, prevState, link, *signer, false)
   806  		if err != nil {
   807  			return res, err
   808  		}
   809  		newState = &iRes.newState
   810  	}
   811  
   812  	newState.inner.LastSeqno = link.Seqno()
   813  	newState.inner.LastLinkID = link.LinkID().Export()
   814  	newState.inner.LinkIDs[link.Seqno()] = link.LinkID().Export()
   815  
   816  	if link.isStubbed() {
   817  		newState.inner.StubbedLinks[link.Seqno()] = true
   818  	}
   819  
   820  	// Store the head merkle sequence to the DB, for use in the audit mechanism.
   821  	// For the purposes of testing, we disable this feature, so we can check
   822  	// the lazy-repopulation scheme for old stored teams (without a full cache bust).
   823  	if link.Seqno() == keybase1.Seqno(1) && !link.isStubbed() && !t.G().Env.Test.TeamNoHeadMerkleStore {
   824  		tmp := link.inner.Body.MerkleRoot.ToMerkleRootV2()
   825  		newState.inner.HeadMerkle = &tmp
   826  	}
   827  
   828  	if !link.isStubbed() && newState.inner.MerkleRoots != nil {
   829  		newState.inner.MerkleRoots[link.Seqno()] = link.inner.Body.MerkleRoot.ToMerkleRootV2()
   830  	}
   831  
   832  	return *newState, nil
   833  }
   834  
   835  func (t *teamSigchainPlayer) checkOuterLink(ctx context.Context, prevState *TeamSigChainState, link *ChainLinkUnpacked) (err error) {
   836  	if prevState == nil {
   837  		if link.Seqno() != 1 {
   838  			return NewUnexpectedSeqnoError(keybase1.Seqno(1), link.Seqno())
   839  		}
   840  	} else {
   841  		if link.Seqno() != prevState.inner.LastSeqno+1 {
   842  			return NewUnexpectedSeqnoError(prevState.inner.LastSeqno+1, link.Seqno())
   843  		}
   844  	}
   845  
   846  	if prevState == nil {
   847  		if len(link.Prev()) != 0 {
   848  			return fmt.Errorf("expected outer nil prev but got:%s", link.Prev())
   849  		}
   850  	} else {
   851  		prevStateLastLinkID, err := libkb.ImportLinkID(prevState.inner.LastLinkID)
   852  		if err != nil {
   853  			return fmt.Errorf("invalid prev last link id: %v", err)
   854  		}
   855  		if !link.Prev().Eq(prevStateLastLinkID) {
   856  			return fmt.Errorf("wrong outer prev: %s != %s", link.Prev(), prevState.inner.LastLinkID)
   857  		}
   858  	}
   859  
   860  	return nil
   861  }
   862  
   863  type checkInnerLinkResult struct {
   864  	newState TeamSigChainState
   865  }
   866  
   867  // Check and add the inner link.
   868  // `isInflate` is false if this is a new link and true if it is a link which has already been added as stubbed.
   869  // Does not modify `prevState` but returns a new state.
   870  func (t *teamSigchainPlayer) addInnerLink(mctx libkb.MetaContext,
   871  	prevState *TeamSigChainState, link *ChainLinkUnpacked, signer SignerX,
   872  	isInflate bool) (res checkInnerLinkResult, err error) {
   873  
   874  	if link.inner == nil {
   875  		return res, NewStubbedError(link)
   876  	}
   877  	payload := *link.inner
   878  
   879  	if !signer.signer.Uid.Exists() {
   880  		return res, NewInvalidLink(link, "empty link signer")
   881  	}
   882  
   883  	// This may be superfluous.
   884  	err = link.AssertInnerOuterMatch()
   885  	if err != nil {
   886  		return res, err
   887  	}
   888  
   889  	// completely ignore these fields
   890  	_ = payload.ExpireIn
   891  	_ = payload.SeqType
   892  
   893  	if payload.Tag != "signature" {
   894  		return res, NewInvalidLink(link, "unrecognized tag: '%s'", payload.Tag)
   895  	}
   896  
   897  	if payload.Body.Team == nil {
   898  		return res, NewInvalidLink(link, "missing team section")
   899  	}
   900  	team := payload.Body.Team
   901  
   902  	if len(team.ID) == 0 {
   903  		return res, NewInvalidLink(link, "missing team id")
   904  	}
   905  	teamID, err := keybase1.TeamIDFromString(string(team.ID))
   906  	if err != nil {
   907  		return res, err
   908  	}
   909  
   910  	if teamID.IsPublic() != team.Public {
   911  		return res, fmt.Errorf("link specified public:%v but ID is public:%v",
   912  			team.Public, teamID.IsPublic())
   913  	}
   914  
   915  	if prevState != nil && !prevState.inner.Id.Equal(teamID) {
   916  		return res, fmt.Errorf("wrong team id: %s != %s", teamID.String(), prevState.inner.Id.String())
   917  	}
   918  
   919  	if prevState != nil && prevState.IsImplicit() != team.Implicit {
   920  		return res, fmt.Errorf("link specified implicit:%v but team was already implicit:%v",
   921  			team.Implicit, prevState.IsImplicit())
   922  	}
   923  
   924  	if prevState != nil && prevState.IsPublic() != team.Public {
   925  		return res, fmt.Errorf("link specified public:%v but team was already public:%v",
   926  			team.Implicit, prevState.IsImplicit())
   927  	}
   928  
   929  	if team.Public && (!team.Implicit || (prevState != nil && !prevState.IsImplicit())) {
   930  		return res, fmt.Errorf("public non-implicit teams are not supported")
   931  	}
   932  
   933  	err = t.checkSeqnoToAdd(prevState, link.Seqno(), isInflate)
   934  	if err != nil {
   935  		return res, err
   936  	}
   937  	// When isInflate then it is likely prevSeqno != prevState.GetLatestSeqno()
   938  	prevSeqno := link.Seqno() - 1
   939  
   940  	allowInflate := func(allow bool) error {
   941  		if isInflate && !allow {
   942  			return fmt.Errorf("inflating link type not supported: %v", payload.Body.Type)
   943  		}
   944  		return nil
   945  	}
   946  	allowInImplicitTeam := func(allow bool) error {
   947  		if team.Implicit && !allow {
   948  			return NewImplicitTeamOperationError(payload.Body.Type)
   949  		}
   950  		return nil
   951  	}
   952  	enforceFirstInChain := func(firstInChain bool) error {
   953  		if firstInChain {
   954  			if prevState != nil {
   955  				return fmt.Errorf("link type '%s' unexpected at seqno:%v", payload.Body.Type, prevState.inner.LastSeqno+1)
   956  			}
   957  		} else {
   958  			if prevState == nil {
   959  				return fmt.Errorf("link type '%s' unexpected at beginning", payload.Body.Type)
   960  			}
   961  		}
   962  		return nil
   963  	}
   964  	enforceGeneric := func(name string, rule Tristate, hasReal bool) error {
   965  		switch rule {
   966  		case TristateDisallow:
   967  			if hasReal {
   968  				return fmt.Errorf("sigchain link contains unexpected '%s'", name)
   969  			}
   970  		case TristateRequire:
   971  			if !hasReal {
   972  				return fmt.Errorf("sigchain link missing %s", name)
   973  			}
   974  		case TristateOptional:
   975  		default:
   976  			return fmt.Errorf("unsupported tristate (fault): %v", rule)
   977  		}
   978  		return nil
   979  	}
   980  	enforce := func(rules LinkRules) error {
   981  		return libkb.PickFirstError(
   982  			enforceGeneric("name", rules.Name, team.Name != nil),
   983  			enforceGeneric("members", rules.Members, team.Members != nil),
   984  			enforceGeneric("parent", rules.Parent, team.Parent != nil),
   985  			enforceGeneric("subteam", rules.Subteam, team.Subteam != nil),
   986  			enforceGeneric("per-team-key", rules.PerTeamKey, team.PerTeamKey != nil),
   987  			enforceGeneric("admin", rules.Admin, team.Admin != nil),
   988  			enforceGeneric("invites", rules.Invites, team.Invites != nil),
   989  			enforceGeneric("completed-invites", rules.CompletedInvites, team.CompletedInvites != nil),
   990  			enforceGeneric("settings", rules.Settings, team.Settings != nil),
   991  			enforceGeneric("kbfs", rules.KBFS, team.KBFS != nil),
   992  			enforceGeneric("box-summary-hash", rules.BoxSummaryHash, team.BoxSummaryHash != nil),
   993  			enforceGeneric("bot_settings", rules.BotSettings, team.BotSettings != nil),
   994  			allowInImplicitTeam(rules.AllowInImplicitTeam),
   995  			allowInflate(rules.AllowInflate),
   996  			enforceFirstInChain(rules.FirstInChain),
   997  		)
   998  	}
   999  
  1000  	checkAdmin := func(op string) (signerIsExplicitOwner bool, err error) {
  1001  		signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno)
  1002  		if err != nil {
  1003  			signerRole = keybase1.TeamRole_NONE
  1004  		}
  1005  		signerIsExplicitOwner = signerRole == keybase1.TeamRole_OWNER
  1006  		if signerRole.IsAdminOrAbove() || signer.implicitAdmin {
  1007  			return signerIsExplicitOwner, nil
  1008  		}
  1009  		return signerIsExplicitOwner, fmt.Errorf("link signer does not have permission to %s: %v is a %v", op, signer, signerRole)
  1010  	}
  1011  
  1012  	checkExplicitWriter := func(op string) (err error) {
  1013  		signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno)
  1014  		if err != nil {
  1015  			signerRole = keybase1.TeamRole_NONE
  1016  		}
  1017  		if !signerRole.IsWriterOrAbove() {
  1018  			return fmt.Errorf("link signer does not have writer permission to %s: %v is a %v", op, signer, signerRole)
  1019  		}
  1020  		return nil
  1021  	}
  1022  
  1023  	moveState := func() {
  1024  		// Move prevState to res.newState.
  1025  		// Re-use the object without deep copying. There must be no other live references into prevState.
  1026  		res.newState = *prevState
  1027  		prevState = nil
  1028  	}
  1029  	isHighLink := false
  1030  
  1031  	teamSigMeta := keybase1.NewTeamSigMeta(payload.SignatureMetadata(), signer.signer)
  1032  
  1033  	switch libkb.LinkType(payload.Body.Type) {
  1034  	case libkb.LinkTypeTeamRoot:
  1035  		isHighLink = true
  1036  		err = enforce(LinkRules{
  1037  			Name:                TristateRequire,
  1038  			Members:             TristateRequire,
  1039  			PerTeamKey:          TristateRequire,
  1040  			BoxSummaryHash:      TristateOptional,
  1041  			Invites:             TristateOptional,
  1042  			Settings:            TristateOptional,
  1043  			AllowInImplicitTeam: true,
  1044  			FirstInChain:        true,
  1045  		})
  1046  		if err != nil {
  1047  			return res, err
  1048  		}
  1049  
  1050  		// Check the team name
  1051  		teamName, err := keybase1.TeamNameFromString(string(*team.Name))
  1052  		if err != nil {
  1053  			return res, err
  1054  		}
  1055  		if !teamName.IsRootTeam() {
  1056  			return res, fmt.Errorf("root team has subteam name: %s", teamName)
  1057  		}
  1058  
  1059  		// Whether this is an implicit team
  1060  		isImplicit := teamName.IsImplicit()
  1061  		if isImplicit != team.Implicit {
  1062  			return res, fmt.Errorf("link specified implicit:%v but name specified implicit:%v",
  1063  				team.Implicit, isImplicit)
  1064  		}
  1065  
  1066  		// Check the team ID
  1067  		// assert that team_name = hash(team_id)
  1068  		// this is only true for root teams
  1069  		if !teamID.Equal(teamName.ToTeamID(team.Public)) {
  1070  			return res, fmt.Errorf("team id:%s does not match team name:%s", teamID, teamName)
  1071  		}
  1072  		if teamID.IsSubTeam() {
  1073  			return res, fmt.Errorf("malformed root team id")
  1074  		}
  1075  
  1076  		roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{
  1077  			requireOwners:       true,
  1078  			allowRemovals:       false,
  1079  			onlyOwnersOrReaders: isImplicit,
  1080  		})
  1081  		if err != nil {
  1082  			return res, err
  1083  		}
  1084  
  1085  		perTeamKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, nil)
  1086  		if err != nil {
  1087  			return res, err
  1088  		}
  1089  
  1090  		perTeamKeys := make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKey)
  1091  		perTeamKeys[keybase1.PerTeamKeyGeneration(1)] = perTeamKey
  1092  
  1093  		res.newState = TeamSigChainState{
  1094  			inner: keybase1.TeamSigChainState{
  1095  				Reader:       t.reader,
  1096  				Id:           teamID,
  1097  				Implicit:     isImplicit,
  1098  				Public:       team.Public,
  1099  				RootAncestor: teamName.RootAncestorName(),
  1100  				NameDepth:    teamName.Depth(),
  1101  				NameLog: []keybase1.TeamNameLogPoint{{
  1102  					LastPart: teamName.LastPart(),
  1103  					Seqno:    1,
  1104  				}},
  1105  				LastSeqno:               1,
  1106  				LastLinkID:              link.LinkID().Export(),
  1107  				ParentID:                nil,
  1108  				UserLog:                 make(map[keybase1.UserVersion][]keybase1.UserLogPoint),
  1109  				SubteamLog:              make(map[keybase1.TeamID][]keybase1.SubteamLogPoint),
  1110  				PerTeamKeys:             perTeamKeys,
  1111  				MaxPerTeamKeyGeneration: keybase1.PerTeamKeyGeneration(1),
  1112  				PerTeamKeyCTime:         keybase1.UnixTime(payload.Ctime),
  1113  				LinkIDs:                 make(map[keybase1.Seqno]keybase1.LinkID),
  1114  				StubbedLinks:            make(map[keybase1.Seqno]bool),
  1115  				InviteMetadatas:         make(map[keybase1.TeamInviteID]keybase1.TeamInviteMetadata),
  1116  				TlfLegacyUpgrade:        make(map[keybase1.TeamApplication]keybase1.TeamLegacyTLFUpgradeChainInfo),
  1117  				MerkleRoots:             make(map[keybase1.Seqno]keybase1.MerkleRootV2),
  1118  				Bots:                    make(map[keybase1.UserVersion]keybase1.TeamBotSettings),
  1119  			}}
  1120  
  1121  		t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata())
  1122  		if team.Invites != nil {
  1123  			if isImplicit {
  1124  				signerIsExplicitOwner := true
  1125  				additions, cancelations, err := t.sanityCheckInvites(mctx, signer.signer, signerIsExplicitOwner,
  1126  					*team.Invites, link.SigID(), sanityCheckInvitesOptions{
  1127  						isRootTeam:   true,
  1128  						implicitTeam: isImplicit,
  1129  					})
  1130  				if err != nil {
  1131  					return res, err
  1132  				}
  1133  				t.updateInvites(&res.newState, additions, cancelations, teamSigMeta)
  1134  			} else {
  1135  				return res, fmt.Errorf("invites not allowed in root link")
  1136  			}
  1137  		}
  1138  
  1139  		// check that the signer is an owner
  1140  		if res.newState.getUserRole(signer.signer) != keybase1.TeamRole_OWNER {
  1141  			return res, fmt.Errorf("signer is not an owner: %v (%v)", signer, team.Members.Owners)
  1142  		}
  1143  
  1144  		if settings := team.Settings; settings != nil {
  1145  			err = t.parseTeamSettings(settings, &res.newState)
  1146  			if err != nil {
  1147  				return res, err
  1148  			}
  1149  		}
  1150  	case libkb.LinkTypeChangeMembership:
  1151  		err = enforce(LinkRules{
  1152  			Members:             TristateRequire,
  1153  			PerTeamKey:          TristateOptional,
  1154  			Admin:               TristateOptional,
  1155  			CompletedInvites:    TristateOptional,
  1156  			BoxSummaryHash:      TristateOptional,
  1157  			AllowInImplicitTeam: true,
  1158  		})
  1159  		if err != nil {
  1160  			return res, err
  1161  		}
  1162  
  1163  		// Check that the signer is at least an ADMIN or is an IMPLICIT ADMIN to have permission to make this link.
  1164  		var signerIsExplicitOwner bool
  1165  		signerIsExplicitOwner, err = checkAdmin("change membership")
  1166  		if err != nil {
  1167  			return res, err
  1168  		}
  1169  
  1170  		roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{
  1171  			disallowOwners:      prevState.IsSubteam(),
  1172  			allowRemovals:       true,
  1173  			onlyOwnersOrReaders: prevState.IsImplicit(),
  1174  		})
  1175  		if err != nil {
  1176  			return res, err
  1177  		}
  1178  
  1179  		// Only owners can add more owners.
  1180  		if (len(roleUpdates[keybase1.TeamRole_OWNER]) > 0) && !signerIsExplicitOwner {
  1181  			return res, fmt.Errorf("non-owner cannot add owners")
  1182  		}
  1183  
  1184  		// Only owners can remove owners.
  1185  		if t.roleUpdatesDemoteOwners(prevState, roleUpdates) && !signerIsExplicitOwner {
  1186  			return res, fmt.Errorf("non-owner cannot demote owners")
  1187  		}
  1188  
  1189  		if prevState.IsImplicit() {
  1190  			// In implicit teams there are only 3 kinds of membership changes allowed:
  1191  			// 1. Resolve an invite. Adds 1 user and completes 1 invite.
  1192  			//    Though sometimes a new user is not added, due to a conflict.
  1193  			// 2. Accept a reset user. Adds 1 user and removes 1 user.
  1194  			//    Where the new one has the same UID and role as the old and a greater EldestSeqno.
  1195  			// 3. Add/remove a bot user. The bot can be a RESTRICTEDBOT or regular BOT member.
  1196  
  1197  			// Here's a case that is not straightforward:
  1198  			// There is an impteam alice,leland%2,bob@twitter.
  1199  			// Leland resets and then proves bob@twitter. On the team chain alice accepts Leland's reset.
  1200  			// So she removes leland%2, adds leland%3, and completes the bob@twitter invite.
  1201  
  1202  			// Here's another:
  1203  			// There is an impteam leland#bob@twitter.
  1204  			// Leland proves bob@twitter. On the team chain leland completes the invite.
  1205  			// Now it's just leland.
  1206  
  1207  			// Check that the invites being completed are all active.
  1208  			// For non-implicit teams we are more lenient, but here we need the counts to match up.
  1209  			invitees := make(map[keybase1.UID]bool)
  1210  			parsedCompletedInvites := make(map[keybase1.TeamInviteID]keybase1.UserVersion)
  1211  			for inviteID, invitee := range team.CompletedInvites {
  1212  				_, found := prevState.FindActiveInviteMDByID(inviteID)
  1213  				if !found {
  1214  					return res, NewImplicitTeamOperationError("completed invite %v but was not active",
  1215  						inviteID)
  1216  				}
  1217  				uv, err := keybase1.ParseUserVersion(invitee)
  1218  				if err != nil {
  1219  					return res, err
  1220  				}
  1221  				invitees[uv.Uid] = true
  1222  				parsedCompletedInvites[inviteID] = uv
  1223  			}
  1224  			nCompleted := len(team.CompletedInvites)
  1225  
  1226  			// Check these properties:
  1227  			// - Every removal must come with an addition of a successor. Ignore role.
  1228  			// - Every addition must either be paired with a removal, or
  1229  			// resolve an invite. Ignore role when not dealing with bots.
  1230  			// This is a coarse check that ignores role changes.
  1231  
  1232  			type removal struct {
  1233  				uv        keybase1.UserVersion
  1234  				satisfied bool
  1235  			}
  1236  			removals := make(map[keybase1.UID]removal)
  1237  			for _, uv := range roleUpdates[keybase1.TeamRole_NONE] {
  1238  				removals[uv.Uid] = removal{uv: uv}
  1239  			}
  1240  			var nCompletedExpected int
  1241  			additions := make(map[keybase1.UID]bool)
  1242  			// Every addition must either be paired with a removal or resolve an invite.
  1243  			for _, uv := range append(roleUpdates[keybase1.TeamRole_OWNER], roleUpdates[keybase1.TeamRole_READER]...) {
  1244  				removal, ok := removals[uv.Uid]
  1245  				if ok {
  1246  					if removal.uv.EldestSeqno >= uv.EldestSeqno {
  1247  						return res, NewImplicitTeamOperationError("replaced with older eldest seqno: %v -> %v",
  1248  							removal.uv.EldestSeqno, uv.EldestSeqno)
  1249  					}
  1250  					removal.satisfied = true
  1251  					removals[uv.Uid] = removal
  1252  					if invitees[uv.Uid] && uv.EldestSeqno > removal.uv.EldestSeqno {
  1253  						// If we are removing someone that is also a completed invite, then it must
  1254  						// be replacing a reset user with a new version. Expect an invite in this case.
  1255  						nCompletedExpected++
  1256  						additions[uv.Uid] = true
  1257  					}
  1258  				} else {
  1259  					// This is a new user, so must be a completed invite.
  1260  					nCompletedExpected++
  1261  					additions[uv.Uid] = true
  1262  				}
  1263  			}
  1264  			// All removals must have come with successor.
  1265  			for _, r := range removals {
  1266  				role := prevState.getUserRole(r.uv)
  1267  				if !(r.satisfied || role.IsBotLike()) {
  1268  					return res, NewImplicitTeamOperationError("removal without addition for %v", r.uv)
  1269  				}
  1270  			}
  1271  			// Completed invites that do not bring in new members mean
  1272  			// SBS consolidations.
  1273  			for _, uv := range parsedCompletedInvites {
  1274  				_, ok := additions[uv.Uid]
  1275  				if !ok {
  1276  					if prevState.getUserRole(uv) == keybase1.TeamRole_NONE {
  1277  						return res, NewImplicitTeamOperationError("trying to moot invite but there is no member for %v", uv)
  1278  					}
  1279  					nCompleted--
  1280  				}
  1281  			}
  1282  			// The number of completed invites must match.
  1283  			if nCompletedExpected != nCompleted {
  1284  				return res, NewImplicitTeamOperationError("illegal membership change: %d != %d",
  1285  					nCompletedExpected, nCompleted)
  1286  			}
  1287  		}
  1288  
  1289  		isHighLink, err = t.roleUpdateChangedHighSet(prevState, roleUpdates)
  1290  		if err != nil {
  1291  			return res, fmt.Errorf("could not determine if high user set changed")
  1292  		}
  1293  
  1294  		moveState()
  1295  		t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata())
  1296  
  1297  		if err := t.completeInvites(&res.newState, team.CompletedInvites, teamSigMeta); err != nil {
  1298  			return res, fmt.Errorf("illegal completed_invites: %s", err)
  1299  		}
  1300  		t.obsoleteActiveInvites(&res.newState, roleUpdates, payload.SignatureMetadata())
  1301  
  1302  		if err := t.useInvites(&res.newState, roleUpdates, team.UsedInvites); err != nil {
  1303  			return res, fmt.Errorf("illegal used_invites: %s", err)
  1304  		}
  1305  
  1306  		// Note: If someone was removed, the per-team-key should be rotated. This is not checked though.
  1307  
  1308  		if team.PerTeamKey != nil {
  1309  			newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, &res.newState)
  1310  			if err != nil {
  1311  				return res, err
  1312  			}
  1313  			res.newState.inner.PerTeamKeys[newKey.Gen] = newKey
  1314  			if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration {
  1315  				res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen
  1316  			}
  1317  			res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime)
  1318  		}
  1319  	case libkb.LinkTypeRotateKey:
  1320  		err = enforce(LinkRules{
  1321  			PerTeamKey:          TristateRequire,
  1322  			Admin:               TristateOptional,
  1323  			BoxSummaryHash:      TristateOptional,
  1324  			AllowInImplicitTeam: true,
  1325  		})
  1326  		if err != nil {
  1327  			return res, err
  1328  		}
  1329  
  1330  		// Check that the signer is at least a writer to have permission to make this link.
  1331  		if !signer.implicitAdmin {
  1332  			signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno)
  1333  			if err != nil {
  1334  				return res, err
  1335  			}
  1336  			switch signerRole {
  1337  			case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER:
  1338  				// ok
  1339  			default:
  1340  				return res, fmt.Errorf("link signer does not have permission to rotate key: %v is a %v", signer, signerRole)
  1341  			}
  1342  		}
  1343  
  1344  		newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, prevState)
  1345  		if err != nil {
  1346  			return res, err
  1347  		}
  1348  
  1349  		moveState()
  1350  		res.newState.inner.PerTeamKeys[newKey.Gen] = newKey
  1351  		res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime)
  1352  		if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration {
  1353  			res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen
  1354  		}
  1355  	case libkb.LinkTypeLeave:
  1356  		err = enforce(LinkRules{ /* Just about everything is restricted. */ })
  1357  		if err != nil {
  1358  			return res, err
  1359  		}
  1360  		// Key rotation should never be allowed since FullVerify sometimes does not run on leave links.
  1361  
  1362  		// Check that the signer is at least a bot.
  1363  		// Implicit admins cannot leave a subteam.
  1364  		signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno)
  1365  		if err != nil {
  1366  			return res, err
  1367  		}
  1368  		switch signerRole {
  1369  		case keybase1.TeamRole_RESTRICTEDBOT,
  1370  			keybase1.TeamRole_BOT,
  1371  			keybase1.TeamRole_READER,
  1372  			keybase1.TeamRole_WRITER,
  1373  			keybase1.TeamRole_ADMIN,
  1374  			keybase1.TeamRole_OWNER:
  1375  			// ok
  1376  		default:
  1377  			return res, fmt.Errorf("link signer does not have permission to leave: %v is a %v", signer, signerRole)
  1378  		}
  1379  
  1380  		// The last owner of a team should not leave.
  1381  		// But that's really up to them and the server. We're just reading what has happened.
  1382  
  1383  		moveState()
  1384  		res.newState.inform(signer.signer, keybase1.TeamRole_NONE, payload.SignatureMetadata())
  1385  	case libkb.LinkTypeNewSubteam:
  1386  		err = enforce(LinkRules{
  1387  			Subteam:      TristateRequire,
  1388  			Admin:        TristateOptional,
  1389  			AllowInflate: true,
  1390  		})
  1391  		if err != nil {
  1392  			return res, err
  1393  		}
  1394  
  1395  		// Check the subteam ID
  1396  		subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID))
  1397  		if err != nil {
  1398  			return res, err
  1399  		}
  1400  
  1401  		// Check the subteam name
  1402  		subteamName, err := t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name))
  1403  		if err != nil {
  1404  			return res, err
  1405  		}
  1406  
  1407  		_, err = checkAdmin("make subteam")
  1408  		if err != nil {
  1409  			return res, err
  1410  		}
  1411  
  1412  		moveState()
  1413  
  1414  		// informSubteam will take care of asserting that these links are inflated
  1415  		// in order for each subteam.
  1416  		err = res.newState.informSubteam(subteamID, subteamName, link.Seqno())
  1417  		if err != nil {
  1418  			return res, fmt.Errorf("adding new subteam: %v", err)
  1419  		}
  1420  	case libkb.LinkTypeSubteamHead:
  1421  		isHighLink = true
  1422  
  1423  		err = enforce(LinkRules{
  1424  			Name:           TristateRequire,
  1425  			Members:        TristateRequire,
  1426  			Parent:         TristateRequire,
  1427  			PerTeamKey:     TristateRequire,
  1428  			Admin:          TristateOptional,
  1429  			Settings:       TristateOptional,
  1430  			BoxSummaryHash: TristateOptional,
  1431  			FirstInChain:   true,
  1432  		})
  1433  		if err != nil {
  1434  			return res, err
  1435  		}
  1436  
  1437  		if team.Public {
  1438  			return res, fmt.Errorf("public subteams are not supported")
  1439  		}
  1440  
  1441  		// Check the subteam ID
  1442  		if !teamID.IsSubTeam() {
  1443  			return res, fmt.Errorf("malformed subteam id")
  1444  		}
  1445  
  1446  		// Check parent ID
  1447  		parentID, err := keybase1.TeamIDFromString(string(team.Parent.ID))
  1448  		if err != nil {
  1449  			return res, fmt.Errorf("invalid parent id: %v", err)
  1450  		}
  1451  
  1452  		// Check the initial subteam name
  1453  		teamName, err := keybase1.TeamNameFromString(string(*team.Name))
  1454  		if err != nil {
  1455  			return res, err
  1456  		}
  1457  		if teamName.IsRootTeam() {
  1458  			return res, fmt.Errorf("subteam has root team name: %s", teamName)
  1459  		}
  1460  		if teamName.IsImplicit() || team.Implicit {
  1461  			return res, NewImplicitTeamOperationError(payload.Body.Type)
  1462  		}
  1463  
  1464  		roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{
  1465  			disallowOwners: true,
  1466  			allowRemovals:  false,
  1467  		})
  1468  		if err != nil {
  1469  			return res, err
  1470  		}
  1471  
  1472  		perTeamKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, nil)
  1473  		if err != nil {
  1474  			return res, err
  1475  		}
  1476  
  1477  		perTeamKeys := make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKey)
  1478  		perTeamKeys[keybase1.PerTeamKeyGeneration(1)] = perTeamKey
  1479  
  1480  		res.newState = TeamSigChainState{
  1481  			inner: keybase1.TeamSigChainState{
  1482  				Reader:       t.reader,
  1483  				Id:           teamID,
  1484  				Implicit:     false,
  1485  				Public:       false,
  1486  				RootAncestor: teamName.RootAncestorName(),
  1487  				NameDepth:    teamName.Depth(),
  1488  				NameLog: []keybase1.TeamNameLogPoint{{
  1489  					LastPart: teamName.LastPart(),
  1490  					Seqno:    1,
  1491  				}},
  1492  				LastSeqno:               1,
  1493  				LastLinkID:              link.LinkID().Export(),
  1494  				ParentID:                &parentID,
  1495  				UserLog:                 make(map[keybase1.UserVersion][]keybase1.UserLogPoint),
  1496  				SubteamLog:              make(map[keybase1.TeamID][]keybase1.SubteamLogPoint),
  1497  				PerTeamKeys:             perTeamKeys,
  1498  				MaxPerTeamKeyGeneration: keybase1.PerTeamKeyGeneration(1),
  1499  				PerTeamKeyCTime:         keybase1.UnixTime(payload.Ctime),
  1500  				LinkIDs:                 make(map[keybase1.Seqno]keybase1.LinkID),
  1501  				StubbedLinks:            make(map[keybase1.Seqno]bool),
  1502  				InviteMetadatas:         make(map[keybase1.TeamInviteID]keybase1.TeamInviteMetadata),
  1503  				MerkleRoots:             make(map[keybase1.Seqno]keybase1.MerkleRootV2),
  1504  				Bots:                    make(map[keybase1.UserVersion]keybase1.TeamBotSettings),
  1505  			}}
  1506  
  1507  		t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata())
  1508  		if settings := team.Settings; settings != nil {
  1509  			err = t.parseTeamSettings(settings, &res.newState)
  1510  			if err != nil {
  1511  				return res, err
  1512  			}
  1513  		}
  1514  	case libkb.LinkTypeRenameSubteam:
  1515  		err = enforce(LinkRules{
  1516  			Subteam:      TristateRequire,
  1517  			Admin:        TristateOptional,
  1518  			AllowInflate: true,
  1519  		})
  1520  		if err != nil {
  1521  			return res, err
  1522  		}
  1523  
  1524  		_, err = checkAdmin("rename subteam")
  1525  		if err != nil {
  1526  			return res, err
  1527  		}
  1528  
  1529  		// Check the subteam ID
  1530  		subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID))
  1531  		if err != nil {
  1532  			return res, err
  1533  		}
  1534  
  1535  		// Check the subteam name
  1536  		subteamName, err := t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name))
  1537  		if err != nil {
  1538  			return res, err
  1539  		}
  1540  
  1541  		moveState()
  1542  
  1543  		// informSubteam will take care of asserting that these links are inflated
  1544  		// in order for each subteam.
  1545  		err = res.newState.informSubteam(subteamID, subteamName, link.Seqno())
  1546  		if err != nil {
  1547  			return res, fmt.Errorf("adding new subteam: %v", err)
  1548  		}
  1549  	case libkb.LinkTypeRenameUpPointer:
  1550  		err = enforce(LinkRules{
  1551  			Name:   TristateRequire,
  1552  			Parent: TristateRequire,
  1553  			Admin:  TristateOptional,
  1554  		})
  1555  		if err != nil {
  1556  			return res, err
  1557  		}
  1558  
  1559  		// These links only occur in subteam.
  1560  		if !prevState.IsSubteam() {
  1561  			return res, fmt.Errorf("got %v in root team", payload.Body.Type)
  1562  		}
  1563  
  1564  		// Sanity check that the parent doesn't claim to have changed.
  1565  		parentID, err := keybase1.TeamIDFromString(string(team.Parent.ID))
  1566  		if err != nil {
  1567  			return res, fmt.Errorf("invalid parent team id: %v", err)
  1568  		}
  1569  		if !parentID.Eq(*prevState.GetParentID()) {
  1570  			return res, fmt.Errorf("wrong parent team ID: %s != %s", parentID, prevState.GetParentID())
  1571  		}
  1572  
  1573  		// Ideally we would assert that the name
  1574  		// But we may not have an up-to-date picture at this time of the parent's name.
  1575  		// So assert this:
  1576  		// - The root team name is the same.
  1577  		// - The depth of the new name is the same.
  1578  		newName, err := keybase1.TeamNameFromString(string(*team.Name))
  1579  		if err != nil {
  1580  			return res, fmt.Errorf("invalid team name '%s': %v", *team.Name, err)
  1581  		}
  1582  		if newName.IsRootTeam() {
  1583  			return res, fmt.Errorf("cannot rename to root team name: %v", newName.String())
  1584  		}
  1585  		if !newName.RootAncestorName().Eq(prevState.inner.RootAncestor) {
  1586  			return res, fmt.Errorf("rename cannot change root ancestor team name: %v -> %v", prevState.inner.RootAncestor, newName)
  1587  		}
  1588  		if newName.Depth() != prevState.inner.NameDepth {
  1589  			return res, fmt.Errorf("rename cannot change team nesting depth: %v -> %v", prevState.inner.NameDepth, newName)
  1590  		}
  1591  
  1592  		moveState()
  1593  
  1594  		res.newState.inner.NameLog = append(res.newState.inner.NameLog, keybase1.TeamNameLogPoint{
  1595  			LastPart: newName.LastPart(),
  1596  			Seqno:    link.Seqno(),
  1597  		})
  1598  	case libkb.LinkTypeDeleteSubteam:
  1599  		err = enforce(LinkRules{
  1600  			Subteam:      TristateRequire,
  1601  			Admin:        TristateOptional,
  1602  			AllowInflate: true,
  1603  		})
  1604  		if err != nil {
  1605  			return res, err
  1606  		}
  1607  
  1608  		_, err = checkAdmin("delete subteam")
  1609  		if err != nil {
  1610  			return res, err
  1611  		}
  1612  
  1613  		// Check the subteam ID
  1614  		subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID))
  1615  		if err != nil {
  1616  			return res, err
  1617  		}
  1618  
  1619  		// Check the subteam name
  1620  		_, err = t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name))
  1621  		if err != nil {
  1622  			return res, err
  1623  		}
  1624  
  1625  		moveState()
  1626  
  1627  		err = res.newState.informSubteamDelete(subteamID, link.Seqno())
  1628  		if err != nil {
  1629  			return res, fmt.Errorf("error deleting subteam: %v", err)
  1630  		}
  1631  	case libkb.LinkTypeInvite:
  1632  		err = enforce(LinkRules{
  1633  			Admin:               TristateOptional,
  1634  			Invites:             TristateRequire,
  1635  			AllowInImplicitTeam: true,
  1636  		})
  1637  		if err != nil {
  1638  			return res, err
  1639  		}
  1640  
  1641  		signerIsExplicitOwner, err := checkAdmin("invite")
  1642  		if err != nil {
  1643  			return res, err
  1644  		}
  1645  
  1646  		additions, cancelations, err := t.sanityCheckInvites(mctx, signer.signer, signerIsExplicitOwner,
  1647  			*team.Invites, link.SigID(), sanityCheckInvitesOptions{
  1648  				isRootTeam:   !prevState.IsSubteam(),
  1649  				implicitTeam: prevState.IsImplicit(),
  1650  			})
  1651  		if err != nil {
  1652  			return res, err
  1653  		}
  1654  
  1655  		if prevState.IsImplicit() {
  1656  			// Check to see if the additions were previously members of the team
  1657  			checkImpteamInvites := func() error {
  1658  				addedUIDs := make(map[keybase1.UID]bool)
  1659  				for _, invites := range additions {
  1660  					for _, invite := range invites {
  1661  						cat, err := invite.Type.C()
  1662  						if err != nil {
  1663  							return err
  1664  						}
  1665  						if cat == keybase1.TeamInviteCategory_KEYBASE {
  1666  							uv, err := invite.KeybaseUserVersion()
  1667  							if err != nil {
  1668  								return err
  1669  							}
  1670  							addedUIDs[uv.Uid] = true
  1671  							_, err = prevState.GetLatestUVWithUID(uv.Uid)
  1672  							if err == nil {
  1673  								// Found crypto member in previous
  1674  								// state, we are good!
  1675  								continue
  1676  							}
  1677  							_, _, found := prevState.FindActiveKeybaseInvite(uv.Uid)
  1678  							if found {
  1679  								// Found PUKless member in previous
  1680  								// state, still fine!
  1681  								continue
  1682  							}
  1683  							// Neither crypto member nor PUKless member
  1684  							// found, we can't allow this addition.
  1685  							return fmt.Errorf("Not found previous version of user %s", uv.Uid)
  1686  						}
  1687  						return fmt.Errorf("invalid invite type in implicit team: %v", cat)
  1688  					}
  1689  				}
  1690  
  1691  				var cancelledUVs []keybase1.UserVersion
  1692  				for _, inviteID := range cancelations {
  1693  					inviteMD, found := prevState.FindActiveInviteMDByID(inviteID)
  1694  					if !found {
  1695  						// This is harmless and also we might be canceling
  1696  						// an obsolete invite.
  1697  						continue
  1698  					}
  1699  					inviteUv, err := inviteMD.Invite.KeybaseUserVersion()
  1700  					if err != nil {
  1701  						return fmt.Errorf("cancelled invite is not valid keybase-type invite: %v", err)
  1702  					}
  1703  					cancelledUVs = append(cancelledUVs, inviteUv)
  1704  				}
  1705  
  1706  				for _, uv := range cancelledUVs {
  1707  					if !addedUIDs[uv.Uid] {
  1708  						return fmt.Errorf("cancelling invite for %v without inviting back a new version", uv)
  1709  					}
  1710  				}
  1711  				return nil
  1712  			}
  1713  			if err := checkImpteamInvites(); err != nil {
  1714  				return res, NewImplicitTeamOperationError("Error in link %q: %v", payload.Body.Type, err)
  1715  			}
  1716  		}
  1717  
  1718  		moveState()
  1719  		t.updateInvites(&res.newState, additions, cancelations, teamSigMeta)
  1720  	case libkb.LinkTypeSettings:
  1721  		err = enforce(LinkRules{
  1722  			Admin:    TristateOptional,
  1723  			Settings: TristateRequire,
  1724  			// Allow key rotation in settings link. Closing an open team
  1725  			// should rotate team key.
  1726  			PerTeamKey: TristateOptional,
  1727  			// At the moment the only team setting is banned in implicit teams.
  1728  			// But in the future there could be allowed settings that also use this link type.
  1729  			AllowInImplicitTeam: true,
  1730  		})
  1731  		if err != nil {
  1732  			return res, err
  1733  		}
  1734  
  1735  		_, err = checkAdmin("change settings")
  1736  		if err != nil {
  1737  			return res, err
  1738  		}
  1739  
  1740  		moveState()
  1741  		err = t.parseTeamSettings(team.Settings, &res.newState)
  1742  		if err != nil {
  1743  			return res, err
  1744  		}
  1745  
  1746  		// When team is changed from open to closed, per-team-key should be rotated. But
  1747  		// this is not enforced.
  1748  		if team.PerTeamKey != nil {
  1749  			newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, &res.newState)
  1750  			if err != nil {
  1751  				return res, err
  1752  			}
  1753  			res.newState.inner.PerTeamKeys[newKey.Gen] = newKey
  1754  			res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime)
  1755  			if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration {
  1756  				res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen
  1757  			}
  1758  		}
  1759  	case libkb.LinkTypeDeleteRoot:
  1760  		return res, NewTeamDeletedError()
  1761  	case libkb.LinkTypeDeleteUpPointer:
  1762  		return res, NewTeamDeletedError()
  1763  	case libkb.LinkTypeKBFSSettings:
  1764  		err = enforce(LinkRules{
  1765  			Admin:               TristateOptional,
  1766  			KBFS:                TristateRequire,
  1767  			AllowInImplicitTeam: true,
  1768  		})
  1769  		if err != nil {
  1770  			return res, err
  1771  		}
  1772  
  1773  		err = checkExplicitWriter("change KBFS settings")
  1774  		if err != nil {
  1775  			return res, err
  1776  		}
  1777  
  1778  		moveState()
  1779  		err = t.parseKBFSTLFUpgrade(team.KBFS, &res.newState)
  1780  		if err != nil {
  1781  			return res, err
  1782  		}
  1783  	case libkb.LinkTypeTeamBotSettings:
  1784  		if err = enforce(LinkRules{
  1785  			Admin:               TristateOptional,
  1786  			BotSettings:         TristateRequire,
  1787  			AllowInImplicitTeam: true,
  1788  		}); err != nil {
  1789  			return res, err
  1790  		}
  1791  
  1792  		if _, err = checkAdmin("change bots"); err != nil {
  1793  			return res, err
  1794  		}
  1795  
  1796  		moveState()
  1797  		if err = t.parseTeamBotSettings(*team.BotSettings, &res.newState); err != nil {
  1798  			return res, err
  1799  		}
  1800  	case "":
  1801  		return res, errors.New("empty body type")
  1802  	default:
  1803  		if link.outerLink.IgnoreIfUnsupported {
  1804  			moveState()
  1805  		} else {
  1806  			return res, fmt.Errorf("unsupported link type: %s", payload.Body.Type)
  1807  		}
  1808  	}
  1809  
  1810  	if isHighLink {
  1811  		res.newState.inner.LastHighLinkID = link.LinkID().Export()
  1812  		res.newState.inner.LastHighSeqno = link.Seqno()
  1813  	}
  1814  	return res, nil
  1815  }
  1816  
  1817  func (t *teamSigchainPlayer) roleUpdateChangedHighSet(prevState *TeamSigChainState, roleUpdates chainRoleUpdates) (bool, error) {
  1818  	// The high set of users can be changed by promotion to Admin/Owner or
  1819  	// demotion from Admin/Owner or any movement between those two roles.
  1820  	for newRole, uvs := range roleUpdates {
  1821  		if newRole.IsAdminOrAbove() {
  1822  			return true, nil
  1823  		}
  1824  		// were any of these users previously an admin or above
  1825  		for _, uv := range uvs {
  1826  			prevRole, err := prevState.GetUserRole(uv)
  1827  			if err != nil {
  1828  				return false, err
  1829  			}
  1830  			if prevRole.IsAdminOrAbove() {
  1831  				return true, nil
  1832  			}
  1833  		}
  1834  
  1835  	}
  1836  	return false, nil
  1837  }
  1838  
  1839  func (t *teamSigchainPlayer) checkSeqnoToAdd(prevState *TeamSigChainState, linkSeqno keybase1.Seqno, isInflate bool) error {
  1840  	if linkSeqno < 1 {
  1841  		return fmt.Errorf("link seqno (%v) cannot be less than 1", linkSeqno)
  1842  	}
  1843  	if prevState == nil {
  1844  		if isInflate {
  1845  			return fmt.Errorf("cannot inflate link %v with no previous state", linkSeqno)
  1846  		}
  1847  		if linkSeqno != 1 {
  1848  			return fmt.Errorf("first team link must have seqno 1 but got %v", linkSeqno)
  1849  		}
  1850  	} else {
  1851  		if isInflate {
  1852  			if prevState.IsLinkFilled(linkSeqno) {
  1853  				return fmt.Errorf("link %v is already filled", linkSeqno)
  1854  			}
  1855  		} else {
  1856  			if linkSeqno != prevState.GetLatestSeqno()+1 {
  1857  				return fmt.Errorf("link had unexpected seqno %v != %v", linkSeqno, prevState.GetLatestSeqno()+1)
  1858  			}
  1859  		}
  1860  		if linkSeqno > prevState.GetLatestSeqno()+1 {
  1861  			return fmt.Errorf("link had far-future seqno %v > %v", linkSeqno, prevState.GetLatestSeqno()+1)
  1862  		}
  1863  	}
  1864  	return nil
  1865  }
  1866  
  1867  type sanityCheckInvitesOptions struct {
  1868  	isRootTeam   bool
  1869  	implicitTeam bool
  1870  }
  1871  
  1872  func assertIsKeybaseInvite(mctx libkb.MetaContext, i SCTeamInvite) bool {
  1873  	typ, err := TeamInviteTypeFromString(mctx, i.Type)
  1874  	if err != nil {
  1875  		mctx.Info("bad invite type: %s", err)
  1876  		return false
  1877  	}
  1878  	cat, err := typ.C()
  1879  	if err != nil {
  1880  		mctx.Info("bad invite category: %s", err)
  1881  		return false
  1882  	}
  1883  	return cat == keybase1.TeamInviteCategory_KEYBASE
  1884  }
  1885  
  1886  // These signatures contain non-owners inviting owners.
  1887  // They slipped in before that was banned. They are excepted from the rule.
  1888  var hardcodedInviteRuleExceptionSigIDs = map[keybase1.SigIDMapKey]bool{
  1889  	"c06e8da2959d8c8054fb10e005910716f776b3c3df9ef2eb4c4b8584f45e187f0f": true,
  1890  	"e800db474fa75f39503e9241990c3707121c7c414687a7b1f5ef579a625eaf820f": true,
  1891  	"46d9f2700b8d4287a2dc46dae00974a794b5778149214cf91fa4b69229a6abbc0f": true,
  1892  }
  1893  
  1894  // sanityCheckInvites sanity checks a raw SCTeamInvites section and coerces it into a
  1895  // format that we can use. It checks:
  1896  //   - inviting owners is sometimes banned
  1897  //   - invite IDs aren't repeated
  1898  //   - <name,type> pairs aren't reused
  1899  //   - IDs parse into proper keybase1.TeamInviteIDs
  1900  //   - the invite type parses into proper TeamInviteType, or that it's an unknown
  1901  //     invite that we're OK to not act upon.
  1902  //
  1903  // Implicit teams are different:
  1904  // - owners and readers are the only allowed roles
  1905  func (t *teamSigchainPlayer) sanityCheckInvites(mctx libkb.MetaContext,
  1906  	signer keybase1.UserVersion, signerIsExplicitOwner bool, invites SCTeamInvites, sigID keybase1.SigID,
  1907  	options sanityCheckInvitesOptions,
  1908  ) (additions map[keybase1.TeamRole][]keybase1.TeamInvite, cancelations []keybase1.TeamInviteID, err error) {
  1909  
  1910  	type assignment struct {
  1911  		i    SCTeamInvite
  1912  		role keybase1.TeamRole
  1913  	}
  1914  	var all []assignment
  1915  	additions = make(map[keybase1.TeamRole][]keybase1.TeamInvite)
  1916  
  1917  	if invites.Owners != nil && len(*invites.Owners) > 0 {
  1918  		additions[keybase1.TeamRole_OWNER] = nil
  1919  		for _, i := range *invites.Owners {
  1920  			if !options.isRootTeam {
  1921  				return nil, nil, fmt.Errorf("encountered invite of owner in non-root team")
  1922  			}
  1923  			if !signerIsExplicitOwner {
  1924  				if !hardcodedInviteRuleExceptionSigIDs[sigID.ToMapKey()] {
  1925  					return nil, nil, fmt.Errorf("encountered invite of owner by non-owner")
  1926  				}
  1927  			}
  1928  			if !(options.implicitTeam || assertIsKeybaseInvite(mctx, i)) {
  1929  				return nil, nil, fmt.Errorf("encountered a disallowed owner invite")
  1930  			}
  1931  			all = append(all, assignment{i, keybase1.TeamRole_OWNER})
  1932  		}
  1933  	}
  1934  
  1935  	if invites.Admins != nil && len(*invites.Admins) > 0 {
  1936  		if options.implicitTeam {
  1937  			return nil, nil, NewImplicitTeamOperationError("encountered admin invite")
  1938  		}
  1939  		additions[keybase1.TeamRole_ADMIN] = nil
  1940  		for _, i := range *invites.Admins {
  1941  			all = append(all, assignment{i, keybase1.TeamRole_ADMIN})
  1942  		}
  1943  	}
  1944  
  1945  	if invites.Writers != nil && len(*invites.Writers) > 0 {
  1946  		if options.implicitTeam {
  1947  			return nil, nil, NewImplicitTeamOperationError("encountered writer invite")
  1948  		}
  1949  		additions[keybase1.TeamRole_WRITER] = nil
  1950  		for _, i := range *invites.Writers {
  1951  			all = append(all, assignment{i, keybase1.TeamRole_WRITER})
  1952  		}
  1953  	}
  1954  
  1955  	if invites.Readers != nil && len(*invites.Readers) > 0 {
  1956  		additions[keybase1.TeamRole_READER] = nil
  1957  		for _, i := range *invites.Readers {
  1958  			all = append(all, assignment{i, keybase1.TeamRole_READER})
  1959  		}
  1960  	}
  1961  
  1962  	// Set to `true` if it was an addition and `false` if it was a deletion
  1963  	byID := make(map[keybase1.TeamInviteID]bool)
  1964  	byName := make(map[string]bool)
  1965  
  1966  	keyFunc := func(i SCTeamInvite) string {
  1967  		return fmt.Sprintf("%s:%s", i.Type, i.Name)
  1968  	}
  1969  
  1970  	if invites.Cancel != nil {
  1971  		for _, c := range *invites.Cancel {
  1972  			id, err := c.TeamInviteID()
  1973  			if err != nil {
  1974  				return nil, nil, err
  1975  			}
  1976  			if byID[id] {
  1977  				return nil, nil, NewInviteError(id, fmt.Errorf("ID %s appears twice as a cancellation", c))
  1978  			}
  1979  			byID[id] = false
  1980  			cancelations = append(cancelations, id)
  1981  		}
  1982  	}
  1983  
  1984  	for _, invite := range all {
  1985  		res, err := invite.i.TeamInvite(mctx, invite.role, signer)
  1986  		if err != nil {
  1987  			return nil, nil, err
  1988  		}
  1989  		id := res.Id
  1990  		_, seen := byID[id]
  1991  		if seen {
  1992  			return nil, nil, NewInviteError(id, fmt.Errorf("appears twice in invite set"))
  1993  		}
  1994  		key := keyFunc(invite.i)
  1995  		if byName[key] {
  1996  			return nil, nil, NewInviteError(id, fmt.Errorf("invite %s appears twice in invite set", key))
  1997  		}
  1998  
  1999  		isNewStyle, err := IsNewStyleInvite(res)
  2000  		if err != nil {
  2001  			return nil, nil, NewInviteError(id, fmt.Errorf("failed to check if invite is new-style: %s", err))
  2002  		}
  2003  		if isNewStyle {
  2004  			if options.implicitTeam {
  2005  				return nil, nil, NewInviteError(id, fmt.Errorf("new-style in implicit team"))
  2006  			}
  2007  			if res.MaxUses == nil {
  2008  				return nil, nil, NewInviteError(id, fmt.Errorf("new-style but has no max-uses"))
  2009  			}
  2010  			if !res.MaxUses.IsNotNilAndValid() {
  2011  				return nil, nil, NewInviteError(id, fmt.Errorf("invalid max_uses %d", *res.MaxUses))
  2012  			}
  2013  			if res.Etime != nil {
  2014  				if *res.Etime <= 0 {
  2015  					return nil, nil, NewInviteError(id, fmt.Errorf("invalid etime %d", *res.Etime))
  2016  				}
  2017  			}
  2018  		}
  2019  
  2020  		// NOTE: We are allowing etime without max_uses right now in sigchain
  2021  		// player. It would rejected on the server though. We want to allow
  2022  		// this now in case we do expiring invites in the future.
  2023  
  2024  		category, categoryErr := res.Type.C()
  2025  		// Do not error out if there's a new invite category we don't recognize
  2026  		if categoryErr == nil && category == keybase1.TeamInviteCategory_INVITELINK {
  2027  			if res.Role.IsAdminOrAbove() {
  2028  				return nil, nil, NewInviteError(id, NewInvitelinkBadRoleError(res.Role))
  2029  			}
  2030  		}
  2031  
  2032  		byName[key] = true
  2033  		byID[id] = true
  2034  		additions[res.Role] = append(additions[res.Role], res)
  2035  	}
  2036  
  2037  	return additions, cancelations, nil
  2038  }
  2039  
  2040  // A map describing an intent to change users' roles.
  2041  // Each item means: change that user to that role.
  2042  // To be clear: An omission does NOT mean to remove the existing role.
  2043  type chainRoleUpdates map[keybase1.TeamRole][]keybase1.UserVersion
  2044  
  2045  type sanityCheckMembersOptions struct {
  2046  	// At least one owner must be added
  2047  	requireOwners bool
  2048  	// Adding owners is blocked
  2049  	disallowOwners bool
  2050  	// Removals are allowed, blocked if false
  2051  	allowRemovals bool
  2052  	// Only additions of OWNER or READER are allowed. Does not affect removals.
  2053  	onlyOwnersOrReaders bool
  2054  }
  2055  
  2056  // Check that all the users are formatted correctly.
  2057  // Check that there are no duplicate members.
  2058  // Do not check that all removals are members. That should be true, but not strictly enforced when reading.
  2059  func (t *teamSigchainPlayer) sanityCheckMembers(members SCTeamMembers, options sanityCheckMembersOptions) (chainRoleUpdates, error) {
  2060  	type assignment struct {
  2061  		m    SCTeamMember
  2062  		role keybase1.TeamRole
  2063  	}
  2064  	var all []assignment
  2065  
  2066  	if options.requireOwners {
  2067  		if members.Owners == nil {
  2068  			return nil, fmt.Errorf("team has no owner list")
  2069  		}
  2070  		if len(*members.Owners) < 1 {
  2071  			return nil, fmt.Errorf("team has no owners")
  2072  		}
  2073  	}
  2074  	if options.disallowOwners {
  2075  		if members.Owners != nil && len(*members.Owners) > 0 {
  2076  			return nil, fmt.Errorf("team has owners")
  2077  		}
  2078  	}
  2079  	if !options.allowRemovals {
  2080  		if members.None != nil && len(*members.None) != 0 {
  2081  			return nil, fmt.Errorf("team has removals in link")
  2082  		}
  2083  	}
  2084  
  2085  	// Map from roles to users.
  2086  	res := make(map[keybase1.TeamRole][]keybase1.UserVersion)
  2087  
  2088  	if members.Owners != nil && len(*members.Owners) > 0 {
  2089  		res[keybase1.TeamRole_OWNER] = nil
  2090  		for _, m := range *members.Owners {
  2091  			all = append(all, assignment{m, keybase1.TeamRole_OWNER})
  2092  		}
  2093  	}
  2094  	if members.Admins != nil && len(*members.Admins) > 0 {
  2095  		if options.onlyOwnersOrReaders {
  2096  			return nil, NewImplicitTeamOperationError("encountered add admin")
  2097  		}
  2098  		res[keybase1.TeamRole_ADMIN] = nil
  2099  		for _, m := range *members.Admins {
  2100  			all = append(all, assignment{m, keybase1.TeamRole_ADMIN})
  2101  		}
  2102  	}
  2103  	if members.Writers != nil && len(*members.Writers) > 0 {
  2104  		if options.onlyOwnersOrReaders {
  2105  			return nil, NewImplicitTeamOperationError("encountered add writer")
  2106  		}
  2107  		res[keybase1.TeamRole_WRITER] = nil
  2108  		for _, m := range *members.Writers {
  2109  			all = append(all, assignment{m, keybase1.TeamRole_WRITER})
  2110  		}
  2111  	}
  2112  	if members.Readers != nil && len(*members.Readers) > 0 {
  2113  		res[keybase1.TeamRole_READER] = nil
  2114  		for _, m := range *members.Readers {
  2115  			all = append(all, assignment{m, keybase1.TeamRole_READER})
  2116  		}
  2117  	}
  2118  	if members.Bots != nil && len(*members.Bots) > 0 {
  2119  		res[keybase1.TeamRole_BOT] = nil
  2120  		for _, m := range *members.Bots {
  2121  			all = append(all, assignment{m, keybase1.TeamRole_BOT})
  2122  		}
  2123  	}
  2124  	if members.RestrictedBots != nil && len(*members.RestrictedBots) > 0 {
  2125  		res[keybase1.TeamRole_RESTRICTEDBOT] = nil
  2126  		for _, m := range *members.RestrictedBots {
  2127  			all = append(all, assignment{m, keybase1.TeamRole_RESTRICTEDBOT})
  2128  		}
  2129  	}
  2130  	if members.None != nil && len(*members.None) > 0 {
  2131  		res[keybase1.TeamRole_NONE] = nil
  2132  		for _, m := range *members.None {
  2133  			all = append(all, assignment{m, keybase1.TeamRole_NONE})
  2134  		}
  2135  	}
  2136  
  2137  	// Set of users who have already been seen.
  2138  	seen := make(map[keybase1.UserVersion]bool)
  2139  
  2140  	for _, pair := range all {
  2141  		uv := keybase1.UserVersion(pair.m)
  2142  
  2143  		if seen[uv] {
  2144  			return nil, fmt.Errorf("duplicate UID in members: %s", uv.Uid)
  2145  		}
  2146  
  2147  		res[pair.role] = append(res[pair.role], uv)
  2148  
  2149  		seen[uv] = true
  2150  	}
  2151  
  2152  	return res, nil
  2153  }
  2154  
  2155  // Whether the roleUpdates would demote any current owner to a lesser role.
  2156  func (t *teamSigchainPlayer) roleUpdatesDemoteOwners(prev *TeamSigChainState, roleUpdates map[keybase1.TeamRole][]keybase1.UserVersion) bool {
  2157  
  2158  	// It is OK to readmit an owner if the owner reset and is coming in at a lower permission
  2159  	// level. So check that case here.
  2160  	readmittingResetUser := func(uv keybase1.UserVersion) bool {
  2161  		for toRole, uvs := range roleUpdates {
  2162  			if toRole == keybase1.TeamRole_NONE {
  2163  				continue
  2164  			}
  2165  			for _, newUV := range uvs {
  2166  				if newUV.Uid.Equal(uv.Uid) && newUV.EldestSeqno > uv.EldestSeqno {
  2167  					return true
  2168  				}
  2169  			}
  2170  		}
  2171  		return false
  2172  	}
  2173  
  2174  	for toRole, uvs := range roleUpdates {
  2175  		if toRole == keybase1.TeamRole_OWNER {
  2176  			continue
  2177  		}
  2178  		for _, uv := range uvs {
  2179  			fromRole, err := prev.GetUserRole(uv)
  2180  			if err != nil {
  2181  				continue // ignore error, user not in team
  2182  			}
  2183  			if toRole == keybase1.TeamRole_NONE && fromRole == keybase1.TeamRole_OWNER && readmittingResetUser(uv) {
  2184  				continue
  2185  			}
  2186  			if fromRole == keybase1.TeamRole_OWNER {
  2187  				// This is an intent to demote an owner.
  2188  				return true
  2189  			}
  2190  		}
  2191  	}
  2192  	return false
  2193  }
  2194  
  2195  func (t *teamSigchainPlayer) checkPerTeamKey(link SCChainLink, perTeamKey SCPerTeamKey, prevState *TeamSigChainState) (res keybase1.PerTeamKey, err error) {
  2196  
  2197  	// Check that the new key doesn't conflict with keys we already have.
  2198  	err = prevState.checkNewPTK(perTeamKey)
  2199  	if err != nil {
  2200  		return res, err
  2201  	}
  2202  
  2203  	// validate signing kid
  2204  	sigKey, err := libkb.ImportNaclSigningKeyPairFromHex(perTeamKey.SigKID.String())
  2205  	if err != nil {
  2206  		return res, fmt.Errorf("invalid per-team-key signing KID: %s", perTeamKey.SigKID)
  2207  	}
  2208  
  2209  	// validate encryption kid
  2210  	_, err = libkb.ImportNaclDHKeyPairFromHex(perTeamKey.EncKID.String())
  2211  	if err != nil {
  2212  		return res, fmt.Errorf("invalid per-team-key encryption KID: %s", perTeamKey.EncKID)
  2213  	}
  2214  
  2215  	// verify the reverse sig
  2216  	// jw is the expected reverse sig contents
  2217  	jw, err := jsonw.Unmarshal([]byte(link.Payload))
  2218  	if err != nil {
  2219  		return res, libkb.NewReverseSigError("per-team-key reverse sig: failed to parse payload: %s", err)
  2220  	}
  2221  	err = libkb.VerifyReverseSig(t.G(), sigKey, "body.team.per_team_key.reverse_sig", jw, perTeamKey.ReverseSig)
  2222  	if err != nil {
  2223  		return res, libkb.NewReverseSigError("per-team-key reverse sig: %s", err)
  2224  	}
  2225  
  2226  	return keybase1.PerTeamKey{
  2227  		Gen:    perTeamKey.Generation,
  2228  		Seqno:  link.Seqno,
  2229  		SigKID: perTeamKey.SigKID,
  2230  		EncKID: perTeamKey.EncKID,
  2231  	}, nil
  2232  }
  2233  
  2234  // Update `userLog` with the membership in roleUpdates.
  2235  // The `NONE` list removes users.
  2236  // The other lists add users.
  2237  func (t *teamSigchainPlayer) updateMembership(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, sigMeta keybase1.SignatureMetadata) {
  2238  	for role, uvs := range roleUpdates {
  2239  		for _, uv := range uvs {
  2240  			stateToUpdate.inform(uv, role, sigMeta)
  2241  		}
  2242  	}
  2243  }
  2244  
  2245  func (t *teamSigchainPlayer) updateInvites(stateToUpdate *TeamSigChainState, additions map[keybase1.TeamRole][]keybase1.TeamInvite, cancelations []keybase1.TeamInviteID, teamSigMeta keybase1.TeamSignatureMetadata) {
  2246  	for _, invites := range additions {
  2247  		for _, invite := range invites {
  2248  			stateToUpdate.informNewInvite(invite, teamSigMeta)
  2249  		}
  2250  	}
  2251  	for _, cancelation := range cancelations {
  2252  		stateToUpdate.informCanceledInvite(cancelation, teamSigMeta)
  2253  	}
  2254  }
  2255  
  2256  func (t *teamSigchainPlayer) completeInvites(stateToUpdate *TeamSigChainState,
  2257  	completed map[keybase1.TeamInviteID]keybase1.UserVersionPercentForm,
  2258  	teamSigMeta keybase1.TeamSignatureMetadata) error {
  2259  	for id := range completed {
  2260  		inviteMD, found := stateToUpdate.FindActiveInviteMDByID(id)
  2261  		if !found {
  2262  			// Invite doesn't exist or we don't know about it because invite
  2263  			// links were stubbed. We could do a similar check here that we do
  2264  			// in teamSigchainPlayer.useInvites, but we haven't been doing it
  2265  			// in the past, so we might have links with issues like that in the
  2266  			// wild.
  2267  			continue
  2268  		}
  2269  		isNewStyle, err := IsNewStyleInvite(inviteMD.Invite)
  2270  		if err != nil {
  2271  			return err
  2272  		}
  2273  		if isNewStyle {
  2274  			return fmt.Errorf("`completed_invites` for a new-style invite (id: %q)", id)
  2275  		}
  2276  		stateToUpdate.informCompletedInvite(id, teamSigMeta)
  2277  	}
  2278  	return nil
  2279  }
  2280  
  2281  func (t *teamSigchainPlayer) obsoleteActiveInvites(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, sigMeta keybase1.SignatureMetadata) {
  2282  	if len(stateToUpdate.inner.InviteMetadatas) == 0 {
  2283  		return
  2284  	}
  2285  
  2286  	m := make(map[keybase1.UID]struct{})
  2287  	for _, uvs := range roleUpdates {
  2288  		for _, uv := range uvs {
  2289  			m[uv.Uid] = struct{}{}
  2290  		}
  2291  	}
  2292  
  2293  	for _, inviteMD := range stateToUpdate.ActiveInvites() {
  2294  		if inviteUv, err := inviteMD.Invite.KeybaseUserVersion(); err == nil {
  2295  			if _, ok := m[inviteUv.Uid]; ok {
  2296  				inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithObsolete()
  2297  				stateToUpdate.inner.InviteMetadatas[inviteMD.Invite.Id] = inviteMD
  2298  			}
  2299  		}
  2300  	}
  2301  }
  2302  
  2303  func (t *teamSigchainPlayer) useInvites(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, used []SCMapInviteIDUVPair) error {
  2304  	if len(used) == 0 {
  2305  		return nil
  2306  	}
  2307  
  2308  	seenUVs := make(map[keybase1.UserVersion]bool)
  2309  	hasStubbedLinks := stateToUpdate.HasAnyStubbedLinks()
  2310  	for _, pair := range used {
  2311  		inviteID, err := pair.InviteID.TeamInviteID()
  2312  		if err != nil {
  2313  			return err
  2314  		}
  2315  		uv, err := keybase1.ParseUserVersion(pair.UV)
  2316  		if err != nil {
  2317  			return err
  2318  		}
  2319  
  2320  		inviteMD, foundInvite := stateToUpdate.FindActiveInviteMDByID(inviteID)
  2321  		if !foundInvite {
  2322  			if hasStubbedLinks {
  2323  				// We didn't find it, possibly because server stubbed it out (we're not allowed to
  2324  				// see it; didn't load with admin perms).
  2325  				continue
  2326  			} else {
  2327  				// We couldn't find the invite, and we have no stubbed links, which
  2328  				// means that inviteID is invalid.
  2329  				return fmt.Errorf("could not find active invite ID in used_invites: %s", inviteID)
  2330  			}
  2331  		}
  2332  
  2333  		isNewStyle, err := IsNewStyleInvite(inviteMD.Invite)
  2334  		if err != nil {
  2335  			return err
  2336  		}
  2337  		if !isNewStyle {
  2338  			return fmt.Errorf("`used_invites` for a non-new-style invite (id: %q)", inviteID)
  2339  		}
  2340  
  2341  		alreadyUsed := len(inviteMD.UsedInvites)
  2342  		// Note that we append to inviteMD.UsedInvites at the end of this for loop, so alreadyUsed
  2343  		// updates correctly when processing multiple invite pairs.
  2344  		if inviteMD.Invite.IsUsedUp(alreadyUsed) {
  2345  			return fmt.Errorf("invite %s is expired after %d uses", inviteID, alreadyUsed)
  2346  		}
  2347  
  2348  		// We explicitly don't check invite.Etime here; it's used as a hint for admins.
  2349  		// but not checked in the sigchain player.
  2350  
  2351  		// If we have the invite, also check if invite role matches role
  2352  		// added.
  2353  		var foundUV bool
  2354  		for _, updatedUV := range roleUpdates[inviteMD.Invite.Role] {
  2355  			if uv.Eq(updatedUV) {
  2356  				foundUV = true
  2357  				break
  2358  			}
  2359  		}
  2360  		if !foundUV {
  2361  			return fmt.Errorf("used_invite for UV %s that was not added as role %s", pair.UV,
  2362  				inviteMD.Invite.Role.HumanString())
  2363  		}
  2364  
  2365  		if seen := seenUVs[uv]; seen {
  2366  			return fmt.Errorf("duplicate used_invite for UV %s", pair.UV)
  2367  		}
  2368  		seenUVs[uv] = true
  2369  
  2370  		// Because we use information from the UserLog here, useInvites should be called after
  2371  		// updateMembership.
  2372  		logPoint := len(stateToUpdate.inner.UserLog[uv]) - 1
  2373  		if logPoint < 0 {
  2374  			return fmt.Errorf("used_invite for UV %s that was not added to to the team", pair.UV)
  2375  		}
  2376  		inviteMD.UsedInvites = append(inviteMD.UsedInvites,
  2377  			keybase1.TeamUsedInviteLogPoint{
  2378  				Uv:       uv,
  2379  				LogPoint: logPoint,
  2380  			})
  2381  		stateToUpdate.inner.InviteMetadatas[inviteID] = inviteMD
  2382  	}
  2383  	return nil
  2384  }
  2385  
  2386  // Check that the subteam name is valid and kind of is a child of this chain.
  2387  // Returns the parsed subteam name.
  2388  func (t *teamSigchainPlayer) assertSubteamName(parent *TeamSigChainState, parentSeqno keybase1.Seqno, subteamNameStr string) (keybase1.TeamName, error) {
  2389  	// Ideally, we would assert the team name is a direct child of this team's name.
  2390  	// But the middle parts of the names might be out of date.
  2391  	// Instead assert:
  2392  	// - The root team name is same.
  2393  	// - The subteam is 1 level deeper.
  2394  	// - The last part of the parent team's name matched
  2395  	//   at the parent-seqno that this subteam name was mentioned.
  2396  	//   (If the subteam is a.b.c.d then c should match)
  2397  	//   The reason this is pegged at seqno instead of simply the latest parent name is
  2398  	//   hard to explain, see TestRenameInflateSubteamAfterRenameParent.
  2399  
  2400  	subteamName, err := keybase1.TeamNameFromString(subteamNameStr)
  2401  	if err != nil {
  2402  		return subteamName, fmt.Errorf("invalid subteam team name '%s': %v", subteamNameStr, err)
  2403  	}
  2404  
  2405  	if !parent.inner.RootAncestor.Eq(subteamName.RootAncestorName()) {
  2406  		return subteamName, fmt.Errorf("subteam is of a different root team: %v != %v",
  2407  			subteamName.RootAncestorName(),
  2408  			parent.inner.RootAncestor)
  2409  	}
  2410  
  2411  	expectedDepth := parent.inner.NameDepth + 1
  2412  	if subteamName.Depth() != expectedDepth {
  2413  		return subteamName, fmt.Errorf("subteam name has depth %v but expected %v",
  2414  			subteamName.Depth(), expectedDepth)
  2415  	}
  2416  
  2417  	// The last part of the parent name at parentSeqno.
  2418  	var parentLastPart keybase1.TeamNamePart
  2419  	for _, point := range parent.inner.NameLog {
  2420  		if point.Seqno > parentSeqno {
  2421  			break
  2422  		}
  2423  		parentLastPart = point.LastPart
  2424  	}
  2425  
  2426  	subteamSecondToLastPart := subteamName.Parts[len(subteamName.Parts)-2]
  2427  	if !subteamSecondToLastPart.Eq(parentLastPart) {
  2428  		return subteamName, fmt.Errorf("subteam name has wrong name for us: %v != %v",
  2429  			subteamSecondToLastPart, parentLastPart)
  2430  	}
  2431  
  2432  	return subteamName, nil
  2433  }
  2434  
  2435  func (t *teamSigchainPlayer) assertIsSubteamID(subteamIDStr string) (keybase1.TeamID, error) {
  2436  	// Check the subteam ID
  2437  	subteamID, err := keybase1.TeamIDFromString(subteamIDStr)
  2438  	if err != nil {
  2439  		return subteamID, fmt.Errorf("invalid subteam id: %v", err)
  2440  	}
  2441  	if !subteamID.IsSubTeam() {
  2442  		return subteamID, fmt.Errorf("malformed subteam id")
  2443  	}
  2444  	return subteamID, nil
  2445  }
  2446  
  2447  func (t *teamSigchainPlayer) parseTeamSettings(settings *SCTeamSettings, newState *TeamSigChainState) error {
  2448  	if open := settings.Open; open != nil {
  2449  		if newState.inner.Implicit {
  2450  			return fmt.Errorf("implicit team cannot be open")
  2451  		}
  2452  
  2453  		newState.inner.Open = open.Enabled
  2454  		if options := open.Options; options != nil {
  2455  			if !open.Enabled {
  2456  				return fmt.Errorf("closed team shouldn't define team.settings.open.options")
  2457  			}
  2458  
  2459  			switch options.JoinAs {
  2460  			case "reader":
  2461  				newState.inner.OpenTeamJoinAs = keybase1.TeamRole_READER
  2462  			case "writer":
  2463  				newState.inner.OpenTeamJoinAs = keybase1.TeamRole_WRITER
  2464  			default:
  2465  				return fmt.Errorf("invalid join_as role in open team: %s", options.JoinAs)
  2466  			}
  2467  		} else if open.Enabled {
  2468  			return fmt.Errorf("team set to open but team.settings.open.options is missing")
  2469  		}
  2470  	}
  2471  
  2472  	return nil
  2473  }
  2474  
  2475  func (t *teamSigchainPlayer) parseTeamBotSettings(bots []SCTeamBot, newState *TeamSigChainState) error {
  2476  
  2477  	for _, bot := range bots {
  2478  		// Bots listed here must have the RESTRICTEDBOT role
  2479  		role, err := newState.GetUserRole(bot.Bot.ToUserVersion())
  2480  		if err != nil {
  2481  			return err
  2482  		}
  2483  		if !role.IsRestrictedBot() {
  2484  			return fmt.Errorf("found bot settings for %v. Expected role RESTRICTEDBOT, found %v", bot, role)
  2485  		}
  2486  
  2487  		var convs, triggers []string
  2488  		if bot.Triggers != nil {
  2489  			triggers = *bot.Triggers
  2490  		}
  2491  		if bot.Convs != nil {
  2492  			convs = *bot.Convs
  2493  		}
  2494  		if newState.inner.Bots == nil {
  2495  			// If an old client cached this as nil, then just make a new map here for this link
  2496  			newState.inner.Bots = make(map[keybase1.UserVersion]keybase1.TeamBotSettings)
  2497  		}
  2498  		newState.inner.Bots[bot.Bot.ToUserVersion()] = keybase1.TeamBotSettings{
  2499  			Cmds:     bot.Cmds,
  2500  			Mentions: bot.Mentions,
  2501  			Triggers: triggers,
  2502  			Convs:    convs,
  2503  		}
  2504  	}
  2505  	return nil
  2506  }
  2507  
  2508  func (t *teamSigchainPlayer) parseKBFSTLFUpgrade(upgrade *SCTeamKBFS, newState *TeamSigChainState) error {
  2509  	if upgrade.TLF != nil {
  2510  		newState.inner.TlfIDs = append(newState.inner.TlfIDs, upgrade.TLF.ID)
  2511  	}
  2512  	if upgrade.Keyset != nil {
  2513  		if newState.inner.TlfLegacyUpgrade == nil {
  2514  			// If an old client cached this as nil, then just make a new map here for this link
  2515  			newState.inner.TlfLegacyUpgrade =
  2516  				make(map[keybase1.TeamApplication]keybase1.TeamLegacyTLFUpgradeChainInfo)
  2517  		}
  2518  		newState.inner.TlfLegacyUpgrade[upgrade.Keyset.AppType] = keybase1.TeamLegacyTLFUpgradeChainInfo{
  2519  			KeysetHash:       upgrade.Keyset.KeysetHash,
  2520  			TeamGeneration:   upgrade.Keyset.TeamGeneration,
  2521  			LegacyGeneration: upgrade.Keyset.LegacyGeneration,
  2522  			AppType:          upgrade.Keyset.AppType,
  2523  		}
  2524  	}
  2525  	return nil
  2526  }
  2527  
  2528  type Tristate int
  2529  
  2530  const (
  2531  	TristateDisallow Tristate = 0 // default
  2532  	TristateRequire  Tristate = 1
  2533  	TristateOptional Tristate = 2
  2534  )
  2535  
  2536  // LinkRules describes what fields and properties are required for a link type.
  2537  // Default values are the strictest.
  2538  // Keep this in sync with `func enforce`.
  2539  type LinkRules struct {
  2540  	// Sections
  2541  	Name             Tristate
  2542  	Members          Tristate
  2543  	Parent           Tristate
  2544  	Subteam          Tristate
  2545  	PerTeamKey       Tristate
  2546  	Admin            Tristate
  2547  	Invites          Tristate
  2548  	CompletedInvites Tristate
  2549  	Settings         Tristate
  2550  	KBFS             Tristate
  2551  	BoxSummaryHash   Tristate
  2552  	BotSettings      Tristate
  2553  
  2554  	AllowInImplicitTeam bool // whether this link is allowed in implicit team chains
  2555  	AllowInflate        bool // whether this link is allowed to be filled later
  2556  	FirstInChain        bool // whether this link must be the beginning of the chain
  2557  }