github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/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  }