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