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 }