<commit_before>package teams

import (
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"

	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams/hidden"
	"github.com/keybase/go-codec/codec"
)

// loader2.go contains methods on TeamLoader.
// It would be normal for them to be in loader.go but:
// These functions do not call any functions in loader.go except for load2.
// They are here so that the files can be more manageable in size and
// people can work on loader.go and loader2.go simultaneously with less conflict.

// If links are needed in full that are stubbed in state, go out and get them from the server.
// Does not ask for any links above state's seqno, those will be fetched by getNewLinksFromServer.
func (l *TeamLoader) fillInStubbedLinks(ctx context.Context,
	me keybase1.UserVersion, teamID keybase1.TeamID, state *keybase1.TeamData,
	needSeqnos []keybase1.Seqno, readSubteamID keybase1.TeamID,
	proofSet *proofSetT, parentChildOperations []*parentChildOperation, lkc *loadKeyCache) (
	*keybase1.TeamData, *proofSetT, []*parentChildOperation, error) {

	upperLimit := keybase1.Seqno(0)
	if state != nil {
		upperLimit = state.Chain.LastSeqno
	}

	// seqnos needed from the server
	var requestSeqnos []keybase1.Seqno
	for _, seqno := range needSeqnos {
		linkIsAlreadyFilled := TeamSigChainState{inner: state.Chain}.IsLinkFilled(seqno)
		if seqno <= upperLimit && !linkIsAlreadyFilled {
			requestSeqnos = append(requestSeqnos, seqno)
		}
	}
	if len(requestSeqnos) == 0 {
		// early out
		return state, proofSet, parentChildOperations, nil
	}

	teamUpdate, err := l.world.getLinksFromServer(ctx, state.Chain.Id, requestSeqnos, &readSubteamID)
	if err != nil {
		return state, proofSet, parentChildOperations, err
	}
	newLinks, err := teamUpdate.unpackLinks(ctx)
	if err != nil {
		return state, proofSet, parentChildOperations, err
	}

	parentsCache := make(parentChainCache)
	for _, link := range newLinks {
		if link.isStubbed() {
			return state, proofSet, parentChildOperations, NewStubbedErrorWithNote(
				link, "filling stubbed link")
		}

		var signer *SignerX
		var fullVerifyCutoff keybase1.Seqno // Always fullVerify when inflating. No reasoning has been done on whether it could be skipped.
		signer, err = l.verifyLink(ctx, teamID, state, me, link, fullVerifyCutoff, readSubteamID,
			proofSet, lkc, parentsCache)
		if err != nil {
			return state, proofSet, parentChildOperations, err
		}

		if signer == nil || !signer.signer.Uid.Exists() {
			return state, proofSet, parentChildOperations, fmt.Errorf("blank signer for full link: %v", signer)
		}

		state, err = l.inflateLink(ctx, state, link, *signer, me)
		if err != nil {
			return state, proofSet, parentChildOperations, err
		}

		if l.isParentChildOperation(ctx, link) {
			pco, err := l.toParentChildOperation(ctx, link)
			if err != nil {
				return state, proofSet, parentChildOperations, err
			}
			parentChildOperations = append(parentChildOperations, pco)
		}
	}

	return state, proofSet, parentChildOperations, nil

}

type getLinksLows struct {
	// Latest seqno on file
	Seqno keybase1.Seqno
	// Latest PTK generation secret we have
	PerTeamKey keybase1.PerTeamKeyGeneration
	// Latest RKM semi-secret we have
	ReaderKeyMask keybase1.PerTeamKeyGeneration
	// Latest hidden chain seqno on file
	HiddenChainSeqno keybase1.Seqno
	// Ratcheted chain tail
	HiddenChainRatchet keybase1.Seqno
}

// checkStubbed checks if it's OK if a link is stubbed.
func (l *TeamLoader) checkStubbed(ctx context.Context, arg load2ArgT, link *ChainLinkUnpacked) error {
	if !link.isStubbed() {
		return nil
	}
	if l.seqnosContains(arg.needSeqnos, link.Seqno()) {
		return NewStubbedErrorWithNote(link, "Need seqno")
	}
	if arg.needAdmin || !link.outerLink.LinkType.TeamAllowStubWithAdminFlag(arg.needAdmin) {
		return NewStubbedErrorWithNote(link, "Need admin privilege for this action")
	}
	return nil
}

