github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/ephemeral/device_ek_storage.go (about) 1 package ephemeral 2 3 import ( 4 "fmt" 5 "log" 6 "sort" 7 "strconv" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/logger" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 ) 16 17 const ( 18 deviceEKSubDir = "device-eks" 19 deviceEKPrefix = "deviceEphemeralKey" 20 deviceEKSuffix = ".ek" 21 ) 22 23 type deviceEKCacheItem struct { 24 DeviceEK keybase1.DeviceEk 25 Err error 26 } 27 28 type deviceEKCache map[keybase1.EkGeneration]deviceEKCacheItem 29 type DeviceEKMap map[keybase1.EkGeneration]keybase1.DeviceEk 30 31 type DeviceEKStorage struct { 32 sync.Mutex 33 storage libkb.ErasableKVStore 34 cache deviceEKCache 35 indexed bool 36 logger *log.Logger 37 } 38 39 func getLogger(mctx libkb.MetaContext) *log.Logger { 40 filename := mctx.G().Env.GetEKLogFile() 41 lfc := mctx.G().Env.GetLogFileConfig(filename) 42 lfc.SkipRedirectStdErr = true 43 lfw := logger.NewLogFileWriter(*lfc) 44 if err := lfw.Open(mctx.G().GetClock().Now()); err != nil { 45 mctx.Debug("Unable to getLogger %v", err) 46 return nil 47 } 48 l := log.New(lfw, getLogPrefix(mctx), log.LstdFlags|log.Lmicroseconds|log.Lshortfile) 49 return l 50 } 51 52 func getLogPrefix(mctx libkb.MetaContext) string { 53 return fmt.Sprintf("[username=%v] ", mctx.G().Env.GetUsername()) 54 } 55 56 func getLocalStorageSecretBoxKey(mctx libkb.MetaContext) (fkey [32]byte, err error) { 57 // Get secret device key 58 encKey, err := mctx.ActiveDevice().EncryptionKey() 59 if err != nil { 60 return fkey, err 61 } 62 kp, ok := encKey.(libkb.NaclDHKeyPair) 63 if !ok || kp.Private == nil { 64 return fkey, libkb.KeyCannotDecryptError{} 65 } 66 67 // Derive symmetric key from device key 68 skey, err := encKey.SecretSymmetricKey(libkb.EncryptionReasonErasableKVLocalStorage) 69 if err != nil { 70 return fkey, err 71 } 72 73 copy(fkey[:], skey[:]) 74 return fkey, nil 75 } 76 77 func deviceEKKeygen(mctx libkb.MetaContext, noiseBytes libkb.NoiseBytes) (fkey [32]byte, err error) { 78 enckey, err := getLocalStorageSecretBoxKey(mctx) 79 if err != nil { 80 return fkey, err 81 } 82 83 xor, err := libkb.NoiseXOR(enckey, noiseBytes) 84 if err != nil { 85 return fkey, err 86 } 87 copy(fkey[:], xor) 88 return fkey, nil 89 } 90 91 func NewDeviceEKStorage(mctx libkb.MetaContext) *DeviceEKStorage { 92 return &DeviceEKStorage{ 93 storage: libkb.NewFileErasableKVStore(mctx, deviceEKSubDir, deviceEKKeygen), 94 cache: make(deviceEKCache), 95 logger: getLogger(mctx), 96 } 97 } 98 99 func (s *DeviceEKStorage) SetLogPrefix(mctx libkb.MetaContext) { 100 s.Lock() 101 defer s.Unlock() 102 if s.logger != nil { 103 s.logger.SetPrefix(getLogPrefix(mctx)) 104 } 105 } 106 107 // Log sensitive deletion actions to a separate log file so we don't lose the 108 // logs during normal rotation. 109 func (s *DeviceEKStorage) ekLogf(mctx libkb.MetaContext, format string, args ...interface{}) { 110 mctx.Debug(format, args...) 111 if s.logger != nil { 112 s.logger.Printf(format, args...) 113 } 114 } 115 116 func (s *DeviceEKStorage) ekLogCTrace(mctx libkb.MetaContext, msg string, err *error) func() { 117 if s.logger != nil { 118 s.logger.Print(msg) 119 } 120 return mctx.Trace(msg, err) 121 } 122 123 func (s *DeviceEKStorage) keyPrefixFromUsername(username libkb.NormalizedUsername) string { 124 return fmt.Sprintf("%s-%s-", deviceEKPrefix, username) 125 } 126 127 func (s *DeviceEKStorage) keyPrefix(mctx libkb.MetaContext) (prefix string, err error) { 128 uv, err := mctx.G().GetMeUV(mctx.Ctx()) 129 if err != nil { 130 return prefix, err 131 } 132 username := mctx.ActiveDevice().Username(mctx) 133 return fmt.Sprintf("%s%s-", s.keyPrefixFromUsername(username), uv.EldestSeqno), nil 134 } 135 136 func (s *DeviceEKStorage) key(mctx libkb.MetaContext, generation keybase1.EkGeneration) (key string, err error) { 137 prefix, err := s.keyPrefix(mctx) 138 if err != nil { 139 return key, err 140 } 141 return fmt.Sprintf("%s%d%s", prefix, generation, deviceEKSuffix), nil 142 } 143 144 // keyToEldestSeqno parses out the `eldestSeqno` from a key of the form 145 // deviceEKPrefix-username-eldestSeqno-generation.ek. If we have a key for a 146 // eldestSeqno that is not our current, we purge it since we don't want the 147 // ephemeral key to stick around if we've reset. If we are unable to parse out 148 // the value, the key is not valid, or not for the logged in user we return -1 149 func (s *DeviceEKStorage) keyToEldestSeqno(mctx libkb.MetaContext, key string) keybase1.Seqno { 150 if !strings.HasPrefix(key, deviceEKPrefix) { 151 return -1 152 } 153 parts := strings.Split(key, "-") 154 if len(parts) != 4 { 155 return -1 156 } 157 // Make sure this key is for our current user and not a different one. 158 username := mctx.ActiveDevice().Username(mctx) 159 if parts[1] != username.String() { 160 return -1 161 } 162 e, err := strconv.ParseUint(parts[2], 10, 64) 163 if err != nil { 164 return -1 165 } 166 return keybase1.Seqno(e) 167 } 168 169 // keyToEldestSeqno parses out the `generation` from a key of the form 170 // deviceEKPrefix-username-eldestSeqno-generation.ek. Unparseable keys return a 171 // generation of -1 and should be ignored. 172 func (s *DeviceEKStorage) keyToGeneration(mctx libkb.MetaContext, key string) keybase1.EkGeneration { 173 prefix, err := s.keyPrefix(mctx) 174 if err != nil { 175 mctx.Debug("keyToGeneration: unable to get keyPrefix: %v", err) 176 return -1 177 } 178 if !strings.HasPrefix(key, prefix) || !strings.HasSuffix(key, deviceEKSuffix) { 179 mctx.Debug("keyToGeneration: key missing prefix: %v or suffix: %s", prefix, deviceEKSuffix) 180 return -1 181 } 182 183 key = strings.TrimSuffix(key, deviceEKSuffix) 184 parts := strings.Split(key, prefix) 185 if len(parts) != 2 { 186 mctx.Debug("keyToGeneration: unexpected parts: %v, prefix: %v", parts) 187 return -1 188 } 189 g, err := strconv.ParseUint(parts[1], 10, 64) 190 if err != nil { 191 mctx.Debug("keyToGeneration: unable to parseUint: %v", err) 192 return -1 193 } 194 return keybase1.EkGeneration(g) 195 } 196 197 func (s *DeviceEKStorage) Put(mctx libkb.MetaContext, generation keybase1.EkGeneration, deviceEK keybase1.DeviceEk) (err error) { 198 defer mctx.Trace(fmt.Sprintf("DeviceEKStorage#Put: generation:%v", generation), &err)() 199 200 s.Lock() 201 defer s.Unlock() 202 203 // sanity check that we got the right generation 204 if deviceEK.Metadata.Generation != generation { 205 return newEKCorruptedErr(mctx, DeviceEKKind, generation, deviceEK.Metadata.Generation) 206 } 207 208 key, err := s.key(mctx, generation) 209 if err != nil { 210 return err 211 } 212 // Fill in this puppy. 213 if deviceEK.Metadata.DeviceCtime == 0 { 214 deviceEK.Metadata.DeviceCtime = keybase1.ToTime(time.Now()) 215 } 216 if err = s.storage.Put(mctx, key, deviceEK); err != nil { 217 return err 218 } 219 220 // cache the result 221 cache, err := s.getCache(mctx) 222 if err != nil { 223 return err 224 } 225 cache[generation] = deviceEKCacheItem{ 226 DeviceEK: deviceEK, 227 Err: nil, 228 } 229 return nil 230 } 231 232 func (s *DeviceEKStorage) Get(mctx libkb.MetaContext, generation keybase1.EkGeneration) (deviceEK keybase1.DeviceEk, err error) { 233 defer mctx.Trace(fmt.Sprintf("DeviceEKStorage#Get: generation:%v", generation), &err)() 234 s.Lock() 235 defer s.Unlock() 236 237 // Try the cache first 238 cache, err := s.getCache(mctx) 239 if err != nil { 240 return deviceEK, err 241 } 242 cacheItem, ok := cache[generation] 243 if ok { 244 return cacheItem.DeviceEK, cacheItem.Err 245 } 246 // Try persistent storage. 247 deviceEK, err = s.get(mctx, generation) 248 switch err.(type) { 249 case nil, libkb.UnboxError: 250 // cache the result 251 cache[generation] = deviceEKCacheItem{ 252 DeviceEK: deviceEK, 253 Err: err, 254 } 255 return deviceEK, err 256 default: 257 return deviceEK, err 258 } 259 } 260 261 func (s *DeviceEKStorage) get(mctx libkb.MetaContext, generation keybase1.EkGeneration) (deviceEK keybase1.DeviceEk, err error) { 262 defer mctx.Trace(fmt.Sprintf("DeviceEKStorage#get: generation:%v", generation), &err)() 263 264 key, err := s.key(mctx, generation) 265 if err != nil { 266 return deviceEK, err 267 } 268 269 if err = s.storage.Get(mctx, key, &deviceEK); err != nil { 270 if _, ok := err.(libkb.UnboxError); ok { 271 s.ekLogf(mctx, "DeviceEKStorage#get: corrupted generation: %v -> %v: %v", key, generation, err) 272 if ierr := s.storage.Erase(mctx, key); ierr != nil { 273 s.ekLogf(mctx, "DeviceEKStorage#get: unable to delete corrupted generation: %v", ierr) 274 } 275 } 276 return deviceEK, err 277 } 278 // sanity check that we got the right generation 279 if deviceEK.Metadata.Generation != generation { 280 return deviceEK, newEKCorruptedErr(mctx, DeviceEKKind, generation, deviceEK.Metadata.Generation) 281 } 282 return deviceEK, nil 283 } 284 285 func (s *DeviceEKStorage) Delete(mctx libkb.MetaContext, generation keybase1.EkGeneration, 286 reason string, args ...interface{}) (err error) { 287 s.Lock() 288 defer s.Unlock() 289 return s.delete(mctx, generation, reason, args...) 290 } 291 292 func (s *DeviceEKStorage) delete(mctx libkb.MetaContext, generation keybase1.EkGeneration, 293 reason string, args ...interface{}) (err error) { 294 defer s.ekLogCTrace(mctx, fmt.Sprintf("DeviceEKStorage#delete: generation:%v reason: %s", generation, fmt.Sprintf(reason, args...)), &err)() 295 296 // clear the cache 297 cache, err := s.getCache(mctx) 298 if err != nil { 299 return err 300 } 301 key, err := s.key(mctx, generation) 302 if err != nil { 303 return err 304 } 305 if err = s.storage.Erase(mctx, key); err != nil { 306 return err 307 } 308 delete(cache, generation) 309 return nil 310 } 311 312 func (s *DeviceEKStorage) getCache(mctx libkb.MetaContext) (cache deviceEKCache, err error) { 313 if !s.indexed { 314 keys, err := s.storage.AllKeys(mctx, deviceEKSuffix) 315 if err != nil { 316 return nil, err 317 } 318 for _, key := range keys { 319 generation := s.keyToGeneration(mctx, key) 320 if generation < 0 { 321 mctx.Debug("DeviceEKStorage#getCache: unable to get generation from key: %s", key) 322 continue 323 } 324 deviceEK, err := s.get(mctx, generation) 325 switch err.(type) { 326 case nil, libkb.UnboxError: 327 s.cache[generation] = deviceEKCacheItem{ 328 DeviceEK: deviceEK, 329 Err: err, 330 } 331 default: 332 return nil, err 333 } 334 } 335 s.indexed = true 336 } 337 return s.cache, nil 338 } 339 340 func (s *DeviceEKStorage) ClearCache() { 341 s.Lock() 342 defer s.Unlock() 343 s.clearCache() 344 } 345 346 func (s *DeviceEKStorage) clearCache() { 347 s.cache = make(deviceEKCache) 348 s.indexed = false 349 } 350 351 func (s *DeviceEKStorage) GetAll(mctx libkb.MetaContext) (deviceEKs DeviceEKMap, err error) { 352 defer mctx.Trace("DeviceEKStorage#GetAll", &err)() 353 354 s.Lock() 355 defer s.Unlock() 356 357 cache, err := s.getCache(mctx) 358 if err != nil { 359 return nil, err 360 } 361 deviceEKs = make(DeviceEKMap) 362 for gen, cacheItem := range cache { 363 if cacheItem.Err != nil { 364 continue 365 } 366 deviceEKs[gen] = cacheItem.DeviceEK 367 } 368 return deviceEKs, nil 369 } 370 371 func (s *DeviceEKStorage) GetAllActive(mctx libkb.MetaContext, merkleRoot libkb.MerkleRoot) (metadatas []keybase1.DeviceEkMetadata, err error) { 372 defer mctx.Trace("GetAllActive", &err)() 373 374 s.Lock() 375 defer s.Unlock() 376 377 cache, err := s.getCache(mctx) 378 if err != nil { 379 return nil, err 380 } 381 382 activeKeysInOrder := []keybase1.DeviceEkMetadata{} 383 for _, cacheItem := range cache { 384 if cacheItem.Err != nil { 385 continue 386 } 387 deviceEK := cacheItem.DeviceEK 388 // Skip expired keys. Expired keys are spared from deletion past for a 389 // window past their expiry date, in case they're needed for 390 // decryption, but they're never signed over or used for encryption. 391 if ctimeIsStale(deviceEK.Metadata.Ctime.Time(), merkleRoot) { 392 continue 393 } 394 // Collect out of order, then sort below. 395 activeKeysInOrder = append(activeKeysInOrder, deviceEK.Metadata) 396 } 397 sort.Slice(activeKeysInOrder, func(a, b int) bool { return activeKeysInOrder[a].Generation < activeKeysInOrder[b].Generation }) 398 399 return activeKeysInOrder, nil 400 } 401 402 // ListAllForUser lists the internal storage name of deviceEKs of the logged in 403 // user. This is used for logsend purposes to debug ek state. 404 func (s *DeviceEKStorage) ListAllForUser(mctx libkb.MetaContext) (all []string, err error) { 405 defer mctx.Trace("DeviceEKStorage#ListAllForUser", &err)() 406 407 s.Lock() 408 defer s.Unlock() 409 410 return s.listAllForUser(mctx, mctx.ActiveDevice().Username(mctx)) 411 } 412 413 func (s *DeviceEKStorage) listAllForUser(mctx libkb.MetaContext, username libkb.NormalizedUsername) (all []string, err error) { 414 // key in the sense of a key-value pair, not a crypto key! 415 keys, err := s.storage.AllKeys(mctx, deviceEKSuffix) 416 if err != nil { 417 return nil, err 418 } 419 prefix := s.keyPrefixFromUsername(username) 420 for _, key := range keys { 421 if strings.HasPrefix(key, prefix) { 422 all = append(all, key) 423 } 424 } 425 return all, nil 426 } 427 428 func (s *DeviceEKStorage) MaxGeneration(mctx libkb.MetaContext, includeErrs bool) (maxGeneration keybase1.EkGeneration, err error) { 429 defer mctx.Trace("DeviceEKStorage#MaxGeneration", &err)() 430 431 s.Lock() 432 defer s.Unlock() 433 434 maxGeneration = -1 435 cache, err := s.getCache(mctx) 436 if err != nil { 437 return maxGeneration, err 438 } 439 for generation, cacheItem := range cache { 440 if cacheItem.Err != nil && !includeErrs { 441 continue 442 } 443 if generation > maxGeneration { 444 maxGeneration = generation 445 } 446 } 447 return maxGeneration, nil 448 } 449 450 func (s *DeviceEKStorage) DeleteExpired(mctx libkb.MetaContext, merkleRoot libkb.MerkleRoot) (expired []keybase1.EkGeneration, err error) { 451 defer mctx.Trace("DeviceEKStorage#DeleteExpired", &err)() 452 453 s.Lock() 454 defer s.Unlock() 455 456 cache, err := s.getCache(mctx) 457 if err != nil { 458 return nil, err 459 } 460 461 // Fall back to the device's local time if we don't have a merkle root so 462 // we can complete deletions offline. 463 var now time.Time 464 if merkleRoot.IsNil() { 465 now = time.Now() 466 } else { 467 now = keybase1.TimeFromSeconds(merkleRoot.Ctime()).Time() 468 } 469 470 keyMap := make(keyExpiryMap) 471 // We delete expired and invalid cache entries but only return the expired. 472 for generation, cacheItem := range cache { 473 if cacheItem.Err != nil { 474 continue 475 } else { 476 deviceEK := cacheItem.DeviceEK 477 var ctime keybase1.Time 478 // If we have a nil root _and_ a valid DeviceCtime, use that. If we're 479 // missing a DeviceCtime it's better to use the slightly off 480 // merkleCtime than a 0 481 if merkleRoot.IsNil() && deviceEK.Metadata.DeviceCtime > 0 { 482 ctime = deviceEK.Metadata.DeviceCtime 483 } else { 484 ctime = deviceEK.Metadata.Ctime 485 } 486 keyMap[generation] = ctime 487 } 488 } 489 490 expired = s.getExpiredGenerations(mctx, keyMap, now) 491 epick := libkb.FirstErrorPicker{} 492 for _, generation := range expired { 493 epick.Push(s.delete(mctx, generation, "generation expired")) 494 } 495 496 epick.Push(s.deletedWrongEldestSeqno(mctx)) 497 return expired, epick.Error() 498 } 499 500 // getExpiredGenerations calculates which keys have expired and are safe to 501 // delete. Keys normally expire after `libkb.MaxEphemeralContentLifetime` 502 // unless there has been a gap in their generation. If there has been a gap of 503 // more than a day (the normal generation time), a key can be re-used for up to 504 // `libkb.MaxEphemeralKeyStaleness` until it is considered expired. To 505 // determine expiration, we look at all of the current keys and account for any 506 // gaps since we don't want to expire a key if it is still used to encrypt a 507 // different key or ephemeral content. With deviceEKs we also have to account 508 // for a deviceEK being created out of lock step with a userEK. Consider the 509 // following scenario: 510 // 511 // At t=0, deviceA creates deviceEK_A_1 and userEK_1. At t=0.5, deviceB creates 512 // devicekEK_B_1. At t=1, deviceEK_A_2 and userEK_2 are created and at t=1.5 513 // deviceEK_B_2 is created. deviceEK_B_1 cannot be deleted until userEK_2 is 514 // expired, or deviceB will delete it's deviceEK early. Since userEK_3 has not 515 // yet been created, we may have to keep deviceEK_B_1 around until userEK_2 is 516 // stale, at which time no more teamEKs will be encrypted by it. To account for 517 // this (without having to interact with the userEK level via server 518 // assistance) we extend the lifetime of deviceEK_B_1 to expire 519 // `libkb.MaxEphemeralContentLifetime` after the creation of deviceEK_B_3, with 520 // a maximum window of `libkb.MaxEphemeralKeyStaleness`. This is correct 521 // because userEK_3 *must* be created at or before deviceEK_B_3's creation. 522 func (s *DeviceEKStorage) getExpiredGenerations(mctx libkb.MetaContext, keyMap keyExpiryMap, now time.Time) (expired []keybase1.EkGeneration) { 523 // Sort the generations we have so we can walk through them in order. 524 var keys []keybase1.EkGeneration 525 for k := range keyMap { 526 keys = append(keys, k) 527 } 528 sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) 529 530 for i, generation := range keys { 531 keyCtime := keyMap[generation].Time() 532 533 // Offset between the current key and the generation after it. Allowed 534 // to be at most libkb.MaxEphemeralKeyStaleness 535 expiryOffset1 := libkb.MaxEphemeralKeyStaleness 536 if i < len(keys)-1 { 537 expiryOffset1 = keyMap[keys[i+1]].Time().Sub(keyCtime) 538 // Offset can be max libkb.MaxEphemeralKeyStaleness 539 if expiryOffset1 > libkb.MaxEphemeralKeyStaleness { 540 expiryOffset1 = libkb.MaxEphemeralKeyStaleness 541 } 542 } 543 544 // Offset between the key one generation older and two generations 545 // older than the current key. Allowed to be at most 546 // libkb.MaxEphemeralKeyStaleness 547 expiryOffset2 := libkb.MaxEphemeralKeyStaleness 548 if i < len(keys)-2 { 549 expiryOffset2 = keyMap[keys[i+2]].Time().Sub(keyMap[keys[i+1]].Time()) 550 if expiryOffset2 > libkb.MaxEphemeralKeyStaleness { 551 expiryOffset2 = libkb.MaxEphemeralKeyStaleness 552 } 553 } 554 555 expiryOffset := expiryOffset1 + expiryOffset2 556 if now.Sub(keyCtime) >= (libkb.MinEphemeralKeyLifetime + expiryOffset) { 557 s.ekLogf(mctx, "getExpiredGenerations: expired generation:%v, now: %v, keyCtime:%v, expiryOffset:%v, keyMap: %v, i:%v", 558 generation, now, keyCtime, expiryOffset, keyMap, i) 559 expired = append(expired, generation) 560 } 561 } 562 return expired 563 } 564 565 func (s *DeviceEKStorage) deletedWrongEldestSeqno(mctx libkb.MetaContext) (err error) { 566 keys, err := s.storage.AllKeys(mctx, deviceEKSuffix) 567 if err != nil { 568 return err 569 } 570 uv, err := mctx.G().GetMeUV(mctx.Ctx()) 571 if err != nil { 572 return err 573 } 574 epick := libkb.FirstErrorPicker{} 575 for _, key := range keys { 576 eldestSeqno := s.keyToEldestSeqno(mctx, key) 577 if eldestSeqno < 0 { 578 continue 579 } 580 if eldestSeqno != uv.EldestSeqno { 581 s.ekLogf(mctx, "DeviceEKStorage#deletedWrongEldestSeqno: key: %v, uv: %v", key, uv) 582 epick.Push(s.storage.Erase(mctx, key)) 583 } 584 } 585 return epick.Error() 586 } 587 588 func (s *DeviceEKStorage) ForceDeleteAll(mctx libkb.MetaContext, username libkb.NormalizedUsername) (err error) { 589 defer s.ekLogCTrace(mctx, "DeviceEKStorage#ForceDeleteAll", &err)() 590 591 s.Lock() 592 defer s.Unlock() 593 594 // only delete if the key is owned by the current user 595 keys, err := s.listAllForUser(mctx, username) 596 if err != nil { 597 return err 598 } 599 epick := libkb.FirstErrorPicker{} 600 for _, key := range keys { 601 s.ekLogf(mctx, "DeviceEKStorage#ForceDeleteAll: key: %v", key) 602 epick.Push(s.storage.Erase(mctx, key)) 603 } 604 605 s.clearCache() 606 return epick.Error() 607 }