github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/ephemeral/team_ek_box_storage_test.go (about)

     1  package ephemeral
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/keybase/client/go/libkb"
     9  	"github.com/keybase/client/go/protocol/keybase1"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestTeamEKBoxStorage(t *testing.T) {
    14  	tc, mctx, _ := ephemeralKeyTestSetup(t)
    15  	defer tc.Cleanup()
    16  
    17  	merkleRootPtr, err := tc.G.GetMerkleClient().FetchRootFromServer(mctx, libkb.EphemeralKeyMerkleFreshness)
    18  	require.NoError(t, err)
    19  	merkleRoot := *merkleRootPtr
    20  
    21  	// Login hooks should have run
    22  	deviceEKStorage := tc.G.GetDeviceEKStorage()
    23  	deviceEKMaxGen, err := deviceEKStorage.MaxGeneration(mctx, false)
    24  	require.True(t, deviceEKMaxGen > 0)
    25  	require.NoError(t, err)
    26  
    27  	userEKBoxStorage := tc.G.GetUserEKBoxStorage()
    28  	userEKMaxGen, err := userEKBoxStorage.MaxGeneration(mctx, false)
    29  	require.True(t, userEKMaxGen > 0)
    30  	require.NoError(t, err)
    31  
    32  	teamID := createTeam(tc)
    33  	invalidID := teamID + keybase1.TeamID("foo")
    34  
    35  	teamEKMetadata, err := publishNewTeamEK(mctx, teamID, merkleRoot, nil)
    36  	require.NoError(t, err)
    37  
    38  	s := tc.G.GetTeamEKBoxStorage()
    39  
    40  	// Test invalid teamID
    41  	nonexistent2, err := s.Get(mctx, invalidID, teamEKMetadata.Generation+1, nil)
    42  	require.Error(t, err)
    43  	_, ok := err.(EphemeralKeyError)
    44  	require.False(t, ok)
    45  	require.Equal(t, keybase1.TeamEphemeralKey{}, nonexistent2)
    46  
    47  	// Test get valid & unbox
    48  	ek, err := s.Get(mctx, teamID, teamEKMetadata.Generation, nil)
    49  	require.NoError(t, err)
    50  
    51  	// Make sure we don't pollute bot storage
    52  	botS := tc.G.GetTeambotEKBoxStorage()
    53  	botNonexistant, err := botS.Get(mctx, teamID, teamEKMetadata.Generation, nil)
    54  	require.Error(t, err)
    55  	require.IsType(t, EphemeralKeyError{}, err)
    56  	ekErr := err.(EphemeralKeyError)
    57  	require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError())
    58  	require.Equal(t, keybase1.TeamEphemeralKey{}, botNonexistant)
    59  
    60  	verifyTeamEK(t, teamEKMetadata, ek)
    61  
    62  	// Test Get nonexistent
    63  	nonexistent, err := s.Get(mctx, teamID, teamEKMetadata.Generation+1, nil)
    64  	require.Error(t, err)
    65  	require.IsType(t, EphemeralKeyError{}, err)
    66  	ekErr = err.(EphemeralKeyError)
    67  	require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError())
    68  	require.Equal(t, keybase1.TeamEphemeralKey{}, nonexistent)
    69  
    70  	// include the cached error in the max
    71  	maxGeneration, err := s.MaxGeneration(mctx, teamID, true)
    72  	require.NoError(t, err)
    73  	require.EqualValues(t, 2, maxGeneration)
    74  
    75  	// Test MaxGeneration
    76  	maxGeneration, err = s.MaxGeneration(mctx, teamID, false)
    77  	require.NoError(t, err)
    78  	require.EqualValues(t, 1, maxGeneration)
    79  
    80  	// Invalid id
    81  	maxGeneration2, err := s.MaxGeneration(mctx, invalidID, false)
    82  	require.NoError(t, err)
    83  	require.EqualValues(t, -1, maxGeneration2)
    84  
    85  	//	NOTE: We don't expose Delete on the interface put on the GlobalContext
    86  	//	since they should never be called, only DeleteExpired should be used.
    87  	//	GetAll is also not exposed since it' only needed for tests.
    88  	rawTeamEKBoxStorage := NewTeamEKBoxStorage(NewTeamEphemeralKeyer())
    89  	teamEKs, err := rawTeamEKBoxStorage.GetAll(mctx, teamID)
    90  	require.NoError(t, err)
    91  	require.Equal(t, 1, len(teamEKs))
    92  
    93  	ek, ok = teamEKs[teamEKMetadata.Generation]
    94  	require.True(t, ok)
    95  
    96  	verifyTeamEK(t, teamEKMetadata, ek)
    97  
    98  	// Test invalid
    99  	teamEKs2, err := rawTeamEKBoxStorage.GetAll(mctx, invalidID)
   100  	require.NoError(t, err)
   101  	require.Equal(t, 0, len(teamEKs2))
   102  
   103  	// Let's delete our userEK and verify we will refetch and unbox properly
   104  	rawUserEKBoxStorage := NewUserEKBoxStorage()
   105  	err = rawUserEKBoxStorage.Delete(mctx, userEKMaxGen)
   106  	require.NoError(t, err)
   107  
   108  	userEKBoxStorage.ClearCache()
   109  
   110  	ek, err = s.Get(mctx, teamID, teamEKMetadata.Generation, nil)
   111  	require.NoError(t, err)
   112  	verifyTeamEK(t, teamEKMetadata, ek)
   113  
   114  	// No let's the deviceEK which we can't recover from
   115  	rawDeviceEKStorage := NewDeviceEKStorage(mctx)
   116  	err = rawDeviceEKStorage.Delete(mctx, deviceEKMaxGen, "")
   117  	require.NoError(t, err)
   118  
   119  	deviceEKStorage.ClearCache()
   120  	deviceEK, err := deviceEKStorage.Get(mctx, deviceEKMaxGen)
   121  	require.Error(t, err)
   122  	_, ok = err.(libkb.UnboxError)
   123  	require.True(t, ok)
   124  	require.Equal(t, keybase1.DeviceEk{}, deviceEK)
   125  
   126  	bad, err := s.Get(mctx, teamID, teamEKMetadata.Generation, nil)
   127  	require.Error(t, err)
   128  	require.IsType(t, EphemeralKeyError{}, err)
   129  	ekErr = err.(EphemeralKeyError)
   130  	require.Equal(t, DefaultHumanErrMsg, ekErr.HumanError())
   131  	require.Equal(t, keybase1.TeamEphemeralKey{}, bad)
   132  
   133  	// test delete
   134  	err = rawTeamEKBoxStorage.Delete(mctx, teamID, teamEKMetadata.Generation)
   135  	require.NoError(t, err)
   136  	// delete invalid
   137  	err = rawTeamEKBoxStorage.Delete(mctx, invalidID, teamEKMetadata.Generation)
   138  	require.NoError(t, err)
   139  
   140  	teamEKs, err = rawTeamEKBoxStorage.GetAll(mctx, teamID)
   141  	require.NoError(t, err)
   142  	require.Equal(t, 0, len(teamEKs))
   143  
   144  	s.ClearCache()
   145  
   146  	maxGeneration3, err := s.MaxGeneration(mctx, teamID, false)
   147  	require.NoError(t, err)
   148  	require.EqualValues(t, -1, maxGeneration3)
   149  
   150  	expired, err := s.DeleteExpired(mctx, teamID, merkleRoot)
   151  	expected := []keybase1.EkGeneration(nil)
   152  	require.NoError(t, err)
   153  	require.Equal(t, expected, expired)
   154  
   155  	// Verify we store failures in the cache
   156  	t.Logf("cache failures")
   157  	nonexistent, err = rawTeamEKBoxStorage.Get(mctx, teamID, teamEKMetadata.Generation+1, nil)
   158  	require.Error(t, err)
   159  	require.Equal(t, keybase1.TeamEphemeralKey{}, nonexistent)
   160  	cache, found, err := rawTeamEKBoxStorage.getCacheForTeamID(mctx, teamID)
   161  	require.NoError(t, err)
   162  	require.True(t, found)
   163  	require.Len(t, cache, 1)
   164  
   165  	cacheItem, ok := cache[teamEKMetadata.Generation+1]
   166  	require.True(t, ok)
   167  	require.True(t, cacheItem.HasError())
   168  }
   169  
   170  // If we change the key format intentionally, we have to introduce some form of
   171  // migration or versioning between the keys. This test should blow up if we
   172  // break it unintentionally.
   173  func TestTeamEKStorageKeyFormat(t *testing.T) {
   174  	tc, mctx, _ := ephemeralKeyTestSetup(t)
   175  	defer tc.Cleanup()
   176  
   177  	s := NewTeamEKBoxStorage(NewTeamEphemeralKeyer())
   178  	uv, err := tc.G.GetMeUV(context.TODO())
   179  	require.NoError(t, err)
   180  
   181  	teamID := createTeam(tc)
   182  
   183  	key, err := s.dbKey(mctx, teamID)
   184  	require.NoError(t, err)
   185  	expected := fmt.Sprintf("teamEphemeralKeyBox-%s-%s-%s-%s-%d", keybase1.TeamEphemeralKeyType_TEAM,
   186  		teamID, mctx.G().Env.GetUsername(), uv.EldestSeqno, teamEKBoxStorageDBVersion)
   187  	require.Equal(t, expected, key.Key)
   188  
   189  	s = NewTeamEKBoxStorage(NewTeambotEphemeralKeyer())
   190  	key, err = s.dbKey(mctx, teamID)
   191  	require.NoError(t, err)
   192  	expected = fmt.Sprintf("teamEphemeralKeyBox-%s-%s-%s-%s-%d", keybase1.TeamEphemeralKeyType_TEAMBOT,
   193  		teamID, mctx.G().Env.GetUsername(), uv.EldestSeqno, teamEKBoxStorageDBVersion)
   194  	require.Equal(t, expected, key.Key)
   195  }