github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/audit_test.go (about)

     1  package teams
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/sig3"
    11  	"github.com/keybase/client/go/teams/hidden"
    12  
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  // See CORE-8860. We should be able to audit a stale team. That is, ever the merkle tree
    19  // is advertising a tail at 5, and we're only loaded through 3 (due to an unbusted cache),
    20  // the audit should still succeed.
    21  func TestAuditStaleTeam(t *testing.T) {
    22  
    23  	fus, tcs, cleanup := setupNTests(t, 5)
    24  	defer cleanup()
    25  
    26  	t.Logf("create team")
    27  	teamName, _ := createTeam2(*tcs[0])
    28  	m := make([]libkb.MetaContext, 5)
    29  	for i, tc := range tcs {
    30  		m[i] = libkb.NewMetaContextForTest(*tc)
    31  	}
    32  
    33  	// We set up codenames for 3 users, A, B, C, D and E
    34  	const (
    35  		A = 0
    36  		B = 1
    37  		C = 2
    38  		D = 3
    39  		E = 4
    40  	)
    41  
    42  	t.Logf("A adds B to the team as an admin")
    43  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_ADMIN, nil)
    44  	require.NoError(t, err)
    45  
    46  	load := func(asUser int) {
    47  		_, err = Load(m[asUser].Ctx(), tcs[asUser].G, keybase1.LoadTeamArg{
    48  			Name:    teamName.String(),
    49  			Public:  false,
    50  			StaleOK: true,
    51  		})
    52  		require.NoError(t, err)
    53  	}
    54  
    55  	addC := func(asUser int) {
    56  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[C].Username, keybase1.TeamRole_READER, nil)
    57  		require.NoError(t, err)
    58  	}
    59  
    60  	addD := func(asUser int) {
    61  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[D].Username, keybase1.TeamRole_BOT, nil)
    62  		require.NoError(t, err)
    63  	}
    64  
    65  	addE := func(asUser int) {
    66  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[E].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{})
    67  		require.NoError(t, err)
    68  	}
    69  
    70  	rmC := func(asUser int) {
    71  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[C].Username)
    72  		require.NoError(t, err)
    73  	}
    74  
    75  	rmD := func(asUser int) {
    76  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[D].Username)
    77  		require.NoError(t, err)
    78  	}
    79  
    80  	rmE := func(asUser int) {
    81  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[E].Username)
    82  		require.NoError(t, err)
    83  	}
    84  
    85  	setFastAudits := func(m libkb.MetaContext) {
    86  		// do a lot of probes so we're likely to find issues
    87  		m.G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
    88  			NumPostProbes:         10,
    89  			MerkleMovementTrigger: keybase1.Seqno(1),
    90  			RootFreshness:         time.Duration(1),
    91  			LRUSize:               500,
    92  			NumPreProbes:          3,
    93  			Parallelism:           3,
    94  		}
    95  	}
    96  
    97  	setSlowAudits := func(m libkb.MetaContext) {
    98  		m.G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
    99  			NumPostProbes:         1,
   100  			MerkleMovementTrigger: keybase1.Seqno(1000000),
   101  			RootFreshness:         time.Hour,
   102  			LRUSize:               500,
   103  			NumPreProbes:          3,
   104  			Parallelism:           3,
   105  		}
   106  	}
   107  
   108  	// A adds C, D and E to the team and triggers an Audit
   109  	setFastAudits(m[A])
   110  	addC(A)
   111  	addD(A)
   112  	addE(A)
   113  
   114  	// A removes C, D and E from the team, and loads the team, but does *not* trigger an audit
   115  	setSlowAudits(m[A])
   116  	rmC(A)
   117  	rmD(A)
   118  	rmE(A)
   119  	load(A)
   120  
   121  	t.Logf("User B rotates the key a bunch of times")
   122  
   123  	// B rotates the key by adding and remove C a bunch of times.
   124  	for i := 0; i < 3; i++ {
   125  		addC(B)
   126  		rmC(B)
   127  	}
   128  
   129  	// A updates to the latest merkle root.
   130  	_, err = tcs[A].G.MerkleClient.FetchRootFromServer(m[A], 0)
   131  	require.NoError(t, err)
   132  
   133  	// A forces an audit on a stale team.
   134  	setFastAudits(m[A])
   135  	t.Logf("User A loading the team, and auditing on an primed cached")
   136  	load(A)
   137  }
   138  
   139  func TestAuditRotateAudit(t *testing.T) {
   140  	fus, tcs, cleanup := setupNTests(t, 4)
   141  	defer cleanup()
   142  
   143  	t.Logf("create team")
   144  	teamName, teamID := createTeam2(*tcs[0])
   145  	m := make([]libkb.MetaContext, 4)
   146  	for i, tc := range tcs {
   147  		m[i] = libkb.NewMetaContextForTest(*tc)
   148  	}
   149  
   150  	// We set up codenames for 3 users, A, B, C, and D
   151  	const (
   152  		A = 0
   153  		B = 1
   154  		C = 2
   155  		D = 3
   156  	)
   157  
   158  	load := func() {
   159  		_, err := Load(m[A].Ctx(), tcs[A].G, keybase1.LoadTeamArg{
   160  			Name:        teamName.String(),
   161  			Public:      false,
   162  			ForceRepoll: true,
   163  		})
   164  		require.NoError(t, err)
   165  	}
   166  
   167  	addB := func() keybase1.Seqno {
   168  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[B].Username, keybase1.TeamRole_READER, nil)
   169  		require.NoError(t, err)
   170  		return 1
   171  	}
   172  
   173  	addC := func() keybase1.Seqno {
   174  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[C].Username, keybase1.TeamRole_BOT, nil)
   175  		require.NoError(t, err)
   176  		return 1
   177  	}
   178  
   179  	addD := func() keybase1.Seqno {
   180  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[D].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{})
   181  		require.NoError(t, err)
   182  		// adding a RESTRICTEDBOT adds an additional bot_settings link
   183  		return 2
   184  	}
   185  
   186  	rmB := func() keybase1.Seqno {
   187  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[B].Username)
   188  		require.NoError(t, err)
   189  		return 1
   190  	}
   191  
   192  	rmC := func() keybase1.Seqno {
   193  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[C].Username)
   194  		require.NoError(t, err)
   195  		return 1
   196  	}
   197  
   198  	rmD := func() keybase1.Seqno {
   199  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[D].Username)
   200  		require.NoError(t, err)
   201  		return 1
   202  	}
   203  
   204  	setFastAudits := func() {
   205  		// do a lot of probes so we're likely to find issues
   206  		m[A].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   207  			NumPostProbes:         10,
   208  			MerkleMovementTrigger: keybase1.Seqno(1),
   209  			RootFreshness:         time.Duration(1),
   210  			LRUSize:               500,
   211  			NumPreProbes:          3,
   212  			Parallelism:           3,
   213  		}
   214  	}
   215  
   216  	assertAuditTo := func(n keybase1.Seqno) {
   217  		auditor := m[A].G().GetTeamAuditor().(*Auditor)
   218  		history, err := auditor.getFromCache(m[A], teamID, auditor.getLRU())
   219  		require.NoError(t, err)
   220  		require.Equal(t, n, lastAudit(history).MaxChainSeqno)
   221  	}
   222  
   223  	setFastAudits()
   224  	actions := []func() keybase1.Seqno{addB, rmB, addC, rmC, addD, rmD}
   225  	expectedSeqno := keybase1.Seqno(1)
   226  	for _, action := range actions {
   227  		expectedSeqno += action()
   228  		load()
   229  		assertAuditTo(expectedSeqno)
   230  	}
   231  }
   232  
   233  type CorruptingMerkleClient struct {
   234  	libkb.MerkleClientInterface
   235  
   236  	corruptor func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error)
   237  }
   238  
   239  func (c CorruptingMerkleClient) LookupLeafAtSeqnoForAudit(m libkb.MetaContext, leafID keybase1.UserOrTeamID, s keybase1.Seqno, processHiddenResponseFunc libkb.ProcessHiddenRespFunc) (leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) {
   240  	return c.corruptor(c.MerkleClientInterface.LookupLeafAtSeqnoForAudit(m, leafID, s, processHiddenResponseFunc))
   241  }
   242  
   243  var _ libkb.MerkleClientInterface = CorruptingMerkleClient{}
   244  
   245  type MockRandom struct {
   246  	libkb.SecureRandom
   247  	nextOutputs []int64
   248  	t           *testing.T
   249  }
   250  
   251  func (m *MockRandom) RndRange(lo, hi int64) (int64, error) {
   252  	// pop and return the first output which is appropriate
   253  	for i, n := range m.nextOutputs {
   254  		if lo <= n && n <= hi {
   255  			m.nextOutputs = append(m.nextOutputs[0:i], m.nextOutputs[i+1:]...)
   256  			m.t.Logf("MockRandom: Output %v in range %v,%v (have %v left)", n, lo, hi, m.nextOutputs)
   257  			return n, nil
   258  		}
   259  	}
   260  	return 0, fmt.Errorf("MockRandom: output not found in range %v,%v (have %v left)", lo, hi, m.nextOutputs)
   261  }
   262  
   263  var _ libkb.Random = (*MockRandom)(nil)
   264  
   265  func TestAuditFailsIfDataIsInconsistent(t *testing.T) {
   266  	fus, tcs, cleanup := setupNTests(t, 3)
   267  	defer cleanup()
   268  
   269  	t.Logf("create team")
   270  	teamName, teamID := createTeam2(*tcs[0])
   271  	m := make([]libkb.MetaContext, 3)
   272  	for i, tc := range tcs {
   273  		m[i] = libkb.NewMetaContextForTest(*tc)
   274  	}
   275  
   276  	// We set up codenames for 3 users, A, B, C
   277  	const (
   278  		A = 0
   279  		B = 1
   280  		C = 2
   281  	)
   282  
   283  	add := func(adder, addee int) keybase1.Seqno {
   284  		_, err := AddMember(m[adder].Ctx(), tcs[adder].G, teamName.String(), fus[addee].Username, keybase1.TeamRole_READER, nil)
   285  		require.NoError(t, err)
   286  		return 1
   287  	}
   288  
   289  	setAudits := func(user int) {
   290  		m[user].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   291  			NumPostProbes:         3,
   292  			MerkleMovementTrigger: keybase1.Seqno(1),
   293  			RootFreshness:         time.Duration(1),
   294  			LRUSize:               500,
   295  			NumPreProbes:          3,
   296  			Parallelism:           3,
   297  		}
   298  	}
   299  
   300  	assertAuditTo := func(user int, mainSeqno, hiddenSeqno keybase1.Seqno) {
   301  		auditor := m[user].G().GetTeamAuditor().(*Auditor)
   302  		history, err := auditor.getFromCache(m[user], teamID, auditor.getLRU())
   303  		require.NoError(t, err)
   304  		require.Equal(t, mainSeqno, lastAudit(history).MaxChainSeqno)
   305  		require.Equal(t, hiddenSeqno, lastAudit(history).MaxHiddenSeqno)
   306  	}
   307  
   308  	setAudits(B)
   309  
   310  	// A adds B to the team
   311  	add(A, B)
   312  
   313  	makeHiddenRotation(t, m[A].G(), teamName)
   314  	requestNewBlindTreeFromArchitectAndWaitUntilDone(t, tcs[A])
   315  	makeHiddenRotation(t, m[A].G(), teamName)
   316  	requestNewBlindTreeFromArchitectAndWaitUntilDone(t, tcs[A])
   317  	add(A, C)
   318  
   319  	team, err := GetForTestByStringName(context.TODO(), m[A].G(), teamName.String())
   320  	require.NoError(t, err)
   321  
   322  	headMerkleSeqno := int64(team.MainChain().Chain.HeadMerkle.Seqno)
   323  	t.Logf("headMerkleSeqno: %v", headMerkleSeqno)
   324  
   325  	firstWithHiddenS, err := m[A].G().GetMerkleClient().FirstMainRootWithHiddenRootHash(m[A])
   326  	require.NoError(t, err)
   327  	firstWithHidden := int64(firstWithHiddenS)
   328  	t.Logf("firstWithHidden: %v", firstWithHidden)
   329  	root := m[A].G().GetMerkleClient().LastRoot(m[A])
   330  	require.NotNil(t, root)
   331  	high := int64(*root.Seqno())
   332  	t.Logf("latest root: %v %X", root.Seqno(), root.HashMeta())
   333  
   334  	for i := headMerkleSeqno; i <= high; i++ {
   335  		leaf, _, hiddenResp, err := m[B].G().GetMerkleClient().LookupLeafAtSeqnoForAudit(m[B], teamID.AsUserOrTeam(), keybase1.Seqno(i), hidden.ProcessHiddenResponseFunc)
   336  		require.NoError(t, err)
   337  		if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   338  			t.Logf("Seqno %v Leaf %v Hidden %v", i, leaf.Private.Seqno, hiddenResp)
   339  		} else {
   340  			t.Logf("Seqno %v Leaf EMPTY Hidden %v", i, hiddenResp)
   341  		}
   342  
   343  	}
   344  
   345  	merkle := m[B].G().GetMerkleClient()
   346  	rand := m[B].G().GetRandom()
   347  
   348  	corruptMerkle := CorruptingMerkleClient{
   349  		MerkleClientInterface: merkle,
   350  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   351  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   352  			if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   353  				leaf.Private.LinkID[0] ^= 0xff
   354  				t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   355  			}
   356  			return leaf, root, hiddenResp, err
   357  		},
   358  	}
   359  	m[B].G().SetMerkleClient(corruptMerkle)
   360  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   361  
   362  	auditor := m[B].G().GetTeamAuditor().(*Auditor)
   363  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   364  	require.Error(t, err)
   365  	require.IsType(t, AuditError{}, err)
   366  	require.Contains(t, err.Error(), "team chain linkID mismatch")
   367  
   368  	// repeat a second time to ensure that a failed audit is not cached (and thus skipped the second time)
   369  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   370  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   371  	require.Error(t, err)
   372  	require.IsType(t, AuditError{}, err)
   373  	require.Contains(t, err.Error(), "team chain linkID mismatch")
   374  
   375  	corruptMerkle = CorruptingMerkleClient{
   376  		MerkleClientInterface: merkle,
   377  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   378  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   379  			if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   380  				leaf.Private.Seqno += 5
   381  				t.Logf("Corruptor: altering Seqno, leaf = %+v", leaf)
   382  			}
   383  			return leaf, root, hiddenResp, err
   384  		},
   385  	}
   386  	m[B].G().SetMerkleClient(corruptMerkle)
   387  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   388  
   389  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   390  	require.Error(t, err)
   391  	require.IsType(t, AuditError{}, err)
   392  	require.Contains(t, err.Error(), "team chain rollback")
   393  
   394  	// now, let's try to mess with the preProbes, by making it appear as if the team existed before it was actually created.
   395  	corruptMerkle = CorruptingMerkleClient{
   396  		MerkleClientInterface: merkle,
   397  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   398  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   399  			if leaf == nil {
   400  				leaf = &libkb.MerkleGenericLeaf{
   401  					LeafID: teamID.AsUserOrTeam(),
   402  				}
   403  			}
   404  			if leaf.Private == nil {
   405  				t.Logf("Corruptor: creating a fake leaf when there should have been none")
   406  				leaf.Private = &libkb.MerkleTriple{
   407  					Seqno:  4,
   408  					LinkID: []byte{0x00, 0x01, 0x02},
   409  				}
   410  			}
   411  			return leaf, root, hiddenResp, err
   412  		},
   413  	}
   414  	m[B].G().SetMerkleClient(corruptMerkle)
   415  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   416  
   417  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   418  	require.Error(t, err)
   419  	require.IsType(t, AuditError{}, err)
   420  	require.Contains(t, err.Error(), "merkle root should not have had a leaf for team")
   421  
   422  	// now, test that the server cannot cheat on the hidden chain.
   423  	team, err = GetForTestByStringName(context.TODO(), m[A].G(), teamName.String())
   424  	require.NoError(t, err)
   425  	root = m[A].G().GetMerkleClient().LastRoot(m[A])
   426  	require.NotNil(t, root)
   427  
   428  	corruptMerkle = CorruptingMerkleClient{
   429  		MerkleClientInterface: merkle,
   430  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   431  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   432  			if hiddenResp.RespType == libkb.MerkleHiddenResponseTypeABSENCEPROOF {
   433  				t.Logf("Corruptor: creating a fake hidden leaf leaf when there should have been none")
   434  				hiddenResp.RespType = libkb.MerkleHiddenResponseTypeOK
   435  				hiddenResp.CommittedHiddenTail = &sig3.Tail{
   436  					ChainType: keybase1.SeqType_TEAM_PRIVATE_HIDDEN,
   437  					Seqno:     5,
   438  				}
   439  			}
   440  			return leaf, root, hiddenResp, err
   441  		},
   442  	}
   443  	m[B].G().SetMerkleClient(corruptMerkle)
   444  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   445  
   446  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   447  	require.Error(t, err)
   448  	require.IsType(t, AuditError{}, err)
   449  	require.Contains(t, err.Error(), "expected an ABSENCE PROOF")
   450  
   451  	corruptMerkle = CorruptingMerkleClient{
   452  		MerkleClientInterface: merkle,
   453  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   454  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   455  			if hiddenResp.RespType == libkb.MerkleHiddenResponseTypeOK {
   456  				t.Logf("Corruptor: altering hidden seqno")
   457  				hiddenResp.CommittedHiddenTail.Seqno += 5
   458  			}
   459  			return leaf, root, hiddenResp, err
   460  		},
   461  	}
   462  	m[B].G().SetMerkleClient(corruptMerkle)
   463  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   464  
   465  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   466  	require.Error(t, err)
   467  	require.IsType(t, AuditError{}, err)
   468  	require.Contains(t, err.Error(), "team hidden chain rollback")
   469  
   470  	corruptMerkle = CorruptingMerkleClient{
   471  		MerkleClientInterface: merkle,
   472  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   473  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   474  			if hiddenResp.RespType == libkb.MerkleHiddenResponseTypeOK {
   475  				hiddenResp.CommittedHiddenTail.Hash[0] ^= 0xff
   476  				t.Logf("Corruptor: altering LINKID for hidden seqno %v", hiddenResp.CommittedHiddenTail.Seqno)
   477  			}
   478  			return leaf, root, hiddenResp, err
   479  		},
   480  	}
   481  	m[B].G().SetMerkleClient(corruptMerkle)
   482  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   483  
   484  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   485  	require.Error(t, err)
   486  	require.IsType(t, AuditError{}, err)
   487  	require.Contains(t, err.Error(), "hidden team chain linkID mismatch")
   488  
   489  	// with the original merkle client (i.e. when the server response is not altered), the audit should succeed
   490  	m[B].G().SetMerkleClient(merkle)
   491  	m[B].G().SetRandom(rand)
   492  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   493  	require.NoError(t, err)
   494  	assertAuditTo(B, 3, 2)
   495  }
   496  
   497  func TestFailedProbesAreRetried(t *testing.T) {
   498  	fus, tcs, cleanup := setupNTests(t, 2)
   499  	defer cleanup()
   500  
   501  	// We set up codenames for 2 users, A, B
   502  	const (
   503  		A = 0
   504  		B = 1
   505  	)
   506  
   507  	makePaperKey(t, tcs[A])
   508  	makePaperKey(t, tcs[B])
   509  
   510  	t.Logf("create team")
   511  	teamName, teamID := createTeam2(*tcs[A])
   512  	m := make([]libkb.MetaContext, 3)
   513  	for i, tc := range tcs {
   514  		m[i] = libkb.NewMetaContextForTest(*tc)
   515  	}
   516  
   517  	add := func(adder, addee int) keybase1.Seqno {
   518  		_, err := AddMember(m[adder].Ctx(), tcs[adder].G, teamName.String(), fus[addee].Username, keybase1.TeamRole_READER, nil)
   519  		require.NoError(t, err)
   520  		return 1
   521  	}
   522  
   523  	setFastAudits := func(user int) {
   524  		m[user].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   525  			NumPostProbes:         2,
   526  			MerkleMovementTrigger: keybase1.Seqno(1),
   527  			RootFreshness:         time.Duration(1),
   528  			LRUSize:               500,
   529  			NumPreProbes:          2,
   530  			Parallelism:           3,
   531  		}
   532  	}
   533  
   534  	setFastAudits(B)
   535  
   536  	// A adds B to the team
   537  	add(A, B)
   538  
   539  	// make some extra merkle tree versions
   540  	makePaperKey(t, tcs[A])
   541  	makePaperKey(t, tcs[B])
   542  	makePaperKey(t, tcs[A])
   543  	makePaperKey(t, tcs[B])
   544  	makePaperKey(t, tcs[A])
   545  	makePaperKey(t, tcs[B])
   546  
   547  	team, err := GetForTestByStringName(context.TODO(), m[A].G(), teamName.String())
   548  	require.NoError(t, err)
   549  	root := m[A].G().GetMerkleClient().LastRoot(m[A])
   550  	require.NotNil(t, root)
   551  	latestRootSeqno := *root.Seqno()
   552  	t.Logf("latest root: %v %X", root.Seqno(), root.HashMeta())
   553  	headMerkleSeqno := team.MainChain().Chain.HeadMerkle.Seqno
   554  	t.Logf("headMerkleSeqno: %v", headMerkleSeqno)
   555  	firstWithHiddenS, err := m[A].G().GetMerkleClient().FirstMainRootWithHiddenRootHash(m[A])
   556  	require.NoError(t, err)
   557  	firstWithHidden := firstWithHiddenS
   558  	t.Logf("firstWithHidden: %v", firstWithHidden)
   559  
   560  	for i := headMerkleSeqno; i <= latestRootSeqno; i++ {
   561  		leaf, _, hiddenResp, err := m[B].G().GetMerkleClient().LookupLeafAtSeqnoForAudit(m[B], teamID.AsUserOrTeam(), i, hidden.ProcessHiddenResponseFunc)
   562  		require.NoError(t, err)
   563  		if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   564  			t.Logf("Seqno %v Leaf %v Hidden %v", i, leaf.Private.Seqno, hiddenResp)
   565  		} else {
   566  			t.Logf("Seqno %v Leaf EMPTY Hidden %v", i, hiddenResp)
   567  		}
   568  	}
   569  
   570  	auditor := m[B].G().GetTeamAuditor().(*Auditor)
   571  	lru := auditor.getLRU()
   572  
   573  	// no audits yet, history is nil.
   574  	history, err := auditor.getFromCache(m[B], teamID, lru)
   575  	require.NoError(t, err)
   576  	require.Nil(t, history)
   577  
   578  	merkle := m[B].G().GetMerkleClient()
   579  	rand := m[B].G().GetRandom()
   580  
   581  	// first we corrupt postProbes and test that those are retried
   582  	corruptMerkle := CorruptingMerkleClient{
   583  		MerkleClientInterface: merkle,
   584  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   585  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   586  			if *root.Seqno() > headMerkleSeqno {
   587  				if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   588  					leaf.Private.LinkID[0] ^= 0xff
   589  					t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   590  				} else {
   591  					t.Logf("Corruptor: introducing leaf at roor %v", *root.Seqno())
   592  					leaf = &libkb.MerkleGenericLeaf{
   593  						LeafID:  teamID.AsUserOrTeam(),
   594  						Private: &libkb.MerkleTriple{Seqno: keybase1.Seqno(100)},
   595  					}
   596  				}
   597  			}
   598  			return leaf, root, hiddenResp, err
   599  		},
   600  	}
   601  	m[B].G().SetMerkleClient(corruptMerkle)
   602  	// the first two are for preprobes, the last for post probes
   603  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 1, int64(headMerkleSeqno) + 2}})
   604  
   605  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   606  	require.Error(t, err)
   607  	require.IsType(t, AuditError{}, err)
   608  
   609  	history, err = auditor.getFromCache(m[B], teamID, lru)
   610  	require.NoError(t, err)
   611  	require.Len(t, history.PreProbesToRetry, 0)
   612  	require.Len(t, history.PostProbesToRetry, 2)
   613  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   614  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   615  
   616  	var probesToTestLock sync.Mutex
   617  	probesToTest := make(map[keybase1.Seqno]bool)
   618  	probesToTestLock.Lock()
   619  	probesToTest[headMerkleSeqno+1] = true
   620  	probesToTest[headMerkleSeqno+2] = true
   621  	numProbes := 2
   622  	probesToTestLock.Unlock()
   623  
   624  	corruptMerkle = CorruptingMerkleClient{
   625  		MerkleClientInterface: merkle,
   626  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   627  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   628  			probeSeqno := *root.Seqno()
   629  			if probeSeqno > headMerkleSeqno {
   630  				if probesToTest[probeSeqno] {
   631  					t.Logf("PostProbes: Seqno %v was retried", probeSeqno)
   632  					probesToTestLock.Lock()
   633  					probesToTest[probeSeqno] = false
   634  					numProbes--
   635  					probesToTestLock.Unlock()
   636  				}
   637  				if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   638  					leaf.Private.LinkID[0] ^= 0xff
   639  					t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   640  				} else {
   641  					t.Logf("Corruptor: introducing leaf at roor %v", *root.Seqno())
   642  					leaf = &libkb.MerkleGenericLeaf{
   643  						LeafID:  teamID.AsUserOrTeam(),
   644  						Private: &libkb.MerkleTriple{Seqno: keybase1.Seqno(100)},
   645  					}
   646  				}
   647  			}
   648  			return leaf, root, hiddenResp, err
   649  		},
   650  	}
   651  	m[B].G().SetMerkleClient(corruptMerkle)
   652  	// note that the postProbes we will sample now are different from the ones which we failed on the first time, so we can test we are actually retrying those.
   653  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   654  
   655  	// repeat a second time and make sure that we retry the same probes
   656  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   657  	require.Error(t, err)
   658  	require.IsType(t, AuditError{}, err)
   659  	require.Zero(t, numProbes, "not all probes were retried")
   660  
   661  	// now test the preprobes are saved and retried on failure
   662  	corruptMerkle = CorruptingMerkleClient{
   663  		MerkleClientInterface: merkle,
   664  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   665  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   666  			if leaf == nil {
   667  				leaf = &libkb.MerkleGenericLeaf{
   668  					LeafID: teamID.AsUserOrTeam(),
   669  				}
   670  			}
   671  			if leaf.Private == nil {
   672  				t.Logf("Corruptor: creating a fake leaf when there should have been none")
   673  				leaf.Private = &libkb.MerkleTriple{
   674  					Seqno:  4,
   675  					LinkID: []byte{0x00, 0x01, 0x02},
   676  				}
   677  			}
   678  			return leaf, root, hiddenResp, err
   679  		},
   680  	}
   681  	m[B].G().SetMerkleClient(corruptMerkle)
   682  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   683  
   684  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   685  	require.Error(t, err)
   686  	require.IsType(t, AuditError{}, err)
   687  	require.Contains(t, err.Error(), "merkle root should not have had a leaf for team")
   688  
   689  	history, err = auditor.getFromCache(m[B], teamID, lru)
   690  	require.NoError(t, err)
   691  	require.Len(t, history.PreProbesToRetry, 2)
   692  	require.Contains(t, history.PreProbesToRetry, firstWithHidden-1)
   693  	require.Contains(t, history.PreProbesToRetry, firstWithHidden+1)
   694  
   695  	// the old failed postprobes are still in the cache
   696  	require.Len(t, history.PostProbesToRetry, 2)
   697  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   698  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   699  
   700  	probesToTest = make(map[keybase1.Seqno]bool)
   701  	probesToTestLock.Lock()
   702  	probesToTest[firstWithHidden-1] = true
   703  	probesToTest[firstWithHidden+1] = true
   704  	probesToTestLock.Unlock()
   705  	numProbes = 2
   706  
   707  	corruptMerkle = CorruptingMerkleClient{
   708  		MerkleClientInterface: merkle,
   709  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   710  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   711  			probeSeqno := *root.Seqno()
   712  			if probeSeqno <= headMerkleSeqno {
   713  				if probesToTest[probeSeqno] {
   714  					t.Logf("PreProbes: Seqno %v was retried", probeSeqno)
   715  					probesToTestLock.Lock()
   716  					probesToTest[probeSeqno] = false
   717  					numProbes--
   718  					probesToTestLock.Unlock()
   719  				}
   720  				if leaf == nil {
   721  					leaf = &libkb.MerkleGenericLeaf{
   722  						LeafID: teamID.AsUserOrTeam(),
   723  					}
   724  				}
   725  				if leaf.Private == nil {
   726  					t.Logf("Corruptor: creating a fake leaf when there should have been none")
   727  					leaf.Private = &libkb.MerkleTriple{
   728  						Seqno:  4,
   729  						LinkID: []byte{0x00, 0x01, 0x02},
   730  					}
   731  				}
   732  			}
   733  			return leaf, root, hiddenResp, err
   734  		},
   735  	}
   736  	m[B].G().SetMerkleClient(corruptMerkle)
   737  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 2, int64(firstWithHidden) + 2, int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   738  
   739  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   740  	require.Error(t, err)
   741  	require.IsType(t, AuditError{}, err)
   742  	require.Zero(t, numProbes, "not all probes were retried")
   743  
   744  	history, err = auditor.getFromCache(m[B], teamID, lru)
   745  	require.NoError(t, err)
   746  	require.Len(t, history.PreProbesToRetry, 2)
   747  	require.Contains(t, history.PreProbesToRetry, firstWithHidden-1)
   748  	require.Contains(t, history.PreProbesToRetry, firstWithHidden+1)
   749  
   750  	// the old failed postprobes are still in the cache
   751  	require.Len(t, history.PostProbesToRetry, 2)
   752  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   753  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   754  
   755  	// now stop any corruption and ensure the audit succeeds
   756  	m[B].G().SetMerkleClient(merkle)
   757  	m[B].G().SetRandom(rand)
   758  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   759  	require.NoError(t, err)
   760  }