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  }