github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/secret_store_file_test.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"bytes"
     8  
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"sort"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func newNilMetaContext() MetaContext {
    19  	return NewMetaContextTODO(nil)
    20  }
    21  
    22  func testSSDir(t *testing.T) (string, func()) {
    23  	td, err := os.MkdirTemp("", "ss")
    24  	require.NoError(t, err)
    25  
    26  	create := func(name, secret string) {
    27  		err := os.WriteFile(filepath.Join(td, name+".ss"), []byte(secret), PermFile)
    28  		require.NoError(t, err)
    29  	}
    30  
    31  	// create some ss files
    32  	create("alice", "alicealicealicealicealicealiceal")
    33  	create("bob", "bobbobbobbobbobbobbobbobbobbobbo")
    34  
    35  	cleanup := func() {
    36  		if err := os.RemoveAll(td); err != nil {
    37  			t.Log(err)
    38  		}
    39  	}
    40  
    41  	return td, cleanup
    42  }
    43  
    44  func TestSecretStoreFileRetrieveSecret(t *testing.T) {
    45  	tc := SetupTest(t, "SecretStoreFile", 1)
    46  	defer tc.Cleanup()
    47  	m := NewMetaContextForTest(tc)
    48  
    49  	td, tdClean := testSSDir(t)
    50  	defer tdClean()
    51  
    52  	cases := map[string]struct {
    53  		username NormalizedUsername
    54  		secret   []byte
    55  		err      error
    56  	}{
    57  		"alice":     {"alice", []byte("alicealicealicealicealicealiceal"), nil},
    58  		"bob":       {"bob", []byte("bobbobbobbobbobbobbobbobbobbobbo"), nil},
    59  		"not found": {"nobody", nil, NewErrSecretForUserNotFound("nobody")},
    60  	}
    61  
    62  	ss := NewSecretStoreFile(td)
    63  
    64  	for _, test := range cases {
    65  		secret, err := ss.RetrieveSecret(m, test.username)
    66  		require.Equal(t, test.err, err)
    67  		require.True(t, bytes.Equal(secret.Bytes(), test.secret))
    68  	}
    69  }
    70  
    71  func TestSecretStoreFileStoreSecret(t *testing.T) {
    72  	tc := SetupTest(t, "SecretStoreFile", 1)
    73  	defer tc.Cleanup()
    74  	m := NewMetaContextForTest(tc)
    75  
    76  	td, tdClean := testSSDir(t)
    77  	defer tdClean()
    78  
    79  	cases := map[string]struct {
    80  		username NormalizedUsername
    81  		secret   []byte
    82  	}{
    83  		"new entry": {"charlie", []byte("charliecharliecharliecharliechar")},
    84  		"replace":   {"alice", []byte("alice_next_secret_alice_next_sec")},
    85  	}
    86  
    87  	ss := NewSecretStoreFile(td)
    88  
    89  	for _, test := range cases {
    90  		fs, err := newLKSecFullSecretFromBytes(test.secret)
    91  		require.NoError(t, err)
    92  		err = ss.StoreSecret(m, test.username, fs)
    93  		require.NoError(t, err)
    94  
    95  		secret, err := ss.RetrieveSecret(m, test.username)
    96  		require.NoError(t, err)
    97  		require.True(t, bytes.Equal(secret.Bytes(), test.secret))
    98  	}
    99  }
   100  
   101  func TestSecretStoreFileClearSecret(t *testing.T) {
   102  	tc := SetupTest(t, "SecretStoreFile", 1)
   103  	defer tc.Cleanup()
   104  	m := NewMetaContextForTest(tc)
   105  
   106  	td, tdClean := testSSDir(t)
   107  	defer tdClean()
   108  
   109  	ss := NewSecretStoreFile(td)
   110  
   111  	err := ss.ClearSecret(m, "alice")
   112  	require.NoError(t, err)
   113  
   114  	secret, err := ss.RetrieveSecret(m, "alice")
   115  	require.IsType(t, SecretStoreError{}, err)
   116  	require.True(t, secret.IsNil())
   117  }
   118  
   119  func TestSecretStoreFileGetUsersWithStoredSecrets(t *testing.T) {
   120  	tc := SetupTest(t, "SecretStoreFile", 1)
   121  	defer tc.Cleanup()
   122  	m := NewMetaContextForTest(tc)
   123  
   124  	td, tdClean := testSSDir(t)
   125  	defer tdClean()
   126  
   127  	ss := NewSecretStoreFile(td)
   128  
   129  	users, err := ss.GetUsersWithStoredSecrets(m)
   130  	require.NoError(t, err)
   131  	require.Len(t, users, 2)
   132  	sort.Strings(users)
   133  	require.Equal(t, users[0], "alice")
   134  	require.Equal(t, users[1], "bob")
   135  
   136  	fs, err := newLKSecFullSecretFromBytes([]byte("xavierxavierxavierxavierxavierxa"))
   137  	require.NoError(t, err)
   138  
   139  	err = ss.StoreSecret(m, "xavier", fs)
   140  	require.NoError(t, err)
   141  
   142  	users, err = ss.GetUsersWithStoredSecrets(m)
   143  	require.NoError(t, err)
   144  	require.Len(t, users, 3)
   145  
   146  	sort.Strings(users)
   147  	require.Equal(t, users[0], "alice")
   148  	require.Equal(t, users[1], "bob")
   149  	require.Equal(t, users[2], "xavier")
   150  
   151  	err = ss.ClearSecret(m, "bob")
   152  	require.NoError(t, err)
   153  
   154  	users, err = ss.GetUsersWithStoredSecrets(m)
   155  	require.NoError(t, err)
   156  	require.Len(t, users, 2)
   157  
   158  	sort.Strings(users)
   159  	require.Equal(t, users[0], "alice")
   160  	require.Equal(t, users[1], "xavier")
   161  }
   162  
   163  func assertExists(t *testing.T, path string) {
   164  	exists, err := FileExists(path)
   165  	require.NoError(t, err)
   166  	require.True(t, exists)
   167  }
   168  
   169  func assertNotExists(t *testing.T, path string) {
   170  	exists, err := FileExists(path)
   171  	require.NoError(t, err)
   172  	require.False(t, exists)
   173  }
   174  
   175  func TestSecretStoreFileRetrieveUpgrade(t *testing.T) {
   176  	tc := SetupTest(t, "SecretStoreFile", 1)
   177  	defer tc.Cleanup()
   178  	m := NewMetaContextForTest(tc)
   179  
   180  	td, tdClean := testSSDir(t)
   181  	defer tdClean()
   182  
   183  	assertExists(t, filepath.Join(td, "alice.ss"))
   184  	assertNotExists(t, filepath.Join(td, "alice.ss2"))
   185  	assertNotExists(t, filepath.Join(td, "alice.ns2"))
   186  	assertExists(t, filepath.Join(td, "bob.ss"))
   187  	assertNotExists(t, filepath.Join(td, "bob.ss2"))
   188  	assertNotExists(t, filepath.Join(td, "bob.ns2"))
   189  
   190  	ss := NewSecretStoreFile(td)
   191  
   192  	// retrieve secret for alice should upgrade from alice.ss to alice.ss2
   193  	secret, err := ss.RetrieveSecret(m, "alice")
   194  	require.NoError(t, err)
   195  
   196  	assertNotExists(t, filepath.Join(td, "alice.ss"))
   197  	assertExists(t, filepath.Join(td, "alice.ss2"))
   198  	assertExists(t, filepath.Join(td, "alice.ns2"))
   199  
   200  	secretUpgraded, err := ss.RetrieveSecret(m, "alice")
   201  	require.NoError(t, err)
   202  	require.True(t, bytes.Equal(secret.Bytes(), secretUpgraded.Bytes()))
   203  
   204  	// bob v1 should be untouched
   205  	assertExists(t, filepath.Join(td, "bob.ss"))
   206  	assertNotExists(t, filepath.Join(td, "bob.ss2"))
   207  	assertNotExists(t, filepath.Join(td, "bob.ns2"))
   208  }
   209  
   210  func TestSecretStoreFileNoise(t *testing.T) {
   211  	tc := SetupTest(t, "SecretStoreFile", 1)
   212  	defer tc.Cleanup()
   213  	m := NewMetaContextForTest(tc)
   214  
   215  	td, tdClean := testSSDir(t)
   216  	defer tdClean()
   217  
   218  	secret, err := RandBytes(32)
   219  	require.NoError(t, err)
   220  
   221  	lksec, err := newLKSecFullSecretFromBytes(secret)
   222  	require.NoError(t, err)
   223  
   224  	ss := NewSecretStoreFile(td)
   225  	err = ss.StoreSecret(m, "ogden", lksec)
   226  	require.NoError(t, err)
   227  	noise, err := os.ReadFile(filepath.Join(td, "ogden.ns2"))
   228  	require.NoError(t, err)
   229  
   230  	// flip one bit
   231  	noise[0] ^= 0x01
   232  
   233  	err = os.WriteFile(filepath.Join(td, "ogden.ns2"), noise, PermFile)
   234  	require.NoError(t, err)
   235  
   236  	corrupt, err := ss.RetrieveSecret(m, "ogden")
   237  	require.NoError(t, err)
   238  
   239  	require.False(t, bytes.Equal(lksec.Bytes(), corrupt.Bytes()))
   240  }
   241  
   242  func TestPrimeSecretStoreFile(t *testing.T) {
   243  	td, tdClean := testSSDir(t)
   244  	defer tdClean()
   245  
   246  	tc := SetupTest(t, "secret_store_file", 1)
   247  	defer tc.Cleanup()
   248  	tc.G.Env.Test.SecretStorePrimingDisabled = false
   249  
   250  	mctx := NewMetaContextForTest(tc)
   251  	secretStore := NewSecretStoreFile(td)
   252  	err := PrimeSecretStore(mctx, secretStore)
   253  	require.NoError(t, err)
   254  }
   255  
   256  func TestPrimeSecretStoreFileFail(t *testing.T) {
   257  	if runtime.GOOS == "windows" {
   258  		t.Skip("this test uses chmod, skipping on Windows")
   259  	}
   260  
   261  	tc := SetupTest(t, "secret_store_file", 1)
   262  	defer tc.Cleanup()
   263  	tc.G.Env.Test.SecretStorePrimingDisabled = false
   264  
   265  	td, cleanup := CreateReadOnlySecretStoreDir(tc)
   266  	defer cleanup()
   267  
   268  	mctx := NewMetaContextForTest(tc)
   269  	secretStore := NewSecretStoreFile(td)
   270  	err := PrimeSecretStore(mctx, secretStore)
   271  	require.Error(t, err)
   272  	require.Contains(t, err.Error(), "permission denied")
   273  }