github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/ephemeral/lib_test.go (about) 1 package ephemeral 2 3 import ( 4 "fmt" 5 "net/url" 6 "os" 7 "path/filepath" 8 "testing" 9 "time" 10 11 "github.com/keybase/client/go/engine" 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/client/go/teams" 15 "github.com/keybase/clockwork" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func getNoiseFilePath(tc libkb.TestContext, key string) string { 20 noiseName := fmt.Sprintf("%s.ns", url.QueryEscape(key)) 21 return filepath.Join(tc.G.Env.GetDataDir(), "eraseablekvstore", "device-eks", noiseName) 22 } 23 24 func TestKeygenIfNeeded(t *testing.T) { 25 tc, mctx, _ := ephemeralKeyTestSetup(t) 26 defer tc.Cleanup() 27 28 ekLib, ok := tc.G.GetEKLib().(*EKLib) 29 require.True(t, ok) 30 deviceEKStorage := tc.G.GetDeviceEKStorage() 31 userEKBoxStorage := tc.G.GetUserEKBoxStorage() 32 err := ekLib.KeygenIfNeeded(mctx) 33 require.NoError(t, err) 34 35 expectedDeviceEKGen, err := deviceEKStorage.MaxGeneration(mctx, false) 36 require.NoError(t, err) 37 if expectedDeviceEKGen < 0 { 38 expectedDeviceEKGen = 1 39 deviceEKNeeded, err := ekLib.NewDeviceEKNeeded(mctx) 40 require.NoError(t, err) 41 require.True(t, deviceEKNeeded) 42 } 43 44 expectedUserEKGen, err := userEKBoxStorage.MaxGeneration(mctx, false) 45 require.NoError(t, err) 46 if expectedUserEKGen < 0 { 47 expectedUserEKGen = 1 48 userEKNeeded, err := ekLib.NewUserEKNeeded(mctx) 49 require.NoError(t, err) 50 require.True(t, userEKNeeded) 51 } 52 53 keygen := func(expectedDeviceEKGen, expectedUserEKGen keybase1.EkGeneration) { 54 err := ekLib.KeygenIfNeeded(mctx) 55 require.NoError(t, err) 56 57 // verify deviceEK 58 deviceEKNeeded, err := ekLib.NewDeviceEKNeeded(mctx) 59 require.NoError(t, err) 60 require.False(t, deviceEKNeeded) 61 62 deviceEKMaxGen, err := deviceEKStorage.MaxGeneration(mctx, false) 63 require.NoError(t, err) 64 require.Equal(t, expectedDeviceEKGen, deviceEKMaxGen) 65 66 // verify userEK 67 userEKNeeded, err := ekLib.NewUserEKNeeded(mctx) 68 require.NoError(t, err) 69 require.False(t, userEKNeeded) 70 71 userEKMaxGen, err := userEKBoxStorage.MaxGeneration(mctx, false) 72 require.NoError(t, err) 73 require.Equal(t, expectedUserEKGen, userEKMaxGen) 74 } 75 76 // If we retry keygen, we don't regenerate keys 77 t.Logf("Initial keygen") 78 keygen(expectedDeviceEKGen, expectedUserEKGen) 79 t.Logf("Keygen again does not create new keys") 80 keygen(expectedDeviceEKGen, expectedUserEKGen) 81 82 rawDeviceEKStorage := NewDeviceEKStorage(mctx) 83 rawUserEKBoxStorage := NewUserEKBoxStorage() 84 85 // Let's purge our local userEK store and make sure we don't regenerate 86 // (respecting the server max) 87 err = rawUserEKBoxStorage.Delete(mctx, expectedUserEKGen) 88 require.NoError(t, err) 89 userEKBoxStorage.ClearCache() 90 keygen(expectedDeviceEKGen, expectedUserEKGen) 91 92 // Now let's kill our deviceEK as well by deleting the noise file, we 93 // should regenerate a new userEK since we can't access the old one 94 key, err := rawDeviceEKStorage.key(mctx, expectedDeviceEKGen) 95 require.NoError(t, err) 96 noiseFilePath := getNoiseFilePath(tc, key) 97 err = os.Remove(noiseFilePath) 98 require.NoError(t, err) 99 100 deviceEKStorage.ClearCache() 101 expectedDeviceEKGen++ 102 expectedUserEKGen++ 103 t.Logf("Keygen with corrupted deviceEK works") 104 keygen(expectedDeviceEKGen, expectedUserEKGen) 105 106 // Test ForceDeleteAll 107 err = deviceEKStorage.ForceDeleteAll(mctx, tc.G.Env.GetUsername()) 108 require.NoError(t, err) 109 deviceEKs, err := rawDeviceEKStorage.GetAll(mctx) 110 require.NoError(t, err) 111 require.Len(t, deviceEKs, 0) 112 } 113 114 func TestNewTeamEKNeeded(t *testing.T) { 115 tc, mctx, _ := ephemeralKeyTestSetup(t) 116 defer tc.Cleanup() 117 118 teamID := createTeam(tc) 119 ekLib, ok := tc.G.GetEKLib().(*EKLib) 120 require.True(t, ok) 121 fc := clockwork.NewFakeClockAt(time.Now()) 122 ekLib.SetClock(fc) 123 deviceEKStorage := tc.G.GetDeviceEKStorage() 124 userEKBoxStorage := tc.G.GetUserEKBoxStorage() 125 teamEKBoxStorage := tc.G.GetTeamEKBoxStorage() 126 127 // We don't have any keys, so we should need a new teamEK 128 needed, err := ekLib.NewTeamEKNeeded(mctx, teamID) 129 require.NoError(t, err) 130 require.True(t, needed) 131 132 expectedTeamEKGen, err := teamEKBoxStorage.MaxGeneration(mctx, teamID, false) 133 require.NoError(t, err) 134 if expectedTeamEKGen < 0 { 135 expectedTeamEKGen = 1 136 } 137 138 expectedDeviceEKGen, err := deviceEKStorage.MaxGeneration(mctx, false) 139 require.NoError(t, err) 140 if expectedDeviceEKGen < 0 { 141 expectedDeviceEKGen = 1 142 } 143 144 expectedUserEKGen, err := userEKBoxStorage.MaxGeneration(mctx, false) 145 require.NoError(t, err) 146 if expectedUserEKGen < 0 { 147 expectedUserEKGen = 1 148 } 149 150 assertKeyGenerations := func(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen keybase1.EkGeneration, shouldCreate, teamEKCreationInProgress bool) { 151 teamEK, created, err := ekLib.GetOrCreateLatestTeamEK(mctx, teamID) 152 require.NoError(t, err) 153 require.Equal(t, shouldCreate, created) 154 155 // verify the ekLib teamEKGenCache is working 156 cacheKey := ekLib.cacheKey(teamID, keybase1.TeamEphemeralKeyType_TEAM) 157 val, ok := ekLib.teamEKGenCache.Get(cacheKey) 158 require.True(t, ok) 159 cacheEntry, expired := ekLib.isEntryExpired(val) 160 require.False(t, expired) 161 require.NotNil(t, cacheEntry) 162 require.Equal(t, teamEKCreationInProgress, cacheEntry.CreationInProgress) 163 require.Equal(t, teamEK.Generation(), cacheEntry.Generation) 164 165 // verify deviceEK 166 deviceEKNeeded, err := ekLib.NewDeviceEKNeeded(mctx) 167 require.NoError(t, err) 168 require.False(t, deviceEKNeeded) 169 170 deviceEKMaxGen, err := deviceEKStorage.MaxGeneration(mctx, false) 171 require.NoError(t, err) 172 require.Equal(t, expectedDeviceEKGen, deviceEKMaxGen) 173 174 // verify userEK 175 userEKNeeded, err := ekLib.NewUserEKNeeded(mctx) 176 require.NoError(t, err) 177 require.False(t, userEKNeeded) 178 179 userEKMaxGen, err := userEKBoxStorage.MaxGeneration(mctx, false) 180 require.NoError(t, err) 181 require.Equal(t, expectedUserEKGen, userEKMaxGen) 182 183 // verify teamEK 184 teamEKGen, err := teamEKBoxStorage.MaxGeneration(mctx, teamID, false) 185 require.NoError(t, err) 186 require.Equal(t, expectedTeamEKGen, teamEKGen) 187 require.Equal(t, expectedTeamEKGen, teamEK.Generation()) 188 189 teamEKNeeded, err := ekLib.NewTeamEKNeeded(mctx, teamID) 190 require.NoError(t, err) 191 require.False(t, teamEKNeeded) 192 } 193 194 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, true /*created*/, false /* teamEKCreationInProgress */) 195 // If we retry keygen, we don't regenerate keys 196 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, false /* teamEKCreationInProgress */) 197 198 rawDeviceEKStorage := NewDeviceEKStorage(mctx) 199 rawUserEKBoxStorage := NewUserEKBoxStorage() 200 rawTeamEKBoxStorage := NewTeamEKBoxStorage(NewTeamEphemeralKeyer()) 201 202 // Let's purge our local teamEK store and make sure we don't regenerate 203 // (respecting the server max) 204 err = rawTeamEKBoxStorage.Delete(mctx, teamID, expectedTeamEKGen) 205 require.NoError(t, err) 206 teamEKBoxStorage.ClearCache() 207 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created */, false /* teamEKCreationInProgress */) 208 209 // Now let's kill our userEK, we should gracefully not regenerate 210 // since we can still fetch the userEK from the server. 211 err = rawUserEKBoxStorage.Delete(mctx, expectedUserEKGen) 212 require.NoError(t, err) 213 tc.G.GetDeviceEKStorage().ClearCache() 214 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, false /* teamEKCreationInProgress */) 215 216 // Now let's kill our deviceEK as well, and we should generate all new keys 217 err = rawDeviceEKStorage.Delete(mctx, expectedDeviceEKGen, "") 218 require.NoError(t, err) 219 tc.G.GetDeviceEKStorage().ClearCache() 220 expectedDeviceEKGen++ 221 expectedUserEKGen++ 222 expectedTeamEKGen++ 223 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, true /*created*/, false /* teamEKCreationInProgress */) 224 225 // If we try to access an older teamEK that we cannot access, we don't 226 // create a new teamEK 227 teamEK, err := ekLib.GetTeamEK(mctx, teamID, expectedTeamEKGen-1, nil) 228 require.Error(t, err) 229 require.IsType(t, EphemeralKeyError{}, err) 230 ekErr := err.(EphemeralKeyError) 231 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 232 require.Equal(t, teamEK, keybase1.TeamEphemeralKey{}) 233 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, false /* teamEKCreationInProgress */) 234 235 // Now let's kill our deviceEK by corrupting a single bit in the noiseFile, 236 // so we can no longer access the latest teamEK and will generate a new one 237 // and verify it is the new valid max. 238 key, err := rawDeviceEKStorage.key(mctx, expectedDeviceEKGen) 239 require.NoError(t, err) 240 noiseFilePath := getNoiseFilePath(tc, key) 241 noise, err := os.ReadFile(noiseFilePath) 242 require.NoError(t, err) 243 244 // flip one bit 245 corruptedNoise := make([]byte, len(noise)) 246 copy(corruptedNoise, noise) 247 corruptedNoise[0] ^= 0x01 248 249 err = os.WriteFile(noiseFilePath, corruptedNoise, libkb.PermFile) 250 require.NoError(t, err) 251 tc.G.GetDeviceEKStorage().ClearCache() 252 253 ch := make(chan bool, 1) 254 ekLib.setBackgroundCreationTestCh(ch) 255 teamEK, err = ekLib.GetTeamEK(mctx, teamID, expectedTeamEKGen, nil) 256 require.Error(t, err) 257 require.IsType(t, EphemeralKeyError{}, err) 258 ekErr = err.(EphemeralKeyError) 259 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 260 require.Equal(t, teamEK, keybase1.TeamEphemeralKey{}) 261 t.Logf("before expectedTeamEkGen: %v", expectedTeamEKGen) 262 select { 263 case created := <-ch: 264 require.True(t, created) 265 case <-time.After(time.Second * 20): 266 t.Fatalf("teamEK background creation failed") 267 } 268 269 expectedDeviceEKGen++ 270 expectedUserEKGen++ 271 expectedTeamEKGen++ 272 t.Logf("after expectedTeamEkGen: %v", expectedTeamEKGen) 273 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, false /* teamEKCreationInProgress */) 274 275 // Fake the teamEK creation time so we are forced to generate a new one. 276 forceEKCtime := func(generation keybase1.EkGeneration, d time.Duration) { 277 _, err = rawTeamEKBoxStorage.Get(mctx, teamID, generation, nil) 278 require.NoError(t, err) 279 cache, found, err := rawTeamEKBoxStorage.getCacheForTeamID(mctx, teamID) 280 require.NoError(t, err) 281 require.True(t, found) 282 cacheItem, ok := cache[generation] 283 require.True(t, ok) 284 require.False(t, cacheItem.HasError()) 285 boxed := cacheItem.TeamEKBoxed 286 typ, err := boxed.KeyType() 287 require.NoError(t, err) 288 require.True(t, typ.IsTeam()) 289 teamEKBoxed := boxed.Team() 290 teamEKBoxed.Metadata.Ctime = keybase1.ToTime(teamEKBoxed.Metadata.Ctime.Time().Add(d)) 291 err = teamEKBoxStorage.Put(mctx, teamID, generation, 292 keybase1.NewTeamEphemeralKeyBoxedWithTeam(teamEKBoxed)) 293 require.NoError(t, err) 294 } 295 296 // First we ensure that we don't do background generation for expired teamEKs. 297 fc.Advance(LibCacheEntryLifetime) // expire our cache 298 forceEKCtime(expectedTeamEKGen, -libkb.EphemeralKeyGenInterval) 299 expectedTeamEKGen++ 300 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, true /*created*/, false /* teamEKCreationInProgress */) 301 302 // If we are *almost* expired, background generation is possible. 303 fc.Advance(LibCacheEntryLifetime) // expire our cache 304 forceEKCtime(expectedTeamEKGen, -libkb.EphemeralKeyGenInterval+30*time.Minute) 305 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, true /* teamEKCreationInProgress */) 306 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, true /* teamEKCreationInProgress */) 307 // Signal background generation should start 308 ch <- true 309 310 // Wait until background generation completes 311 select { 312 case created := <-ch: 313 require.True(t, created) 314 case <-time.After(time.Second * 20): 315 t.Fatalf("teamEK background creation failed") 316 } 317 close(ch) 318 expectedTeamEKGen++ 319 assertKeyGenerations(expectedDeviceEKGen, expectedUserEKGen, expectedTeamEKGen, false /*created*/, false /* teamEKCreationInProgress */) 320 } 321 322 func TestCleanupStaleUserAndDeviceEKs(t *testing.T) { 323 tc, mctx, _ := ephemeralKeyTestSetup(t) 324 defer tc.Cleanup() 325 326 seed, err := newDeviceEphemeralSeed() 327 require.NoError(t, err) 328 s := tc.G.GetDeviceEKStorage() 329 ctimeExpired := time.Now().Add(-libkb.MaxEphemeralKeyStaleness * 3) 330 err = s.Put(mctx, 0, keybase1.DeviceEk{ 331 Seed: keybase1.Bytes32(seed), 332 Metadata: keybase1.DeviceEkMetadata{ 333 Ctime: keybase1.ToTime(ctimeExpired), 334 }, 335 }) 336 require.NoError(t, err) 337 338 ekLib, ok := tc.G.GetEKLib().(*EKLib) 339 require.True(t, ok) 340 err = ekLib.CleanupStaleUserAndDeviceEKs(mctx) 341 require.NoError(t, err) 342 343 deviceEK, err := s.Get(mctx, 0) 344 require.Error(t, err) 345 _, ok = err.(libkb.UnboxError) 346 require.True(t, ok) 347 require.Equal(t, keybase1.DeviceEk{}, deviceEK) 348 349 err = ekLib.CleanupStaleUserAndDeviceEKs(mctx) 350 require.NoError(t, err) 351 } 352 353 func TestCleanupStaleUserAndDeviceEKsOffline(t *testing.T) { 354 tc, mctx, _ := ephemeralKeyTestSetup(t) 355 defer tc.Cleanup() 356 357 seed, err := newDeviceEphemeralSeed() 358 require.NoError(t, err) 359 s := tc.G.GetDeviceEKStorage() 360 ctimeExpired := time.Now().Add(-libkb.MaxEphemeralKeyStaleness * 3) 361 err = s.Put(mctx, 0, keybase1.DeviceEk{ 362 Seed: keybase1.Bytes32(seed), 363 Metadata: keybase1.DeviceEkMetadata{ 364 Ctime: keybase1.ToTime(ctimeExpired), 365 DeviceCtime: keybase1.ToTime(ctimeExpired), 366 }, 367 }) 368 require.NoError(t, err) 369 370 ekLib, ok := tc.G.GetEKLib().(*EKLib) 371 require.True(t, ok) 372 ch := make(chan bool, 1) 373 ekLib.setBackgroundDeleteTestCh(ch) 374 err = ekLib.keygenIfNeeded(mctx, libkb.MerkleRoot{}, true /* shouldCleanup */) 375 require.Error(t, err) 376 _, ok = err.(EphemeralKeyError) 377 require.False(t, ok) 378 require.Equal(t, SkipKeygenNilMerkleRoot, err.Error()) 379 380 // Even though we return an error, we charge through on the deletion 381 // successfully. 382 <-ch 383 deviceEK, err := s.Get(mctx, 0) 384 require.Error(t, err) 385 _, ok = err.(libkb.UnboxError) 386 require.True(t, ok) 387 require.Equal(t, keybase1.DeviceEk{}, deviceEK) 388 err = ekLib.keygenIfNeeded(mctx, libkb.MerkleRoot{}, true /* shouldCleanup */) 389 require.Error(t, err) 390 _, ok = err.(libkb.UnboxError) 391 require.False(t, ok) 392 require.Equal(t, SkipKeygenNilMerkleRoot, err.Error()) 393 } 394 395 func TestLoginOneshotWithEphemeral(t *testing.T) { 396 tc, mctx, user := ephemeralKeyTestSetup(t) 397 defer tc.Cleanup() 398 uis := libkb.UIs{ 399 LogUI: tc.G.UI.GetLogUI(), 400 LoginUI: &libkb.TestLoginUI{RevokeBackup: false}, 401 SecretUI: &libkb.TestSecretUI{}, 402 } 403 mctx = mctx.WithUIs(uis) 404 teamID := createTeam(tc) 405 406 eng := engine.NewPaperKey(tc.G) 407 err := engine.RunEngine2(mctx, eng) 408 require.NoError(t, err) 409 require.NotZero(t, len(eng.Passphrase())) 410 require.NoError(t, tc.Logout()) 411 412 keygenWithOneshot := func() (keybase1.DeviceEk, keybase1.UserEk, keybase1.TeamEphemeralKey) { 413 tc := libkb.SetupTest(t, "ephemeral", 2) 414 defer tc.Cleanup() 415 mctx := libkb.NewMetaContextForTest(tc) 416 NewEphemeralStorageAndInstall(mctx) 417 teams.ServiceInit(tc.G) 418 419 eng := engine.NewLoginOneshot(tc.G, keybase1.LoginOneshotArg{ 420 Username: user.NormalizedUsername().String(), 421 PaperKey: eng.Passphrase(), 422 }) 423 err = engine.RunEngine2(mctx, eng) 424 require.NoError(t, err) 425 426 err = tc.G.GetEKLib().KeygenIfNeeded(mctx) 427 require.NoError(t, err) 428 429 deks := tc.G.GetDeviceEKStorage() 430 dekGen, err := deks.MaxGeneration(mctx, false) 431 require.NoError(t, err) 432 dek, err := deks.Get(mctx, dekGen) 433 require.NoError(t, err) 434 435 ueks := tc.G.GetUserEKBoxStorage() 436 uekGen, err := ueks.MaxGeneration(mctx, false) 437 require.NoError(t, err) 438 uek, err := ueks.Get(mctx, uekGen, nil) 439 require.NoError(t, err) 440 441 tek, created, err := tc.G.GetEKLib().GetOrCreateLatestTeamEK(mctx, teamID) 442 require.NoError(t, err) 443 require.True(t, created) 444 445 return dek, uek, tek 446 } 447 448 dek, uek, tek := keygenWithOneshot() 449 dek2, uek2, tek2 := keygenWithOneshot() 450 require.NotEqual(t, dek, dek2) 451 require.NotEqual(t, uek, uek2) 452 require.NotEqual(t, tek, tek2) 453 }