github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systests/ephemeral_test.go (about) 1 package systests 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/keybase/client/go/engine" 8 "github.com/keybase/client/go/ephemeral" 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/gregor1" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 "github.com/keybase/client/go/teambot" 13 "github.com/keybase/clockwork" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func initEphemeralForTests(t *testing.T, mctx libkb.MetaContext) { 18 ephemeral.ServiceInit(mctx) 19 err := mctx.G().GetEKLib().KeygenIfNeeded(mctx) 20 require.NoError(t, err) 21 } 22 23 func TestEphemeralNewTeamEKNotif(t *testing.T) { 24 tt := newTeamTester(t) 25 defer tt.cleanup() 26 27 user1 := tt.addUser("one") 28 user2 := tt.addUser("wtr") 29 mctx := user1.MetaContext() 30 ekLib := user1.tc.G.GetEKLib() 31 err := ekLib.KeygenIfNeeded(mctx) 32 require.NoError(t, err) 33 34 mctx2 := user2.MetaContext() 35 err = mctx2.G().GetEKLib().KeygenIfNeeded(mctx2) 36 require.NoError(t, err) 37 38 teamID, teamName := user1.createTeam2() 39 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 40 41 teamEK, created, err := ekLib.GetOrCreateLatestTeamEK(mctx, teamID) 42 require.NoError(t, err) 43 require.True(t, created) 44 45 expectedArg := keybase1.NewTeamEkArg{ 46 Id: teamID, 47 Generation: teamEK.Generation(), 48 } 49 50 checkNewTeamEKNotifications(user1.tc, user1.notifications, expectedArg) 51 checkNewTeamEKNotifications(user2.tc, user2.notifications, expectedArg) 52 } 53 54 func checkNewTeamEKNotifications(tc *libkb.TestContext, notifications *teamNotifyHandler, expectedArg keybase1.NewTeamEkArg) { 55 select { 56 case arg := <-notifications.newTeamEKCh: 57 require.Equal(tc.T, expectedArg, arg) 58 return 59 case <-time.After(time.Second * libkb.CITimeMultiplier(tc.G)): 60 require.Fail(tc.T, "no notification on newTeamEK") 61 } 62 } 63 64 func TestEphemeralNewTeambotEKNotif(t *testing.T) { 65 tt := newTeamTester(t) 66 defer tt.cleanup() 67 68 user1 := tt.addUser("one") 69 botua := tt.addUser("botua") 70 botuaUID := gregor1.UID(botua.uid.ToBytes()) 71 mctx := user1.MetaContext() 72 ekLib := user1.tc.G.GetEKLib() 73 err := ekLib.KeygenIfNeeded(mctx) 74 require.NoError(t, err) 75 76 botuaMctx := botua.MetaContext() 77 err = botuaMctx.G().GetEKLib().KeygenIfNeeded(botuaMctx) 78 require.NoError(t, err) 79 80 teamID, teamName := user1.createTeam2() 81 user1.addRestrictedBotTeamMember(teamName.String(), botua.username, keybase1.TeamBotSettings{}) 82 83 teambotEK, created, err := ekLib.GetOrCreateLatestTeambotEK(mctx, teamID, botuaUID) 84 require.NoError(t, err) 85 require.True(t, created) 86 87 expectedArg := keybase1.NewTeambotEkArg{ 88 Id: teamID, 89 Generation: teambotEK.Generation(), 90 } 91 92 checkNewTeambotEKNotifications(botua.tc, botua.notifications, expectedArg) 93 } 94 95 func checkNewTeambotEKNotifications(tc *libkb.TestContext, notifications *teamNotifyHandler, expectedArg keybase1.NewTeambotEkArg) { 96 select { 97 case arg := <-notifications.newTeambotEKCh: 98 require.Equal(tc.T, expectedArg, arg) 99 return 100 case <-time.After(time.Second * libkb.CITimeMultiplier(tc.G)): 101 require.Fail(tc.T, "no notification on newTeambotEK") 102 } 103 } 104 105 func checkTeambotEKNeededNotifications(tc *libkb.TestContext, notifications *teamNotifyHandler, expectedArg keybase1.TeambotEkNeededArg) { 106 select { 107 case arg := <-notifications.teambotEKNeededCh: 108 require.Equal(tc.T, expectedArg, arg) 109 return 110 case <-time.After(time.Second * libkb.CITimeMultiplier(tc.G)): 111 require.Fail(tc.T, "no notification on teambotEKNeeded") 112 } 113 } 114 115 func noNewTeambotEKNotification(tc *libkb.TestContext, notifications *teamNotifyHandler) { 116 select { 117 case arg := <-notifications.newTeambotEKCh: 118 require.Fail(tc.T, "unexpected newTeambotEK notification", arg) 119 default: 120 } 121 } 122 123 func noTeambotEKNeeded(tc *libkb.TestContext, notifications *teamNotifyHandler) { 124 select { 125 case arg := <-notifications.teambotEKNeededCh: 126 require.Fail(tc.T, "unexpected teambotEKNeeded notification", arg) 127 default: 128 } 129 } 130 131 func TestEphemeralTeambotEK(t *testing.T) { 132 tt := newTeamTester(t) 133 defer tt.cleanup() 134 135 fc := clockwork.NewFakeClockAt(time.Now()) 136 137 user1 := tt.addUser("one") 138 user2 := tt.addUserWithPaper("two") 139 botua := tt.addUser("botua") 140 botuaUID := gregor1.UID(botua.uid.ToBytes()) 141 mctx1 := user1.MetaContext() 142 mctx2 := user2.MetaContext() 143 mctx3 := botua.MetaContext() 144 ekLib1 := mctx1.G().GetEKLib() 145 ekLib2 := mctx2.G().GetEKLib() 146 ekLib3 := mctx3.G().GetEKLib().(*ephemeral.EKLib) 147 ekLib3.SetClock(fc) 148 err := ekLib1.KeygenIfNeeded(mctx1) 149 require.NoError(t, err) 150 err = ekLib2.KeygenIfNeeded(mctx2) 151 require.NoError(t, err) 152 err = ekLib3.KeygenIfNeeded(mctx3) 153 require.NoError(t, err) 154 155 teamID, teamName := user1.createTeam2() 156 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 157 user1.addRestrictedBotTeamMember(teamName.String(), botua.username, keybase1.TeamBotSettings{}) 158 159 // bot gets a key on addition to the team 160 newEkArg := keybase1.NewTeambotEkArg{ 161 Id: teamID, 162 Generation: 1, 163 } 164 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 165 166 // grab the latest teamEK and make sure the generation lines up with the teambotEK 167 teamEK, _, err := ekLib1.GetOrCreateLatestTeamEK(mctx1, teamID) 168 require.NoError(t, err) 169 170 // now created = false since we published on member addition 171 teambotEK, created, err := ekLib1.GetOrCreateLatestTeambotEK(mctx1, teamID, botuaUID) 172 require.NoError(t, err) 173 require.False(t, created) 174 require.Equal(t, teamEK.Generation(), teambotEK.Generation()) 175 176 teambotEK2, _, err := ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 177 require.NoError(t, err) 178 require.Equal(t, teambotEK2.Generation(), teambotEK.Generation()) 179 require.Equal(t, teambotEK2.Material(), teambotEK.Material()) 180 noTeambotEKNeeded(user1.tc, user1.notifications) 181 noTeambotEKNeeded(user2.tc, user2.notifications) 182 noNewTeambotEKNotification(botua.tc, botua.notifications) 183 184 // simulate a bot restarting in one shot mode and losing it's deviceEKs 185 botDeviceEKStore := mctx3.G().GetDeviceEKStorage() 186 err = botDeviceEKStore.ForceDeleteAll(mctx3, libkb.NormalizedUsername(botua.username)) 187 require.NoError(t, err) 188 ekLib3.ClearCaches(mctx3) 189 190 _, created, err = ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 191 require.Error(t, err) 192 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 193 require.False(t, created) 194 195 // cry for help has been issued. 196 forceCreateGen := teambotEK.Generation() 197 ekNeededArg := keybase1.TeambotEkNeededArg{ 198 Id: teamID, 199 Uid: botua.uid, 200 Generation: 0, 201 ForceCreateGeneration: &forceCreateGen, 202 } 203 checkTeambotEKNeededNotifications(user1.tc, user1.notifications, ekNeededArg) 204 checkTeambotEKNeededNotifications(user2.tc, user2.notifications, ekNeededArg) 205 206 // and answered. 207 newEkArg = keybase1.NewTeambotEkArg{ 208 Id: teamID, 209 Generation: 2, 210 } 211 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 212 213 // bot can access the key 214 teambotEK2, created, err = ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 215 require.NoError(t, err) 216 require.False(t, created) 217 218 teambotEK, _, err = ekLib1.GetOrCreateLatestTeambotEK(mctx1, teamID, botuaUID) 219 require.NoError(t, err) 220 221 require.Equal(t, teambotEK.Generation(), teambotEK2.Generation()) 222 require.Equal(t, teambotEK.Material(), teambotEK2.Material()) 223 noTeambotEKNeeded(user1.tc, user1.notifications) 224 noTeambotEKNeeded(user2.tc, user2.notifications) 225 noNewTeambotEKNotification(botua.tc, botua.notifications) 226 227 // force a PTK rotation 228 user2.revokePaperKey() 229 user1.waitForRotateByID(teamID, keybase1.Seqno(4)) 230 231 // bot gets a new EK on rotation 232 newEkArg = keybase1.NewTeambotEkArg{ 233 Id: teamID, 234 Generation: 3, 235 } 236 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 237 238 // simulate a bot restarting in one shot mode and losing it's deviceEKs 239 botDeviceEKStore = mctx3.G().GetDeviceEKStorage() 240 err = botDeviceEKStore.ForceDeleteAll(mctx3, libkb.NormalizedUsername(botua.username)) 241 require.NoError(t, err) 242 ekLib3.ClearCaches(mctx3) 243 244 // Force a wrongKID error on the bot user by expiring the wrongKID cache 245 key := teambot.TeambotEKWrongKIDCacheKey(teamID, botua.uid, teambotEK2.Generation()) 246 expired := keybase1.ToTime(fc.Now()) 247 err = mctx3.G().GetKVStore().PutObj(key, nil, expired) 248 require.NoError(t, err) 249 permitted, ctime, err := teambot.TeambotEKWrongKIDPermitted(mctx3, teamID, botua.uid, 250 teambotEK2.Generation(), keybase1.ToTime(fc.Now())) 251 require.NoError(t, err) 252 require.True(t, permitted) 253 require.Equal(t, expired, ctime) 254 255 fc.Advance(teambot.MaxTeambotKeyWrongKIDPermitted) // expire wrong KID cache 256 permitted, ctime, err = teambot.TeambotEKWrongKIDPermitted(mctx3, teamID, botua.uid, 257 teambotEK2.Generation(), keybase1.ToTime(fc.Now())) 258 require.NoError(t, err) 259 require.False(t, permitted) 260 require.Equal(t, expired, ctime) 261 262 fc.Advance(ephemeral.LibCacheEntryLifetime) // expire lib ek caches 263 _, created, err = ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 264 require.Error(t, err) 265 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 266 require.False(t, created) 267 forceCreateGen = keybase1.EkGeneration(3) 268 ekNeededArg = keybase1.TeambotEkNeededArg{ 269 Id: teamID, 270 Uid: botua.uid, 271 Generation: 0, 272 ForceCreateGeneration: &forceCreateGen, 273 } 274 checkTeambotEKNeededNotifications(user1.tc, user1.notifications, ekNeededArg) 275 checkTeambotEKNeededNotifications(user2.tc, user2.notifications, ekNeededArg) 276 newEkArg = keybase1.NewTeambotEkArg{ 277 Id: teamID, 278 Generation: forceCreateGen + 1, 279 } 280 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 281 282 teambotEK3, created, err := ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 283 require.NoError(t, err) 284 require.False(t, created) 285 noTeambotEKNeeded(user1.tc, user1.notifications) 286 noTeambotEKNeeded(user2.tc, user2.notifications) 287 noNewTeambotEKNotification(botua.tc, botua.notifications) 288 289 // another PTK rotation happens, this time the bot proceeded with a key 290 // signed by the old PTK since the wrongKID cache did not expire 291 user1.removeTeamMember(teamName.String(), user2.username) 292 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 293 user2.waitForNewlyAddedToTeamByID(teamID) 294 botua.waitForNewlyAddedToTeamByID(teamID) 295 296 // bot gets a new EK on rotation 297 newEkArg = keybase1.NewTeambotEkArg{ 298 Id: teamID, 299 Generation: 5, 300 } 301 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 302 303 // delete to check regeneration flow 304 err = teambot.DeleteTeambotEKForTest(mctx3, teamID, 5) 305 require.NoError(t, err) 306 307 // bot can access the old teambotEK, but asks for a new one to 308 // be created since it was signed by the old PTK 309 fc.Advance(ephemeral.LibCacheEntryLifetime) // expire lib ek caches 310 teambotEK4, created, err := ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 311 require.NoError(t, err) 312 require.False(t, created) 313 require.Equal(t, teambotEK3.Generation(), teambotEK4.Generation()) 314 require.Equal(t, teambotEK3.Material(), teambotEK4.Material()) 315 forceCreateGen = keybase1.EkGeneration(5) 316 ekNeededArg = keybase1.TeambotEkNeededArg{ 317 Id: teamID, 318 Uid: botua.uid, 319 Generation: 0, 320 ForceCreateGeneration: &forceCreateGen, 321 } 322 checkTeambotEKNeededNotifications(user1.tc, user1.notifications, ekNeededArg) 323 checkTeambotEKNeededNotifications(user2.tc, user2.notifications, ekNeededArg) 324 325 newEkArg = keybase1.NewTeambotEkArg{ 326 Id: teamID, 327 Generation: forceCreateGen + 1, 328 } 329 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 330 331 teambotEK, created, err = ekLib1.GetOrCreateLatestTeambotEK(mctx1, teamID, botuaUID) 332 require.NoError(t, err) 333 require.False(t, created) 334 require.Equal(t, forceCreateGen+1, teambotEK.Generation()) 335 336 teambotEK2, created, err = ekLib3.GetOrCreateLatestTeambotEK(mctx3, teamID, botuaUID) 337 require.NoError(t, err) 338 require.False(t, created) 339 require.Equal(t, teambotEK.Generation(), teambotEK2.Generation()) 340 require.Equal(t, teambotEK.Material(), teambotEK2.Material()) 341 noTeambotEKNeeded(user1.tc, user1.notifications) 342 noTeambotEKNeeded(user2.tc, user2.notifications) 343 noNewTeambotEKNotification(botua.tc, botua.notifications) 344 345 // kill the ek cache and make sure we don't republish 346 ekLib1.ClearCaches(mctx1) 347 teambotEKNoCache, created, err := ekLib1.GetOrCreateLatestTeambotEK(mctx1, teamID, botuaUID) 348 require.NoError(t, err) 349 // created is True since we attempt to publish but the generation remains 350 require.True(t, created) 351 require.Equal(t, teambotEK.Generation(), teambotEKNoCache.Generation()) 352 353 // Make sure we can access the teambotEK at various generations 354 for i := keybase1.EkGeneration(1); i < teambotEK.Generation(); i++ { 355 teambotEKNonBot1, err := ekLib1.GetTeambotEK(mctx1, teamID, botuaUID, i, nil) 356 require.NoError(t, err) 357 358 teambotEKNonBot2, err := ekLib2.GetTeambotEK(mctx2, teamID, botuaUID, i, nil) 359 require.NoError(t, err) 360 361 teambotEKBot, err := ekLib3.GetTeambotEK(mctx3, teamID, botuaUID, i, nil) 362 switch i { 363 case 1, 2, 3: 364 require.Error(t, err) 365 default: 366 require.NoError(t, err) 367 require.Equal(t, teambotEKBot.Generation(), teambotEKNonBot1.Generation()) 368 require.Equal(t, teambotEKBot.Material(), teambotEKNonBot1.Material()) 369 require.Equal(t, teambotEKBot.Generation(), teambotEKNonBot2.Generation()) 370 require.Equal(t, teambotEKBot.Material(), teambotEKNonBot2.Material()) 371 } 372 if i == 5 { 373 newEkArg = keybase1.NewTeambotEkArg{ 374 Id: teamID, 375 Generation: 5, 376 } 377 checkNewTeambotEKNotifications(botua.tc, botua.notifications, newEkArg) 378 } 379 noNewTeambotEKNotification(botua.tc, botua.notifications) 380 noTeambotEKNeeded(user1.tc, user1.notifications) 381 noTeambotEKNeeded(user2.tc, user2.notifications) 382 } 383 384 // bot asks for a non-existent generation, no new key is created. 385 badGen := teambotEK.Generation() + 50 386 _, err = ekLib3.GetTeambotEK(mctx3, teamID, botuaUID, badGen, nil) 387 require.Error(t, err) 388 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 389 ekNeededArg = keybase1.TeambotEkNeededArg{ 390 Id: teamID, 391 Uid: botua.uid, 392 Generation: 0, 393 } 394 checkTeambotEKNeededNotifications(user1.tc, user1.notifications, ekNeededArg) 395 checkTeambotEKNeededNotifications(user2.tc, user2.notifications, ekNeededArg) 396 noNewTeambotEKNotification(botua.tc, botua.notifications) 397 } 398 399 func TestEphemeralAddMemberWithTeamEK(t *testing.T) { 400 runAddMember(t, true /* createTeamEK*/) 401 } 402 403 func TestEphemeralAddMemberNoTeamEK(t *testing.T) { 404 runAddMember(t, false /* createTeamEK*/) 405 } 406 407 func getTeamEK(mctx libkb.MetaContext, teamID keybase1.TeamID, generation keybase1.EkGeneration) (keybase1.TeamEk, error) { 408 ek, err := mctx.G().GetTeamEKBoxStorage().Get(mctx, teamID, generation, nil) 409 if err != nil { 410 return keybase1.TeamEk{}, err 411 } 412 413 typ, err := ek.KeyType() 414 if err != nil { 415 return keybase1.TeamEk{}, err 416 } 417 if !typ.IsTeam() { 418 return keybase1.TeamEk{}, ephemeral.NewIncorrectTeamEphemeralKeyTypeError(typ, keybase1.TeamEphemeralKeyType_TEAM) 419 } 420 return ek.Team(), nil 421 } 422 423 func runAddMember(t *testing.T, createTeamEK bool) { 424 ctx := newSMUContext(t) 425 defer ctx.cleanup() 426 427 ann := ctx.installKeybaseForUser("ann", 10) 428 ann.signup() 429 bob := ctx.installKeybaseForUser("bob", 10) 430 bob.signup() 431 432 annMctx := ann.MetaContext() 433 initEphemeralForTests(t, annMctx) 434 bobMctx := bob.MetaContext() 435 initEphemeralForTests(t, bobMctx) 436 437 team := ann.createTeam([]*smuUser{}) 438 teamName, err := keybase1.TeamNameFromString(team.name) 439 require.NoError(t, err) 440 teamID := teamName.ToPrivateTeamID() 441 442 var expectedMetadata keybase1.TeamEkMetadata 443 var expectedGeneration keybase1.EkGeneration 444 if createTeamEK { 445 ekLib := annMctx.G().GetEKLib() 446 ek, created, err := ekLib.GetOrCreateLatestTeamEK(annMctx, teamID) 447 require.NoError(t, err) 448 require.True(t, created) 449 typ, err := ek.KeyType() 450 require.NoError(t, err) 451 require.True(t, typ.IsTeam()) 452 teamEK := ek.Team() 453 454 expectedMetadata = teamEK.Metadata 455 expectedGeneration = expectedMetadata.Generation 456 } else { 457 expectedMetadata = keybase1.TeamEkMetadata{} 458 expectedGeneration = 1 459 } 460 461 ann.addWriter(team, bob) 462 463 annTeamEK, annErr := getTeamEK(annMctx, teamID, expectedGeneration) 464 bobTeamEK, bobErr := getTeamEK(bobMctx, teamID, expectedGeneration) 465 if createTeamEK { 466 require.NoError(t, annErr) 467 require.NoError(t, bobErr) 468 } else { 469 require.Error(t, annErr) 470 require.IsType(t, ephemeral.EphemeralKeyError{}, annErr) 471 ekErr := annErr.(ephemeral.EphemeralKeyError) 472 require.Equal(t, ephemeral.DefaultHumanErrMsg, ekErr.HumanError()) 473 474 require.Error(t, bobErr) 475 require.IsType(t, ephemeral.EphemeralKeyError{}, bobErr) 476 ekErr = bobErr.(ephemeral.EphemeralKeyError) 477 require.Equal(t, ephemeral.DefaultHumanErrMsg, ekErr.HumanError()) 478 } 479 require.Equal(t, bobTeamEK.Metadata, expectedMetadata) 480 require.Equal(t, annTeamEK.Metadata, expectedMetadata) 481 } 482 483 func TestEphemeralResetMember(t *testing.T) { 484 t.Skip() 485 ctx := newSMUContext(t) 486 defer ctx.cleanup() 487 488 ann := ctx.installKeybaseForUser("ann", 10) 489 ann.signup() 490 ann.registerForNotifications() 491 bob := ctx.installKeybaseForUser("bob", 10) 492 bob.signup() 493 joe := ctx.installKeybaseForUser("joe", 10) 494 joe.signup() 495 496 annMctx := ann.MetaContext() 497 initEphemeralForTests(t, annMctx) 498 bobMctx := bob.MetaContext() 499 initEphemeralForTests(t, bobMctx) 500 joeMctx := joe.MetaContext() 501 initEphemeralForTests(t, joeMctx) 502 503 team := ann.createTeam([]*smuUser{bob}) 504 teamName, err := keybase1.TeamNameFromString(team.name) 505 require.NoError(t, err) 506 teamID := teamName.ToPrivateTeamID() 507 508 // Reset bob, invaliding any userEK he has. 509 bob.reset() 510 511 annEkLib := annMctx.G().GetEKLib() 512 ek, created, err := annEkLib.GetOrCreateLatestTeamEK(annMctx, teamID) 513 require.NoError(t, err) 514 require.True(t, created) 515 516 typ, err := ek.KeyType() 517 require.NoError(t, err) 518 require.True(t, typ.IsTeam()) 519 teamEK := ek.Team() 520 521 expectedMetadata := teamEK.Metadata 522 expectedGeneration := expectedMetadata.Generation 523 524 annTeamEK, annErr := getTeamEK(annMctx, teamID, expectedGeneration) 525 require.NoError(t, annErr) 526 require.Equal(t, annTeamEK.Metadata, expectedMetadata) 527 528 // Bob should not have access to this teamEK since he's no longer in the 529 // team after resetting. 530 bob.loginAfterReset(10) 531 bobMctx = bob.MetaContext() 532 // make sure bob has new EKs 533 err = bobMctx.G().GetEKLib().KeygenIfNeeded(bobMctx) 534 require.NoError(t, err) 535 536 _, bobErr := getTeamEK(bobMctx, teamID, expectedGeneration) 537 require.Error(t, bobErr) 538 require.IsType(t, libkb.AppStatusError{}, bobErr) 539 appStatusErr := bobErr.(libkb.AppStatusError) 540 require.Equal(t, appStatusErr.Code, libkb.SCNotFound) 541 542 // Also add joe who has a valid userEK 543 ann.addWriter(team, bob) 544 ann.addWriter(team, joe) 545 ann.waitForNewlyAddedToTeamByID(teamID) 546 547 // ann gets the new teamEk which joe can access but bob cannot after he reset. 548 ek2, created, err := annEkLib.GetOrCreateLatestTeamEK(annMctx, teamID) 549 require.NoError(t, err) 550 require.False(t, created) 551 typ, err = ek.KeyType() 552 require.NoError(t, err) 553 require.True(t, typ.IsTeam()) 554 teamEK2 := ek2.Team() 555 556 expectedMetadata2 := teamEK2.Metadata 557 expectedGeneration2 := expectedMetadata2.Generation 558 // We can't require that the next generation is exactly 1 greater than the 559 // previous, because there's a race where a CLKR sneaks in here. 560 require.True(t, expectedGeneration < expectedGeneration2) 561 562 annTeamEK, annErr = getTeamEK(annMctx, teamID, expectedGeneration2) 563 require.NoError(t, annErr) 564 require.Equal(t, annTeamEK.Metadata, expectedMetadata2) 565 566 bobTeamEK, bobErr := getTeamEK(bobMctx, teamID, expectedGeneration2) 567 require.NoError(t, bobErr) 568 require.Equal(t, bobTeamEK.Metadata, expectedMetadata2) 569 570 joeTeamEk, joeErr := getTeamEK(joeMctx, teamID, expectedGeneration2) 571 require.NoError(t, joeErr) 572 require.Equal(t, joeTeamEk.Metadata, expectedMetadata2) 573 } 574 575 func TestEphemeralRotateWithTeamEK(t *testing.T) { 576 runRotate(t, true /* createTeamEK*/) 577 } 578 579 func TestEphemeralRotateNoTeamEK(t *testing.T) { 580 runRotate(t, false /* createTeamEK*/) 581 } 582 583 func runRotate(t *testing.T, createTeamEK bool) { 584 tt := newTeamTester(t) 585 defer tt.cleanup() 586 587 ann := tt.addUser("ann") 588 bob := tt.addUserWithPaper("bob") 589 590 annMctx := ann.MetaContext() 591 err := annMctx.G().GetEKLib().KeygenIfNeeded(annMctx) 592 require.NoError(t, err) 593 bobMctx := bob.MetaContext() 594 err = bobMctx.G().GetEKLib().KeygenIfNeeded(bobMctx) 595 require.NoError(t, err) 596 597 teamID, teamName := ann.createTeam2() 598 599 // After rotate, we should have rolled the teamEK if one existed. 600 var expectedGeneration keybase1.EkGeneration 601 if createTeamEK { 602 ekLib := annMctx.G().GetEKLib() 603 teamEK, created, err := ekLib.GetOrCreateLatestTeamEK(annMctx, teamID) 604 require.NoError(t, err) 605 require.True(t, created) 606 expectedGeneration = teamEK.Generation() + 1 607 } else { 608 expectedGeneration = 1 609 } 610 611 ann.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER) 612 613 bob.revokePaperKey() 614 ann.waitForAnyRotateByID(teamID, keybase1.Seqno(2) /* toSeqno */, keybase1.Seqno(1) /* toHiddenSeqno */) 615 616 storage := annMctx.G().GetTeamEKBoxStorage() 617 teamEK, err := storage.Get(annMctx, teamID, expectedGeneration, nil) 618 if createTeamEK { 619 require.NoError(t, err) 620 } else { 621 require.Error(t, err) 622 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 623 ekErr := err.(ephemeral.EphemeralKeyError) 624 require.Equal(t, ephemeral.DefaultHumanErrMsg, ekErr.HumanError()) 625 require.Equal(t, keybase1.TeamEphemeralKey{}, teamEK) 626 } 627 } 628 629 func TestEphemeralRotateSkipTeamEKRoll(t *testing.T) { 630 tt := newTeamTester(t) 631 defer tt.cleanup() 632 633 ann := tt.addUser("ann") 634 bob := tt.addUserWithPaper("bob") 635 636 annMctx := ann.MetaContext() 637 err := annMctx.G().GetEKLib().KeygenIfNeeded(annMctx) 638 require.NoError(t, err) 639 bobMctx := bob.MetaContext() 640 err = bobMctx.G().GetEKLib().KeygenIfNeeded(bobMctx) 641 require.NoError(t, err) 642 643 teamID, teamName := ann.createTeam2() 644 645 // Get our ephemeral keys before the revoke and ensure we can still access 646 // them after. 647 ekLib := annMctx.G().GetEKLib() 648 teamEKPreRoll, created, err := ekLib.GetOrCreateLatestTeamEK(annMctx, teamID) 649 require.NoError(t, err) 650 require.True(t, created) 651 652 // This is a hack to skip the teamEK generation during the PTK roll. 653 // We want to validate that we can create a new teamEK after this roll even 654 // though our existing teamEK is signed by a (now) invalid PTK 655 annMctx.G().SetEKLib(nil) 656 657 ann.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER) 658 659 bob.revokePaperKey() 660 ann.waitForAnyRotateByID(teamID, keybase1.Seqno(2) /* toSeqno */, keybase1.Seqno(1) /* toHiddenSeqno */) 661 annMctx.G().SetEKLib(ekLib) 662 663 // Ensure that we access the old teamEK even though it was signed by a 664 // non-latest PTK 665 teamEKBoxStorage := annMctx.G().GetTeamEKBoxStorage() 666 teamEKBoxStorage.ClearCache() 667 _, err = annMctx.G().LocalDb.Nuke() // Force us to refetch and verify the key from the server 668 require.NoError(t, err) 669 teamEKPostRoll, err := teamEKBoxStorage.Get(annMctx, teamID, teamEKPreRoll.Generation(), nil) 670 require.NoError(t, err) 671 require.Equal(t, teamEKPreRoll, teamEKPostRoll) 672 673 // After rotating, ensure we can create a new TeamEK without issue. 674 needed, err := ekLib.NewTeamEKNeeded(annMctx, teamID) 675 require.NoError(t, err) 676 require.True(t, needed) 677 678 merkleRoot, err := annMctx.G().GetMerkleClient().FetchRootFromServer(ann.MetaContext(), libkb.EphemeralKeyMerkleFreshness) 679 require.NoError(t, err) 680 metadata, err := ephemeral.ForcePublishNewTeamEKForTesting(annMctx, teamID, *merkleRoot) 681 require.NoError(t, err) 682 require.Equal(t, teamEKPreRoll.Generation()+1, metadata.Generation) 683 } 684 685 func TestEphemeralNewUserEKAndTeamEKAfterRevokes(t *testing.T) { 686 tt := newTeamTester(t) 687 defer tt.cleanup() 688 689 ann := tt.addUserWithPaper("ann") 690 691 teamID, _ := ann.createTeam2() 692 693 annMctx := ann.MetaContext() 694 ekLib := annMctx.G().GetEKLib() 695 err := ekLib.KeygenIfNeeded(annMctx) 696 require.NoError(t, err) 697 698 _, created, err := ekLib.GetOrCreateLatestTeamEK(annMctx, teamID) 699 require.NoError(t, err) 700 require.True(t, created) 701 userEKBoxStorage := annMctx.G().GetUserEKBoxStorage() 702 gen, err := userEKBoxStorage.MaxGeneration(annMctx, false) 703 require.NoError(t, err) 704 userEKPreRevoke, err := userEKBoxStorage.Get(annMctx, gen, nil) 705 require.NoError(t, err) 706 707 // Provision a new device that we can revoke. 708 newDevice, cleanup := ann.provisionNewDevice() 709 defer cleanup() 710 711 // Revoke it. 712 revokeEngine := engine.NewRevokeDeviceEngine(annMctx.G(), engine.RevokeDeviceEngineArgs{ 713 ID: newDevice.deviceKey.DeviceID, 714 ForceSelf: true, 715 ForceLast: false, 716 // We don't need a UserEK here since we force generate it below 717 SkipUserEKForTesting: true, 718 }) 719 uis := libkb.UIs{ 720 LogUI: annMctx.G().Log, 721 SecretUI: ann.newSecretUI(), 722 } 723 m := ann.MetaContext().WithUIs(uis) 724 err = engine.RunEngine2(m, revokeEngine) 725 require.NoError(t, err) 726 727 // Ensure that we access the old userEKs even though it was signed by a 728 // non-latest PUK 729 userEKBoxStorage.ClearCache() 730 _, err = annMctx.G().LocalDb.Nuke() // Force us to refetch and verify the key from the server 731 require.NoError(t, err) 732 userEKPostRevoke, err := userEKBoxStorage.Get(annMctx, userEKPreRevoke.Metadata.Generation, nil) 733 require.NoError(t, err) 734 require.Equal(t, userEKPreRevoke, userEKPostRevoke) 735 736 // Now provision a new userEK. This makes sure that we don't get confused 737 // by the revoked device's deviceEKs. 738 merkleRoot, err := annMctx.G().GetMerkleClient().FetchRootFromServer(annMctx, libkb.EphemeralKeyMerkleFreshness) 739 require.NoError(t, err) 740 _, err = ephemeral.ForcePublishNewUserEKForTesting(annMctx, *merkleRoot) 741 require.NoError(t, err) 742 743 // And do the same for the teamEK, just to be sure. 744 _, err = ephemeral.ForcePublishNewTeamEKForTesting(annMctx, teamID, *merkleRoot) 745 require.NoError(t, err) 746 } 747 748 func readdToTeamWithEKs(t *testing.T, leave bool) { 749 tt := newTeamTester(t) 750 defer tt.cleanup() 751 752 // Make standalone user that will not run gregor. This is 753 // important in the *leave* case, where we want to observe 754 // effects of team key and EK not being rotated. 755 user1 := makeUserStandalone(t, tt, "user1", standaloneUserArgs{ 756 disableGregor: true, 757 suppressTeamChatAnnounce: true, 758 }) 759 user2 := tt.addUser("wtr") 760 761 teamID, teamName := user1.createTeam2() 762 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 763 user2.waitForNewlyAddedToTeamByID(teamID) 764 765 mctx1 := user1.MetaContext() 766 ekLib := mctx1.G().GetEKLib() 767 err := ekLib.KeygenIfNeeded(mctx1) 768 require.NoError(t, err) 769 770 mctx2 := user2.MetaContext() 771 err = mctx2.G().GetEKLib().KeygenIfNeeded(mctx2) 772 require.NoError(t, err) 773 774 teamEK, created, err := ekLib.GetOrCreateLatestTeamEK(mctx1, teamID) 775 require.NoError(t, err) 776 require.True(t, created) 777 778 currentGen := teamEK.Generation() 779 var expectedGen keybase1.EkGeneration 780 if leave { 781 user2.leave(teamName.String()) 782 expectedGen = currentGen // user left, no one to rotate keys. 783 } else { 784 user1.removeTeamMember(teamName.String(), user2.username) 785 expectedGen = currentGen + 1 // admin removes user, rotates TK and EK 786 } 787 788 // After leaving user2 won't have access to the current teamEK 789 _, err = user2.tc.G.GetTeamEKBoxStorage().Get(user2.MetaContext(), teamID, currentGen, nil) 790 require.Error(t, err) 791 require.IsType(t, libkb.AppStatusError{}, err) 792 appStatusErr := err.(libkb.AppStatusError) 793 require.Equal(t, appStatusErr.Code, libkb.SCNotFound) 794 795 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 796 user2.waitForNewlyAddedToTeamByID(teamID) 797 798 // Test that user1 and user2 both have access to the currentTeamEK 799 // (whether we recreated or reboxed) 800 teamEK2U1, err := user1.tc.G.GetTeamEKBoxStorage().Get(mctx1, teamID, expectedGen, nil) 801 require.NoError(t, err) 802 803 teamEK2U2, err := user2.tc.G.GetTeamEKBoxStorage().Get(user2.MetaContext(), teamID, expectedGen, nil) 804 require.NoError(t, err) 805 806 require.Equal(t, teamEK2U1, teamEK2U2) 807 } 808 809 func TestEphemeralTeamMemberLeaveAndReadd(t *testing.T) { 810 readdToTeamWithEKs(t, true /* leave */) 811 } 812 813 func TestEphemeralTeamMemberRemoveAndReadd(t *testing.T) { 814 readdToTeamWithEKs(t, false /* leave */) 815 } 816 817 func TestEphemeralAfterEKError(t *testing.T) { 818 tt := newTeamTester(t) 819 defer tt.cleanup() 820 821 user1 := makeUserStandalone(t, tt, "user1", standaloneUserArgs{ 822 disableGregor: true, 823 suppressTeamChatAnnounce: true, 824 }) 825 teamID, teamName := user1.createTeam2() 826 g1 := user1.tc.G 827 mctx1 := user1.MetaContext() 828 err := mctx1.G().GetEKLib().KeygenIfNeeded(mctx1) 829 require.NoError(t, err) 830 merkleRoot, err := g1.GetMerkleClient().FetchRootFromServer(mctx1, libkb.EphemeralKeyMerkleFreshness) 831 require.NoError(t, err) 832 // Force two team EKs to be created and then create/add u2 to the team. 833 // They should not be able to access the first key since they were added 834 // after (they are reboxed for the second as part of the add 835 teamEKMetadata1, err := ephemeral.ForcePublishNewTeamEKForTesting(mctx1, teamID, *merkleRoot) 836 require.NoError(t, err) 837 teamEKMetadata2, err := ephemeral.ForcePublishNewTeamEKForTesting(mctx1, teamID, *merkleRoot) 838 require.NoError(t, err) 839 840 user2 := tt.addUserWithPaper("u2") 841 mctx2 := user2.MetaContext() 842 err = mctx2.G().GetEKLib().KeygenIfNeeded(mctx2) 843 require.NoError(t, err) 844 user1.addTeamMember(teamName.String(), user2.username, keybase1.TeamRole_WRITER) 845 user2.waitForNewlyAddedToTeamByID(teamID) 846 847 _, err = mctx2.G().GetTeamEKBoxStorage().Get(mctx2, teamID, teamEKMetadata1.Generation, nil) 848 require.Error(t, err) 849 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 850 ekErr := err.(ephemeral.EphemeralKeyError) 851 require.Equal(t, libkb.SCEphemeralMemberAfterEK, ekErr.StatusCode) 852 853 ek2, err := mctx2.G().GetTeamEKBoxStorage().Get(mctx2, teamID, teamEKMetadata2.Generation, nil) 854 require.NoError(t, err) 855 typ, err := ek2.KeyType() 856 require.NoError(t, err) 857 require.True(t, typ.IsTeam()) 858 teamEK2 := ek2.Team() 859 require.Equal(t, teamEKMetadata2, teamEK2.Metadata) 860 861 // Force a second userEK so when the new device is provisioned it is only 862 // reboxed for the second userEK. Try to access the first userEK and fail. 863 userEKMetdata, err := ephemeral.ForcePublishNewUserEKForTesting(mctx2, *merkleRoot) 864 require.NoError(t, err) 865 newDevice, cleanup := user2.provisionNewDevice() 866 defer cleanup() 867 mctx2 = libkb.NewMetaContextForTest(*newDevice.tctx) 868 869 _, err = mctx2.G().GetUserEKBoxStorage().Get(mctx2, userEKMetdata.Generation-1, nil) 870 require.Error(t, err) 871 require.IsType(t, ephemeral.EphemeralKeyError{}, err) 872 ekErr = err.(ephemeral.EphemeralKeyError) 873 require.Equal(t, libkb.SCEphemeralDeviceAfterEK, ekErr.StatusCode) 874 }