github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/hidden/loader.go (about)

     1  package hidden
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/keybase/client/go/libkb"
     8  	"github.com/keybase/client/go/protocol/keybase1"
     9  	"github.com/keybase/client/go/sig3"
    10  )
    11  
    12  const (
    13  	MaxDelayInCommittingHiddenLinks = 30 * 24 * time.Hour
    14  )
    15  
    16  // LoaderPackage contains a snapshot of the hidden team chain, used during the process of loading a team.
    17  // It additionally can have new chain links loaded from the server, since it might need to be queried
    18  // in the process of loading the team as if the new links were already committed to the data store.
    19  type LoaderPackage struct {
    20  	id                     keybase1.TeamID
    21  	encKID                 keybase1.KID
    22  	encKIDGen              keybase1.PerTeamKeyGeneration
    23  	data                   *keybase1.HiddenTeamChain
    24  	newData                *keybase1.HiddenTeamChain
    25  	expectedPrev           *keybase1.LinkTriple
    26  	rbks                   *RatchetBlindingKeySet
    27  	allNewRatchets         map[keybase1.Seqno]keybase1.LinkTripleAndTime
    28  	newRatchetSet          keybase1.HiddenTeamChainRatchetSet
    29  	role                   keybase1.TeamRole
    30  	lastCommittedSeqno     keybase1.Seqno
    31  	disableHiddenChainData bool
    32  }
    33  
    34  // NewLoaderPackage creates a loader package that can work in the FTL of slow team loading settings. As a preliminary,
    35  // it loads any stored hidden team data for the team from local storage. The getter function is used to get a recent PTK
    36  // for this team, which is needed to poll the Merkle Tree endpoint when asking "does a hidden team chain exist for this team?"
    37  func NewLoaderPackage(mctx libkb.MetaContext, id keybase1.TeamID,
    38  	getter func() (keybase1.KID, keybase1.PerTeamKeyGeneration, keybase1.TeamRole, error)) (ret *LoaderPackage, err error) {
    39  	encKID, gen, role, err := getter()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	ret = newLoaderPackage(id, encKID, gen, role)
    44  	err = ret.Load(mctx)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return ret, nil
    49  }
    50  
    51  // NewLoaderPackageForPrecheck makes a loader package just for the purposes of prechecking a link we're about to send
    52  // up to the server. It doesn't bother to load the team from storage.
    53  func NewLoaderPackageForPrecheck(mctx libkb.MetaContext, id keybase1.TeamID, data *keybase1.HiddenTeamChain) *LoaderPackage {
    54  	return &LoaderPackage{
    55  		id:   id,
    56  		data: data,
    57  	}
    58  }
    59  
    60  // newLoaderPackage creates an object used to load the hidden team chain along with the
    61  // slow or fast team loader. It manages internal state during the loading process. Pass an
    62  // encryption KID from the main chain for authentication purposes, that we can prove to the server
    63  // that we've previously seen data for this team (and therefor we're allowed to know whether or not
    64  // the team has a hidden chain (but nothing more)).
    65  func newLoaderPackage(id keybase1.TeamID, e keybase1.KID, g keybase1.PerTeamKeyGeneration, role keybase1.TeamRole) *LoaderPackage {
    66  	return &LoaderPackage{id: id, encKID: e, encKIDGen: g, role: role}
    67  }
    68  
    69  // Load in data from storage for this chain. We're going to make a deep copy so that
    70  // we don't worry about mutating the object in the storage layer's memory LRU.
    71  func (l *LoaderPackage) Load(mctx libkb.MetaContext) (err error) {
    72  	tmp, err := mctx.G().GetHiddenTeamChainManager().Load(mctx, l.id)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	if tmp == nil {
    77  		return nil
    78  	}
    79  	cp := tmp.DeepCopy()
    80  	l.data = &cp
    81  	return err
    82  }
    83  
    84  // IsStale returns true if we got a gregor hint from the server that there is a new link and we haven't
    85  // pulled it down yet from the server.
    86  func (l *LoaderPackage) IsStale() bool {
    87  	if l.data == nil {
    88  		return false
    89  	}
    90  	return l.data.IsStale()
    91  }
    92  
    93  // checkPrev checks the earliest chainlink in the update against previously fetched chainlinks.
    94  // It requires the prev to be there and to not clash.
    95  func (l *LoaderPackage) checkPrev(mctx libkb.MetaContext, first sig3.Generic) (err error) {
    96  	q := first.Seqno()
    97  	prev := first.Prev()
    98  	if (q == keybase1.Seqno(1)) != (prev == nil) {
    99  		return NewLoaderError("bad link; seqno=%d, prev=%v (want 1 and nil or >1 and non-nil)", q, prev)
   100  	}
   101  	if q == keybase1.Seqno(1) {
   102  		return nil
   103  	}
   104  	if l.data == nil {
   105  		return NewLoaderError("didn't get prior data and update was for a chain middle")
   106  	}
   107  	link, ok := l.data.Outer[q-1]
   108  	if !ok {
   109  		return NewLoaderError("previous link wasn't found")
   110  	}
   111  	if !link.Eq(prev.Export()) {
   112  		return NewLoaderError("prev mismatch at %d", q)
   113  	}
   114  
   115  	// We check prevs again when we commit this change to the hidden team chain manager. It's not
   116  	// strictly required, but it seems a good safeguard against future programming bugs. So
   117  	// store it away here.
   118  	l.expectedPrev = &keybase1.LinkTriple{
   119  		Seqno:   q - 1,
   120  		LinkID:  link,
   121  		SeqType: keybase1.SeqType_TEAM_PRIVATE_HIDDEN,
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  // checkExpectedHighSeqno enforces that the links we got down from the server
   128  // (links) are at or surpass the sequence number ther server promised through
   129  // the ratchet sets and the maxUncommittedSeqnoPromised obtained through the
   130  // merkle/path api call. We look at both the loaded and the received downloaded
   131  // ratchets for this check.
   132  func (l *LoaderPackage) checkExpectedHighSeqno(mctx libkb.MetaContext, links []sig3.Generic, maxUncommittedSeqnoPromised keybase1.Seqno) (err error) {
   133  	if !l.HiddenChainDataEnabled() {
   134  		mctx.Debug("skipping checkExpectedHighSeqno, since we didn't ask for any data")
   135  		return nil
   136  	}
   137  	last := l.LastSeqno()
   138  	max := l.MaxRatchet()
   139  	if max < maxUncommittedSeqnoPromised {
   140  		max = maxUncommittedSeqnoPromised
   141  	}
   142  	if max <= last {
   143  		return nil
   144  	}
   145  	if len(links) > 0 && links[len(links)-1].Seqno() >= max {
   146  		return nil
   147  	}
   148  	return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorServerWitholdingLinks,
   149  		"Server promised a hidden chain up to %d, but never received; is it withholding?", max)
   150  }
   151  
   152  // checkLoadedRatchet checks the given loaded ratchet against the consumed update and verifies a (seqno, linkID) match
   153  // for that ratchet.
   154  func (l *LoaderPackage) checkLoadedRatchet(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain, ratchet keybase1.LinkTripleAndTime) (err error) {
   155  	q := ratchet.Triple.Seqno
   156  	link, ok := update.Outer[q]
   157  	if ok && !link.Eq(ratchet.Triple.LinkID) {
   158  		return NewLoaderError("update data failed to match ratchet %+v v %s", ratchet, link)
   159  	}
   160  	return nil
   161  }
   162  
   163  // checkLoadedRatchetSet checks the hidden chain update against the ratchet set that we loaded from storage before
   164  // we brought the updated down from the server. It will not check against ratchets that came down with the update
   165  // (in the visible chain). This works by checking the update for validity against each type of ratchet
   166  // (and there are 3: self, main, and blinded tree).
   167  func (l *LoaderPackage) checkLoadedRatchetSet(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain) (err error) {
   168  	if l.data == nil {
   169  		return nil
   170  	}
   171  	for _, r := range l.data.RatchetSet.Flat() {
   172  		err = l.checkLoadedRatchet(mctx, update, r)
   173  		if err != nil {
   174  			return err
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  // CheckPTKsForDuplicates checks that the new per-team-keys don't duplicate keys we've gotten along the
   181  // visible chain, via the given getter.
   182  func (l *LoaderPackage) CheckPTKsForDuplicates(mctx libkb.MetaContext, getter func(g keybase1.PerTeamKeyGeneration) bool) error {
   183  	if l.newData == nil {
   184  		return nil
   185  	}
   186  	for k := range l.newData.ReaderPerTeamKeys {
   187  		if getter(k) {
   188  			return newRepeatPTKGenerationError(k, "clashes a previously-loaded visible rotation")
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  func (l *LoaderPackage) CheckNoPTK(mctx libkb.MetaContext, g keybase1.PerTeamKeyGeneration) (err error) {
   195  	var found bool
   196  	if l.newData != nil {
   197  		_, found = l.newData.ReaderPerTeamKeys[g]
   198  	}
   199  	if l.data != nil && !found {
   200  		_, found = l.data.ReaderPerTeamKeys[g]
   201  	}
   202  	if found {
   203  		return newRepeatPTKGenerationError(g, "clashes a previously-loaded hidden rotation")
   204  	}
   205  	return nil
   206  }
   207  
   208  func (l *LoaderPackage) UpdateTeamMetadata(encKID keybase1.KID, encKIDGen keybase1.PerTeamKeyGeneration, role keybase1.TeamRole) {
   209  	l.encKID = encKID
   210  	l.encKIDGen = encKIDGen
   211  	l.role = role
   212  }
   213  
   214  // Update combines the preloaded data with any downloaded updates from the server, and stores
   215  // the result local to this object.
   216  func (l *LoaderPackage) Update(mctx libkb.MetaContext, update []sig3.ExportJSON, maxUncommittedSeqnoPromised keybase1.Seqno) (err error) {
   217  	defer mctx.Trace(fmt.Sprintf("LoaderPackage#Update(%s, %d)", l.id, len(update)), &err)()
   218  	mctx.G().GetVDebugLog().CLogf(mctx.Ctx(), libkb.VLog1,
   219  		"LoaderPackage#Update pre: %s", l.data.LinkAndKeySummary())
   220  
   221  	var data *keybase1.HiddenTeamChain
   222  	data, err = l.updatePrecheck(mctx, update, maxUncommittedSeqnoPromised)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	err = l.mergeData(mctx, data)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	if l.newData != nil && l.lastCommittedSeqno > l.newData.LastCommittedSeqno {
   232  		l.newData.LastCommittedSeqno = l.lastCommittedSeqno
   233  	}
   234  
   235  	// If we received a new uncommitted link.
   236  	if l.newData != nil && l.newData.Last > 0 {
   237  		if l.newData.LinkReceiptTimes == nil {
   238  			l.newData.LinkReceiptTimes = make(map[keybase1.Seqno]keybase1.Time)
   239  		}
   240  		if _, found := l.newData.LinkReceiptTimes[l.data.Last]; !found && l.newData.Last > l.LastCommittedSeqno() {
   241  			mctx.Debug("Adding seqno %v to LinkReceiptTimes", l.newData.Last)
   242  			l.newData.LinkReceiptTimes[l.newData.Last] = keybase1.ToTime(mctx.G().Clock().Now())
   243  		}
   244  	}
   245  
   246  	mctx.G().GetVDebugLog().CLogf(mctx.Ctx(), libkb.VLog1,
   247  		"LoaderPackage#Update post: %s", l.data.LinkAndKeySummary())
   248  	return nil
   249  }
   250  
   251  // checkNewLinksAgainstNewRatchtets checks a link sent down with the hidden update against the ratchets sent down
   252  // with the visible team update. It makes sure they match up.
   253  func (l *LoaderPackage) checkNewLinkAgainstNewRatchets(mctx libkb.MetaContext, q keybase1.Seqno, h keybase1.LinkID) (err error) {
   254  	if l.allNewRatchets == nil {
   255  		return nil
   256  	}
   257  	found, ok := l.allNewRatchets[q]
   258  	if !ok {
   259  		return nil
   260  	}
   261  	if !found.Triple.LinkID.Eq(h) {
   262  		return NewLoaderError("link ID at %d fails to check against ratchet: %s != %s", q, found.Triple.LinkID, h)
   263  	}
   264  	return nil
   265  }
   266  
   267  // checkNewLinksAgainstNewRatchets checks all links in the update sent down from the server against all racthets
   268  // sent down in the same update.
   269  func (l *LoaderPackage) checkNewLinksAgainstNewRatchets(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain) error {
   270  	for k, v := range update.Outer {
   271  		err := l.checkNewLinkAgainstNewRatchets(mctx, k, v)
   272  		if err != nil {
   273  			return err
   274  		}
   275  	}
   276  	return nil
   277  }
   278  
   279  // VerifyOldChainLinksAreCommitted checks that uncommitted links which we previously got from the
   280  // server have indeed been included in the blind tree (the server has a short
   281  // grace period to do this to account for potential downtime)
   282  func (l *LoaderPackage) VerifyOldChainLinksAreCommitted(mctx libkb.MetaContext, newCommittedSeqno keybase1.Seqno) error {
   283  	mctx.Debug("VerifyOldChainLinksAreCommitted at time %v", mctx.G().Clock().Now())
   284  	if l.data == nil || l.data.LinkReceiptTimes == nil {
   285  		return nil
   286  	}
   287  	for s := range l.data.LinkReceiptTimes {
   288  		if s <= newCommittedSeqno {
   289  			continue
   290  		}
   291  		// No longer forcing server to eventually commit links
   292  	}
   293  	return nil
   294  }
   295  
   296  // updatePrecheck runs a series of cryptographic validations on the update sent down from the server, to ensure that
   297  // it can be accepted and used during the team loading process. It also converts the raw export Sig3 links into a
   298  // HiddenTeamChain, which can be eventually merged with the existing hidden chain state for this team.
   299  func (l *LoaderPackage) updatePrecheck(mctx libkb.MetaContext, update []sig3.ExportJSON, maxUncommittedSeqnoPromised keybase1.Seqno) (ret *keybase1.HiddenTeamChain, err error) {
   300  	var links []sig3.Generic
   301  	links, err = importChain(mctx, update)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	err = sig3.CheckLinkSequence(links)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	err = l.checkExpectedHighSeqno(mctx, links, maxUncommittedSeqnoPromised)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	if len(links) == 0 {
   317  		mctx.Debug("short-circuiting since no update")
   318  		return nil, nil
   319  	}
   320  
   321  	err = l.checkPrev(mctx, links[0])
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	data, err := l.toHiddenTeamChain(mctx, links)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  
   331  	err = l.checkLoadedRatchetSet(mctx, data)
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  
   336  	err = l.checkNewLinksAgainstNewRatchets(mctx, data)
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  
   341  	return data, nil
   342  }
   343  
   344  // lastRotator returns the last user/KID combination to have signed a rotation into this hidden team chain.
   345  // Or nil if the chain is empty.
   346  func (l *LoaderPackage) lastRotator(mctx libkb.MetaContext, typ keybase1.PTKType) *keybase1.Signer {
   347  	if l.data == nil {
   348  		return nil
   349  	}
   350  	last, ok := l.data.LastPerTeamKeys[typ]
   351  	if !ok {
   352  		return nil
   353  	}
   354  	inner, ok := l.data.Inner[last]
   355  	if !ok {
   356  		return nil
   357  	}
   358  	return &inner.Signer
   359  }
   360  
   361  // LastReaderKeyRotator returns a signer object that signifies the last KID/UID pair to sign
   362  // a reader PTK into this chain.
   363  func (l *LoaderPackage) LastReaderKeyRotator(mctx libkb.MetaContext) *keybase1.Signer {
   364  	return l.lastRotator(mctx, keybase1.PTKType_READER)
   365  }
   366  
   367  // mergeData takes the data from the update and merges it with the last load of this hidden team chain
   368  // from local storage. The result is just in memory, not stored to disk yet. That happens in Commit().
   369  func (l *LoaderPackage) mergeData(mctx libkb.MetaContext, newData *keybase1.HiddenTeamChain) (err error) {
   370  
   371  	if newData == nil && (!l.newRatchetSet.IsEmpty() || l.lastCommittedSeqno > 0) {
   372  		newData = keybase1.NewHiddenTeamChain(l.id)
   373  	}
   374  	if !l.newRatchetSet.IsEmpty() {
   375  		newData.RatchetSet.Merge(l.newRatchetSet)
   376  	}
   377  
   378  	if l.lastCommittedSeqno > 0 && newData.LastCommittedSeqno < l.lastCommittedSeqno {
   379  		newData.LastCommittedSeqno = l.lastCommittedSeqno
   380  	}
   381  
   382  	l.newData = newData
   383  
   384  	if l.data == nil {
   385  		l.data = newData
   386  		return nil
   387  	}
   388  	if newData != nil {
   389  		_, err = l.data.Merge(*newData)
   390  		if err != nil {
   391  			return err
   392  		}
   393  	}
   394  	return nil
   395  }
   396  
   397  func (l *LoaderPackage) toHiddenTeamChain(mctx libkb.MetaContext, links []sig3.Generic) (ret *keybase1.HiddenTeamChain, err error) {
   398  	ret = keybase1.NewHiddenTeamChain(l.id)
   399  	ret.Public = l.id.IsPublic()
   400  	for _, link := range links {
   401  		err = populateLink(mctx, ret, link)
   402  		if err != nil {
   403  			return nil, err
   404  		}
   405  	}
   406  	return ret, nil
   407  }
   408  
   409  func checkUpdateAgainstSeed(mctx libkb.MetaContext, getSeed func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck, update keybase1.HiddenTeamChainLink) (err error) {
   410  	readerKey, ok := update.Ptk[keybase1.PTKType_READER]
   411  	if !ok {
   412  		// No reader key found in link, so no need to check it.
   413  		return nil
   414  	}
   415  	gen := readerKey.Ptk.Gen
   416  	check := getSeed(gen)
   417  	if check == nil {
   418  		return NewLoaderError("seed check at generation %d wasn't found", gen)
   419  	}
   420  	hash, err := check.Hash()
   421  	if err != nil {
   422  		return err
   423  	}
   424  	if readerKey.Check.Version != keybase1.PerTeamSeedCheckVersion_V1 {
   425  		return NewLoaderError("can only handle seed check version 1; got %d", readerKey.Check.Version)
   426  	}
   427  	if check.Version != keybase1.PerTeamSeedCheckVersion_V1 {
   428  		return NewLoaderError("can only handle seed check version 1; got computed check %s", check.Version)
   429  	}
   430  	if !hash.Eq(readerKey.Check) {
   431  		return NewLoaderError("wrong seed check at generation %d", gen)
   432  	}
   433  	return nil
   434  }
   435  
   436  func (l *LoaderPackage) CheckUpdatesAgainstSeedsWithMap(mctx libkb.MetaContext, seeds map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem) (err error) {
   437  	return l.CheckUpdatesAgainstSeeds(mctx, func(g keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck {
   438  		item, ok := seeds[g]
   439  		if !ok {
   440  			return nil
   441  		}
   442  		return item.Check
   443  	})
   444  }
   445  
   446  // CheckUpdatesAgainstSeeds checks the update inside this loader package against unverified team seeds. It
   447  // enforces equality and will error out if not. Through this check, a client can convince itself that the
   448  // recent keyers knew the old keys.
   449  func (l *LoaderPackage) CheckUpdatesAgainstSeeds(mctx libkb.MetaContext, f func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck) (err error) {
   450  	defer mctx.Trace("LoaderPackage#CheckUpdatesAgainstSeeds", &err)()
   451  	// RESTRICTEDBOTs are excluded since they do not have any seed access
   452  	if l.newData == nil || l.role.IsRestrictedBot() {
   453  		return nil
   454  	}
   455  	for _, update := range l.newData.Inner {
   456  		err = checkUpdateAgainstSeed(mctx, f, update)
   457  		if err != nil {
   458  			return err
   459  		}
   460  	}
   461  	return nil
   462  }
   463  
   464  // LastSeqno returns the last seqno when the preloaded sequence and the update are taken together.
   465  func (l *LoaderPackage) LastSeqno() keybase1.Seqno {
   466  	if l.data == nil {
   467  		return keybase1.Seqno(0)
   468  	}
   469  	return l.data.Last
   470  }
   471  
   472  // LastFullSeqno returns the last seqno before the end of the chain, or before an unstubbed
   473  // hole is found (as a result of FTL).
   474  func (l *LoaderPackage) LastFullSeqno() keybase1.Seqno {
   475  	if l.data == nil {
   476  		return keybase1.Seqno(0)
   477  	}
   478  	return l.data.LastFullPopulateIfUnset()
   479  }
   480  
   481  // MaxRatchet returns the greatest sequence number across all ratchets in the loaded data and also
   482  // in the data from the recent update from the server.
   483  func (l *LoaderPackage) MaxRatchet() (ret keybase1.Seqno) {
   484  	if l.data != nil {
   485  		ret = l.data.RatchetSet.Max()
   486  	}
   487  	tmp := l.newRatchetSet.Max()
   488  	if tmp > ret {
   489  		ret = tmp
   490  	}
   491  	return ret
   492  }
   493  
   494  // LastCommittedSeqno returns the greatest sequence number which we have seen
   495  // committed by the server, to prevent rollbacks to the blind tree sigchain. It
   496  // does not include the seqno in the update which was recently received from the
   497  // server. It returns 0 if we have never seen a non empty link committed to the
   498  // blind tree before.
   499  func (l *LoaderPackage) LastCommittedSeqno() (ret keybase1.Seqno) {
   500  	if l.newData != nil && l.newData.LastCommittedSeqno > ret {
   501  		ret = l.newData.LastCommittedSeqno
   502  	}
   503  	if l.lastCommittedSeqno > ret {
   504  		ret = l.lastCommittedSeqno
   505  	}
   506  	if l.data != nil && l.data.LastCommittedSeqno > ret {
   507  		ret = l.data.LastCommittedSeqno
   508  	}
   509  	return ret
   510  }
   511  
   512  func (l *LoaderPackage) SetLastCommittedSeqno(mctx libkb.MetaContext, lcs keybase1.Seqno) error {
   513  	last := l.LastCommittedSeqno()
   514  	if lcs >= last {
   515  		l.lastCommittedSeqno = lcs
   516  		return nil
   517  	}
   518  	// this should never happen, as we already test for this condition inside CheckHiddenMerklePathResponseAndAddRatchets
   519  	return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno,
   520  		"Tries to set a LastCommittedSeqno %v smaller than the one we know about: %v", lcs, last)
   521  }
   522  
   523  func (l *LoaderPackage) CheckHiddenMerklePathResponseAndAddRatchets(mctx libkb.MetaContext, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) {
   524  	oldHiddenTailSeqno := l.LastSeqno()
   525  	if oldHiddenTailSeqno == hiddenResp.UncommittedSeqno {
   526  		hiddenIsFresh = true
   527  	} else if oldHiddenTailSeqno < hiddenResp.UncommittedSeqno {
   528  		hiddenIsFresh = false
   529  	} else {
   530  		return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackUncommittedSeqno,
   531  			"The server indicated that the last hidden link has Seqno %v, but we knew of a link with seqno %v already!", hiddenResp.UncommittedSeqno, oldHiddenTailSeqno)
   532  	}
   533  
   534  	return hiddenIsFresh, nil
   535  }
   536  
   537  // HasReaderPerTeamKeyAtGeneration returns true if the LoaderPackage has a sigchain entry for
   538  // the PTK at the given generation. Whether in the preloaded data or the update.
   539  func (l *LoaderPackage) HasReaderPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) bool {
   540  	// RESTRICTEDBOTs are excluded since they do not have any PTK access
   541  	if l.data == nil || l.role.IsRestrictedBot() {
   542  		return false
   543  	}
   544  	_, ok := l.data.ReaderPerTeamKeys[gen]
   545  	return ok
   546  }
   547  
   548  // Commit the update from the server to main HiddenTeamChain storage.
   549  func (l *LoaderPackage) Commit(mctx libkb.MetaContext) error {
   550  	if l.newData == nil {
   551  		mctx.Debug("LoaderPackage#Commit: nil newData for team %s", l.id)
   552  		return nil
   553  	}
   554  	mctx.Debug("LoaderPackage#Commit: %s", l.newData.Summary())
   555  	err := mctx.G().GetHiddenTeamChainManager().Advance(mctx, *l.newData, l.expectedPrev)
   556  	return err
   557  }
   558  
   559  // ChainData returns the merge of the preloaded hidden chain data and the recently downloaded chain update.
   560  func (l *LoaderPackage) ChainData() *keybase1.HiddenTeamChain {
   561  	return l.data
   562  }
   563  
   564  // MaxReaderTeamKeyGeneration returns the highest Reader PTK generation from the preloaded and hidden
   565  // data.
   566  func (l *LoaderPackage) MaxReaderPerTeamKeyGeneration() keybase1.PerTeamKeyGeneration {
   567  	// RESTRICTEDBOTs are excluded since they do not have any PTK access
   568  	if l.data == nil || l.role.IsRestrictedBot() {
   569  		return keybase1.PerTeamKeyGeneration(0)
   570  	}
   571  	return l.data.MaxReaderPerTeamKeyGeneration()
   572  }
   573  
   574  func (l *LoaderPackage) RatchetBlindingKeySet() *RatchetBlindingKeySet {
   575  	return l.rbks
   576  }
   577  
   578  func (l *LoaderPackage) SetRatchetBlindingKeySet(r *RatchetBlindingKeySet) {
   579  	l.rbks = r
   580  }
   581  
   582  // AddRatchets calls AddRatchet on each SCTeamRatchet in v.
   583  func (l *LoaderPackage) AddRatchets(mctx libkb.MetaContext, v []SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
   584  	for _, r := range v {
   585  		err := l.AddRatchet(mctx, r, ctime, typ)
   586  		if err != nil {
   587  			return err
   588  		}
   589  	}
   590  	return nil
   591  }
   592  
   593  // AddRatchet is called whenever we pull a ratchet out of a visible team link. The first thing we'll need to
   594  // do is to make sure that we can look the unblinded ratchet up using the blinding keys we got down from the
   595  // server. Then we'll check the ratchets again the old (loaded) and new (downloaded) data. Finally, we'll
   596  // ensure that this ratchet doesn't clash another ratchet that came down in this update. If all checks work,
   597  // then add this ratchet to the set of all new ratchets, and also the max ratchet set that we're keeping locally.
   598  func (l *LoaderPackage) AddRatchet(mctx libkb.MetaContext, r SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
   599  	tail := l.rbks.Get(r)
   600  	if tail == nil {
   601  		return NewLoaderError("missing unblind for ratchet %s", r.String())
   602  	}
   603  	return l.AddUnblindedRatchet(mctx, tail, ctime, typ)
   604  }
   605  
   606  func (l *LoaderPackage) AddUnblindedRatchet(mctx libkb.MetaContext, tail *sig3.Tail, ctime int, typ keybase1.RatchetType) (err error) {
   607  	ratchet := keybase1.LinkTripleAndTime{
   608  		Triple: tail.Export(),
   609  		Time:   keybase1.TimeFromSeconds(int64(ctime)),
   610  	}
   611  	err = checkRatchet(mctx, l.data, ratchet)
   612  	if err != nil {
   613  		return err
   614  	}
   615  	err = checkRatchet(mctx, l.newData, ratchet)
   616  	if err != nil {
   617  		return err
   618  	}
   619  	if l.allNewRatchets == nil {
   620  		l.allNewRatchets = make(map[keybase1.Seqno]keybase1.LinkTripleAndTime)
   621  	}
   622  	q := ratchet.Triple.Seqno
   623  	found, ok := l.allNewRatchets[q]
   624  	if ok && !found.Triple.LinkID.Eq(ratchet.Triple.LinkID) {
   625  		return NewLoaderError("ratchet for seqno %d contradicts another ratchet", q)
   626  	}
   627  	if !ok {
   628  		l.allNewRatchets[q] = ratchet
   629  	}
   630  	l.newRatchetSet.Add(typ, ratchet)
   631  	return nil
   632  }
   633  
   634  func (l *LoaderPackage) checkParentPointer(mctx libkb.MetaContext, getter func(q keybase1.Seqno) (keybase1.LinkID, bool), parentPointer keybase1.LinkTriple, fullLoad bool) (err error) {
   635  	q := parentPointer.Seqno
   636  	link, ok := getter(q)
   637  	switch {
   638  	case !ok && fullLoad:
   639  		return newParentPointerError(q, "link wasn't found in parent chain")
   640  	case !ok && !fullLoad:
   641  		return nil
   642  	}
   643  	if !link.Eq(parentPointer.LinkID) {
   644  		return newParentPointerError(q, "link ID mismatch")
   645  	}
   646  	if parentPointer.SeqType != keybase1.SeqType_SEMIPRIVATE {
   647  		return newParentPointerError(q, "wrong chain type")
   648  	}
   649  	return nil
   650  }
   651  
   652  // CheckParentPointersOnFullLoad looks at all of the new hidden links we got down and makes sure that they
   653  // the point to loaded links in the visible chain. Because it's a full load, the pointers must land. They
   654  // can dangle on FTL loads, for instance.
   655  func (l *LoaderPackage) CheckParentPointersOnFullLoad(mctx libkb.MetaContext, team *keybase1.TeamData) (err error) {
   656  	if l.newData == nil {
   657  		return nil
   658  	}
   659  	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
   660  		if team == nil {
   661  			return ret, false
   662  		}
   663  		ret, found = team.Chain.LinkIDs[q]
   664  		return ret, found
   665  	}
   666  	for _, v := range l.newData.Inner {
   667  		if err := l.checkParentPointer(mctx, getter, v.ParentChain, true /* full load */); err != nil {
   668  			return err
   669  		}
   670  	}
   671  	return nil
   672  }
   673  
   674  // CheckParentPointersOnFastLoad looks at all of the new hidden links we got down and makes sure that they
   675  // the point to loaded links in the visible chain. Because it's a fast load, the pointers can dangle.
   676  func (l *LoaderPackage) CheckParentPointersOnFastLoad(mctx libkb.MetaContext, team *keybase1.FastTeamData) (err error) {
   677  	if l.newData == nil {
   678  		return nil
   679  	}
   680  	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
   681  		if team == nil {
   682  			return ret, false
   683  		}
   684  		ret, found = team.Chain.LinkIDs[q]
   685  		return ret, found
   686  	}
   687  	for _, v := range l.newData.Inner {
   688  		if err := l.checkParentPointer(mctx, getter, v.ParentChain, false /* full load */); err != nil {
   689  			return err
   690  		}
   691  	}
   692  	return nil
   693  }
   694  
   695  // DisableHiddenChainData tells the LoaderPackage to disable reading and consuming
   696  // of hidden chain data. On if we are a subteam reader, or if we are feature-flagged off
   697  func (l *LoaderPackage) DisableHiddenChainData() {
   698  	l.disableHiddenChainData = true
   699  }
   700  
   701  // HiddenChainDataEnabled is true if we are doing a full hidden chain load (and off if we're skipping
   702  // due to the above).
   703  func (l *LoaderPackage) HiddenChainDataEnabled() bool {
   704  	return !l.disableHiddenChainData
   705  }