github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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, t := range l.data.LinkReceiptTimes {
   288  		if s <= newCommittedSeqno {
   289  			continue
   290  		}
   291  		if mctx.G().Clock().Since(t.Time()) > MaxDelayInCommittingHiddenLinks {
   292  			return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorOldLinkNotYetCommitted,
   293  				"Link for seqno %v was added %v ago and has not been included in the blind tree yet.", s, mctx.G().Clock().Since(t.Time()))
   294  		}
   295  	}
   296  	return nil
   297  }
   298  
   299  // updatePrecheck runs a series of cryptographic validations on the update sent down from the server, to ensure that
   300  // it can be accepted and used during the team loading process. It also converts the raw export Sig3 links into a
   301  // HiddenTeamChain, which can be eventually merged with the existing hidden chain state for this team.
   302  func (l *LoaderPackage) updatePrecheck(mctx libkb.MetaContext, update []sig3.ExportJSON, maxUncommittedSeqnoPromised keybase1.Seqno) (ret *keybase1.HiddenTeamChain, err error) {
   303  	var links []sig3.Generic
   304  	links, err = importChain(mctx, update)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	err = sig3.CheckLinkSequence(links)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	err = l.checkExpectedHighSeqno(mctx, links, maxUncommittedSeqnoPromised)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	if len(links) == 0 {
   320  		mctx.Debug("short-circuiting since no update")
   321  		return nil, nil
   322  	}
   323  
   324  	err = l.checkPrev(mctx, links[0])
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	data, err := l.toHiddenTeamChain(mctx, links)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	err = l.checkLoadedRatchetSet(mctx, data)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	err = l.checkNewLinksAgainstNewRatchets(mctx, data)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	return data, nil
   345  }
   346  
   347  // lastRotator returns the last user/KID combination to have signed a rotation into this hidden team chain.
   348  // Or nil if the chain is empty.
   349  func (l *LoaderPackage) lastRotator(mctx libkb.MetaContext, typ keybase1.PTKType) *keybase1.Signer {
   350  	if l.data == nil {
   351  		return nil
   352  	}
   353  	last, ok := l.data.LastPerTeamKeys[typ]
   354  	if !ok {
   355  		return nil
   356  	}
   357  	inner, ok := l.data.Inner[last]
   358  	if !ok {
   359  		return nil
   360  	}
   361  	return &inner.Signer
   362  }
   363  
   364  // LastReaderKeyRotator returns a signer object that signifies the last KID/UID pair to sign
   365  // a reader PTK into this chain.
   366  func (l *LoaderPackage) LastReaderKeyRotator(mctx libkb.MetaContext) *keybase1.Signer {
   367  	return l.lastRotator(mctx, keybase1.PTKType_READER)
   368  }
   369  
   370  // mergeData takes the data from the update and merges it with the last load of this hidden team chain
   371  // from local storage. The result is just in memory, not stored to disk yet. That happens in Commit().
   372  func (l *LoaderPackage) mergeData(mctx libkb.MetaContext, newData *keybase1.HiddenTeamChain) (err error) {
   373  
   374  	if newData == nil && (!l.newRatchetSet.IsEmpty() || l.lastCommittedSeqno > 0) {
   375  		newData = keybase1.NewHiddenTeamChain(l.id)
   376  	}
   377  	if !l.newRatchetSet.IsEmpty() {
   378  		newData.RatchetSet.Merge(l.newRatchetSet)
   379  	}
   380  
   381  	if l.lastCommittedSeqno > 0 && newData.LastCommittedSeqno < l.lastCommittedSeqno {
   382  		newData.LastCommittedSeqno = l.lastCommittedSeqno
   383  	}
   384  
   385  	l.newData = newData
   386  
   387  	if l.data == nil {
   388  		l.data = newData
   389  		return nil
   390  	}
   391  	if newData != nil {
   392  		_, err = l.data.Merge(*newData)
   393  		if err != nil {
   394  			return err
   395  		}
   396  	}
   397  	return nil
   398  }
   399  
   400  func (l *LoaderPackage) toHiddenTeamChain(mctx libkb.MetaContext, links []sig3.Generic) (ret *keybase1.HiddenTeamChain, err error) {
   401  	ret = keybase1.NewHiddenTeamChain(l.id)
   402  	ret.Public = l.id.IsPublic()
   403  	for _, link := range links {
   404  		err = populateLink(mctx, ret, link)
   405  		if err != nil {
   406  			return nil, err
   407  		}
   408  	}
   409  	return ret, nil
   410  }
   411  
   412  func checkUpdateAgainstSeed(mctx libkb.MetaContext, getSeed func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck, update keybase1.HiddenTeamChainLink) (err error) {
   413  	readerKey, ok := update.Ptk[keybase1.PTKType_READER]
   414  	if !ok {
   415  		// No reader key found in link, so no need to check it.
   416  		return nil
   417  	}
   418  	gen := readerKey.Ptk.Gen
   419  	check := getSeed(gen)
   420  	if check == nil {
   421  		return NewLoaderError("seed check at generation %d wasn't found", gen)
   422  	}
   423  	hash, err := check.Hash()
   424  	if err != nil {
   425  		return err
   426  	}
   427  	if readerKey.Check.Version != keybase1.PerTeamSeedCheckVersion_V1 {
   428  		return NewLoaderError("can only handle seed check version 1; got %d", readerKey.Check.Version)
   429  	}
   430  	if check.Version != keybase1.PerTeamSeedCheckVersion_V1 {
   431  		return NewLoaderError("can only handle seed check version 1; got computed check %s", check.Version)
   432  	}
   433  	if !hash.Eq(readerKey.Check) {
   434  		return NewLoaderError("wrong seed check at generation %d", gen)
   435  	}
   436  	return nil
   437  }
   438  
   439  func (l *LoaderPackage) CheckUpdatesAgainstSeedsWithMap(mctx libkb.MetaContext, seeds map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem) (err error) {
   440  	return l.CheckUpdatesAgainstSeeds(mctx, func(g keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck {
   441  		item, ok := seeds[g]
   442  		if !ok {
   443  			return nil
   444  		}
   445  		return item.Check
   446  	})
   447  }
   448  
   449  // CheckUpdatesAgainstSeeds checks the update inside this loader package against unverified team seeds. It
   450  // enforces equality and will error out if not. Through this check, a client can convince itself that the
   451  // recent keyers knew the old keys.
   452  func (l *LoaderPackage) CheckUpdatesAgainstSeeds(mctx libkb.MetaContext, f func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck) (err error) {
   453  	defer mctx.Trace("LoaderPackage#CheckUpdatesAgainstSeeds", &err)()
   454  	// RESTRICTEDBOTs are excluded since they do not have any seed access
   455  	if l.newData == nil || l.role.IsRestrictedBot() {
   456  		return nil
   457  	}
   458  	for _, update := range l.newData.Inner {
   459  		err = checkUpdateAgainstSeed(mctx, f, update)
   460  		if err != nil {
   461  			return err
   462  		}
   463  	}
   464  	return nil
   465  }
   466  
   467  // LastSeqno returns the last seqno when the preloaded sequence and the update are taken together.
   468  func (l *LoaderPackage) LastSeqno() keybase1.Seqno {
   469  	if l.data == nil {
   470  		return keybase1.Seqno(0)
   471  	}
   472  	return l.data.Last
   473  }
   474  
   475  // LastFullSeqno returns the last seqno before the end of the chain, or before an unstubbed
   476  // hole is found (as a result of FTL).
   477  func (l *LoaderPackage) LastFullSeqno() keybase1.Seqno {
   478  	if l.data == nil {
   479  		return keybase1.Seqno(0)
   480  	}
   481  	return l.data.LastFullPopulateIfUnset()
   482  }
   483  
   484  // MaxRatchet returns the greatest sequence number across all ratchets in the loaded data and also
   485  // in the data from the recent update from the server.
   486  func (l *LoaderPackage) MaxRatchet() (ret keybase1.Seqno) {
   487  	if l.data != nil {
   488  		ret = l.data.RatchetSet.Max()
   489  	}
   490  	tmp := l.newRatchetSet.Max()
   491  	if tmp > ret {
   492  		ret = tmp
   493  	}
   494  	return ret
   495  }
   496  
   497  // LastCommittedSeqno returns the greatest sequence number which we have seen
   498  // committed by the server, to prevent rollbacks to the blind tree sigchain. It
   499  // does not include the seqno in the update which was recently received from the
   500  // server. It returns 0 if we have never seen a non empty link committed to the
   501  // blind tree before.
   502  func (l *LoaderPackage) LastCommittedSeqno() (ret keybase1.Seqno) {
   503  	if l.newData != nil && l.newData.LastCommittedSeqno > ret {
   504  		ret = l.newData.LastCommittedSeqno
   505  	}
   506  	if l.lastCommittedSeqno > ret {
   507  		ret = l.lastCommittedSeqno
   508  	}
   509  	if l.data != nil && l.data.LastCommittedSeqno > ret {
   510  		ret = l.data.LastCommittedSeqno
   511  	}
   512  	return ret
   513  }
   514  
   515  func (l *LoaderPackage) SetLastCommittedSeqno(mctx libkb.MetaContext, lcs keybase1.Seqno) error {
   516  	last := l.LastCommittedSeqno()
   517  	if lcs >= last {
   518  		l.lastCommittedSeqno = lcs
   519  		return nil
   520  	}
   521  	// this should never happen, as we already test for this condition inside CheckHiddenMerklePathResponseAndAddRatchets
   522  	return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno,
   523  		"Tries to set a LastCommittedSeqno %v smaller than the one we know about: %v", lcs, last)
   524  }
   525  
   526  func (l *LoaderPackage) CheckHiddenMerklePathResponseAndAddRatchets(mctx libkb.MetaContext, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) {
   527  
   528  	oldHiddenTailSeqno := l.LastSeqno()
   529  	oldCommittedHiddenTailSeqno := l.LastCommittedSeqno()
   530  	lastCommittedHiddenTailSeqno := oldCommittedHiddenTailSeqno
   531  
   532  	switch hiddenResp.RespType {
   533  	case libkb.MerkleHiddenResponseTypeNONE:
   534  		return false, NewLoaderError("Logic error in CheckHiddenMerklePathResponseAndAddRatchets: should not call this function with a NONE response.")
   535  	case libkb.MerkleHiddenResponseTypeFLAGOFF:
   536  		mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as feature flag is off")
   537  		return true, nil
   538  	case libkb.MerkleHiddenResponseTypeOK:
   539  		newCommittedHiddenTail := hiddenResp.CommittedHiddenTail
   540  		newCommittedHiddenTailSeqno := newCommittedHiddenTail.Seqno
   541  		lastCommittedHiddenTailSeqno = newCommittedHiddenTailSeqno
   542  
   543  		// ensure the server is self consistent in its answer
   544  		if hiddenResp.UncommittedSeqno < newCommittedHiddenTailSeqno {
   545  			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInconsistentUncommittedSeqno,
   546  				"The server claims that the lastHiddenSeqno for this team (seqno %v) is smaller than the one in the blind merkle update it sent (%v)", hiddenResp.UncommittedSeqno, newCommittedHiddenTailSeqno)
   547  		}
   548  		// prevent rollbacks in the blind tree
   549  		if newCommittedHiddenTailSeqno < oldCommittedHiddenTailSeqno {
   550  			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno,
   551  				"Server rollback of the blind merkle tree leaf: we had previously seen a leaf at seqno %v, but this update contains a leaf at seqno %v", oldCommittedHiddenTailSeqno, newCommittedHiddenTailSeqno)
   552  		}
   553  		// add ratchet to ensure consistency
   554  		err = l.AddUnblindedRatchet(mctx, newCommittedHiddenTail, int(mctx.G().Clock().Now().Unix()), keybase1.RatchetType_BLINDED)
   555  		if err != nil {
   556  			return false, err
   557  		}
   558  		err = l.SetLastCommittedSeqno(mctx, newCommittedHiddenTailSeqno)
   559  		if err != nil {
   560  			return false, err
   561  		}
   562  	case libkb.MerkleHiddenResponseTypeABSENCEPROOF:
   563  		if oldCommittedHiddenTailSeqno > 0 {
   564  			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorUnexpectedAbsenceProof,
   565  				"Server claimed (and proved) there are no committed hidden chain links in the chain, but we had previously seen a committed link with seqno %v", oldCommittedHiddenTailSeqno)
   566  		}
   567  	default:
   568  		return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInvalidHiddenResponseType,
   569  			"Unrecognized response type: %v", hiddenResp.RespType)
   570  	}
   571  
   572  	if err := l.VerifyOldChainLinksAreCommitted(mctx, lastCommittedHiddenTailSeqno); err != nil {
   573  		return false, err
   574  	}
   575  
   576  	if oldHiddenTailSeqno == hiddenResp.UncommittedSeqno {
   577  		hiddenIsFresh = true
   578  	} else if oldHiddenTailSeqno < hiddenResp.UncommittedSeqno {
   579  		hiddenIsFresh = false
   580  	} else {
   581  		return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackUncommittedSeqno,
   582  			"The server indicated that the last hidden link has Seqno %v, but we knew of a link with seqno %v already!", hiddenResp.UncommittedSeqno, oldHiddenTailSeqno)
   583  	}
   584  
   585  	return hiddenIsFresh, nil
   586  }
   587  
   588  // HasReaderPerTeamKeyAtGeneration returns true if the LoaderPackage has a sigchain entry for
   589  // the PTK at the given generation. Whether in the preloaded data or the update.
   590  func (l *LoaderPackage) HasReaderPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) bool {
   591  	// RESTRICTEDBOTs are excluded since they do not have any PTK access
   592  	if l.data == nil || l.role.IsRestrictedBot() {
   593  		return false
   594  	}
   595  	_, ok := l.data.ReaderPerTeamKeys[gen]
   596  	return ok
   597  }
   598  
   599  // Commit the update from the server to main HiddenTeamChain storage.
   600  func (l *LoaderPackage) Commit(mctx libkb.MetaContext) error {
   601  	if l.newData == nil {
   602  		mctx.Debug("LoaderPackage#Commit: nil newData for team %s", l.id)
   603  		return nil
   604  	}
   605  	mctx.Debug("LoaderPackage#Commit: %s", l.newData.Summary())
   606  	err := mctx.G().GetHiddenTeamChainManager().Advance(mctx, *l.newData, l.expectedPrev)
   607  	return err
   608  }
   609  
   610  // ChainData returns the merge of the preloaded hidden chain data and the recently downloaded chain update.
   611  func (l *LoaderPackage) ChainData() *keybase1.HiddenTeamChain {
   612  	return l.data
   613  }
   614  
   615  // MaxReaderTeamKeyGeneration returns the highest Reader PTK generation from the preloaded and hidden
   616  // data.
   617  func (l *LoaderPackage) MaxReaderPerTeamKeyGeneration() keybase1.PerTeamKeyGeneration {
   618  	// RESTRICTEDBOTs are excluded since they do not have any PTK access
   619  	if l.data == nil || l.role.IsRestrictedBot() {
   620  		return keybase1.PerTeamKeyGeneration(0)
   621  	}
   622  	return l.data.MaxReaderPerTeamKeyGeneration()
   623  }
   624  
   625  func (l *LoaderPackage) RatchetBlindingKeySet() *RatchetBlindingKeySet {
   626  	return l.rbks
   627  }
   628  
   629  func (l *LoaderPackage) SetRatchetBlindingKeySet(r *RatchetBlindingKeySet) {
   630  	l.rbks = r
   631  }
   632  
   633  // AddRatchets calls AddRatchet on each SCTeamRatchet in v.
   634  func (l *LoaderPackage) AddRatchets(mctx libkb.MetaContext, v []SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
   635  	for _, r := range v {
   636  		err := l.AddRatchet(mctx, r, ctime, typ)
   637  		if err != nil {
   638  			return err
   639  		}
   640  	}
   641  	return nil
   642  }
   643  
   644  // AddRatchet is called whenever we pull a ratchet out of a visible team link. The first thing we'll need to
   645  // do is to make sure that we can look the unblinded ratchet up using the blinding keys we got down from the
   646  // server. Then we'll check the ratchets again the old (loaded) and new (downloaded) data. Finally, we'll
   647  // ensure that this ratchet doesn't clash another ratchet that came down in this update. If all checks work,
   648  // then add this ratchet to the set of all new ratchets, and also the max ratchet set that we're keeping locally.
   649  func (l *LoaderPackage) AddRatchet(mctx libkb.MetaContext, r SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
   650  	tail := l.rbks.Get(r)
   651  	if tail == nil {
   652  		return NewLoaderError("missing unblind for ratchet %s", r.String())
   653  	}
   654  	return l.AddUnblindedRatchet(mctx, tail, ctime, typ)
   655  }
   656  
   657  func (l *LoaderPackage) AddUnblindedRatchet(mctx libkb.MetaContext, tail *sig3.Tail, ctime int, typ keybase1.RatchetType) (err error) {
   658  	ratchet := keybase1.LinkTripleAndTime{
   659  		Triple: tail.Export(),
   660  		Time:   keybase1.TimeFromSeconds(int64(ctime)),
   661  	}
   662  	err = checkRatchet(mctx, l.data, ratchet)
   663  	if err != nil {
   664  		return err
   665  	}
   666  	err = checkRatchet(mctx, l.newData, ratchet)
   667  	if err != nil {
   668  		return err
   669  	}
   670  	if l.allNewRatchets == nil {
   671  		l.allNewRatchets = make(map[keybase1.Seqno]keybase1.LinkTripleAndTime)
   672  	}
   673  	q := ratchet.Triple.Seqno
   674  	found, ok := l.allNewRatchets[q]
   675  	if ok && !found.Triple.LinkID.Eq(ratchet.Triple.LinkID) {
   676  		return NewLoaderError("ratchet for seqno %d contradicts another ratchet", q)
   677  	}
   678  	if !ok {
   679  		l.allNewRatchets[q] = ratchet
   680  	}
   681  	l.newRatchetSet.Add(typ, ratchet)
   682  	return nil
   683  }
   684  
   685  func (l *LoaderPackage) checkParentPointer(mctx libkb.MetaContext, getter func(q keybase1.Seqno) (keybase1.LinkID, bool), parentPointer keybase1.LinkTriple, fullLoad bool) (err error) {
   686  	q := parentPointer.Seqno
   687  	link, ok := getter(q)
   688  	switch {
   689  	case !ok && fullLoad:
   690  		return newParentPointerError(q, "link wasn't found in parent chain")
   691  	case !ok && !fullLoad:
   692  		return nil
   693  	}
   694  	if !link.Eq(parentPointer.LinkID) {
   695  		return newParentPointerError(q, "link ID mismatch")
   696  	}
   697  	if parentPointer.SeqType != keybase1.SeqType_SEMIPRIVATE {
   698  		return newParentPointerError(q, "wrong chain type")
   699  	}
   700  	return nil
   701  }
   702  
   703  // CheckParentPointersOnFullLoad looks at all of the new hidden links we got down and makes sure that they
   704  // the point to loaded links in the visible chain. Because it's a full load, the pointers must land. They
   705  // can dangle on FTL loads, for instance.
   706  func (l *LoaderPackage) CheckParentPointersOnFullLoad(mctx libkb.MetaContext, team *keybase1.TeamData) (err error) {
   707  	if l.newData == nil {
   708  		return nil
   709  	}
   710  	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
   711  		if team == nil {
   712  			return ret, false
   713  		}
   714  		ret, found = team.Chain.LinkIDs[q]
   715  		return ret, found
   716  	}
   717  	for _, v := range l.newData.Inner {
   718  		if err := l.checkParentPointer(mctx, getter, v.ParentChain, true /* full load */); err != nil {
   719  			return err
   720  		}
   721  	}
   722  	return nil
   723  }
   724  
   725  // CheckParentPointersOnFastLoad looks at all of the new hidden links we got down and makes sure that they
   726  // the point to loaded links in the visible chain. Because it's a fast load, the pointers can dangle.
   727  func (l *LoaderPackage) CheckParentPointersOnFastLoad(mctx libkb.MetaContext, team *keybase1.FastTeamData) (err error) {
   728  	if l.newData == nil {
   729  		return nil
   730  	}
   731  	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
   732  		if team == nil {
   733  			return ret, false
   734  		}
   735  		ret, found = team.Chain.LinkIDs[q]
   736  		return ret, found
   737  	}
   738  	for _, v := range l.newData.Inner {
   739  		if err := l.checkParentPointer(mctx, getter, v.ParentChain, false /* full load */); err != nil {
   740  			return err
   741  		}
   742  	}
   743  	return nil
   744  }
   745  
   746  // DisableHiddenChainData tells the LoaderPackage to disable reading and consuming
   747  // of hidden chain data. On if we are a subteam reader, or if we are feature-flagged off
   748  func (l *LoaderPackage) DisableHiddenChainData() {
   749  	l.disableHiddenChainData = true
   750  }
   751  
   752  // HiddenChainDataEnabled is true if we are doing a full hidden chain load (and off if we're skipping
   753  // due to the above).
   754  func (l *LoaderPackage) HiddenChainDataEnabled() bool {
   755  	return !l.disableHiddenChainData
   756  }