github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/wot_test.go (about) 1 package engine 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/protocol/keybase1" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func TestWebOfTrustVouch(t *testing.T) { 13 tc1 := SetupEngineTest(t, "wot") 14 tc2 := SetupEngineTest(t, "wot") 15 tc3 := SetupEngineTest(t, "wot") 16 defer tc1.Cleanup() 17 defer tc2.Cleanup() 18 defer tc3.Cleanup() 19 20 fu1 := CreateAndSignupFakeUser(tc1, "wot") 21 fu2 := CreateAndSignupFakeUser(tc2, "wot") 22 fu3 := CreateAndSignupFakeUser(tc3, "wot") 23 24 // make mutual track b/w fu1 and fu2, fu1 and fu3 25 sigVersion := libkb.GetDefaultSigVersion(tc1.G) 26 trackUser(tc2, fu2, fu1.NormalizedUsername(), sigVersion) 27 trackUser(tc1, fu1, fu2.NormalizedUsername(), sigVersion) 28 trackUser(tc1, fu1, fu3.NormalizedUsername(), sigVersion) 29 trackUser(tc3, fu3, fu1.NormalizedUsername(), sigVersion) 30 31 err := fu2.LoadUser(tc2) 32 require.NoError(tc2.T, err) 33 34 err = fu1.LoadUser(tc1) 35 require.NoError(tc1.T, err) 36 idt := fu1.User.IDTable() 37 lenBefore := idt.Len() 38 // should be logged in as fu1, double check: 39 require.Equal(t, tc1.G.ActiveDevice.UID(), fu1.UID()) 40 mctx := NewMetaContextForTest(tc1) 41 42 // fu1 vouches for fu2 43 arg := &WotVouchArg{ 44 Vouchee: fu2.User.ToUserVersion(), 45 Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT}, 46 VouchText: "alice is awesome", 47 } 48 49 eng := NewWotVouch(tc1.G, arg) 50 err = RunEngine2(mctx, eng) 51 require.NoError(t, err) 52 53 err = fu1.LoadUser(tc1) 54 require.NoError(tc1.T, err) 55 idt = fu1.User.IDTable() 56 57 // for now, let's just check that it got bigger: 58 require.Equal(tc1.T, lenBefore+1, idt.Len()) 59 60 err = fu3.LoadUser(tc3) 61 require.NoError(tc3.T, err) 62 63 // make sure that if the user is attesting to something about 64 // a user and eldest seqno changes, that they get an error. 65 uv := fu3.User.ToUserVersion() 66 uv.EldestSeqno++ 67 arg = &WotVouchArg{ 68 Vouchee: uv, 69 Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT}, 70 VouchText: "bob is nice", 71 } 72 eng = NewWotVouch(tc1.G, arg) 73 err = RunEngine2(mctx, eng) 74 require.Error(tc1.T, err) 75 76 err = fu1.LoadUser(tc1) 77 require.NoError(tc1.T, err) 78 idt = fu1.User.IDTable() 79 require.Equal(tc1.T, lenBefore+1, idt.Len()) 80 81 // make an fu1 -> fu3 attest with confidence stuff 82 arg = &WotVouchArg{ 83 Vouchee: fu3.User.ToUserVersion(), 84 VouchText: "charlie rocks", 85 Confidence: confidence, 86 } 87 eng = NewWotVouch(tc1.G, arg) 88 err = RunEngine2(mctx, eng) 89 require.NoError(tc1.T, err) 90 91 err = fu1.LoadUser(tc1) 92 require.NoError(tc1.T, err) 93 idt = fu1.User.IDTable() 94 require.Equal(tc1.T, lenBefore+2, idt.Len()) 95 } 96 97 func TestWebOfTrustPending(t *testing.T) { 98 tcAlice := SetupEngineTest(t, "wot") 99 tcBob := SetupEngineTest(t, "wot") 100 defer tcAlice.Cleanup() 101 defer tcBob.Cleanup() 102 alice := CreateAndSignupFakeUser(tcAlice, "wot") 103 bob := CreateAndSignupFakeUser(tcBob, "wot") 104 mctxA := NewMetaContextForTest(tcAlice) 105 mctxB := NewMetaContextForTest(tcBob) 106 t.Log("alice and bob exist") 107 108 sigVersion := libkb.GetDefaultSigVersion(tcAlice.G) 109 trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion) 110 trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion) 111 err := bob.LoadUser(tcBob) 112 require.NoError(tcBob.T, err) 113 err = alice.LoadUser(tcAlice) 114 require.NoError(tcAlice.T, err) 115 t.Log("alice and bob follow each other") 116 117 aliceName := alice.User.GetName() 118 bobName := bob.User.GetName() 119 120 var vouches []keybase1.WotVouch 121 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 122 require.NoError(t, err) 123 require.Empty(t, vouches) 124 t.Log("alice has no pending vouches") 125 vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName}) 126 require.NoError(t, err) 127 require.Empty(t, vouches) 128 t.Log("bob sees no vouches for Alice") 129 130 firstVouch := "alice is wondibar but i don't have much confidence" 131 arg := &WotVouchArg{ 132 Vouchee: alice.User.ToUserVersion(), 133 Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT}, 134 VouchText: firstVouch, 135 } 136 eng := NewWotVouch(tcBob.G, arg) 137 err = RunEngine2(mctxB, eng) 138 require.NoError(t, err) 139 t.Log("bob vouches for alice without confidence") 140 141 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 142 require.NoError(t, err) 143 require.Len(t, vouches, 1) 144 bobVouch := vouches[0] 145 require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid) 146 require.Equal(t, bobName, bobVouch.VoucherUsername) 147 require.Equal(t, aliceName, bobVouch.VoucheeUsername) 148 require.Equal(t, firstVouch, bobVouch.VouchText) 149 require.NotNil(t, bobVouch.Confidence) 150 require.EqualValues(t, keybase1.UsernameVerificationType_OTHER_CHAT, bobVouch.Confidence.UsernameVerifiedVia) 151 require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status) 152 t.Log("alice sees one pending vouch") 153 vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName}) 154 require.NoError(t, err) 155 require.Equal(t, 1, len(vouches)) 156 t.Log("bob also sees his pending vouch for Alice") 157 158 tcCharlie := SetupEngineTest(t, "wot") 159 defer tcCharlie.Cleanup() 160 charlie := CreateAndSignupFakeUser(tcCharlie, "wot") 161 mctxC := NewMetaContextForTest(tcCharlie) 162 t.Log("charlie exists") 163 164 trackUser(tcCharlie, charlie, alice.NormalizedUsername(), sigVersion) 165 trackUser(tcAlice, alice, charlie.NormalizedUsername(), sigVersion) 166 err = charlie.LoadUser(tcCharlie) 167 require.NoError(tcCharlie.T, err) 168 t.Log("alice and charlie follow each other") 169 170 charlieName := charlie.User.GetName() 171 172 vouchText := "alice is wondibar and doug agrees" 173 arg = &WotVouchArg{ 174 Vouchee: alice.User.ToUserVersion(), 175 VouchText: vouchText, 176 Confidence: confidence, 177 } 178 eng = NewWotVouch(tcCharlie.G, arg) 179 err = RunEngine2(mctxC, eng) 180 require.NoError(t, err) 181 t.Log("charlie vouches for alice with confidence") 182 183 // ensure alice does a full load of bob by adding another link 184 // to bob's chain so the wot.vouch isn't the last one (which is always unstubbed) 185 // and nuking alice's local db to wipe any cache 186 trackUser(tcBob, bob, charlie.NormalizedUsername(), sigVersion) 187 _, err = mctxA.G().LocalDb.Nuke() 188 require.NoError(t, err) 189 190 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 191 require.NoError(t, err) 192 require.Len(t, vouches, 2) 193 require.EqualValues(t, bobVouch, vouches[0]) 194 charlieVouch := vouches[1] 195 require.Equal(t, keybase1.WotStatusType_PROPOSED, charlieVouch.Status) 196 require.Equal(t, confidence, charlieVouch.Confidence) 197 t.Log("alice sees two pending vouches") 198 199 // alice gets just charlie's vouch using FetchWotVouches 200 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{Vouchee: aliceName, Voucher: charlieName}) 201 require.NoError(t, err) 202 require.Len(t, vouches, 1) 203 require.Equal(t, keybase1.WotStatusType_PROPOSED, vouches[0].Status) 204 t.Log("alice sees charlie's pending vouch") 205 } 206 207 func TestWebOfTrustAccept(t *testing.T) { 208 tcAlice := SetupEngineTest(t, "wot") 209 tcBob := SetupEngineTest(t, "wot") 210 defer tcAlice.Cleanup() 211 defer tcBob.Cleanup() 212 alice := CreateAndSignupFakeUser(tcAlice, "wot") 213 bob := CreateAndSignupFakeUser(tcBob, "wot") 214 mctxA := NewMetaContextForTest(tcAlice) 215 mctxB := NewMetaContextForTest(tcBob) 216 t.Log("alice and bob exist") 217 218 sigVersion := libkb.GetDefaultSigVersion(tcAlice.G) 219 trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion) 220 trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion) 221 err := bob.LoadUser(tcBob) 222 require.NoError(tcBob.T, err) 223 err = alice.LoadUser(tcAlice) 224 require.NoError(tcAlice.T, err) 225 t.Log("alice and bob follow each other") 226 227 aliceName := alice.User.GetName() 228 bobName := bob.User.GetName() 229 230 vouchText := "alice is wondibar and doug agrees" 231 argV := &WotVouchArg{ 232 Vouchee: alice.User.ToUserVersion(), 233 VouchText: vouchText, 234 Confidence: confidence, 235 } 236 engV := NewWotVouch(tcBob.G, argV) 237 err = RunEngine2(mctxB, engV) 238 require.NoError(t, err) 239 t.Log("bob vouches for alice with confidence") 240 241 vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 242 require.NoError(t, err) 243 require.Len(t, vouches, 1) 244 bobVouch := vouches[0] 245 require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status) 246 require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid) 247 require.Equal(t, bobName, bobVouch.VoucherUsername) 248 require.Equal(t, aliceName, bobVouch.VoucheeUsername) 249 require.Equal(t, vouchText, bobVouch.VouchText) 250 t.Log("alice fetches one pending vouch") 251 252 argR := &WotReactArg{ 253 Voucher: bob.User.ToUserVersion(), 254 Proof: bobVouch.VouchProof, 255 Reaction: keybase1.WotReactionType_ACCEPT, 256 } 257 engR := NewWotReact(tcAlice.G, argR) 258 err = RunEngine2(mctxA, engR) 259 require.NoError(t, err) 260 t.Log("alice accepts") 261 262 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 263 require.NoError(t, err) 264 require.Equal(t, 1, len(vouches)) 265 vouch := vouches[0] 266 require.Equal(t, keybase1.WotStatusType_ACCEPTED, vouch.Status) 267 require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid) 268 require.Equal(t, vouchText, vouch.VouchText) 269 require.EqualValues(t, confidence, vouch.Confidence) 270 271 vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName}) 272 require.NoError(t, err) 273 require.Equal(t, 1, len(vouches)) 274 vouch = vouches[0] 275 require.Equal(t, keybase1.WotStatusType_ACCEPTED, vouch.Status) 276 require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid) 277 require.Equal(t, vouchText, vouch.VouchText) 278 require.EqualValues(t, confidence, vouch.Confidence) 279 } 280 281 func TestWebOfTrustReject(t *testing.T) { 282 tcAlice := SetupEngineTest(t, "wot") 283 tcBob := SetupEngineTest(t, "wot") 284 defer tcAlice.Cleanup() 285 defer tcBob.Cleanup() 286 alice := CreateAndSignupFakeUser(tcAlice, "wot") 287 bob := CreateAndSignupFakeUser(tcBob, "wot") 288 mctxA := NewMetaContextForTest(tcAlice) 289 mctxB := NewMetaContextForTest(tcBob) 290 t.Log("alice and bob exist") 291 292 sigVersion := libkb.GetDefaultSigVersion(tcAlice.G) 293 trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion) 294 trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion) 295 err := bob.LoadUser(tcBob) 296 require.NoError(tcBob.T, err) 297 err = alice.LoadUser(tcAlice) 298 require.NoError(tcAlice.T, err) 299 t.Log("alice and bob follow each other") 300 301 aliceName := alice.User.GetName() 302 303 vouchText := "alice is wondibar" 304 argV := &WotVouchArg{ 305 Vouchee: alice.User.ToUserVersion(), 306 Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT}, 307 VouchText: vouchText, 308 } 309 engV := NewWotVouch(tcBob.G, argV) 310 err = RunEngine2(mctxB, engV) 311 require.NoError(t, err) 312 t.Log("bob vouches for alice") 313 314 vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 315 require.NoError(t, err) 316 require.Len(t, vouches, 1) 317 bobVouch := vouches[0] 318 require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status) 319 require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid) 320 require.Equal(t, vouchText, bobVouch.VouchText) 321 t.Log("alice fetches one pending vouch") 322 323 argR := &WotReactArg{ 324 Voucher: bob.User.ToUserVersion(), 325 Proof: bobVouch.VouchProof, 326 Reaction: keybase1.WotReactionType_REJECT, 327 } 328 engR := NewWotReact(tcAlice.G, argR) 329 err = RunEngine2(mctxA, engR) 330 require.NoError(t, err) 331 t.Log("alice rejects it") 332 333 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 334 require.NoError(t, err) 335 require.Equal(t, 1, len(vouches)) 336 vouch := vouches[0] 337 require.Equal(t, keybase1.WotStatusType_REJECTED, vouch.Status) 338 require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid) 339 require.Equal(t, vouchText, vouch.VouchText) 340 require.NotNil(t, vouch.Confidence) 341 require.EqualValues(t, keybase1.UsernameVerificationType_OTHER_CHAT, bobVouch.Confidence.UsernameVerifiedVia) 342 t.Log("alice can see it as rejected") 343 344 vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName}) 345 require.NoError(t, err) 346 require.Equal(t, 1, len(vouches)) 347 require.Equal(t, keybase1.WotStatusType_REJECTED, vouches[0].Status) 348 t.Log("bob can also see it as rejected") 349 } 350 351 func TestWebOfTrustRevoke(t *testing.T) { 352 var err error 353 tcAlice := SetupEngineTest(t, "wot") 354 defer tcAlice.Cleanup() 355 tcBob := SetupEngineTest(t, "wot") 356 defer tcBob.Cleanup() 357 alice := CreateAndSignupFakeUser(tcAlice, "wot") 358 uisA := libkb.UIs{ 359 LogUI: tcAlice.G.UI.GetLogUI(), 360 SecretUI: alice.NewSecretUI(), 361 } 362 mctxA := NewMetaContextForTest(tcAlice).WithUIs(uisA) 363 bob := CreateAndSignupFakeUser(tcBob, "wot") 364 uisB := libkb.UIs{ 365 LogUI: tcBob.G.UI.GetLogUI(), 366 SecretUI: bob.NewSecretUI(), 367 } 368 mctxB := NewMetaContextForTest(tcBob).WithUIs(uisB) 369 aliceName := alice.NormalizedUsername().String() 370 bobName := bob.NormalizedUsername().String() 371 t.Logf("alice: %s and bob: %s exist", aliceName, bobName) 372 sigVersion := libkb.GetDefaultSigVersion(tcAlice.G) 373 trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion) 374 trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion) 375 err = bob.LoadUser(tcBob) 376 require.NoError(tcBob.T, err) 377 err = alice.LoadUser(tcAlice) 378 require.NoError(tcAlice.T, err) 379 t.Log("alice and bob follow each other") 380 381 bobVouchesForAlice := func(version int) { 382 vouchText := fmt.Sprintf("alice is wondibar v%d", version) 383 arg := &WotVouchArg{ 384 Vouchee: alice.User.ToUserVersion(), 385 VouchText: vouchText, 386 Confidence: confidence, 387 } 388 eng := NewWotVouch(tcBob.G, arg) 389 err = RunEngine2(mctxB, eng) 390 require.NoError(t, err) 391 } 392 aliceAccepts := func(sigID keybase1.SigID) error { 393 arg := &WotReactArg{ 394 Voucher: bob.User.ToUserVersion(), 395 Proof: sigID, 396 Reaction: keybase1.WotReactionType_ACCEPT, 397 } 398 eng := NewWotReact(tcAlice.G, arg) 399 return RunEngine2(mctxA, eng) 400 } 401 aliceRejects := func(sigID keybase1.SigID) error { 402 arg := &WotReactArg{ 403 Voucher: bob.User.ToUserVersion(), 404 Proof: sigID, 405 Reaction: keybase1.WotReactionType_REJECT, 406 } 407 eng := NewWotReact(tcAlice.G, arg) 408 return RunEngine2(mctxA, eng) 409 } 410 assertFetch := func(mctx libkb.MetaContext, version int, expectedStatus keybase1.WotStatusType) keybase1.WotVouch { 411 vouches, err := libkb.FetchWotVouches(mctx, libkb.FetchWotVouchesArg{Voucher: bobName, Vouchee: aliceName}) 412 require.NoError(t, err) 413 require.Equal(t, 1, len(vouches)) 414 vouch := vouches[0] 415 require.Equal(t, expectedStatus, vouch.Status) 416 require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid) 417 require.Equal(t, alice.User.GetUID(), vouch.Vouchee.Uid) 418 expectedVouchText := fmt.Sprintf("alice is wondibar v%d", version) 419 require.Equal(t, expectedVouchText, vouch.VouchText) 420 require.NotNil(t, vouch.Confidence) 421 return vouch 422 } 423 revokeSig := func(mctx libkb.MetaContext, sigID string) { 424 eng := NewRevokeSigsEngine(mctx.G(), []string{sigID}) 425 err := RunEngine2(mctx, eng) 426 require.NoError(t, err) 427 } 428 429 // bob vouches for alice 430 vouchVersion := 1 431 bobVouchesForAlice(vouchVersion) 432 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 433 wotVouchOne := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 434 t.Log("bob vouches for alice and everything looks good") 435 436 // bob revokes the attestation 437 revokeSig(mctxB, wotVouchOne.VouchProof.String()) 438 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_REVOKED) 439 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REVOKED) 440 t.Log("bob revokes the chainlink with the attestation, and it comes back `revoked` for both of them") 441 442 // bob vouches again 443 vouchVersion++ 444 bobVouchesForAlice(vouchVersion) 445 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 446 wotVouchTwo := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 447 448 // alice cannot accept the revoked proof 449 err = aliceAccepts(wotVouchOne.VouchProof) 450 require.Error(t, err) 451 // alice accepts the new proposed proof 452 err = aliceAccepts(wotVouchTwo.VouchProof) 453 require.NoError(t, err) 454 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_ACCEPTED) 455 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_ACCEPTED) 456 457 // alice revokes her acceptance 458 err = alice.LoadUser(tcAlice) 459 require.NoError(t, err) 460 aliceLastLink := alice.User.GetLastLink() 461 tlink, w := libkb.NewTypedChainLink(aliceLastLink) 462 require.Nil(t, w) 463 reactionLink, ok := tlink.(*libkb.WotReactChainLink) 464 require.True(t, ok) 465 revokeSig(mctxA, reactionLink.GetSigID().String()) 466 // it goes back to proposed 467 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 468 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 469 // and now she accepts it and it looks accepted to both of them 470 err = aliceAccepts(wotVouchTwo.VouchProof) 471 require.NoError(t, err) 472 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_ACCEPTED) 473 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_ACCEPTED) 474 475 // bob revokes and it shows up as revoked 476 revokeSig(mctxB, wotVouchTwo.VouchProof.String()) 477 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_REVOKED) 478 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REVOKED) 479 480 /////////// 481 // VouchWithRevoke 482 /////////// 483 assertUserLastLinkIsVouchWithRevoke := func(user *FakeUser, tc libkb.TestContext) *libkb.WotVouchChainLink { 484 err := user.LoadUser(tc) 485 require.NoError(t, err) 486 lastLink := user.User.GetLastLink() 487 tlink, warning := libkb.NewTypedChainLink(lastLink) 488 require.Nil(t, warning) 489 vouchLink, ok := tlink.(*libkb.WotVouchChainLink) 490 require.True(t, ok) 491 require.Equal(t, 1, len(vouchLink.Revocations)) 492 return vouchLink 493 } 494 vouchVersion++ 495 bobVouchesForAlice(vouchVersion) 496 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 497 vouchToRevoke := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 498 // another vouch on top of an unrevoked previous one should be of type vouchWithRevoke 499 vouchVersion++ 500 bobVouchesForAlice(vouchVersion) 501 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 502 vouchToAccept := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 503 vwrLink := assertUserLastLinkIsVouchWithRevoke(bob, tcBob) 504 require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof) 505 err = aliceAccepts(vouchToRevoke.VouchProof) 506 require.Error(t, err) 507 508 // can vouchWithRevoke an accepted vouch, and then accept it 509 err = aliceAccepts(vouchToAccept.VouchProof) 510 vouchToRevoke = vouchToAccept 511 require.NoError(t, err) 512 vouchVersion++ 513 bobVouchesForAlice(vouchVersion) 514 vouchToAccept = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 515 _ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 516 vwrLink = assertUserLastLinkIsVouchWithRevoke(bob, tcBob) 517 require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof) 518 err = aliceAccepts(vouchToAccept.VouchProof) 519 require.NoError(t, err) 520 // it is significant that bob has not loaded alice between these two steps 521 // see comment in sig_chain.go#GetLinkFromSigID 522 err = aliceRejects(vouchToAccept.VouchProof) 523 require.NoError(t, err) 524 525 // can vouchWithRevoke a rejected vouch, and then accept it 526 vouchToRevoke = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REJECTED) 527 bobVouchesForAlice(vouchVersion) 528 _ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED) 529 vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 530 vwrLink = assertUserLastLinkIsVouchWithRevoke(bob, tcBob) 531 require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof) 532 err = aliceAccepts(vouchToAccept.VouchProof) 533 require.NoError(t, err) 534 535 // confirm that vouch and vouchWithRevoke links are not stubbed by the server 536 // by checking that, when they are not the last link in someone's chain (which is always unstubbed), 537 // an unstubbed load is successful, and subsequently, those vouches are reactable. 538 // ------------------------------------------ 539 // create a new user 540 tcCharlie := SetupEngineTest(t, "wot") 541 defer tcCharlie.Cleanup() 542 charlie := CreateAndSignupFakeUser(tcCharlie, "wot") 543 // clear out so our next vouch link doesn't have a revoke 544 revokeSig(mctxB, vouchToAccept.VouchProof.String()) 545 546 assertVouchLinkLoadsCleanly := func(vouchToAccept keybase1.WotVouch) { 547 // add two stubbable links to the end of bob's chain 548 trackUser(tcBob, bob, charlie.NormalizedUsername(), sigVersion) 549 require.NoError(t, runUntrack(tcBob, bob, charlie.NormalizedUsername().String(), sigVersion)) 550 // wipe alice so the load is definitely uncached 551 _, err := mctxA.G().LocalDb.Nuke() 552 require.NoError(t, err) 553 // alice can do a normal stubbed load of bob (there are no problems with vouch links being stubbed) 554 _, err = libkb.LoadUser(libkb.NewLoadUserByNameArg(tcAlice.G, bobName)) 555 require.NoError(t, err) 556 // alice can still react to this link after having done a stubbed load 557 err = aliceAccepts(vouchToAccept.VouchProof) 558 require.NoError(t, err) 559 } 560 // a vouch link buried inside a sigchain does not cause loading/stubbing issues 561 vouchVersion++ 562 bobVouchesForAlice(vouchVersion) 563 vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 564 assertVouchLinkLoadsCleanly(vouchToAccept) 565 // a vouch_with_revoke link buried inside a sigchain does not cause loading/stubbing issues 566 vouchVersion++ 567 bobVouchesForAlice(vouchVersion) 568 _ = assertUserLastLinkIsVouchWithRevoke(bob, tcBob) 569 vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED) 570 assertVouchLinkLoadsCleanly(vouchToAccept) 571 } 572 573 // perhaps revisit after Y2K-1494 574 func TestWebOfTrustSigBug(t *testing.T) { 575 tcAlice := SetupEngineTest(t, "wot") 576 tcBob := SetupEngineTest(t, "wot") 577 defer tcAlice.Cleanup() 578 defer tcBob.Cleanup() 579 alice := CreateAndSignupFakeUser(tcAlice, "wot") 580 bob := CreateAndSignupFakeUser(tcBob, "wot") 581 mctxA := NewMetaContextForTest(tcAlice) 582 mctxB := NewMetaContextForTest(tcBob) 583 t.Log("alice and bob exist") 584 585 sigVersion := libkb.GetDefaultSigVersion(tcAlice.G) 586 trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion) 587 trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion) 588 err := bob.LoadUser(tcBob) 589 require.NoError(tcBob.T, err) 590 err = alice.LoadUser(tcAlice) 591 require.NoError(tcAlice.T, err) 592 t.Log("alice and bob follow each other") 593 594 // bob vouches for alice 595 firstVouch := "alice is wondibar cause we texted" 596 argV := &WotVouchArg{ 597 Vouchee: alice.User.ToUserVersion(), 598 Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT}, 599 VouchText: firstVouch, 600 } 601 engV := NewWotVouch(tcBob.G, argV) 602 err = RunEngine2(mctxB, engV) 603 require.NoError(t, err) 604 605 // alice rejects 606 vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 607 require.NoError(t, err) 608 require.Len(t, vouches, 1) 609 bobVouch := vouches[0] 610 argR := &WotReactArg{ 611 Voucher: bob.User.ToUserVersion(), 612 Proof: bobVouch.VouchProof, 613 Reaction: keybase1.WotReactionType_REJECT, 614 } 615 engR := NewWotReact(tcAlice.G, argR) 616 err = RunEngine2(mctxA, engR) 617 require.NoError(t, err) 618 t.Log("alice rejects it") 619 _, err = mctxA.G().LocalDb.Nuke() 620 require.NoError(t, err) 621 622 // bob vouches for alice 623 engV2 := NewWotVouch(tcBob.G, argV) 624 err = RunEngine2(mctxB, engV2) 625 require.NoError(t, err) 626 627 // this attestation is correctly recognized as proposed 628 vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{}) 629 require.NoError(t, err) 630 bobVouch = vouches[0] 631 require.Equal(t, bobVouch.Status, keybase1.WotStatusType_PROPOSED) 632 } 633 634 var confidence = keybase1.Confidence{ 635 UsernameVerifiedVia: keybase1.UsernameVerificationType_PROOFS, 636 Proofs: []keybase1.WotProof{ 637 {ProofType: keybase1.ProofType_REDDIT, Name: "reddit", Username: "betaveros"}, 638 {ProofType: keybase1.ProofType_GENERIC_SOCIAL, Name: "mastodon.social", Username: "gammaveros"}, 639 {ProofType: keybase1.ProofType_GENERIC_WEB_SITE, Protocol: "https:", Username: "beta.veros"}, 640 {ProofType: keybase1.ProofType_DNS, Protocol: "dns", Username: "beta.veros"}, 641 }, 642 }