github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkey/key_server_local.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkey 6 7 import ( 8 "context" 9 "os" 10 "path/filepath" 11 "sync" 12 13 "github.com/keybase/client/go/kbfs/idutil" 14 "github.com/keybase/client/go/kbfs/ioutil" 15 "github.com/keybase/client/go/kbfs/kbfscodec" 16 "github.com/keybase/client/go/kbfs/kbfscrypto" 17 "github.com/keybase/client/go/kbfs/kbfsmd" 18 "github.com/keybase/client/go/logger" 19 "github.com/keybase/client/go/protocol/keybase1" 20 "github.com/pkg/errors" 21 "github.com/syndtr/goleveldb/leveldb" 22 "github.com/syndtr/goleveldb/leveldb/storage" 23 ) 24 25 func checkContext(ctx context.Context) error { 26 select { 27 case <-ctx.Done(): 28 return errors.WithStack(ctx.Err()) 29 default: 30 return nil 31 } 32 } 33 34 // KeyServerConfig is a config object containing the outside helper 35 // instances needed by KeyServerLocal. 36 type KeyServerConfig interface { 37 Codec() kbfscodec.Codec 38 KBPKI() idutil.KBPKI 39 } 40 41 // KeyServerLocal puts/gets key server halves in/from a local leveldb instance. 42 type KeyServerLocal struct { 43 config KeyServerConfig 44 storCloser func() error 45 db *leveldb.DB // kbfscrypto.TLFCryptKeyServerHalfID -> TLFCryptKeyServerHalf 46 log logger.Logger 47 48 shutdownLock *sync.RWMutex 49 shutdown *bool 50 shutdownFunc func(logger.Logger) 51 } 52 53 // Test that KeyServerLocal fully implements the KeyServer interface. 54 var _ KeyServer = (*KeyServerLocal)(nil) 55 56 func newKeyServerLocal( 57 config KeyServerConfig, log logger.Logger, storage storage.Storage, 58 shutdownFunc func(logger.Logger)) (ks *KeyServerLocal, err error) { 59 defer func() { 60 if err != nil { 61 _ = storage.Close() 62 } 63 }() 64 65 db, err := leveldb.Open(storage, nil) 66 if err != nil { 67 return nil, err 68 } 69 kops := &KeyServerLocal{ 70 config, storage.Close, db, log, &sync.RWMutex{}, new(bool), 71 shutdownFunc} 72 return kops, nil 73 } 74 75 // NewKeyServerMemory returns a KeyServerLocal with an in-memory leveldb 76 // instance. 77 func NewKeyServerMemory(config KeyServerConfig, log logger.Logger) ( 78 *KeyServerLocal, error) { 79 return newKeyServerLocal(config, log, storage.NewMemStorage(), nil) 80 } 81 82 func newKeyServerDisk( 83 config KeyServerConfig, log logger.Logger, dirPath string, 84 shutdownFunc func(logger.Logger)) (*KeyServerLocal, error) { 85 keyPath := filepath.Join(dirPath, "keys") 86 storage, err := storage.OpenFile(keyPath, false) 87 if err != nil { 88 return nil, err 89 } 90 return newKeyServerLocal(config, log, storage, shutdownFunc) 91 } 92 93 // NewKeyServerDir constructs a new KeyServerLocal that stores its 94 // data in the given directory. 95 func NewKeyServerDir( 96 config KeyServerConfig, log logger.Logger, dirPath string) ( 97 *KeyServerLocal, error) { 98 return newKeyServerDisk(config, log, dirPath, nil) 99 } 100 101 // NewKeyServerTempDir constructs a new KeyServerLocal that stores its 102 // data in a temp directory which is cleaned up on shutdown. 103 func NewKeyServerTempDir( 104 config KeyServerConfig, log logger.Logger) (*KeyServerLocal, error) { 105 tempdir, err := ioutil.TempDir(os.TempDir(), "kbfs_keyserver_tmp") 106 if err != nil { 107 return nil, err 108 } 109 return newKeyServerDisk(config, log, tempdir, func(log logger.Logger) { 110 err := ioutil.RemoveAll(tempdir) 111 if err != nil { 112 log.Warning("error removing %s: %s", tempdir, err) 113 } 114 }) 115 } 116 117 // GetTLFCryptKeyServerHalf implements the KeyServer interface for 118 // KeyServerLocal. 119 func (ks *KeyServerLocal) GetTLFCryptKeyServerHalf( 120 ctx context.Context, serverHalfID kbfscrypto.TLFCryptKeyServerHalfID, 121 key kbfscrypto.CryptPublicKey) ( 122 serverHalf kbfscrypto.TLFCryptKeyServerHalf, err error) { 123 if err := checkContext(ctx); err != nil { 124 return kbfscrypto.TLFCryptKeyServerHalf{}, err 125 } 126 127 ks.shutdownLock.RLock() 128 defer ks.shutdownLock.RUnlock() 129 if *ks.shutdown { 130 err = errors.New("Key server already shut down") 131 } 132 133 buf, err := ks.db.Get(serverHalfID.ID.Bytes(), nil) 134 if err != nil { 135 return 136 } 137 138 err = ks.config.Codec().Decode(buf, &serverHalf) 139 if err != nil { 140 return kbfscrypto.TLFCryptKeyServerHalf{}, err 141 } 142 143 session, err := ks.config.KBPKI().GetCurrentSession(ctx) 144 if err != nil { 145 return kbfscrypto.TLFCryptKeyServerHalf{}, err 146 } 147 148 err = kbfscrypto.VerifyTLFCryptKeyServerHalfID( 149 serverHalfID, session.UID, key, serverHalf) 150 if err != nil { 151 ks.log.CDebugf(ctx, "error verifying server half ID: %+v", err) 152 return kbfscrypto.TLFCryptKeyServerHalf{}, 153 kbfsmd.ServerErrorUnauthorized{Err: err} 154 } 155 return serverHalf, nil 156 } 157 158 // PutTLFCryptKeyServerHalves implements the KeyServer interface for 159 // KeyServerLocal. 160 func (ks *KeyServerLocal) PutTLFCryptKeyServerHalves(ctx context.Context, 161 keyServerHalves kbfsmd.UserDeviceKeyServerHalves) error { 162 if err := checkContext(ctx); err != nil { 163 return err 164 } 165 166 ks.shutdownLock.RLock() 167 defer ks.shutdownLock.RUnlock() 168 if *ks.shutdown { 169 return errors.New("Key server already shut down") 170 } 171 172 // batch up the writes such that they're atomic. 173 batch := &leveldb.Batch{} 174 for uid, deviceMap := range keyServerHalves { 175 for deviceKey, serverHalf := range deviceMap { 176 buf, err := ks.config.Codec().Encode(serverHalf) 177 if err != nil { 178 return err 179 } 180 id, err := kbfscrypto.MakeTLFCryptKeyServerHalfID( 181 uid, deviceKey, serverHalf) 182 if err != nil { 183 return err 184 } 185 batch.Put(id.ID.Bytes(), buf) 186 } 187 } 188 return ks.db.Write(batch, nil) 189 } 190 191 // DeleteTLFCryptKeyServerHalf implements the KeyServer interface for 192 // KeyServerLocal. 193 func (ks *KeyServerLocal) DeleteTLFCryptKeyServerHalf(ctx context.Context, 194 _ keybase1.UID, _ kbfscrypto.CryptPublicKey, 195 serverHalfID kbfscrypto.TLFCryptKeyServerHalfID) error { 196 if err := checkContext(ctx); err != nil { 197 return err 198 } 199 200 ks.shutdownLock.RLock() 201 defer ks.shutdownLock.RUnlock() 202 if *ks.shutdown { 203 return errors.New("Key server already shut down") 204 } 205 206 // TODO: verify that the kid is really valid for the given uid 207 208 return ks.db.Delete(serverHalfID.ID.Bytes(), nil) 209 } 210 211 // CopyWithConfigAndLogger copies a key server but swaps the config 212 // and the logger. 213 func (ks *KeyServerLocal) CopyWithConfigAndLogger( 214 config KeyServerConfig, log logger.Logger) *KeyServerLocal { 215 return &KeyServerLocal{ 216 config, ks.storCloser, ks.db, log, ks.shutdownLock, ks.shutdown, 217 ks.shutdownFunc} 218 } 219 220 // Shutdown implements the KeyServer interface for KeyServerLocal. 221 func (ks *KeyServerLocal) Shutdown() { 222 ks.shutdownLock.Lock() 223 defer ks.shutdownLock.Unlock() 224 if *ks.shutdown { 225 return 226 } 227 *ks.shutdown = true 228 229 if ks.db != nil { 230 _ = ks.db.Close() 231 } 232 233 if ks.storCloser != nil { 234 _ = ks.storCloser() 235 } 236 237 if ks.shutdownFunc != nil { 238 ks.shutdownFunc(ks.log) 239 } 240 }