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  }