github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/ephemeral/user_ek_box_storage.go (about) 1 package ephemeral 2 3 import ( 4 "fmt" 5 "sort" 6 "sync" 7 "time" 8 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/gregor1" 11 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 const userEKBoxStorageDBVersion = 4 15 16 type userEKBoxCacheItem struct { 17 UserEKBoxed keybase1.UserEkBoxed 18 Err *EphemeralKeyError 19 } 20 21 func newUserEKBoxCacheItem(userEKBoxed keybase1.UserEkBoxed, err error) userEKBoxCacheItem { 22 var ekErr *EphemeralKeyError 23 e, ok := err.(EphemeralKeyError) 24 if !ok && err != nil { 25 e = newEphemeralKeyError(err.Error(), DefaultHumanErrMsg, 26 EphemeralKeyErrorKindUNKNOWN, UserEKKind) 27 } 28 if err != nil { 29 ekErr = &e 30 } 31 return userEKBoxCacheItem{ 32 UserEKBoxed: userEKBoxed, 33 Err: ekErr, 34 } 35 } 36 37 func (c userEKBoxCacheItem) HasError() bool { 38 return c.Err != nil 39 } 40 41 func (c userEKBoxCacheItem) Error() error { 42 if c.HasError() { 43 return *c.Err 44 } 45 return nil 46 } 47 48 type userEKBoxCache map[keybase1.EkGeneration]userEKBoxCacheItem 49 type UserEKBoxMap map[keybase1.EkGeneration]keybase1.UserEkBoxed 50 type UserEKUnboxedMap map[keybase1.EkGeneration]keybase1.UserEk 51 52 // We cache UserEKBoxes from the server in memory and a persist to a local 53 // KVStore. 54 type UserEKBoxStorage struct { 55 sync.Mutex 56 indexed bool 57 cache userEKBoxCache 58 } 59 60 func NewUserEKBoxStorage() *UserEKBoxStorage { 61 return &UserEKBoxStorage{ 62 cache: make(userEKBoxCache), 63 } 64 } 65 66 func (s *UserEKBoxStorage) dbKey(mctx libkb.MetaContext) (dbKey libkb.DbKey, err error) { 67 uv, err := mctx.G().GetMeUV(mctx.Ctx()) 68 if err != nil { 69 return dbKey, err 70 } 71 key := fmt.Sprintf("userEphemeralKeyBox-%s-%s-%d", mctx.G().Env.GetUsername(), uv.EldestSeqno, userEKBoxStorageDBVersion) 72 return libkb.DbKey{ 73 Typ: libkb.DBUserEKBox, 74 Key: key, 75 }, nil 76 } 77 78 func (s *UserEKBoxStorage) getCache(mctx libkb.MetaContext) (cache userEKBoxCache, err error) { 79 if !s.indexed { 80 key, err := s.dbKey(mctx) 81 if err != nil { 82 return s.cache, err 83 } 84 if _, err = mctx.G().GetKVStore().GetInto(&s.cache, key); err != nil { 85 return s.cache, err 86 } 87 s.indexed = true 88 } 89 return s.cache, nil 90 } 91 92 func (s *UserEKBoxStorage) Get(mctx libkb.MetaContext, generation keybase1.EkGeneration, 93 contentCtime *gregor1.Time) (userEK keybase1.UserEk, err error) { 94 defer mctx.Trace(fmt.Sprintf("UserEKBoxStorage#Get: generation:%v", generation), &err)() 95 96 s.Lock() 97 98 cache, err := s.getCache(mctx) 99 if err != nil { 100 s.Unlock() 101 return userEK, err 102 } 103 104 // Try cache first 105 cacheItem, ok := cache[generation] 106 if !ok { 107 // We don't have anything in our cache, fetch from the server 108 s.Unlock() // release the lock while we fetch 109 return s.fetchAndStore(mctx, generation) 110 } 111 112 defer s.Unlock() // release the lock after we unbox 113 if cacheItem.HasError() { 114 return userEK, cacheItem.Error() 115 } 116 userEK, err = s.unbox(mctx, generation, cacheItem.UserEKBoxed, contentCtime) 117 switch err.(type) { 118 case EphemeralKeyError: // if we can no longer unbox this, store the error 119 if perr := s.putLocked(mctx, generation, keybase1.UserEkBoxed{}, err); perr != nil { 120 mctx.Debug("unable to store unboxing error %v", perr) 121 } 122 default: 123 // don't store 124 } 125 return userEK, err 126 } 127 128 type UserEKBoxedResponse struct { 129 Result *struct { 130 Box string `json:"box"` 131 DeviceEKGeneration keybase1.EkGeneration `json:"device_ek_generation"` 132 Sig string `json:"sig"` 133 } `json:"result"` 134 } 135 136 func (s *UserEKBoxStorage) fetchAndStore(mctx libkb.MetaContext, generation keybase1.EkGeneration) (userEK keybase1.UserEk, err error) { 137 defer mctx.Trace(fmt.Sprintf("UserEKBoxStorage#fetchAndStore: generation: %v", generation), &err)() 138 139 // cache unboxing/missing box errors so we don't continually try to fetch 140 // something nonexistent. 141 defer func() { 142 if _, ok := err.(EphemeralKeyError); ok { 143 s.Lock() 144 defer s.Unlock() 145 if perr := s.putLocked(mctx, generation, keybase1.UserEkBoxed{}, err); perr != nil { 146 mctx.Debug("unable to store error %v", perr) 147 } 148 } 149 }() 150 151 apiArg := libkb.APIArg{ 152 Endpoint: "user/user_ek_box", 153 SessionType: libkb.APISessionTypeREQUIRED, 154 Args: libkb.HTTPArgs{ 155 "generation": libkb.U{Val: uint64(generation)}, 156 "recipient_device_id": libkb.S{Val: string(mctx.ActiveDevice().DeviceID())}, 157 }, 158 } 159 160 var result UserEKBoxedResponse 161 res, err := mctx.G().GetAPI().Get(mctx, apiArg) 162 if err != nil { 163 err = errFromAppStatus(err) 164 return userEK, err 165 } 166 167 if err = res.Body.UnmarshalAgain(&result); err != nil { 168 return userEK, err 169 } 170 171 if result.Result == nil { 172 err = newEKMissingBoxErr(mctx, UserEKKind, generation) 173 return userEK, err 174 } 175 176 // Although we verify the signature is valid, it's possible that this key 177 // was signed with a PUK that is not our latest and greatest. We allow this 178 // when we are using this ek for *decryption*. When getting a key for 179 // *encryption* callers are responsible for verifying the signature is 180 // signed by the latest PUK or generating a new EK. This logic currently 181 // lives in ephemeral/lib.go#KeygenIfNeeded (#newUserEKNeeded) 182 _, userEKStatement, err := extractUserEKStatementFromSig(result.Result.Sig) 183 if err != nil { 184 return userEK, err 185 } else if userEKStatement == nil { // shouldn't happen 186 return userEK, fmt.Errorf("unable to fetch valid userEKStatement") 187 } 188 189 userEKMetadata := userEKStatement.CurrentUserEkMetadata 190 if generation != userEKMetadata.Generation { 191 // sanity check that we got the right generation 192 return userEK, newEKCorruptedErr(mctx, UserEKKind, generation, userEKMetadata.Generation) 193 } 194 userEKBoxed := keybase1.UserEkBoxed{ 195 Box: result.Result.Box, 196 DeviceEkGeneration: result.Result.DeviceEKGeneration, 197 Metadata: userEKMetadata, 198 } 199 200 userEK, err = s.unbox(mctx, generation, userEKBoxed, nil) 201 if err != nil { 202 return userEK, err 203 } 204 205 seed := UserEKSeed(userEK.Seed) 206 keypair := seed.DeriveDHKey() 207 208 if !keypair.GetKID().Equal(userEKMetadata.Kid) { 209 return userEK, fmt.Errorf("Failed to verify server given seed [%s] against signed KID [%s]. Box %+v", 210 userEKMetadata.Kid, keypair.GetKID(), userEKBoxed) 211 } 212 213 // Store the boxed version, return the unboxed 214 err = s.Put(mctx, generation, userEKBoxed) 215 return userEK, err 216 } 217 218 func (s *UserEKBoxStorage) Put(mctx libkb.MetaContext, generation keybase1.EkGeneration, userEKBoxed keybase1.UserEkBoxed) (err error) { 219 s.Lock() 220 defer s.Unlock() 221 return s.putLocked(mctx, generation, userEKBoxed, nil /* ekErr */) 222 } 223 224 func (s *UserEKBoxStorage) putLocked(mctx libkb.MetaContext, generation keybase1.EkGeneration, 225 userEKBoxed keybase1.UserEkBoxed, ekErr error) (err error) { 226 defer mctx.Trace(fmt.Sprintf("UserEKBoxStorage#putLocked: generation:%v", generation), &err)() 227 228 // sanity check that we got the right generation 229 if userEKBoxed.Metadata.Generation != generation && ekErr == nil { 230 return newEKCorruptedErr(mctx, UserEKKind, generation, userEKBoxed.Metadata.Generation) 231 } 232 233 key, err := s.dbKey(mctx) 234 if err != nil { 235 return err 236 } 237 cache, err := s.getCache(mctx) 238 if err != nil { 239 return err 240 } 241 cache[generation] = newUserEKBoxCacheItem(userEKBoxed, ekErr) 242 return mctx.G().GetKVStore().PutObj(key, nil, cache) 243 } 244 245 func (s *UserEKBoxStorage) unbox(mctx libkb.MetaContext, userEKGeneration keybase1.EkGeneration, 246 userEKBoxed keybase1.UserEkBoxed, contentCtime *gregor1.Time) (userEK keybase1.UserEk, err error) { 247 defer mctx.Trace(fmt.Sprintf("UserEKBoxStorage#unbox: generation:%v", userEKGeneration), &err)() 248 249 deviceEKStorage := mctx.G().GetDeviceEKStorage() 250 deviceEK, err := deviceEKStorage.Get(mctx, userEKBoxed.DeviceEkGeneration) 251 if err != nil { 252 mctx.Debug("unable to get from deviceEKStorage %v", err) 253 if _, ok := err.(libkb.UnboxError); ok { 254 return userEK, newEKUnboxErr(mctx, UserEKKind, userEKGeneration, DeviceEKKind, 255 userEKBoxed.DeviceEkGeneration, contentCtime) 256 } 257 return userEK, err 258 } 259 260 deviceSeed := DeviceEKSeed(deviceEK.Seed) 261 deviceKeypair := deviceSeed.DeriveDHKey() 262 263 msg, _, err := deviceKeypair.DecryptFromString(userEKBoxed.Box) 264 if err != nil { 265 mctx.Debug("unable to decrypt userEKBoxed %v", err) 266 return userEK, newEKUnboxErr(mctx, UserEKKind, userEKGeneration, DeviceEKKind, 267 userEKBoxed.DeviceEkGeneration, contentCtime) 268 } 269 270 seed, err := newUserEKSeedFromBytes(msg) 271 if err != nil { 272 return userEK, err 273 } 274 275 return keybase1.UserEk{ 276 Seed: keybase1.Bytes32(seed), 277 Metadata: userEKBoxed.Metadata, 278 }, nil 279 } 280 281 func (s *UserEKBoxStorage) Delete(mctx libkb.MetaContext, generation keybase1.EkGeneration) (err error) { 282 s.Lock() 283 defer s.Unlock() 284 return s.deleteMany(mctx, []keybase1.EkGeneration{generation}) 285 } 286 287 func (s *UserEKBoxStorage) deleteMany(mctx libkb.MetaContext, generations []keybase1.EkGeneration) (err error) { 288 defer mctx.Trace(fmt.Sprintf("UserEKBoxStorage#deleteMany: generations:%v", generations), &err)() 289 290 cache, err := s.getCache(mctx) 291 if err != nil { 292 return err 293 } 294 for _, generation := range generations { 295 delete(cache, generation) 296 } 297 key, err := s.dbKey(mctx) 298 if err != nil { 299 return err 300 } 301 return mctx.G().GetKVStore().PutObj(key, nil, cache) 302 } 303 304 func (s *UserEKBoxStorage) GetAll(mctx libkb.MetaContext) (userEKs UserEKUnboxedMap, err error) { 305 defer mctx.Trace("UserEKBoxStorage#GetAll", &err)() 306 307 s.Lock() 308 defer s.Unlock() 309 cache, err := s.getCache(mctx) 310 if err != nil { 311 return userEKs, err 312 } 313 314 userEKs = make(UserEKUnboxedMap) 315 for generation, cacheItem := range cache { 316 if cacheItem.HasError() { 317 continue 318 } 319 userEK, err := s.unbox(mctx, generation, cacheItem.UserEKBoxed, nil) 320 if err != nil { 321 return userEKs, err 322 } 323 userEKs[generation] = userEK 324 } 325 return userEKs, err 326 } 327 328 func (s *UserEKBoxStorage) ClearCache() { 329 s.Lock() 330 defer s.Unlock() 331 s.cache = make(userEKBoxCache) 332 s.indexed = false 333 } 334 335 func (s *UserEKBoxStorage) MaxGeneration(mctx libkb.MetaContext, includeErrs bool) (maxGeneration keybase1.EkGeneration, err error) { 336 defer mctx.Trace("UserEKBoxStorage#MaxGeneration", &err)() 337 338 s.Lock() 339 defer s.Unlock() 340 341 maxGeneration = -1 342 cache, err := s.getCache(mctx) 343 if err != nil { 344 return maxGeneration, err 345 } 346 347 for generation, cacheItem := range cache { 348 if cacheItem.HasError() && !includeErrs { 349 continue 350 } 351 if generation > maxGeneration { 352 maxGeneration = generation 353 } 354 } 355 return maxGeneration, nil 356 } 357 358 func (s *UserEKBoxStorage) DeleteExpired(mctx libkb.MetaContext, merkleRoot libkb.MerkleRoot) (expired []keybase1.EkGeneration, err error) { 359 defer mctx.Trace("DeviceEKStorage#DeleteExpired", &err)() 360 361 s.Lock() 362 defer s.Unlock() 363 364 cache, err := s.getCache(mctx) 365 if err != nil { 366 return nil, err 367 } 368 369 keyMap := make(keyExpiryMap) 370 // We delete expired and invalid cache entries but only return the expired. 371 toDelete := []keybase1.EkGeneration{} 372 for generation, cacheItem := range cache { 373 // purge any cached errors here so they don't stick around forever. 374 if cacheItem.HasError() { 375 toDelete = append(toDelete, generation) 376 } else { 377 keyMap[generation] = cacheItem.UserEKBoxed.Metadata.Ctime 378 } 379 } 380 now := keybase1.TimeFromSeconds(merkleRoot.Ctime()).Time() 381 expired = s.getExpiredGenerations(mctx, keyMap, now) 382 toDelete = append(toDelete, expired...) 383 return expired, s.deleteMany(mctx, toDelete) 384 } 385 386 // getExpiredGenerations calculates which keys have expired and are safe to 387 // delete. Keys normally expire after `libkb.MaxEphemeralContentLifetime` 388 // unless there has been a gap in their generation. If there has been a gap of 389 // more than a day (the normal generation time), a key can be re-used for up to 390 // `libkb.MaxEphemeralKeyStaleness` until it is considered expired. To 391 // determine expiration, we look at all of the current keys and account for any 392 // gaps since we don't want to expire a key if it is still used to encrypt a 393 // different key or ephemeral content. 394 func (s *UserEKBoxStorage) getExpiredGenerations(mctx libkb.MetaContext, keyMap keyExpiryMap, now time.Time) (expired []keybase1.EkGeneration) { 395 // Sort the generations we have so we can walk through them in order. 396 var keys []keybase1.EkGeneration 397 for k := range keyMap { 398 keys = append(keys, k) 399 } 400 sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) 401 402 for i, generation := range keys { 403 keyCtime := keyMap[generation].Time() 404 expiryOffset := libkb.MaxEphemeralKeyStaleness 405 if i < len(keys)-1 { 406 expiryOffset = keyMap[keys[i+1]].Time().Sub(keyCtime) 407 // Offset can be max libkb.MaxEphemeralKeyStaleness 408 if expiryOffset > libkb.MaxEphemeralKeyStaleness { 409 expiryOffset = libkb.MaxEphemeralKeyStaleness 410 } 411 } 412 if now.Sub(keyCtime) >= (libkb.MinEphemeralKeyLifetime + expiryOffset) { 413 mctx.Debug("getExpiredGenerations: expired generation:%v, now: %v, keyCtime:%v, expiryOffset:%v, keyMap: %v, i:%v, %v, %v", 414 generation, now, keyCtime, expiryOffset, keyMap, i, now.Sub(keyCtime), (libkb.MinEphemeralKeyLifetime + expiryOffset)) 415 expired = append(expired, generation) 416 } 417 } 418 419 return expired 420 }