func (l *TeamLoader) loadUserAndKeyFromLinkInner(ctx context.Context,
	inner SCChainLinkPayload, lkc *loadKeyCache) (
	signerUV keybase1.UserVersion, key *keybase1.PublicKeyV2NaCl, linkMap linkMapT, err error) {
	if !ShouldSuppressLogging(ctx) {
		defer l.G().CTraceTimed(ctx, fmt.Sprintf("TeamLoader#loadUserForSigVerification(%d)", int(inner.Seqno)), func() error { return err })()
	}
	keySection := inner.Body.Key
	if keySection == nil {
		return signerUV, nil, nil, libkb.NoUIDError{}
	}
	uid := keySection.UID
	kid := keySection.KID
	signerUV, key, linkMap, err = l.world.loadKeyV2(ctx, uid, kid, lkc)
	if err != nil {
		return signerUV, nil, nil, err
	}
	return signerUV, key, linkMap, nil
}

// Get the UV from a link but using server-trust and without verifying anything.
func (l *TeamLoader) loadUserAndKeyFromLinkInnerNoVerify(ctx context.Context,
	link *ChainLinkUnpacked) (signerUV keybase1.UserVersion, err error) {
	if !ShouldSuppressLogging(ctx) {
		defer l.G().CTraceTimed(ctx, fmt.Sprintf("TeamLoader#loadUserAndKeyFromLinkInnerNoVerify(%d)", int(link.inner.Seqno)), func() error { return err })()
	}
	keySection := link.inner.Body.Key
	if keySection == nil {
		return signerUV, libkb.NoUIDError{}
	}
	// Use the UID from the link body and EldestSeqno from the server-trust API response.
	if link.source.EldestSeqno == 0 {
		// We should never hit this case
		return signerUV, fmt.Errorf("missing server hint for team sigchain link signer")
	}
	return NewUserVersion(keySection.UID, link.source.EldestSeqno), nil
}

func (l *TeamLoader) verifySignatureAndExtractKID(ctx context.Context, outer libkb.OuterLinkV2WithMetadata) (keybase1.KID, error) {
	return outer.Verify(l.G().Log)
}

// These sigchain links are not checked dynamically. We assert that they are good.
var whitelistedTeamLinkSigs = []keybase1.SigID{
	// For the privacy of the users involved the issue is described only vaguely here.
	// See CORE-8233 for more details.
	// This team had a rotate_key link signed seconds before the revocation of the key that signed the link.
	// Due to a bug the signing device was allowed to be revoked with a signature that pointed to a merkle
	// root prior to the team link signature. This makes it impossible for the client to independently
	// verify that the team link was signed before the device was revoked. But it was, it's all good.
	"e8279d7c73b8defab299094b73800262239e5a03812040ed381cc613a3db515622",

	// See https://github.com/keybase/client/issues/17573; a server bug allowed a rotate after a revoke, which
	// has been fixed in CORE-10942.
	"070e6d737607109ba17d1d43419d950cde6d206b66c555c837566913a31ca59122",
}

func (l *TeamLoader) addProofsForKeyInUserSigchain(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, uid keybase1.UID, key *keybase1.PublicKeyV2NaCl, userLinkMap linkMapT, proofSet *proofSetT) {
	for _, okSigID := range whitelistedTeamLinkSigs {
		if link.SigID().Equal(okSigID) {
			// This proof is whitelisted, so don't check it.
			return
		}
	}

	event1Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil)
	event2Revoke := key.Base.Revocation
	if event2Revoke != nil {
		proofSet.AddNeededHappensBeforeProof(ctx, event1Link, newProofTerm(uid.AsUserOrTeam(), *event2Revoke, userLinkMap), "team link before user key revocation")
	}
}

// Verify aspects of a link:<commit_msg>teams: fix for a broken team (#20511)<commit_after>package teams

import (
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"

	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams/hidden"
	"github.com/keybase/go-codec/codec"
)

// loader2.go contains methods on TeamLoader.
// It would be normal for them to be in loader.go but:
// These functions do not call any functions in loader.go except for load2.
// They are here so that the files can be more manageable in size and
// people can work on loader.go and loader2.go simultaneously with less conflict.

