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