github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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 }