1package hidden
2
3import (
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
12const (
13	MaxDelayInCommittingHiddenLinks = 7 * 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.
19type 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?"
37func 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.
53func 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)).
65func 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.
71func (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.
86func (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.
95func (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.
132func (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.
154func (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).
167func (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.
182func (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
194func (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
208func (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.
216func (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.
253func (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.
269func (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)
282func (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.
302func (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.
349func (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.
366func (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().
372func (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
400func (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
412func 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
439func (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.
452func (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.
468func (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).
477func (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.
486func (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.
502func (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
515func (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
526func (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	case libkb.MerkleHiddenResponseTypeOK:
538		newCommittedHiddenTail := hiddenResp.CommittedHiddenTail
539		newCommittedHiddenTailSeqno := newCommittedHiddenTail.Seqno
540		lastCommittedHiddenTailSeqno = newCommittedHiddenTailSeqno
541
542		// ensure the server is self consistent in its answer
543		if hiddenResp.UncommittedSeqno < newCommittedHiddenTailSeqno {
544			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInconsistentUncommittedSeqno,
545				"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)
546		}
547		// prevent rollbacks in the blind tree
548		if newCommittedHiddenTailSeqno < oldCommittedHiddenTailSeqno {
549			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno,
550				"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)
551		}
552		// add ratchet to ensure consistency
553		err = l.AddUnblindedRatchet(mctx, newCommittedHiddenTail, int(mctx.G().Clock().Now().Unix()), keybase1.RatchetType_BLINDED)
554		if err != nil {
555			return false, err
556		}
557		err = l.SetLastCommittedSeqno(mctx, newCommittedHiddenTailSeqno)
558		if err != nil {
559			return false, err
560		}
561	case libkb.MerkleHiddenResponseTypeABSENCEPROOF:
562		if oldCommittedHiddenTailSeqno > 0 {
563			return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorUnexpectedAbsenceProof,
564				"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)
565		}
566	default:
567		return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInvalidHiddenResponseType,
568			"Unrecognized response type: %v", hiddenResp.RespType)
569	}
570
571	if err := l.VerifyOldChainLinksAreCommitted(mctx, lastCommittedHiddenTailSeqno); err != nil {
572		return false, err
573	}
574
575	if oldHiddenTailSeqno == hiddenResp.UncommittedSeqno {
576		hiddenIsFresh = true
577	} else if oldHiddenTailSeqno < hiddenResp.UncommittedSeqno {
578		hiddenIsFresh = false
579	} else {
580		return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackUncommittedSeqno,
581			"The server indicated that the last hidden link has Seqno %v, but we knew of a link with seqno %v already!", hiddenResp.UncommittedSeqno, oldHiddenTailSeqno)
582	}
583
584	return hiddenIsFresh, nil
585}
586
587// HasReaderPerTeamKeyAtGeneration returns true if the LoaderPackage has a sigchain entry for
588// the PTK at the given generation. Whether in the preloaded data or the update.
589func (l *LoaderPackage) HasReaderPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) bool {
590	// RESTRICTEDBOTs are excluded since they do not have any PTK access
591	if l.data == nil || l.role.IsRestrictedBot() {
592		return false
593	}
594	_, ok := l.data.ReaderPerTeamKeys[gen]
595	return ok
596}
597
598// Commit the update from the server to main HiddenTeamChain storage.
599func (l *LoaderPackage) Commit(mctx libkb.MetaContext) error {
600	if l.newData == nil {
601		mctx.Debug("LoaderPackage#Commit: nil newData for team %s", l.id)
602		return nil
603	}
604	mctx.Debug("LoaderPackage#Commit: %s", l.newData.Summary())
605	err := mctx.G().GetHiddenTeamChainManager().Advance(mctx, *l.newData, l.expectedPrev)
606	return err
607}
608
609// ChainData returns the merge of the preloaded hidden chain data and the recently downloaded chain update.
610func (l *LoaderPackage) ChainData() *keybase1.HiddenTeamChain {
611	return l.data
612}
613
614// MaxReaderTeamKeyGeneration returns the highest Reader PTK generation from the preloaded and hidden
615// data.
616func (l *LoaderPackage) MaxReaderPerTeamKeyGeneration() keybase1.PerTeamKeyGeneration {
617	// RESTRICTEDBOTs are excluded since they do not have any PTK access
618	if l.data == nil || l.role.IsRestrictedBot() {
619		return keybase1.PerTeamKeyGeneration(0)
620	}
621	return l.data.MaxReaderPerTeamKeyGeneration()
622}
623
624func (l *LoaderPackage) RatchetBlindingKeySet() *RatchetBlindingKeySet {
625	return l.rbks
626}
627
628func (l *LoaderPackage) SetRatchetBlindingKeySet(r *RatchetBlindingKeySet) {
629	l.rbks = r
630}
631
632// AddRatchets calls AddRatchet on each SCTeamRatchet in v.
633func (l *LoaderPackage) AddRatchets(mctx libkb.MetaContext, v []SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
634	for _, r := range v {
635		err := l.AddRatchet(mctx, r, ctime, typ)
636		if err != nil {
637			return err
638		}
639	}
640	return nil
641}
642
643// AddRatchet is called whenever we pull a ratchet out of a visible team link. The first thing we'll need to
644// do is to make sure that we can look the unblinded ratchet up using the blinding keys we got down from the
645// server. Then we'll check the ratchets again the old (loaded) and new (downloaded) data. Finally, we'll
646// ensure that this ratchet doesn't clash another ratchet that came down in this update. If all checks work,
647// then add this ratchet to the set of all new ratchets, and also the max ratchet set that we're keeping locally.
648func (l *LoaderPackage) AddRatchet(mctx libkb.MetaContext, r SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) {
649	tail := l.rbks.Get(r)
650	if tail == nil {
651		return NewLoaderError("missing unblind for ratchet %s", r.String())
652	}
653	return l.AddUnblindedRatchet(mctx, tail, ctime, typ)
654}
655
656func (l *LoaderPackage) AddUnblindedRatchet(mctx libkb.MetaContext, tail *sig3.Tail, ctime int, typ keybase1.RatchetType) (err error) {
657	ratchet := keybase1.LinkTripleAndTime{
658		Triple: tail.Export(),
659		Time:   keybase1.TimeFromSeconds(int64(ctime)),
660	}
661	err = checkRatchet(mctx, l.data, ratchet)
662	if err != nil {
663		return err
664	}
665	err = checkRatchet(mctx, l.newData, ratchet)
666	if err != nil {
667		return err
668	}
669	if l.allNewRatchets == nil {
670		l.allNewRatchets = make(map[keybase1.Seqno]keybase1.LinkTripleAndTime)
671	}
672	q := ratchet.Triple.Seqno
673	found, ok := l.allNewRatchets[q]
674	if ok && !found.Triple.LinkID.Eq(ratchet.Triple.LinkID) {
675		return NewLoaderError("ratchet for seqno %d contradicts another ratchet", q)
676	}
677	if !ok {
678		l.allNewRatchets[q] = ratchet
679	}
680	l.newRatchetSet.Add(typ, ratchet)
681	return nil
682}
683
684func (l *LoaderPackage) checkParentPointer(mctx libkb.MetaContext, getter func(q keybase1.Seqno) (keybase1.LinkID, bool), parentPointer keybase1.LinkTriple, fullLoad bool) (err error) {
685	q := parentPointer.Seqno
686	link, ok := getter(q)
687	switch {
688	case !ok && fullLoad:
689		return newParentPointerError(q, "link wasn't found in parent chain")
690	case !ok && !fullLoad:
691		return nil
692	}
693	if !link.Eq(parentPointer.LinkID) {
694		return newParentPointerError(q, "link ID mismatch")
695	}
696	if parentPointer.SeqType != keybase1.SeqType_SEMIPRIVATE {
697		return newParentPointerError(q, "wrong chain type")
698	}
699	return nil
700}
701
702// CheckParentPointersOnFullLoad looks at all of the new hidden links we got down and makes sure that they
703// the point to loaded links in the visible chain. Because it's a full load, the pointers must land. They
704// can dangle on FTL loads, for instance.
705func (l *LoaderPackage) CheckParentPointersOnFullLoad(mctx libkb.MetaContext, team *keybase1.TeamData) (err error) {
706	if l.newData == nil {
707		return nil
708	}
709	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
710		if team == nil {
711			return ret, false
712		}
713		ret, found = team.Chain.LinkIDs[q]
714		return ret, found
715	}
716	for _, v := range l.newData.Inner {
717		if err := l.checkParentPointer(mctx, getter, v.ParentChain, true /* full load */); err != nil {
718			return err
719		}
720	}
721	return nil
722}
723
724// CheckParentPointersOnFastLoad looks at all of the new hidden links we got down and makes sure that they
725// the point to loaded links in the visible chain. Because it's a fast load, the pointers can dangle.
726func (l *LoaderPackage) CheckParentPointersOnFastLoad(mctx libkb.MetaContext, team *keybase1.FastTeamData) (err error) {
727	if l.newData == nil {
728		return nil
729	}
730	getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) {
731		if team == nil {
732			return ret, false
733		}
734		ret, found = team.Chain.LinkIDs[q]
735		return ret, found
736	}
737	for _, v := range l.newData.Inner {
738		if err := l.checkParentPointer(mctx, getter, v.ParentChain, false /* full load */); err != nil {
739			return err
740		}
741	}
742	return nil
743}
744
745// DisableHiddenChainData tells the LoaderPackage to disable reading and consuming
746// of hidden chain data. On if we are a subteam reader, or if we are feature-flagged off
747func (l *LoaderPackage) DisableHiddenChainData() {
748	l.disableHiddenChainData = true
749}
750
751// HiddenChainDataEnabled is true if we are doing a full hidden chain load (and off if we're skipping
752// due to the above).
753func (l *LoaderPackage) HiddenChainDataEnabled() bool {
754	return !l.disableHiddenChainData
755}
756