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

     1  package teams
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/base64"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"golang.org/x/crypto/nacl/secretbox"
    10  	"golang.org/x/net/context"
    11  
    12  	"github.com/keybase/client/go/libkb"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/client/go/teams/hidden"
    15  	"github.com/keybase/go-codec/codec"
    16  )
    17  
    18  // loader2.go contains methods on TeamLoader.
    19  // It would be normal for them to be in loader.go but:
    20  // These functions do not call any functions in loader.go except for load2.
    21  // They are here so that the files can be more manageable in size and
    22  // people can work on loader.go and loader2.go simultaneously with less conflict.
    23  
    24  // If links are needed in full that are stubbed in state, go out and get them from the server.
    25  // Does not ask for any links above state's seqno, those will be fetched by getNewLinksFromServer.
    26  func (l *TeamLoader) fillInStubbedLinks(mctx libkb.MetaContext,
    27  	me keybase1.UserVersion, teamID keybase1.TeamID, state *keybase1.TeamData,
    28  	needSeqnos []keybase1.Seqno, readSubteamID keybase1.TeamID,
    29  	proofSet *proofSetT, parentChildOperations []*parentChildOperation, lkc *loadKeyCache) (
    30  	*keybase1.TeamData, *proofSetT, []*parentChildOperation, error) {
    31  
    32  	upperLimit := keybase1.Seqno(0)
    33  	if state != nil {
    34  		upperLimit = state.Chain.LastSeqno
    35  	}
    36  
    37  	// seqnos needed from the server
    38  	var requestSeqnos []keybase1.Seqno
    39  	for _, seqno := range needSeqnos {
    40  		linkIsAlreadyFilled := TeamSigChainState{inner: state.Chain}.IsLinkFilled(seqno)
    41  		if seqno <= upperLimit && !linkIsAlreadyFilled {
    42  			requestSeqnos = append(requestSeqnos, seqno)
    43  		}
    44  	}
    45  	if len(requestSeqnos) == 0 {
    46  		// early out
    47  		return state, proofSet, parentChildOperations, nil
    48  	}
    49  
    50  	teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Chain.Id, requestSeqnos, &readSubteamID)
    51  	if err != nil {
    52  		return state, proofSet, parentChildOperations, err
    53  	}
    54  	newLinks, err := teamUpdate.unpackLinks(mctx)
    55  	if err != nil {
    56  		return state, proofSet, parentChildOperations, err
    57  	}
    58  
    59  	parentsCache := make(parentChainCache)
    60  	for _, link := range newLinks {
    61  		if link.isStubbed() {
    62  			return state, proofSet, parentChildOperations, NewStubbedErrorWithNote(
    63  				link, "filling stubbed link")
    64  		}
    65  
    66  		var signer *SignerX
    67  		var fullVerifyCutoff keybase1.Seqno // Always fullVerify when inflating. No reasoning has been done on whether it could be skipped.
    68  		signer, err = l.verifyLink(mctx.Ctx(), teamID, state, me, link, fullVerifyCutoff, readSubteamID,
    69  			proofSet, lkc, parentsCache)
    70  		if err != nil {
    71  			return state, proofSet, parentChildOperations, err
    72  		}
    73  
    74  		if signer == nil || !signer.signer.Uid.Exists() {
    75  			return state, proofSet, parentChildOperations, fmt.Errorf("blank signer for full link: %v", signer)
    76  		}
    77  
    78  		state, err = l.inflateLink(mctx.Ctx(), state, link, *signer, me)
    79  		if err != nil {
    80  			return state, proofSet, parentChildOperations, err
    81  		}
    82  
    83  		if l.isParentChildOperation(mctx.Ctx(), link) {
    84  			pco, err := l.toParentChildOperation(mctx.Ctx(), link)
    85  			if err != nil {
    86  				return state, proofSet, parentChildOperations, err
    87  			}
    88  			parentChildOperations = append(parentChildOperations, pco)
    89  		}
    90  	}
    91  
    92  	return state, proofSet, parentChildOperations, nil
    93  
    94  }
    95  
    96  type getLinksLows struct {
    97  	// Latest seqno on file
    98  	Seqno keybase1.Seqno
    99  	// Latest PTK generation secret we have
   100  	PerTeamKey keybase1.PerTeamKeyGeneration
   101  	// Latest RKM semi-secret we have
   102  	ReaderKeyMask keybase1.PerTeamKeyGeneration
   103  	// Latest hidden chain seqno on file
   104  	HiddenChainSeqno keybase1.Seqno
   105  	// Ratcheted chain tail
   106  	HiddenChainRatchet keybase1.Seqno
   107  }
   108  
   109  // checkStubbed checks if it's OK if a link is stubbed.
   110  func (l *TeamLoader) checkStubbed(ctx context.Context, arg load2ArgT, link *ChainLinkUnpacked) error {
   111  	if !link.isStubbed() {
   112  		return nil
   113  	}
   114  	if l.seqnosContains(arg.needSeqnos, link.Seqno()) {
   115  		return NewStubbedErrorWithNote(link, "Need seqno")
   116  	}
   117  	if arg.needAdmin || !link.outerLink.LinkType.TeamAllowStubWithAdminFlag(arg.needAdmin) {
   118  		return NewStubbedErrorWithNote(link, "Need admin privilege for this action")
   119  	}
   120  	return nil
   121  }
   122  
   123  func (l *TeamLoader) loadUserAndKeyFromLinkInner(ctx context.Context,
   124  	inner SCChainLinkPayload, lkc *loadKeyCache) (
   125  	signerUV keybase1.UserVersion, key *keybase1.PublicKeyV2NaCl, linkMap linkMapT, err error) {
   126  	if !ShouldSuppressLogging(ctx) {
   127  		defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#loadUserForSigVerification(%d)", int(inner.Seqno)), &err)()
   128  	}
   129  	keySection := inner.Body.Key
   130  	if keySection == nil {
   131  		return signerUV, nil, nil, libkb.NoUIDError{}
   132  	}
   133  	uid := keySection.UID
   134  	kid := keySection.KID
   135  	signerUV, key, linkMap, err = l.world.loadKeyV2(ctx, uid, kid, lkc)
   136  	if err != nil {
   137  		return signerUV, nil, nil, err
   138  	}
   139  	return signerUV, key, linkMap, nil
   140  }
   141  
   142  // Get the UV from a link but using server-trust and without verifying anything.
   143  func (l *TeamLoader) loadUserAndKeyFromLinkInnerNoVerify(ctx context.Context,
   144  	link *ChainLinkUnpacked) (signerUV keybase1.UserVersion, err error) {
   145  	if !ShouldSuppressLogging(ctx) {
   146  		defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#loadUserAndKeyFromLinkInnerNoVerify(%d)", int(link.inner.Seqno)), &err)()
   147  	}
   148  	keySection := link.inner.Body.Key
   149  	if keySection == nil {
   150  		return signerUV, libkb.NoUIDError{}
   151  	}
   152  	// Use the UID from the link body and EldestSeqno from the server-trust API response.
   153  	if link.source.EldestSeqno == 0 {
   154  		// We should never hit this case
   155  		return signerUV, fmt.Errorf("missing server hint for team sigchain link signer")
   156  	}
   157  	return NewUserVersion(keySection.UID, link.source.EldestSeqno), nil
   158  }
   159  
   160  func (l *TeamLoader) verifySignatureAndExtractKID(ctx context.Context, outer libkb.OuterLinkV2WithMetadata) (keybase1.KID, error) {
   161  	return outer.Verify(l.G().Log)
   162  }
   163  
   164  // These exceptional sigchain links are not checked dynamically. We assert that they are good.
   165  var whitelistedTeamLinkSigsForKeyInUserSigchain = []keybase1.SigID{
   166  	// For the privacy of the users involved the issue is described only vaguely here.
   167  	// See CORE-8233 for more details.
   168  	// This team had a rotate_key link signed seconds before the revocation of the key that signed the link.
   169  	// Due to a bug the signing device was allowed to be revoked with a signature that pointed to a merkle
   170  	// root prior to the team link signature. This makes it impossible for the client to independently
   171  	// verify that the team link was signed before the device was revoked. But it was, it's all good.
   172  	"e8279d7c73b8defab299094b73800262239e5a03812040ed381cc613a3db515622",
   173  
   174  	// See https://github.com/keybase/client/issues/17573; a server bug allowed a rotate after a revoke, which
   175  	// has been fixed in CORE-10942.
   176  	"070e6d737607109ba17d1d43419d950cde6d206b66c555c837566913a31ca59122",
   177  
   178  	// See https://github.com/keybase/client/issues/20503; a server bug allowed a team leave to interleave
   179  	// with a downgrade lease acquisition for a key revoke on a slow connection. The acquisition should have
   180  	// been blocked until the merkle tree reflected the leave, but the acquistion actually happened before the
   181  	// team leave transation was committed to the DB. The fix on the server is to check for leases before and
   182  	// after the team change is commited (in the same transaction). We were previously only checking before.
   183  	// It has been fixed in Y2K-891.
   184  	"c641d1246493cf04ec2c6141acdb569a457c02d577b392d4eb1872118c563c2822",
   185  }
   186  
   187  // These exceptional sigchain links are not checked dynamically. We assert that they are good.
   188  var whitelistedTeamLinkSigsForAdminPermissionDemote = []keybase1.SigID{
   189  	// A server bug allowed a change_membership to be posted that demoted an adminship
   190  	// that had just been referenced by a rotate_key that was still settling.
   191  	// Timeline: Before the ':' are merkle seqnos minus a base offset.
   192  	// 5: referenced by rotate_key.
   193  	// 6: referenced by change_membership.
   194  	// 7: rotate_key first appeared in the merkle tree.
   195  	// 9: change_membership first appeared in the merkle tree.
   196  	// The problem is that change_membership did not reference a merkle tree that included rotate_key.
   197  	// So the client can't prove (in the way it does) that rotate_key occurred before change_membership.
   198  	// See PICNIC-654 for more details.
   199  	"69cea033758d152c9736596f0a7e544444ec1944843172692db01be1c6fb6ee622",
   200  }
   201  
   202  func (l *TeamLoader) addProofsForKeyInUserSigchain(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, uid keybase1.UID, key *keybase1.PublicKeyV2NaCl, userLinkMap linkMapT, proofSet *proofSetT) {
   203  	for _, okSigID := range whitelistedTeamLinkSigsForKeyInUserSigchain {
   204  		if link.SigID().Eq(okSigID) {
   205  			// This proof is whitelisted, so don't check it.
   206  			l.G().Log.CDebugf(ctx, "addProofsForKeyInUserSigchain: skipping exceptional link: %v", link.SigID())
   207  			return
   208  		}
   209  	}
   210  
   211  	event1Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil)
   212  	event2Revoke := key.Base.Revocation
   213  	if event2Revoke != nil {
   214  		proofSet.AddNeededHappensBeforeProof(ctx, event1Link, newProofTerm(uid.AsUserOrTeam(), *event2Revoke, userLinkMap), "team link before user key revocation")
   215  	}
   216  }
   217  
   218  // Verify aspects of a link:
   219  // - Signature must match the outer link
   220  // - Signature must match the inner link if not stubbed
   221  // - Was signed by a key valid for the user at the time of signing
   222  // - Was signed by a user with permissions to make the link at the time of signing
   223  // - Checks outer-inner match
   224  // Some checks are deferred as entries in the returned proofSet
   225  // Does not:
   226  // - Apply the link nor modify state
   227  // - Check the rest of the format of the inner link
   228  // Returns the signer, or nil if the link was stubbed
   229  func (l *TeamLoader) verifyLink(ctx context.Context,
   230  	teamID keybase1.TeamID, state *keybase1.TeamData, me keybase1.UserVersion, link *ChainLinkUnpacked,
   231  	fullVerifyCutoff keybase1.Seqno, readSubteamID keybase1.TeamID, proofSet *proofSetT, lkc *loadKeyCache,
   232  	parentsCache parentChainCache) (*SignerX, error) {
   233  	ctx, tbs := l.G().CTimeBuckets(ctx)
   234  	defer tbs.Record("TeamLoader.verifyLink")()
   235  
   236  	if link.isStubbed() {
   237  		return nil, nil
   238  	}
   239  
   240  	err := link.AssertInnerOuterMatch()
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	if !teamID.Eq(link.innerTeamID) {
   246  		return nil, fmt.Errorf("team ID mismatch: %s != %s", teamID, link.innerTeamID)
   247  	}
   248  
   249  	signedByKID, err := l.verifySignatureAndExtractKID(ctx, *link.outerLink)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	// FullVerify all links except for `team.leave` links for which there is
   255  	// an admin link later in the chain.
   256  	// Such a link has effectively been verified for us by the admin who signed on top.
   257  	// This trick can be used on `team.leave` links because they do not add admins.
   258  	fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) ||
   259  		(link.Seqno() >= fullVerifyCutoff) ||
   260  		(link.source.EldestSeqno == 0)
   261  
   262  	var signerUV keybase1.UserVersion
   263  	if fullVerify {
   264  		signerUV, err = l.loadUserAndKeyFromLinkInnerAndVerify(ctx, teamID, state, link, signedByKID, proofSet, lkc)
   265  		if err != nil {
   266  			return nil, err
   267  		}
   268  	} else {
   269  		signerUV, err = l.loadUserAndKeyFromLinkInnerNoVerify(ctx, link)
   270  		if err != nil {
   271  			return nil, err
   272  		}
   273  	}
   274  
   275  	signer := SignerX{signer: signerUV}
   276  
   277  	// For a root team link, or a subteam_head, there is no reason to check adminship
   278  	// or writership (or readership) for the team.
   279  	if state == nil {
   280  		return &signer, nil
   281  	}
   282  
   283  	minRole := link.outerLink.LinkType.RequiresAtLeastRole()
   284  	linkType := link.outerLink.LinkType
   285  	// Note: If minRole is OWNER it will be treated as ADMIN here (weaker check).
   286  	if !ShouldSuppressLogging(ctx) {
   287  		l.G().Log.CDebugf(ctx, "verifyLink: %v minRole:%v", linkType, minRole)
   288  	}
   289  
   290  	switch minRole {
   291  	case keybase1.TeamRole_NONE:
   292  		// Anyone can make this link. These didn't exist at the time.
   293  		return &signer, nil
   294  	case keybase1.TeamRole_RESTRICTEDBOT:
   295  		err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_RESTRICTEDBOT)
   296  		if err == nil {
   297  			return &signer, err
   298  		}
   299  		if !ShouldSuppressLogging(ctx) {
   300  			l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_RESTRICTEDBOT, err)
   301  		}
   302  		// Fall through to a higher role check
   303  		fallthrough
   304  	case keybase1.TeamRole_BOT:
   305  		err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_BOT)
   306  		if err == nil {
   307  			return &signer, err
   308  		}
   309  		if !ShouldSuppressLogging(ctx) {
   310  			l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_BOT, err)
   311  		}
   312  		// Fall through to a higher role check
   313  		fallthrough
   314  	case keybase1.TeamRole_READER:
   315  		err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_READER)
   316  		if err == nil {
   317  			return &signer, err
   318  		}
   319  		if !ShouldSuppressLogging(ctx) {
   320  			l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_READER, err)
   321  		}
   322  		// Fall through to a higher role check
   323  		fallthrough
   324  	case keybase1.TeamRole_WRITER:
   325  		err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_WRITER)
   326  		if err == nil {
   327  			return &signer, err
   328  		}
   329  		if !ShouldSuppressLogging(ctx) {
   330  			l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_WRITER, err)
   331  		}
   332  		// Fall through to a higher role check
   333  		fallthrough
   334  	case keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN:
   335  		// Check for admin permissions if they are not an on-chain reader/writer
   336  		// because they might be an implicit admin.
   337  		// Reassigns signer, might set implicitAdmin.
   338  		signer, err = l.verifyAdminPermissions(ctx, state, me, link, readSubteamID, signerUV, proofSet, parentsCache)
   339  		if !ShouldSuppressLogging(ctx) {
   340  			l.G().Log.CDebugf(ctx, "verifyLink: not a %v: %v", minRole, err)
   341  		}
   342  		return &signer, err
   343  	default:
   344  		return nil, fmt.Errorf("unrecognized role %v required for link", minRole)
   345  	}
   346  }
   347  
   348  func (l *TeamLoader) loadUserAndKeyFromLinkInnerAndVerify(ctx context.Context, teamID keybase1.TeamID, state *keybase1.TeamData,
   349  	link *ChainLinkUnpacked, signedByKID keybase1.KID, proofSet *proofSetT, lkc *loadKeyCache) (signer keybase1.UserVersion, err error) {
   350  	signer, key, linkMap, err := l.loadUserAndKeyFromLinkInner(ctx, *link.inner, lkc)
   351  	if err != nil {
   352  		return keybase1.UserVersion{}, err
   353  	}
   354  	if !signedByKID.Equal(key.Base.Kid) {
   355  		return keybase1.UserVersion{}, libkb.NewWrongKidError(signedByKID, key.Base.Kid)
   356  	}
   357  	l.addProofsForKeyInUserSigchain(ctx, teamID, link, signer.Uid, key, linkMap, proofSet)
   358  	return signer, nil
   359  }
   360  
   361  // Verify that the user had the explicit on-chain role just before this `link`.
   362  func (l *TeamLoader) verifyExplicitPermission(ctx context.Context, state *keybase1.TeamData,
   363  	link *ChainLinkUnpacked, uv keybase1.UserVersion, atOrAbove keybase1.TeamRole) error {
   364  	return (TeamSigChainState{inner: state.Chain}).AssertWasRoleOrAboveAt(uv, atOrAbove, link.SigChainLocation().Sub1())
   365  }
   366  
   367  type parentChainCache map[keybase1.TeamID]*keybase1.TeamData
   368  
   369  // Does not return a full TeamData because it might get a subteam-reader version.
   370  func (l *TeamLoader) walkUpToAdmin(
   371  	ctx context.Context, team *keybase1.TeamData, me keybase1.UserVersion, readSubteamID keybase1.TeamID,
   372  	uv keybase1.UserVersion, admin SCTeamAdmin, parentsCache parentChainCache) (*TeamSigChainState, error) {
   373  
   374  	target, err := admin.TeamID.ToTeamID()
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	if t, ok := parentsCache[target]; ok {
   380  		return &TeamSigChainState{inner: t.Chain}, nil
   381  	}
   382  
   383  	for team != nil && !team.Chain.Id.Eq(target) {
   384  		parent := team.Chain.ParentID
   385  		if parent == nil {
   386  			return nil, NewAdminNotFoundError(admin)
   387  		}
   388  		if t, ok := parentsCache[*parent]; ok {
   389  			team = t
   390  			continue
   391  		}
   392  		arg := load2ArgT{
   393  			teamID: *parent,
   394  			reason: "walkUpToAdmin",
   395  			me:     me,
   396  			// Get the latest so that the linkmap is up to date for the proof order checker.
   397  			// But do it only once (hence the `parentsCache`) per team.
   398  			forceRepoll:   true,
   399  			readSubteamID: &readSubteamID,
   400  		}
   401  		if target.Eq(*parent) {
   402  			arg.needSeqnos = []keybase1.Seqno{admin.Seqno}
   403  		}
   404  		load2Res, err := l.load2(ctx, arg)
   405  		if err != nil {
   406  			return nil, err
   407  		}
   408  		team = &load2Res.team
   409  		parentsCache[*parent] = team
   410  	}
   411  	if team == nil {
   412  		return nil, fmt.Errorf("teamloader fault: nil team after admin walk")
   413  	}
   414  	return &TeamSigChainState{inner: team.Chain}, nil
   415  }
   416  
   417  func (l *TeamLoader) addProofsForAdminPermission(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, bookends proofTermBookends, proofSet *proofSetT) {
   418  	event1Promote := bookends.left
   419  	event2Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil)
   420  	event3Demote := bookends.right
   421  	proofSet.AddNeededHappensBeforeProof(ctx, event1Promote, event2Link, "became admin before team link")
   422  	if event3Demote != nil {
   423  		for _, okSigID := range whitelistedTeamLinkSigsForAdminPermissionDemote {
   424  			if link.SigID().Eq(okSigID) {
   425  				// This proof is whitelisted, so don't check it.
   426  				l.G().Log.CDebugf(ctx, "addProofsForAdminPermission: [demote] skipping exceptional link: %v", link.SigID())
   427  				return
   428  			}
   429  		}
   430  		proofSet.AddNeededHappensBeforeProof(ctx, event2Link, *event3Demote, "team link before adminship demotion")
   431  	}
   432  }
   433  
   434  // Verify that a user has admin permissions.
   435  // Because this uses the proofSet, if it is called may return success and fail later.
   436  func (l *TeamLoader) verifyAdminPermissions(ctx context.Context,
   437  	state *keybase1.TeamData, me keybase1.UserVersion, link *ChainLinkUnpacked, readSubteamID keybase1.TeamID,
   438  	uv keybase1.UserVersion, proofSet *proofSetT, parentsCache parentChainCache) (SignerX, error) {
   439  
   440  	signer := SignerX{signer: uv}
   441  	explicitAdmin := link.inner.TeamAdmin()
   442  	teamChain := TeamSigChainState{inner: state.Chain}
   443  
   444  	// In the simple case, we don't ask for explicit adminship, so we have to be admins of
   445  	// the current chain at or before the signature in question.
   446  	if explicitAdmin == nil {
   447  		err := teamChain.AssertWasAdminAt(uv, link.SigChainLocation().Sub1())
   448  		return signer, err
   449  	}
   450  
   451  	// The more complicated case is that there's an explicit admin permission given, perhaps
   452  	// of a parent team.
   453  	adminTeam, err := l.walkUpToAdmin(ctx, state, me, readSubteamID, uv, *explicitAdmin, parentsCache)
   454  	if err != nil {
   455  		return signer, err
   456  	}
   457  	adminBookends, err := adminTeam.assertBecameAdminAt(uv, explicitAdmin.SigChainLocation())
   458  	if err != nil {
   459  		return signer, err
   460  	}
   461  
   462  	// This was an implicit admin action if the team from which admin-power was derived (adminTeam)
   463  	// is not the link's team (state).
   464  	if !adminTeam.GetID().Eq(teamChain.GetID()) {
   465  		signer.implicitAdmin = true
   466  	}
   467  
   468  	l.addProofsForAdminPermission(ctx, state.Chain.Id, link, adminBookends, proofSet)
   469  	return signer, nil
   470  }
   471  
   472  // Whether the chain link is of a (child-half) type
   473  // that affects a parent and child chain in lockstep.
   474  // So far these events: subteam create, and subteam rename
   475  // Technically subteam delete is one of these too, but we don't
   476  // bother because the subteam is rendered inaccessible.
   477  func (l *TeamLoader) isParentChildOperation(ctx context.Context,
   478  	link *ChainLinkUnpacked) bool {
   479  
   480  	switch link.LinkType() {
   481  	case libkb.SigchainV2TypeTeamSubteamHead, libkb.SigchainV2TypeTeamRenameUpPointer:
   482  		return true
   483  	default:
   484  		return false
   485  	}
   486  }
   487  
   488  func (l *TeamLoader) toParentChildOperation(ctx context.Context,
   489  	link *ChainLinkUnpacked) (*parentChildOperation, error) {
   490  
   491  	if !l.isParentChildOperation(ctx, link) {
   492  		return nil, fmt.Errorf("link is not a parent-child operation: (seqno:%v, type:%v)",
   493  			link.Seqno(), link.LinkType())
   494  	}
   495  
   496  	if link.isStubbed() {
   497  		return nil, fmt.Errorf("child half of parent-child operation cannot be stubbed: (seqno:%v, type:%v)",
   498  			link.Seqno(), link.LinkType())
   499  	}
   500  
   501  	switch link.LinkType() {
   502  	case libkb.SigchainV2TypeTeamSubteamHead, libkb.SigchainV2TypeTeamRenameUpPointer:
   503  		if link.inner.Body.Team == nil {
   504  			return nil, fmt.Errorf("bad parent-child operation missing team section: (seqno:%v, type:%v)",
   505  				link.Seqno(), link.LinkType())
   506  		}
   507  		if link.inner.Body.Team.Parent == nil {
   508  			return nil, fmt.Errorf("parent-child operation missing team parent: (seqno:%v, type:%v)",
   509  				link.Seqno(), link.LinkType())
   510  		}
   511  		parentSeqno := link.inner.Body.Team.Parent.Seqno
   512  		if parentSeqno < 1 {
   513  			return nil, fmt.Errorf("bad parent-child up seqno: %v", parentSeqno)
   514  		}
   515  		if link.inner.Body.Team.Name == nil {
   516  			return nil, fmt.Errorf("parent-child operation %v missing new name", link.LinkType())
   517  		}
   518  		newName, err := keybase1.TeamNameFromString((string)(*link.inner.Body.Team.Name))
   519  		if err != nil {
   520  			return nil, fmt.Errorf("parent-child operation %v has invalid new name: %v",
   521  				link.LinkType(), *link.inner.Body.Team.Name)
   522  		}
   523  		return &parentChildOperation{
   524  			parentSeqno: parentSeqno,
   525  			linkType:    link.LinkType(),
   526  			newName:     newName,
   527  		}, nil
   528  	default:
   529  		return nil, fmt.Errorf("unsupported parent-child operation: %v", link.LinkType())
   530  	}
   531  
   532  }
   533  
   534  // Apply a new link to the sigchain state.
   535  // `state` is moved into this function. There must exist no live references into it from now on.
   536  // `signer` may be nil iff link is stubbed.
   537  func (l *TeamLoader) applyNewLink(ctx context.Context,
   538  	state *keybase1.TeamData, hiddenChainState *keybase1.HiddenTeamChain, link *ChainLinkUnpacked,
   539  	signer *SignerX, me keybase1.UserVersion) (*keybase1.TeamData, error) {
   540  	ctx, tbs := l.G().CTimeBuckets(ctx)
   541  	defer tbs.Record("TeamLoader.applyNewLink")()
   542  
   543  	if !ShouldSuppressLogging(ctx) {
   544  		l.G().Log.CDebugf(ctx, "TeamLoader applying link seqno:%v", link.Seqno())
   545  	}
   546  
   547  	var chainState *TeamSigChainState
   548  	var newState *keybase1.TeamData
   549  	if state == nil {
   550  		newState = &keybase1.TeamData{
   551  			// Name is left blank until calculateName updates it.
   552  			// It shall not be blank by the time it is returned from load2.
   553  			Subversion:                2, // see storage/std.go for more info on this
   554  			Name:                      keybase1.TeamName{},
   555  			PerTeamKeySeedsUnverified: make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem),
   556  			ReaderKeyMasks:            make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64),
   557  		}
   558  	} else {
   559  		chainState = &TeamSigChainState{inner: state.Chain, hidden: hiddenChainState}
   560  		newState = state
   561  		state = nil
   562  	}
   563  
   564  	newChainState, err := AppendChainLink(ctx, l.G(), me, chainState, link, signer)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	newState.Chain = newChainState.inner
   569  
   570  	return newState, nil
   571  }
   572  
   573  // Inflate a link that was stubbed with its non-stubbed data.
   574  func (l *TeamLoader) inflateLink(ctx context.Context,
   575  	state *keybase1.TeamData, link *ChainLinkUnpacked,
   576  	signer SignerX, me keybase1.UserVersion) (
   577  	*keybase1.TeamData, error) {
   578  
   579  	l.G().Log.CDebugf(ctx, "TeamLoader inflating link seqno:%v", link.Seqno())
   580  
   581  	if state == nil {
   582  		// The only reason state would be nil is if this is link 1.
   583  		// But link 1 can't be stubbed.
   584  		return nil, NewInflateErrorWithNote(link, "no prior state")
   585  	}
   586  
   587  	newState := state.DeepCopy() // Clone the state and chain so that our parameters don't get consumed.
   588  	newChainState, err := InflateLink(ctx, l.G(), me, TeamSigChainState{inner: newState.Chain}, link, signer)
   589  	if err != nil {
   590  		return nil, err
   591  	}
   592  	newState.Chain = newChainState.inner
   593  
   594  	return &newState, nil
   595  }
   596  
   597  // Check that the parent-child operations appear in the parent sigchains.
   598  func (l *TeamLoader) checkParentChildOperations(ctx context.Context,
   599  	me keybase1.UserVersion, loadingTeamID keybase1.TeamID, parentID *keybase1.TeamID, readSubteamID keybase1.TeamID,
   600  	parentChildOperations []*parentChildOperation, proofSet *proofSetT) error {
   601  
   602  	if len(parentChildOperations) == 0 {
   603  		return nil
   604  	}
   605  	if parentID == nil {
   606  		return fmt.Errorf("cannot check parent-child operations with no parent")
   607  	}
   608  
   609  	var needParentSeqnos []keybase1.Seqno
   610  	for _, pco := range parentChildOperations {
   611  		needParentSeqnos = append(needParentSeqnos, pco.parentSeqno)
   612  	}
   613  
   614  	parent, err := l.load2(ctx, load2ArgT{
   615  		teamID: *parentID,
   616  
   617  		reason: "checkParentChildOperations-parent",
   618  
   619  		needAdmin:                             false,
   620  		needKeyGeneration:                     0,
   621  		needApplicationsAtGenerations:         nil,
   622  		needApplicationsAtGenerationsWithKBFS: nil,
   623  		wantMembers:                           nil,
   624  		wantMembersRole:                       keybase1.TeamRole_NONE,
   625  		forceFullReload:                       false,
   626  		forceRepoll:                           false,
   627  		staleOK:                               true, // stale is fine, as long as get those seqnos.
   628  		skipSeedCheck:                         true,
   629  		auditMode:                             keybase1.AuditMode_SKIP,
   630  
   631  		needSeqnos:    needParentSeqnos,
   632  		readSubteamID: &readSubteamID,
   633  
   634  		me: me,
   635  	})
   636  	if err != nil {
   637  		return fmt.Errorf("error loading parent: %v", err)
   638  	}
   639  
   640  	parentChain := TeamSigChainState{inner: parent.team.Chain}
   641  
   642  	for _, pco := range parentChildOperations {
   643  		err = l.checkOneParentChildOperation(ctx, pco, loadingTeamID, &parentChain)
   644  		if err != nil {
   645  			return err
   646  		}
   647  	}
   648  
   649  	// Give a more up-to-date linkmap to the ordering checker for the parent.
   650  	// Without this it could fail if the parent is new.
   651  	// Because the team linkmap in the proof objects is stale.
   652  	proofSet.SetTeamLinkMap(ctx, parentChain.inner.Id, parentChain.inner.LinkIDs)
   653  
   654  	return nil
   655  }
   656  
   657  func (l *TeamLoader) checkOneParentChildOperation(ctx context.Context,
   658  	pco *parentChildOperation, teamID keybase1.TeamID, parent *TeamSigChainState) error {
   659  
   660  	switch pco.linkType {
   661  	case libkb.SigchainV2TypeTeamSubteamHead:
   662  		return parent.SubteamRenameOccurred(teamID, pco.newName, pco.parentSeqno)
   663  	case libkb.SigchainV2TypeTeamRenameUpPointer:
   664  		return parent.SubteamRenameOccurred(teamID, pco.newName, pco.parentSeqno)
   665  	}
   666  	return fmt.Errorf("unrecognized parent-child operation could not be checked: %v", pco.linkType)
   667  }
   668  
   669  // Check all the proofs and ordering constraints in proofSet
   670  func (l *TeamLoader) checkProofs(ctx context.Context,
   671  	state *keybase1.TeamData, proofSet *proofSetT) error {
   672  
   673  	if state == nil {
   674  		return fmt.Errorf("teamloader fault: nil team for proof ordering check")
   675  	}
   676  	// Give the most up-to-date linkmap to the ordering checker.
   677  	// Without this it would fail in some cases when the team is on the left.
   678  	// Because the team linkmap in the proof objects is stale.
   679  	proofSet.SetTeamLinkMap(ctx, state.Chain.Id, state.Chain.LinkIDs)
   680  	if !proofSet.checkRequired() {
   681  		return nil
   682  	}
   683  	return proofSet.check(ctx, l.world, teamEnv.ProofSetParallel)
   684  }
   685  
   686  func (l *TeamLoader) unboxKBFSCryptKeys(ctx context.Context, key keybase1.TeamApplicationKey,
   687  	keysetHash keybase1.TeamEncryptedKBFSKeysetHash, encryptedKeyset string) ([]keybase1.CryptKey, error) {
   688  
   689  	// Check hash
   690  	sbytes := sha256.Sum256([]byte(encryptedKeyset))
   691  	if !keysetHash.SecureEqual(keybase1.TeamEncryptedKBFSKeysetHashFromBytes(sbytes[:])) {
   692  		return nil, errors.New("encrypted TLF upgrade does not match sigchain hash")
   693  	}
   694  
   695  	// Decode
   696  	packed, err := base64.StdEncoding.DecodeString(encryptedKeyset)
   697  	if err != nil {
   698  		return nil, err
   699  	}
   700  	var keysetRecord keybase1.TeamEncryptedKBFSKeyset
   701  	mh := codec.MsgpackHandle{WriteExt: true}
   702  	decoder := codec.NewDecoderBytes(packed, &mh)
   703  	if err = decoder.Decode(&keysetRecord); err != nil {
   704  		return nil, err
   705  	}
   706  
   707  	// Decrypt
   708  	var encKey [libkb.NaclSecretBoxKeySize]byte = key.Material()
   709  	var nonce [libkb.NaclDHNonceSize]byte
   710  	if len(keysetRecord.N) != libkb.NaclDHNonceSize {
   711  		return nil, libkb.DecryptBadNonceError{}
   712  	}
   713  	copy(nonce[:], keysetRecord.N)
   714  	plain, ok := secretbox.Open(nil, keysetRecord.E, &nonce, &encKey)
   715  	if !ok {
   716  		return nil, libkb.DecryptOpenError{}
   717  	}
   718  
   719  	// Decode again
   720  	var cryptKeys []keybase1.CryptKey
   721  	decoder = codec.NewDecoderBytes(plain, &mh)
   722  	if err = decoder.Decode(&cryptKeys); err != nil {
   723  		return nil, err
   724  	}
   725  
   726  	return cryptKeys, nil
   727  }
   728  
   729  // AddKBFSCryptKeys mutates `state`
   730  func (l *TeamLoader) addKBFSCryptKeys(mctx libkb.MetaContext, team Teamer, upgrades []keybase1.TeamGetLegacyTLFUpgrade) error {
   731  	m := make(map[keybase1.TeamApplication][]keybase1.CryptKey)
   732  	for _, upgrade := range upgrades {
   733  		key, err := ApplicationKeyAtGeneration(mctx, team, upgrade.AppType, upgrade.TeamGeneration)
   734  		if err != nil {
   735  			return err
   736  		}
   737  
   738  		chainInfo, ok := team.MainChain().Chain.TlfLegacyUpgrade[upgrade.AppType]
   739  		if !ok {
   740  			return errors.New("legacy tlf upgrade payload present without chain link")
   741  		}
   742  		if chainInfo.TeamGeneration != upgrade.TeamGeneration {
   743  			return fmt.Errorf("legacy tlf upgrade team generation mismatch: %d != %d",
   744  				chainInfo.TeamGeneration, upgrade.TeamGeneration)
   745  		}
   746  
   747  		cryptKeys, err := l.unboxKBFSCryptKeys(mctx.Ctx(), key, chainInfo.KeysetHash, upgrade.EncryptedKeyset)
   748  		if err != nil {
   749  			return err
   750  		}
   751  		if chainInfo.LegacyGeneration != cryptKeys[len(cryptKeys)-1].KeyGeneration {
   752  			return fmt.Errorf("legacy tlf upgrade legacy generation mismatch: %d != %d",
   753  				chainInfo.LegacyGeneration, cryptKeys[len(cryptKeys)-1].KeyGeneration)
   754  		}
   755  
   756  		m[upgrade.AppType] = cryptKeys
   757  	}
   758  	team.MainChain().TlfCryptKeys = m
   759  	return nil
   760  }
   761  
   762  // Add data to the state that is not included in the sigchain:
   763  // - per team keys
   764  // - reader key masks
   765  // Checks that the off-chain data ends up exactly in sync with the chain, generation-wise.
   766  // Does _not_ check that keys match the sigchain.
   767  // Mutates `state`
   768  func (l *TeamLoader) addSecrets(mctx libkb.MetaContext,
   769  	team Teamer, me keybase1.UserVersion, box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded,
   770  	readerKeyMasks []keybase1.ReaderKeyMask) error {
   771  
   772  	state := team.MainChain()
   773  
   774  	latestReceivedGen, seeds, err := l.unboxPerTeamSecrets(mctx, box, prevs)
   775  	if err != nil {
   776  		return err
   777  	}
   778  	mctx.Debug("TeamLoader#addSecrets at %d", latestReceivedGen)
   779  
   780  	// Earliest generation received.
   781  	earliestReceivedGen := latestReceivedGen - keybase1.PerTeamKeyGeneration(len(seeds)-1)
   782  
   783  	stateWrapper := newTeamSigChainState(team)
   784  
   785  	// Latest generation from the sigchain or the hidden chain...
   786  	latestChainGen := stateWrapper.GetLatestGeneration()
   787  
   788  	mctx.Debug("TeamLoader.addSecrets: received:%v->%v nseeds:%v nprevs:%v",
   789  		earliestReceivedGen, latestReceivedGen, len(seeds), len(prevs))
   790  
   791  	// Check that each key matches the chain.
   792  	for i, seed := range seeds {
   793  		gen := int(latestReceivedGen) + i + 1 - len(seeds)
   794  		if gen < 1 {
   795  			return fmt.Errorf("gen < 1")
   796  		}
   797  
   798  		ptkGen := keybase1.PerTeamKeyGeneration(gen)
   799  
   800  		chainKey, err := stateWrapper.GetPerTeamKeyAtGeneration(ptkGen)
   801  		if err != nil {
   802  			return err
   803  		}
   804  
   805  		// Add it to the snapshot
   806  		state.PerTeamKeySeedsUnverified[ptkGen] = keybase1.PerTeamKeySeedItem{
   807  			Seed:       seed,
   808  			Generation: ptkGen,
   809  			Seqno:      chainKey.Seqno,
   810  		}
   811  	}
   812  
   813  	// Make sure there is not a gap between the latest local key and the earliest received key.
   814  	if earliestReceivedGen > keybase1.PerTeamKeyGeneration(1) {
   815  		// We should have the seed for the generation preceeding the earliest received.
   816  		checkGen := earliestReceivedGen - 1
   817  		if _, ok := state.PerTeamKeySeedsUnverified[earliestReceivedGen-1]; !ok {
   818  			return fmt.Errorf("gap in per-team-keys: latestRecvd:%v earliestRecvd:%v missing:%v",
   819  				latestReceivedGen, earliestReceivedGen, checkGen)
   820  		}
   821  	}
   822  
   823  	role, err := stateWrapper.GetUserRole(me)
   824  	if err != nil {
   825  		role = keybase1.TeamRole_NONE
   826  	}
   827  	if role.IsBotOrAbove() {
   828  		// Insert all reader key masks
   829  		// Then scan to make sure there are no gaps in generations and no missing application masks.
   830  		checkMaskGens := make(map[keybase1.PerTeamKeyGeneration]bool)
   831  		for _, rkm := range readerKeyMasks {
   832  			if rkm.Generation < 1 {
   833  				return fmt.Errorf("reader key mask has generation: %v < 0", rkm.Generation)
   834  			}
   835  			if _, ok := state.ReaderKeyMasks[rkm.Application]; !ok {
   836  				state.ReaderKeyMasks[rkm.Application] = make(
   837  					map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64)
   838  			}
   839  			state.ReaderKeyMasks[rkm.Application][rkm.Generation] = rkm.Mask
   840  
   841  			checkMaskGens[rkm.Generation] = true
   842  			if rkm.Generation > 1 {
   843  				// Check for the previous rkm to make sure there are no gaps
   844  				checkMaskGens[rkm.Generation-1] = true
   845  			}
   846  		}
   847  		mctx.Debug("TeamLoader.addSecrets: loop1")
   848  		// Check that we are all the way up to date
   849  		checkMaskGens[latestChainGen] = true
   850  		for gen := range checkMaskGens {
   851  			err = l.checkReaderKeyMaskCoverage(mctx, state, gen)
   852  			if err != nil {
   853  				return err
   854  			}
   855  		}
   856  		mctx.Debug("TeamLoader.addSecrets: loop2")
   857  	} else {
   858  		// Discard all cached reader key masks if we are not an explicit member of the team.
   859  		state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64)
   860  
   861  		// Also we shouldn't have gotten any from the server.
   862  		if len(readerKeyMasks) > 0 {
   863  			mctx.Warning("TeamLoader got %v reader-key-masks but not an explicit member",
   864  				len(readerKeyMasks))
   865  		}
   866  	}
   867  	return nil
   868  }
   869  
   870  // Check that the RKMs for a generation are covered for all apps.
   871  func (l *TeamLoader) checkReaderKeyMaskCoverage(mctx libkb.MetaContext,
   872  	state *keybase1.TeamData, gen keybase1.PerTeamKeyGeneration) error {
   873  
   874  	for _, app := range keybase1.TeamApplicationMap {
   875  		switch app {
   876  		case keybase1.TeamApplication_STELLAR_RELAY, keybase1.TeamApplication_KVSTORE:
   877  			// TODO CORE-7718 Allow clients to be missing these RKMs for now.
   878  			//                Will need a team cache bust to repair.
   879  			continue
   880  		}
   881  		if _, ok := state.ReaderKeyMasks[app]; !ok {
   882  			return NewMissingReaderKeyMaskError(gen, app)
   883  		}
   884  		if _, ok := state.ReaderKeyMasks[app][gen]; !ok {
   885  			return NewMissingReaderKeyMaskError(gen, app)
   886  		}
   887  	}
   888  
   889  	return nil
   890  }
   891  
   892  // Unbox per team keys
   893  // Does not check that the keys match the chain
   894  // TODO: return the signer and have the caller check it. Not critical because the public half is checked anyway.
   895  // Returns the generation of the box (the greatest generation),
   896  // and a list of the seeds in ascending generation order.
   897  func (l *TeamLoader) unboxPerTeamSecrets(mctx libkb.MetaContext,
   898  	box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded) (keybase1.PerTeamKeyGeneration, []keybase1.PerTeamKeySeed, error) {
   899  
   900  	return unboxPerTeamSecrets(mctx, l.world, box, prevs)
   901  }
   902  
   903  func unboxPerTeamSecrets(m libkb.MetaContext, world LoaderContext, box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded) (keybase1.PerTeamKeyGeneration, []keybase1.PerTeamKeySeed, error) {
   904  
   905  	if box == nil {
   906  		return 0, nil, fmt.Errorf("no key box from server")
   907  	}
   908  
   909  	userKey, err := world.perUserEncryptionKey(m.Ctx(), box.PerUserKeySeqno)
   910  
   911  	if err != nil {
   912  		return 0, nil, err
   913  	}
   914  	secret1, err := box.Open(userKey)
   915  	if err != nil {
   916  		return 0, nil, fmt.Errorf("opening key box: %v", err)
   917  	}
   918  
   919  	// Secrets starts as descending
   920  	secrets := []keybase1.PerTeamKeySeed{secret1}
   921  
   922  	// The generation to work on opening
   923  	openGeneration := box.Generation - keybase1.PerTeamKeyGeneration(1)
   924  
   925  	// Walk down generations until
   926  	// - the map is exhausted
   927  	// - if malformed, the map has a gap
   928  	// - reach generation 0
   929  	for {
   930  		if int(openGeneration) == 0 || int(openGeneration) < 0 {
   931  			break
   932  		}
   933  		// Prevs is keyed by the generation that can decrypt, not the generation contained.
   934  		prev, ok := prevs[openGeneration+1]
   935  		if !ok {
   936  			break
   937  		}
   938  		secret, err := decryptPrevSingle(m.Ctx(), prev, secrets[len(secrets)-1])
   939  		if err != nil {
   940  			return box.Generation, nil, fmt.Errorf("opening prev gen %v: %v", openGeneration, err)
   941  		}
   942  		secrets = append(secrets, *secret)
   943  		openGeneration--
   944  	}
   945  
   946  	// Reverse the list
   947  	// After this secrets is ascending
   948  	// https://github.com/golang/go/wiki/SliceTricks#reversing
   949  	for i := len(secrets)/2 - 1; i >= 0; i-- {
   950  		// opp is the index of the opposite element
   951  		opp := len(secrets) - 1 - i
   952  		secrets[i], secrets[opp] = secrets[opp], secrets[i]
   953  	}
   954  
   955  	return box.Generation, secrets, nil
   956  }
   957  
   958  // Whether the snapshot has fully loaded, non-stubbed, all of the links.
   959  func (l *TeamLoader) checkNeededSeqnos(ctx context.Context,
   960  	state *keybase1.TeamData, needSeqnos []keybase1.Seqno) error {
   961  
   962  	if len(needSeqnos) == 0 {
   963  		return nil
   964  	}
   965  	if state == nil {
   966  		return fmt.Errorf("nil team does not contain needed seqnos")
   967  	}
   968  
   969  	for _, seqno := range needSeqnos {
   970  		if (TeamSigChainState{inner: state.Chain}).HasStubbedSeqno(seqno) {
   971  			return fmt.Errorf("needed seqno is stubbed: %v", seqno)
   972  		}
   973  	}
   974  	return nil
   975  }
   976  
   977  // Calculates the latest name of the team.
   978  // The last part will be as up to date as the sigchain in state.
   979  // The mid-team parts can be as old as the cache time, unless staleOK is false in which case they will be fetched.
   980  func (l *TeamLoader) calculateName(ctx context.Context,
   981  	state *keybase1.TeamData, me keybase1.UserVersion, readSubteamID keybase1.TeamID, staleOK bool) (newName keybase1.TeamName, err error) {
   982  
   983  	chain := TeamSigChainState{inner: state.Chain}
   984  	if !chain.IsSubteam() {
   985  		return chain.inner.RootAncestor, nil
   986  	}
   987  
   988  	// Load the parent. The parent load will recalculate its own name,
   989  	// so this name recalculation is recursive.
   990  	parent, err := l.load2(ctx, load2ArgT{
   991  		teamID:        *chain.GetParentID(),
   992  		reason:        "calculateName",
   993  		staleOK:       staleOK,
   994  		readSubteamID: &readSubteamID,
   995  		me:            me,
   996  	})
   997  	if err != nil {
   998  		return newName, err
   999  	}
  1000  
  1001  	// Swap out the parent name as the base of this name.
  1002  	// Check that the root ancestor name and depth still match the subteam chain.
  1003  
  1004  	newName, err = parent.team.Name.Append(string(chain.LatestLastNamePart()))
  1005  	if err != nil {
  1006  		return newName, fmt.Errorf("invalid new subteam name: %v", err)
  1007  	}
  1008  
  1009  	if !newName.RootAncestorName().Eq(chain.inner.RootAncestor) {
  1010  		return newName, fmt.Errorf("subteam changed root ancestor: %v -> %v",
  1011  			chain.inner.RootAncestor, newName.RootAncestorName())
  1012  	}
  1013  
  1014  	if newName.Depth() != chain.inner.NameDepth {
  1015  		return newName, fmt.Errorf("subteam changed depth: %v -> %v", chain.inner.NameDepth, newName.Depth())
  1016  	}
  1017  
  1018  	return newName, nil
  1019  }
  1020  
  1021  // computeSeedChecks looks at the PerTeamKeySeedsUnverified for the given team and adds the
  1022  // PerTeamSeedChecks to the sequence. We make the assumption that, potentially, all such links are
  1023  // null because it's a legacy team. OR only the new links are null since they were just added.
  1024  // In either case, after this function runs, all seeds get seed checks computed.
  1025  func (l *TeamLoader) computeSeedChecks(ctx context.Context, state *keybase1.TeamData) (err error) {
  1026  	mctx := libkb.NewMetaContext(ctx, l.G())
  1027  	defer mctx.Trace(fmt.Sprintf("TeamLoader#computeSeedChecks(%s)", state.ID()), &err)()
  1028  
  1029  	latestChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified))
  1030  	err = computeSeedChecks(
  1031  		ctx,
  1032  		state.ID(),
  1033  		latestChainGen,
  1034  		func(g keybase1.PerTeamKeyGeneration) (*keybase1.PerTeamSeedCheck, keybase1.PerTeamKeySeed, error) {
  1035  			ptksu, ok := state.PerTeamKeySeedsUnverified[g]
  1036  			if !ok {
  1037  				return nil, keybase1.PerTeamKeySeed{}, fmt.Errorf("unexpected nil PerTeamKeySeedsUnverified at %d", g)
  1038  			}
  1039  			return ptksu.Check, ptksu.Seed, nil
  1040  		},
  1041  		func(g keybase1.PerTeamKeyGeneration, check keybase1.PerTeamSeedCheck) {
  1042  			ptksu := state.PerTeamKeySeedsUnverified[g]
  1043  			ptksu.Check = &check
  1044  			state.PerTeamKeySeedsUnverified[g] = ptksu
  1045  		},
  1046  	)
  1047  	return err
  1048  }
  1049  
  1050  // consumeRatchets finds the hidden chain ratchets in the given link (if it's not stubbed), and adds them
  1051  // into the hidden.LoaderPackage via the AddRatchets call. This call, in turn, attempts to unblind the ratchet
  1052  // and then checks the ratchets against current state and ratchets. Thus, it can fail in many ways if the server
  1053  // is buggy or dishonest.
  1054  func consumeRatchets(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked) (err error) {
  1055  	if link.isStubbed() {
  1056  		return nil
  1057  	}
  1058  	err = hiddenPackage.AddRatchets(mctx, link.inner.Ratchets(), link.inner.Ctime, keybase1.RatchetType_MAIN)
  1059  	return err
  1060  }
  1061  
  1062  func checkPTKGenerationNotOnHiddenChain(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked) (err error) {
  1063  	gen := link.PTKGeneration()
  1064  	if gen == keybase1.PerTeamKeyGeneration(0) {
  1065  		return nil
  1066  	}
  1067  	return hiddenPackage.CheckNoPTK(mctx, gen)
  1068  }