github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/ephemeral/user_ek_box_storage_test.go (about) 1 package ephemeral 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/keybase1" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func TestUserEKBoxStorage(t *testing.T) { 15 tc, mctx, _ := ephemeralKeyTestSetup(t) 16 defer tc.Cleanup() 17 18 merkleRootPtr, err := tc.G.GetMerkleClient().FetchRootFromServer(mctx, libkb.EphemeralKeyMerkleFreshness) 19 require.NoError(t, err) 20 merkleRoot := *merkleRootPtr 21 22 // Login hooks should have run 23 deviceEKStorage := tc.G.GetDeviceEKStorage() 24 deviceEKMaxGen, err := deviceEKStorage.MaxGeneration(mctx, false) 25 require.True(t, deviceEKMaxGen > 0) 26 require.NoError(t, err) 27 28 s := tc.G.GetUserEKBoxStorage() 29 userEKMaxGen, err := s.MaxGeneration(mctx, false) 30 require.True(t, userEKMaxGen > 0) 31 require.NoError(t, err) 32 33 userEKMetadata, err := publishNewUserEK(mctx, merkleRoot) 34 require.NoError(t, err) 35 36 // Test get valid & unbox 37 userEK, err := s.Get(mctx, userEKMetadata.Generation, nil) 38 require.NoError(t, err) 39 40 verifyUserEK(t, userEKMetadata, userEK) 41 42 // Test Get nonexistent 43 nonexistent, err := s.Get(mctx, userEKMetadata.Generation+1, nil) 44 require.Error(t, err) 45 require.IsType(t, EphemeralKeyError{}, err) 46 ekErr := err.(EphemeralKeyError) 47 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 48 require.Equal(t, keybase1.UserEk{}, nonexistent) 49 50 // include the cached error in the max 51 maxGeneration, err := s.MaxGeneration(mctx, true) 52 require.NoError(t, err) 53 require.Equal(t, userEKMetadata.Generation+1, maxGeneration) 54 55 // Test MaxGeneration 56 maxGeneration, err = s.MaxGeneration(mctx, false) 57 require.NoError(t, err) 58 require.True(t, maxGeneration > 0) 59 60 // NOTE: We don't expose Delete on the interface put on the GlobalContext 61 // since they should never be called, only DeleteExpired should be used. 62 // GetAll is also not exposed since it' only needed for tests. 63 rawUserEKBoxStorage := NewUserEKBoxStorage() 64 userEKs, err := rawUserEKBoxStorage.GetAll(mctx) 65 require.NoError(t, err) 66 require.EqualValues(t, maxGeneration, len(userEKs)) 67 68 userEK, ok := userEKs[userEKMetadata.Generation] 69 require.True(t, ok) 70 71 verifyUserEK(t, userEKMetadata, userEK) 72 73 // Let's delete our deviceEK and verify we can't unbox the userEK 74 rawDeviceEKStorage := NewDeviceEKStorage(mctx) 75 err = rawDeviceEKStorage.Delete(mctx, deviceEKMaxGen, "") 76 require.NoError(t, err) 77 78 deviceEKStorage.ClearCache() 79 deviceEK, err := deviceEKStorage.Get(mctx, deviceEKMaxGen) 80 require.Error(t, err) 81 require.IsType(t, libkb.UnboxError{}, err) 82 require.Equal(t, keybase1.DeviceEk{}, deviceEK) 83 84 bad, err := s.Get(mctx, userEKMetadata.Generation, nil) 85 require.Error(t, err) 86 require.IsType(t, EphemeralKeyError{}, err) 87 ekErr = err.(EphemeralKeyError) 88 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 89 require.Equal(t, keybase1.UserEk{}, bad) 90 91 // test delete 92 err = rawUserEKBoxStorage.Delete(mctx, userEKMetadata.Generation) 93 require.NoError(t, err) 94 95 userEK, err = rawUserEKBoxStorage.Get(mctx, userEKMetadata.Generation, nil) 96 require.Error(t, err) 97 require.IsType(t, EphemeralKeyError{}, err) 98 ekErr = err.(EphemeralKeyError) 99 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 100 require.Equal(t, keybase1.UserEk{}, userEK) 101 102 s.ClearCache() 103 104 maxGeneration, err = s.MaxGeneration(mctx, false) 105 require.NoError(t, err) 106 require.EqualValues(t, userEKMaxGen, maxGeneration) 107 108 expired, err := s.DeleteExpired(mctx, merkleRoot) 109 expected := []keybase1.EkGeneration(nil) 110 require.NoError(t, err) 111 require.Equal(t, expected, expired) 112 113 // Verify we store failures in the cache 114 t.Logf("cache failures") 115 nonexistent, err = rawUserEKBoxStorage.Get(mctx, userEKMetadata.Generation+1, nil) 116 require.Error(t, err) 117 require.IsType(t, EphemeralKeyError{}, err) 118 ekErr = err.(EphemeralKeyError) 119 require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError()) 120 require.Equal(t, keybase1.UserEk{}, nonexistent) 121 122 cache, err := rawUserEKBoxStorage.getCache(mctx) 123 require.NoError(t, err) 124 require.Len(t, cache, 3) 125 126 cacheItem, ok := cache[userEKMetadata.Generation+1] 127 require.True(t, ok) 128 require.True(t, cacheItem.HasError()) 129 } 130 131 // If we change the key format intentionally, we have to introduce some form of 132 // migration or versioning between the keys. This test should blow up if we 133 // break it unintentionally. 134 func TestUserEKStorageKeyFormat(t *testing.T) { 135 tc, mctx, _ := ephemeralKeyTestSetup(t) 136 defer tc.Cleanup() 137 138 s := NewUserEKBoxStorage() 139 uv, err := tc.G.GetMeUV(context.TODO()) 140 require.NoError(t, err) 141 142 key, err := s.dbKey(mctx) 143 require.NoError(t, err) 144 expected := fmt.Sprintf("userEphemeralKeyBox-%s-%s-%d", mctx.G().Env.GetUsername(), uv.EldestSeqno, userEKBoxStorageDBVersion) 145 require.Equal(t, expected, key.Key) 146 } 147 148 func TestUserEKBoxStorageDeleteExpiredKeys(t *testing.T) { 149 tc := libkb.SetupTest(t, "ephemeral", 2) 150 defer tc.Cleanup() 151 mctx := libkb.NewMetaContextForTest(tc) 152 153 s := NewUserEKBoxStorage() 154 now := time.Now() 155 156 // Test empty 157 expired := s.getExpiredGenerations(mctx, make(keyExpiryMap), now) 158 expected := []keybase1.EkGeneration(nil) 159 require.Equal(t, expected, expired) 160 161 // Test with a single key that is not expired 162 keyMap := keyExpiryMap{ 163 0: keybase1.ToTime(now), 164 } 165 expired = s.getExpiredGenerations(mctx, keyMap, now) 166 expected = nil 167 require.Equal(t, expected, expired) 168 169 // Test with a single key that is stale but not expired 170 keyMap = keyExpiryMap{ 171 0: keybase1.ToTime(now.Add(-libkb.MaxEphemeralKeyStaleness)), 172 } 173 expired = s.getExpiredGenerations(mctx, keyMap, now) 174 expected = nil 175 require.Equal(t, expected, expired) 176 177 // Test with a single key that is expired 178 keyMap = keyExpiryMap{ 179 0: keybase1.ToTime(now.Add(-(libkb.MaxEphemeralKeyStaleness + libkb.MinEphemeralKeyLifetime))), 180 } 181 expired = s.getExpiredGenerations(mctx, keyMap, now) 182 expected = []keybase1.EkGeneration{0} 183 require.Equal(t, expected, expired) 184 185 // Test with an expired and a stale key 186 keyMap = keyExpiryMap{ 187 0: keybase1.ToTime(now.Add(-(libkb.MaxEphemeralKeyStaleness + libkb.MinEphemeralKeyLifetime))), 188 1: keybase1.ToTime(now.Add(-(libkb.MinEphemeralKeyLifetime))), 189 } 190 expired = s.getExpiredGenerations(mctx, keyMap, now) 191 expected = []keybase1.EkGeneration{0} 192 require.Equal(t, expected, expired) 193 194 // edge of deletion 195 expired = s.getExpiredGenerations(mctx, keyMap, now.Add(-time.Second)) 196 expected = nil 197 require.Equal(t, expected, expired) 198 199 // Test multiple gaps, only the last key is valid though. 200 keyMap = make(keyExpiryMap) 201 numKeys := 5 202 for i := 0; i < numKeys; i++ { 203 keyMap[keybase1.EkGeneration((numKeys - i - 1))] = keybase1.ToTime(now.Add(-libkb.MaxEphemeralKeyStaleness * time.Duration(i))) 204 } 205 expired = s.getExpiredGenerations(mctx, keyMap, now) 206 expected = []keybase1.EkGeneration{0, 1, 2} 207 require.Equal(t, expected, expired) 208 209 // Test case from bug 210 now = keybase1.Time(1528818944000).Time() 211 keyMap = keyExpiryMap{ 212 46: 1528207927000, 213 47: 1528294344000, 214 48: 1528382176000, 215 49: 1528472751000, 216 50: 1528724605000, 217 51: 1528811030000, 218 } 219 expired = s.getExpiredGenerations(mctx, keyMap, now) 220 expected = nil 221 require.Equal(t, expected, expired) 222 }