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

     1  package encrypteddb
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/go-errors/errors"
     7  
     8  	"github.com/keybase/client/go/libkb"
     9  	"golang.org/x/crypto/nacl/secretbox"
    10  	"golang.org/x/net/context"
    11  )
    12  
    13  type DbFn func(g *libkb.GlobalContext) *libkb.JSONLocalDb
    14  type KeyFn func(context.Context) ([32]byte, error)
    15  
    16  type boxedData struct {
    17  	V int
    18  	N [24]byte
    19  	E []byte
    20  }
    21  
    22  // ErrDecryptionFailed is returned when an encrypteddb box fails to decrypt
    23  var ErrDecryptionFailed = errors.New("failed to decrypt item")
    24  
    25  // ***
    26  // If we change this, make sure to update the key derivation reason for all callers of EncryptedDB!
    27  // ***
    28  const cryptoVersion = 1
    29  
    30  // Handle to a db that encrypts values using nacl secretbox.
    31  // Does not encrypt keys.
    32  // Not threadsafe.
    33  type EncryptedDB struct {
    34  	libkb.Contextified
    35  
    36  	getSecretBoxKey KeyFn
    37  	getDB           DbFn
    38  }
    39  
    40  func New(g *libkb.GlobalContext, getDB DbFn, getSecretBoxKey KeyFn) *EncryptedDB {
    41  	return &EncryptedDB{
    42  		Contextified:    libkb.NewContextified(g),
    43  		getDB:           getDB,
    44  		getSecretBoxKey: getSecretBoxKey,
    45  	}
    46  }
    47  
    48  func DecodeBox(ctx context.Context, b []byte, getSecretBoxKey KeyFn,
    49  	res interface{}) error {
    50  	// Decode encrypted box
    51  	var boxed boxedData
    52  	if err := libkb.MPackDecode(b, &boxed); err != nil {
    53  		return err
    54  	}
    55  	if boxed.V > cryptoVersion {
    56  		return fmt.Errorf("bad crypto version: %d current: %d", boxed.V,
    57  			cryptoVersion)
    58  	}
    59  	enckey, err := getSecretBoxKey(ctx)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	pt, ok := secretbox.Open(nil, boxed.E, &boxed.N, &enckey)
    64  	if !ok {
    65  		return ErrDecryptionFailed
    66  	}
    67  
    68  	if err = libkb.MPackDecode(pt, res); err != nil {
    69  		return err
    70  	}
    71  	return nil
    72  }
    73  
    74  // Get a value
    75  // Decodes into res
    76  // Returns (found, err). Res is valid only if (found && err == nil)
    77  func (i *EncryptedDB) Get(ctx context.Context, key libkb.DbKey, res interface{}) (bool, error) {
    78  	var err error
    79  	db := i.getDB(i.G())
    80  	b, found, err := db.GetRaw(key)
    81  	if err != nil {
    82  		return false, err
    83  	}
    84  	if !found {
    85  		return false, nil
    86  	}
    87  	if err = DecodeBox(ctx, b, i.getSecretBoxKey, res); err != nil {
    88  		return false, err
    89  	}
    90  	return true, nil
    91  }
    92  
    93  func EncodeBox(ctx context.Context, data interface{}, getSecretBoxKey KeyFn) ([]byte, error) {
    94  	dat, err := libkb.MPackEncode(data)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	enckey, err := getSecretBoxKey(ctx)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	var nonce []byte
   104  	nonce, err = libkb.RandBytes(24)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	var fnonce [24]byte
   109  	copy(fnonce[:], nonce)
   110  	sealed := secretbox.Seal(nil, dat, &fnonce, &enckey)
   111  	boxed := boxedData{
   112  		V: cryptoVersion,
   113  		E: sealed,
   114  		N: fnonce,
   115  	}
   116  
   117  	// Encode encrypted box
   118  	if dat, err = libkb.MPackEncode(boxed); err != nil {
   119  		return nil, err
   120  	}
   121  	return dat, nil
   122  }
   123  
   124  func (i *EncryptedDB) Put(ctx context.Context, key libkb.DbKey, data interface{}) error {
   125  	db := i.getDB(i.G())
   126  	dat, err := EncodeBox(ctx, data, i.getSecretBoxKey)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	// Write out
   131  	return db.PutRaw(key, dat)
   132  }
   133  
   134  func (i *EncryptedDB) Delete(ctx context.Context, key libkb.DbKey) error {
   135  	db := i.getDB(i.G())
   136  	return db.Delete(key)
   137  }