// If links are needed in full that are stubbed in state, go out and get them from the server.
// Does not ask for any links above state's seqno, those will be fetched by getNewLinksFromServer.
func (l *TeamLoader) fillInStubbedLinks(ctx context.Context,
	me keybase1.UserVersion, teamID keybase1.TeamID, state *keybase1.TeamData,
	needSeqnos []keybase1.Seqno, readSubteamID keybase1.TeamID,
	proofSet *proofSetT, parentChildOperations []*parentChildOperation, lkc *loadKeyCache) (
	*keybase1.TeamData, *proofSetT, []*parentChildOperation, error) {

	upperLimit := keybase1.Seqno(0)
	if state != nil {
		upperLimit = state.Chain.LastSeqno
	}

	// seqnos needed from the server
	var requestSeqnos []keybase1.Seqno
	for _, seqno := range needSeqnos {
		linkIsAlreadyFilled := TeamSigChainState{inner: state.Chain}.IsLinkFilled(seqno)
		if seqno <= upperLimit && !linkIsAlreadyFilled {
			requestSeqnos = append(requestSeqnos, seqno)
		}
	}
	if len(requestSeqnos) == 0 {
		// early out
		return state, proofSet, parentChildOperations, nil
	}

	teamUpdate, err := l.world.getLinksFromServer(ctx, state.Chain.Id, requestSeqnos, &readSubteamID)
	if err != nil {
		return state, proofSet, parentChildOperations, err
	}
	newLinks, err := teamUpdate.unpackLinks(ctx)
	if err != nil {
		return state, proofSet, parentChildOperations, err
	}

	parentsCache := make(parentChainCache)
	for _, link := range newLinks {
		if link.isStubbed() {
			return state, proofSet, parentChildOperations, NewStubbedErrorWithNote(
				link, "filling stubbed link")
		}

		var signer *SignerX
		var fullVerifyCutoff keybase1.Seqno // Always fullVerify when inflating. No reasoning has been done on whether it could be skipped.
		signer, err = l.verifyLink(ctx, teamID, state, me, link, fullVerifyCutoff, readSubteamID,
			proofSet, lkc, parentsCache)
		if err != nil {
			return state, proofSet, parentChildOperations, err
		}

		if signer == nil || !signer.signer.Uid.Exists() {
			return state, proofSet, parentChildOperations, fmt.Errorf("blank signer for full link: %v", signer)
		}

		state, err = l.inflateLink(ctx, state, link, *signer, me)
		if err != nil {
			return state, proofSet, parentChildOperations, err
		}

		if l.isParentChildOperation(ctx, link) {
			pco, err := l.toParentChildOperation(ctx, link)
			if err != nil {
				return state, proofSet, parentChildOperations, err
			}
			parentChildOperations = append(parentChildOperations, pco)
		}
	}

	return state, proofSet, parentChildOperations, nil

}

type getLinksLows struct {
	// Latest seqno on file
	Seqno keybase1.Seqno
	// Latest PTK generation secret we have
	PerTeamKey keybase1.PerTeamKeyGeneration
	// Latest RKM semi-secret we have
	ReaderKeyMask keybase1.PerTeamKeyGeneration
	// Latest hidden chain seqno on file
	HiddenChainSeqno keybase1.Seqno
	// Ratcheted chain tail
	HiddenChainRatchet keybase1.Seqno
}

// checkStubbed checks if it's OK if a link is stubbed.
func (l *TeamLoader) checkStubbed(ctx context.Context, arg load2ArgT, link *ChainLinkUnpacked) error {
	if !link.isStubbed() {
		return nil
	}
	if l.seqnosContains(arg.needSeqnos, link.Seqno()) {
		return NewStubbedErrorWithNote(link, "Need seqno")
	}
	if arg.needAdmin || !link.outerLink.LinkType.TeamAllowStubWithAdminFlag(arg.needAdmin) {
		return NewStubbedErrorWithNote(link, "Need admin privilege for this action")
	}
	return nil
}

