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