github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/revoke_test.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 11 "github.com/keybase/client/go/kbcrypto" 12 "github.com/keybase/client/go/libkb" 13 keybase1 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func getActiveDevicesAndKeys(tc libkb.TestContext, u *FakeUser) ([]libkb.DeviceWithDeviceNumber, []libkb.GenericKey) { 18 arg := libkb.NewLoadUserByNameArg(tc.G, u.Username).WithPublicKeyOptional() 19 user, err := libkb.LoadUser(arg) 20 require.NoError(tc.T, err) 21 22 sibkeys := user.GetComputedKeyFamily().GetAllActiveSibkeys() 23 subkeys := user.GetComputedKeyFamily().GetAllActiveSubkeys() 24 25 activeDevices := []libkb.DeviceWithDeviceNumber{} 26 for _, device := range user.GetComputedKeyFamily().GetAllDevices() { 27 if device.Status != nil && *device.Status == libkb.DeviceStatusActive { 28 activeDevices = append(activeDevices, device) 29 } 30 } 31 return activeDevices, append(sibkeys, subkeys...) 32 } 33 34 func doRevokeKey(tc libkb.TestContext, u *FakeUser, kid keybase1.KID) error { 35 revokeEngine := NewRevokeKeyEngine(tc.G, kid) 36 uis := libkb.UIs{ 37 LogUI: tc.G.UI.GetLogUI(), 38 SecretUI: u.NewSecretUI(), 39 } 40 m := NewMetaContextForTest(tc).WithUIs(uis) 41 err := RunEngine2(m, revokeEngine) 42 return err 43 } 44 45 func doRevokeSig(tc libkb.TestContext, u *FakeUser, sig keybase1.SigID) error { 46 revokeEngine := NewRevokeSigsEngine(tc.G, []string{sig.String()}) 47 uis := libkb.UIs{ 48 LogUI: tc.G.UI.GetLogUI(), 49 SecretUI: u.NewSecretUI(), 50 } 51 m := NewMetaContextForTest(tc).WithUIs(uis) 52 err := RunEngine2(m, revokeEngine) 53 return err 54 } 55 56 func doRevokeDevice(tc libkb.TestContext, u *FakeUser, id keybase1.DeviceID, forceSelf, forceLast bool) error { 57 revokeEngine := NewRevokeDeviceEngine(tc.G, RevokeDeviceEngineArgs{ID: id, ForceSelf: forceSelf, ForceLast: forceLast}) 58 uis := libkb.UIs{ 59 LogUI: tc.G.UI.GetLogUI(), 60 SecretUI: u.NewSecretUI(), 61 } 62 m := NewMetaContextForTest(tc).WithUIs(uis) 63 err := RunEngine2(m, revokeEngine) 64 return err 65 } 66 67 func assertNumDevicesAndKeys(tc libkb.TestContext, u *FakeUser, numDevices, numKeys int) { 68 devices, keys := getActiveDevicesAndKeys(tc, u) 69 if len(devices) != numDevices { 70 for i, d := range devices { 71 tc.T.Logf("device %d: %+v", i, d) 72 } 73 require.Fail(tc.T, fmt.Sprintf("Expected to find %d devices. Found %d.", numDevices, len(devices))) 74 } 75 if len(keys) != numKeys { 76 for i, k := range keys { 77 tc.T.Logf("key %d: %+v", i, k) 78 } 79 require.Fail(tc.T, fmt.Sprintf("Expected to find %d keys. Found %d.", numKeys, len(keys))) 80 } 81 } 82 83 func TestRevokeDevice(t *testing.T) { 84 testRevokeDevice(t, false) 85 } 86 87 func TestRevokeDevicePUK(t *testing.T) { 88 testRevokeDevice(t, true) 89 } 90 91 func testRevokeDevice(t *testing.T, upgradePerUserKey bool) { 92 tc := SetupEngineTest(t, "rev") 93 defer tc.Cleanup() 94 tc.Tp.DisableUpgradePerUserKey = !upgradePerUserKey 95 96 u := CreateAndSignupFakeUserPaper(tc, "rev") 97 98 assertNumDevicesAndKeys(tc, u, 2, 4) 99 100 devices, _ := getActiveDevicesAndKeys(tc, u) 101 var thisDevice libkb.DeviceWithDeviceNumber 102 for _, device := range devices { 103 if device.Type != keybase1.DeviceTypeV2_PAPER { 104 thisDevice = device 105 } 106 } 107 108 // Revoking the current device should fail. 109 err := doRevokeDevice(tc, u, thisDevice.ID, false, false) 110 if err == nil { 111 tc.T.Fatal("Expected revoking the current device to fail.") 112 } 113 114 assertNumDevicesAndKeys(tc, u, 2, 4) 115 116 // But it should succeed with the --force flag. 117 err = doRevokeDevice(tc, u, thisDevice.ID, true, false) 118 if err != nil { 119 tc.T.Fatal(err) 120 } 121 122 assertNumDevicesAndKeys(tc, u, 1, 2) 123 } 124 125 func TestRevokePaperDevice(t *testing.T) { 126 testRevokePaperDevice(t, false) 127 } 128 129 func TestRevokePaperDevicePUK(t *testing.T) { 130 testRevokePaperDevice(t, true) 131 } 132 133 func testRevokePaperDevice(t *testing.T, upgradePerUserKey bool) { 134 tc := SetupEngineTest(t, "rev") 135 defer tc.Cleanup() 136 tc.Tp.DisableUpgradePerUserKey = !upgradePerUserKey 137 138 u := CreateAndSignupFakeUserPaper(tc, "rev") 139 140 t.Logf("username: %s", u.Username) 141 142 assertNumDevicesAndKeys(tc, u, 2, 4) 143 144 assertNumDevicesAndKeys(tc, u, 2, 4) 145 146 revokeAnyPaperKey(tc, u) 147 148 assertNumDevicesAndKeys(tc, u, 1, 2) 149 150 if tc.G.Env.GetUpgradePerUserKey() { 151 checkPerUserKeyring(t, tc.G, 2) 152 } else { 153 checkPerUserKeyring(t, tc.G, 0) 154 } 155 156 arg := libkb.NewLoadUserByNameArg(tc.G, u.Username) 157 user, err := libkb.LoadUser(arg) 158 require.NoError(t, err) 159 160 var nextSeqno int 161 var postedSeqno int 162 if upgradePerUserKey { 163 nextSeqno = 7 164 postedSeqno = 4 165 } else { 166 nextSeqno = 5 167 postedSeqno = 3 168 } 169 nextExpected, err := user.GetExpectedNextHighSkip(libkb.NewMetaContextForTest(tc)) 170 require.NoError(t, err) 171 require.Equal(t, nextExpected.Seqno, keybase1.Seqno(nextSeqno)) 172 assertPostedHighSkipSeqno(t, tc, user.GetName(), postedSeqno) 173 } 174 175 func TestRevokerPaperDeviceTwice(t *testing.T) { 176 testRevokerPaperDeviceTwice(t, false) 177 } 178 179 func TestRevokerPaperDeviceTwicePUK(t *testing.T) { 180 testRevokerPaperDeviceTwice(t, true) 181 } 182 183 func testRevokerPaperDeviceTwice(t *testing.T, upgradePerUserKey bool) { 184 tc := SetupEngineTest(t, "rev") 185 defer tc.Cleanup() 186 tc.Tp.DisableUpgradePerUserKey = !upgradePerUserKey 187 188 u := CreateAndSignupFakeUserPaper(tc, "rev") 189 190 t.Logf("username: %s", u.Username) 191 192 t.Logf("generate second paper key") 193 { 194 uis := libkb.UIs{ 195 LogUI: tc.G.UI.GetLogUI(), 196 LoginUI: &libkb.TestLoginUI{}, 197 SecretUI: &libkb.TestSecretUI{}, 198 } 199 eng := NewPaperKey(tc.G) 200 m := NewMetaContextForTest(tc).WithUIs(uis) 201 err := RunEngine2(m, eng) 202 require.NoError(t, err) 203 require.NotEqual(t, 0, len(eng.Passphrase()), "empty passphrase") 204 } 205 206 t.Logf("check") 207 assertNumDevicesAndKeys(tc, u, 3, 6) 208 209 t.Logf("revoke paper key 1") 210 revokeAnyPaperKey(tc, u) 211 212 t.Logf("revoke paper key 2") 213 revokeAnyPaperKey(tc, u) 214 215 t.Logf("check") 216 assertNumDevicesAndKeys(tc, u, 1, 2) 217 218 if tc.G.Env.GetUpgradePerUserKey() { 219 checkPerUserKeyring(t, tc.G, 3) 220 } 221 } 222 223 func checkPerUserKeyring(t *testing.T, g *libkb.GlobalContext, expectedCurrentGeneration int) { 224 pukring, err := g.GetPerUserKeyring(context.Background()) 225 require.NoError(t, err) 226 // Weakly check. If the keyring was not initialized, don't worry about it. 227 if pukring.HasAnyKeys() == (expectedCurrentGeneration > 0) { 228 require.Equal(t, keybase1.PerUserKeyGeneration(expectedCurrentGeneration), pukring.CurrentGeneration()) 229 } 230 pukring = nil 231 232 // double check that the per-user-keyring is correct 233 g.ClearPerUserKeyring() 234 pukring, err = g.GetPerUserKeyring(context.Background()) 235 require.NoError(t, err) 236 require.NoError(t, pukring.Sync(libkb.NewMetaContextTODO(g))) 237 require.Equal(t, keybase1.PerUserKeyGeneration(expectedCurrentGeneration), pukring.CurrentGeneration()) 238 } 239 240 func TestRevokeKey(t *testing.T) { 241 tc := SetupEngineTest(t, "rev") 242 defer tc.Cleanup() 243 244 u := createFakeUserWithPGPSibkeyPaper(tc) 245 246 assertNumDevicesAndKeys(tc, u, 2, 5) 247 248 _, keys := getActiveDevicesAndKeys(tc, u) 249 var pgpKey *libkb.GenericKey 250 for i, key := range keys { 251 if libkb.IsPGP(key) { 252 // XXX: Don't use &key. That refers to the loop variable, which 253 // gets overwritten. 254 pgpKey = &keys[i] // 255 break 256 } 257 } 258 if pgpKey == nil { 259 t.Fatal("Expected to find PGP key") 260 } 261 262 err := doRevokeKey(tc, u, (*pgpKey).GetKID()) 263 if err != nil { 264 tc.T.Fatal(err) 265 } 266 267 assertNumDevicesAndKeys(tc, u, 2, 4) 268 } 269 270 // See issue #370. 271 func TestTrackAfterRevoke(t *testing.T) { 272 doWithSigChainVersions(func(sigVersion libkb.SigVersion) { 273 _testTrackAfterRevoke(t, sigVersion) 274 }) 275 } 276 277 func _testTrackAfterRevoke(t *testing.T, sigVersion libkb.SigVersion) { 278 tc1 := SetupEngineTest(t, "rev") 279 defer tc1.Cleanup() 280 281 // We need two devices. Use a paperkey to sign into the second device. 282 283 // Sign up on tc1: 284 u := CreateAndSignupFakeUserGPG(tc1, "pgp") 285 286 t.Logf("create a paperkey") 287 beng := NewPaperKey(tc1.G) 288 uis := libkb.UIs{ 289 LogUI: tc1.G.UI.GetLogUI(), 290 LoginUI: &libkb.TestLoginUI{}, 291 SecretUI: &libkb.TestSecretUI{}, 292 } 293 m := NewMetaContextForTest(tc1).WithUIs(uis) 294 err := RunEngine2(m, beng) 295 require.NoError(t, err) 296 paperkey := beng.Passphrase() 297 298 // Redo SetupEngineTest to get a new home directory...should look like a new device. 299 tc2 := SetupEngineTest(t, "login") 300 defer tc2.Cleanup() 301 302 // Login on device tc2 using the paperkey. 303 t.Logf("running LoginWithPaperKey") 304 secUI := u.NewSecretUI() 305 secUI.Passphrase = paperkey 306 provUI := newTestProvisionUIPaper() 307 provLoginUI := &libkb.TestLoginUI{Username: u.Username} 308 uis = libkb.UIs{ 309 ProvisionUI: provUI, 310 LogUI: tc2.G.UI.GetLogUI(), 311 SecretUI: secUI, 312 LoginUI: provLoginUI, 313 GPGUI: &gpgtestui{}, 314 } 315 eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI) 316 m = NewMetaContextForTest(tc2).WithUIs(uis) 317 err = RunEngine2(m, eng) 318 require.NoError(t, err) 319 320 t.Logf("tc2 revokes tc1 device:") 321 err = doRevokeDevice(tc2, u, tc1.G.Env.GetDeviceID(), false, false) 322 require.NoError(t, err) 323 324 // Still logged in on tc1. Try to use it to track someone. It should fail 325 // with a KeyRevokedError. 326 _, _, err = runTrack(tc1, u, "t_alice", sigVersion) 327 if err == nil { 328 t.Fatal("expected runTrack to return an error") 329 } 330 if _, ok := err.(libkb.BadSessionError); !ok { 331 t.Errorf("expected libkb.BadSessionError, got %T", err) 332 } 333 } 334 335 func TestSignAfterRevoke(t *testing.T) { 336 tc1 := SetupEngineTest(t, "rev") 337 defer tc1.Cleanup() 338 339 // We need two devices. Use a paperkey to sign into the second device. 340 341 // Sign up on tc1: 342 u := CreateAndSignupFakeUserGPG(tc1, "pgp") 343 344 t.Logf("create a paperkey") 345 beng := NewPaperKey(tc1.G) 346 uis := libkb.UIs{ 347 LogUI: tc1.G.UI.GetLogUI(), 348 LoginUI: &libkb.TestLoginUI{}, 349 SecretUI: &libkb.TestSecretUI{}, 350 } 351 m := NewMetaContextForTest(tc1).WithUIs(uis) 352 err := RunEngine2(m, beng) 353 require.NoError(t, err) 354 paperkey := beng.Passphrase() 355 356 // Redo SetupEngineTest to get a new home directory...should look like a new device. 357 tc2 := SetupEngineTest(t, "login") 358 defer tc2.Cleanup() 359 360 // Login on device tc2 using the paperkey. 361 t.Logf("running LoginWithPaperKey") 362 secUI := u.NewSecretUI() 363 secUI.Passphrase = paperkey 364 provUI := newTestProvisionUIPaper() 365 provLoginUI := &libkb.TestLoginUI{Username: u.Username} 366 uis = libkb.UIs{ 367 ProvisionUI: provUI, 368 LogUI: tc2.G.UI.GetLogUI(), 369 SecretUI: secUI, 370 LoginUI: provLoginUI, 371 GPGUI: &gpgtestui{}, 372 } 373 eng := NewLogin(tc2.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI) 374 m = NewMetaContextForTest(tc2).WithUIs(uis) 375 err = RunEngine2(m, eng) 376 require.NoError(t, err) 377 378 t.Logf("tc2 revokes tc1 device:") 379 err = doRevokeDevice(tc2, u, tc1.G.Env.GetDeviceID(), false, false) 380 require.NoError(t, err) 381 382 // Still logged in on tc1, a revoked device. 383 384 // Test signing with (revoked) device key on tc1, which works... 385 msg := []byte("test message") 386 ret, err := SignED25519(context.TODO(), tc1.G, keybase1.SignED25519Arg{ 387 Msg: msg, 388 }) 389 if err != nil { 390 t.Fatal(err) 391 } 392 publicKey := kbcrypto.NaclSigningKeyPublic(ret.PublicKey) 393 if !publicKey.Verify(msg, kbcrypto.NaclSignature(ret.Sig)) { 394 t.Error(kbcrypto.VerificationError{}) 395 } 396 397 // This should log out tc1: 398 if err := NewMetaContextForTest(tc1).LogoutAndDeprovisionIfRevoked(); err != nil { 399 t.Fatal(err) 400 } 401 402 err = AssertLoggedOut(tc1) 403 require.NoError(t, err) 404 405 // And now this should fail. 406 ret, err = SignED25519(context.TODO(), tc1.G, keybase1.SignED25519Arg{ 407 Msg: msg, 408 }) 409 if err == nil { 410 t.Fatal("nil error signing after LogoutAndDeprovisionIfRevoked") 411 } 412 if _, ok := err.(libkb.LoginRequiredError); !ok { 413 t.Errorf("error type: %T, expected libkb.LoginRequiredError", err) 414 } 415 } 416 417 // Check that if not on a revoked device that LogoutAndDeprovisionIfRevoked doesn't do anything. 418 func TestLogoutAndDeprovisionIfRevokedNoop(t *testing.T) { 419 tc := SetupEngineTest(t, "rev") 420 defer tc.Cleanup() 421 422 CreateAndSignupFakeUser(tc, "rev") 423 424 err := AssertLoggedIn(tc) 425 require.NoError(t, err) 426 427 if err := NewMetaContextForTest(tc).LogoutAndDeprovisionIfRevoked(); err != nil { 428 t.Fatal(err) 429 } 430 431 err = AssertLoggedIn(tc) 432 require.NoError(t, err) 433 434 msg := []byte("test message") 435 ret, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{ 436 Msg: msg, 437 }) 438 if err != nil { 439 t.Fatal(err) 440 } 441 publicKey := kbcrypto.NaclSigningKeyPublic(ret.PublicKey) 442 if !publicKey.Verify(msg, kbcrypto.NaclSignature(ret.Sig)) { 443 t.Error(kbcrypto.VerificationError{}) 444 } 445 } 446 447 func revokeAnyPaperKey(tc libkb.TestContext, fu *FakeUser) *libkb.Device { 448 t := tc.T 449 t.Logf("revoke a paper key") 450 devices, _ := getActiveDevicesAndKeys(tc, fu) 451 var revokeDevice libkb.DeviceWithDeviceNumber 452 for _, device := range devices { 453 if device.Type == keybase1.DeviceTypeV2_PAPER { 454 revokeDevice = device 455 } 456 } 457 require.NotNil(t, revokeDevice, "no paper key found to revoke") 458 t.Logf("revoke %s", revokeDevice.ID) 459 err := doRevokeDevice(tc, fu, revokeDevice.ID, false, false) 460 require.NoError(t, err) 461 return revokeDevice.Device 462 } 463 464 func TestRevokeLastDevice(t *testing.T) { 465 tc := SetupEngineTest(t, "rev") 466 defer tc.Cleanup() 467 468 u := CreateAndSignupFakeUser(tc, "rev") 469 470 assertNumDevicesAndKeys(tc, u, 1, 2) 471 472 devices, _ := getActiveDevicesAndKeys(tc, u) 473 thisDevice := devices[0] 474 475 // Revoking the current device should fail. 476 err := doRevokeDevice(tc, u, thisDevice.ID, false, false) 477 if err == nil { 478 t.Fatal("Expected revoking the current device to fail.") 479 } 480 481 assertNumDevicesAndKeys(tc, u, 1, 2) 482 483 // Since this is the last device, it should fail with `force` too: 484 err = doRevokeDevice(tc, u, thisDevice.ID, true, false) 485 if err == nil { 486 t.Fatal("Expected revoking the current last device to fail.") 487 } 488 489 assertNumDevicesAndKeys(tc, u, 1, 2) 490 491 // With `force` and `forceLast`, the revoke should succeed 492 err = doRevokeDevice(tc, u, thisDevice.ID, true, true) 493 if err != nil { 494 t.Fatal(err) 495 } 496 497 assertNumDevicesAndKeys(tc, u, 0, 0) 498 } 499 500 func TestRevokeLastDevicePGP(t *testing.T) { 501 tc := SetupEngineTest(t, "rev") 502 u1 := createFakeUserWithPGPOnly(t, tc) 503 assertNumDevicesAndKeys(tc, u1, 0, 1) 504 Logout(tc) 505 tc.Cleanup() 506 507 tc = SetupEngineTest(t, "rev") 508 defer tc.Cleanup() 509 510 uis := libkb.UIs{ 511 ProvisionUI: newTestProvisionUIPassphrase(), 512 LoginUI: &libkb.TestLoginUI{Username: u1.Username}, 513 LogUI: tc.G.UI.GetLogUI(), 514 SecretUI: u1.NewSecretUI(), 515 GPGUI: &gpgtestui{}, 516 } 517 eng := NewLogin(tc.G, keybase1.DeviceTypeV2_DESKTOP, "", keybase1.ClientType_CLI) 518 m := NewMetaContextForTest(tc).WithUIs(uis) 519 if err := RunEngine2(m, eng); err != nil { 520 t.Fatal(err) 521 } 522 testUserHasDeviceKey(tc) 523 hasZeroPaperDev(tc, u1) 524 if err := AssertProvisioned(tc); err != nil { 525 t.Fatal(err) 526 } 527 528 devices, _ := getActiveDevicesAndKeys(tc, u1) 529 thisDevice := devices[0] 530 531 // Revoking the current device should fail. 532 err := doRevokeDevice(tc, u1, thisDevice.ID, false, false) 533 if err == nil { 534 t.Fatal("Expected revoking the current device to fail.") 535 } 536 if _, ok := err.(libkb.RevokeLastDevicePGPError); !ok { 537 t.Fatalf("expected libkb.RevokeLastDevicePGPError, got %T", err) 538 } 539 540 assertNumDevicesAndKeys(tc, u1, 1, 3) 541 542 // Since this is the last device, it should fail with `force` too: 543 err = doRevokeDevice(tc, u1, thisDevice.ID, true, false) 544 if err == nil { 545 t.Fatal("Expected revoking the current last device to fail.") 546 } 547 if _, ok := err.(libkb.RevokeLastDevicePGPError); !ok { 548 t.Fatalf("expected libkb.RevokeLastDevicePGPError, got %T", err) 549 } 550 551 assertNumDevicesAndKeys(tc, u1, 1, 3) 552 553 // With `force` and `forceLast`, the revoke should also fail because of pgp key 554 err = doRevokeDevice(tc, u1, thisDevice.ID, true, true) 555 if err == nil { 556 t.Fatal("Expected revoking current last device with forceLast to fail") 557 } 558 if _, ok := err.(libkb.RevokeLastDevicePGPError); !ok { 559 t.Fatalf("expected libkb.RevokeLastDevicePGPError, got %T", err) 560 } 561 562 assertNumDevicesAndKeys(tc, u1, 1, 3) 563 }