func (l *TeamLoader) loadUserAndKeyFromLinkInner(ctx context.Context,
	inner SCChainLinkPayload, lkc *loadKeyCache) (
	signerUV keybase1.UserVersion, key *keybase1.PublicKeyV2NaCl, linkMap linkMapT, err error) {
	if !ShouldSuppressLogging(ctx) {
		defer l.G().CTraceTimed(ctx, fmt.Sprintf("TeamLoader#loadUserForSigVerification(%d)", int(inner.Seqno)), func() error { return err })()
	}
	keySection := inner.Body.Key
	if keySection == nil {
		return signerUV, nil, nil, libkb.NoUIDError{}
	}
	uid := keySection.UID
	kid := keySection.KID
	signerUV, key, linkMap, err = l.world.loadKeyV2(ctx, uid, kid, lkc)
	if err != nil {
		return signerUV, nil, nil, err
	}
	return signerUV, key, linkMap, nil
}

// Get the UV from a link but using server-trust and without verifying anything.
func (l *TeamLoader) loadUserAndKeyFromLinkInnerNoVerify(ctx context.Context,
	link *ChainLinkUnpacked) (signerUV keybase1.UserVersion, err error) {
	if !ShouldSuppressLogging(ctx) {
		defer l.G().CTraceTimed(ctx, fmt.Sprintf("TeamLoader#loadUserAndKeyFromLinkInnerNoVerify(%d)", int(link.inner.Seqno)), func() error { return err })()
	}
	keySection := link.inner.Body.Key
	if keySection == nil {
		return signerUV, libkb.NoUIDError{}
	}
	// Use the UID from the link body and EldestSeqno from the server-trust API response.
	if link.source.EldestSeqno == 0 {
		// We should never hit this case
		return signerUV, fmt.Errorf("missing server hint for team sigchain link signer")
	}
	return NewUserVersion(keySection.UID, link.source.EldestSeqno), nil
}

func (l *TeamLoader) verifySignatureAndExtractKID(ctx context.Context, outer libkb.OuterLinkV2WithMetadata) (keybase1.KID, error) {
	return outer.Verify(l.G().Log)
}

// These sigchain links are not checked dynamically. We assert that they are good.
var whitelistedTeamLinkSigs = []keybase1.SigID{
	// For the privacy of the users involved the issue is described only vaguely here.
	// See CORE-8233 for more details.
	// This team had a rotate_key link signed seconds before the revocation of the key that signed the link.
	// Due to a bug the signing device was allowed to be revoked with a signature that pointed to a merkle
	// root prior to the team link signature. This makes it impossible for the client to independently
	// verify that the team link was signed before the device was revoked. But it was, it's all good.
	"e8279d7c73b8defab299094b73800262239e5a03812040ed381cc613a3db515622",

	// See https://github.com/keybase/client/issues/17573; a server bug allowed a rotate after a revoke, which
	// has been fixed in CORE-10942.
	"070e6d737607109ba17d1d43419d950cde6d206b66c555c837566913a31ca59122",

	// See https://github.com/keybase/client/issues/20503; a server bug allowed a team leave to interleave
	// with a downgrade lease acquisition for a key revoke on a slow connection. The acquisition should have
	// been blocked until the merkle tree reflected the leave, but the acquistion actually happened before the
	// team leave transation was committed to the DB. The fix on the server is to check for leases before and
	// after the team change is commited (in the same transaction). We were previously only checking before.
	// It has been fixed in Y2K-891.
	"<KEY>",
}

func (l *TeamLoader) addProofsForKeyInUserSigchain(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, uid keybase1.UID, key *keybase1.PublicKeyV2NaCl, userLinkMap linkMapT, proofSet *proofSetT) {
	for _, okSigID := range whitelistedTeamLinkSigs {
		if link.SigID().Equal(okSigID) {
			// This proof is whitelisted, so don't check it.
			return
		}
	}

	event1Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil)
	event2Revoke := key.Base.Revocation
	if event2Revoke != nil {
		proofSet.AddNeededHappensBeforeProof(ctx, event1Link, newProofTerm(uid.AsUserOrTeam(), *event2Revoke, userLinkMap), "team link before user key revocation")
	}
}

// Verify aspects of a link:
{'fraction_non_alphanumeric': 0.09755621994020539, 'fraction_numerical': 0.020928116469517744, 'mean_word_length': 4.581066376496191, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 4, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1026680', 'n_tokens_mistral': 5385, 'n_tokens_neox': 5091, 'n_words': 1524}