file storage refactor to make file and profile stores
This commit is contained in:
parent
d95b8aa9ed
commit
ffc4254f18
|
@ -71,7 +71,7 @@ func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer
|
|||
log.Debugf("CreatePeer(%v)\n", name)
|
||||
|
||||
randomFileName := generateRandomFilename()
|
||||
fileStore := storage.CreateFileProfileStore(path.Join(app.directory, "profiles", randomFileName), password)
|
||||
fileStore := storage.NewFileStore(path.Join(app.directory, "profiles", randomFileName), password)
|
||||
p := peer.NewCwtchPeer(name)
|
||||
err := fileStore.Save(p)
|
||||
if err != nil {
|
||||
|
@ -99,7 +99,7 @@ func (app *application) LoadProfiles(password string) error {
|
|||
|
||||
for _, file := range files {
|
||||
|
||||
fileStore := storage.CreateFileProfileStore(path.Join(app.directory, "profiles", file.Name()), password)
|
||||
fileStore := storage.NewFileStore(path.Join(app.directory, "profiles", file.Name()), password)
|
||||
|
||||
p, err := fileStore.Load()
|
||||
if err != nil {
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
func convertCwtchFile(filename string, password string) error {
|
||||
fileStore := storage2.CreateFileProfileStore(filename, password)
|
||||
fileStore := storage2.NewFileStore(filename, password)
|
||||
peer, err := fileStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -77,7 +77,7 @@ func convertTorFile(filename string, password string) error {
|
|||
peer.GetProfile().Ed25519PrivateKey = sk
|
||||
peer.GetProfile().Ed25519PublicKey = pk
|
||||
peer.GetProfile().Onion = string(onion)
|
||||
fileStore := storage2.CreateFileProfileStore(filename, password)
|
||||
fileStore := storage2.NewFileStore(filename, password)
|
||||
err = fileStore.Save(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -100,7 +100,7 @@ func vanity() error {
|
|||
peer.GetProfile().Ed25519PrivateKey = sk
|
||||
peer.GetProfile().Ed25519PublicKey = pk
|
||||
peer.GetProfile().Onion = onion
|
||||
fileStore := storage2.CreateFileProfileStore(os.Args[3], onion+".cwtch")
|
||||
fileStore := storage2.NewFileStore(os.Args[3], onion+".cwtch")
|
||||
err := fileStore.Save(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -179,7 +179,7 @@ func main() {
|
|||
}
|
||||
pw = pw[:len(pw)-1]
|
||||
|
||||
fileStore := storage2.CreateFileProfileStore(os.Args[2], pw)
|
||||
fileStore := storage2.NewFileStore(os.Args[2], pw)
|
||||
|
||||
peer, err := fileStore.Load()
|
||||
if err != nil {
|
||||
|
@ -195,7 +195,7 @@ func main() {
|
|||
}
|
||||
newpw1 = newpw1[:len(newpw1)-1] // fuck go with this linebreak shit ^ea
|
||||
|
||||
fileStore2 := storage2.CreateFileProfileStore(os.Args[2], newpw1)
|
||||
fileStore2 := storage2.NewFileStore(os.Args[2], newpw1)
|
||||
err = fileStore2.Save(peer)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package storage
|
|
@ -0,0 +1,23 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"io"
|
||||
)
|
||||
|
||||
// createKey derives a key from a password
|
||||
func createKey(password string) ([32]byte, [128]byte, error) {
|
||||
var salt [128]byte
|
||||
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
||||
log.Errorf("Cannot read from random: %v\n", err)
|
||||
return [32]byte{}, salt, err
|
||||
}
|
||||
dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
|
||||
|
||||
var dkr [32]byte
|
||||
copy(dkr[:], dk)
|
||||
return dkr, salt, nil
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// fileProfileStore stores a cwtchPeer in an encrypted file
|
||||
type fileProfileStore struct {
|
||||
profilefile string
|
||||
password string
|
||||
}
|
||||
|
||||
// CreateFileProfileStore instantiates a fileProfileStore given a filename and a password
|
||||
func CreateFileProfileStore(profilefile string, password string) ProfileStore {
|
||||
filestore := new(fileProfileStore)
|
||||
filestore.password = password
|
||||
filestore.profilefile = profilefile
|
||||
return filestore
|
||||
}
|
||||
|
||||
// Save serializes a cwtchPeer to a file
|
||||
func (fps *fileProfileStore) Save(cwtchPeer peer.CwtchPeer) error {
|
||||
|
||||
key, salt, _ := createKey(fps.password)
|
||||
encryptedbytes, err := encryptProfile(cwtchPeer, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the salt for the derived key is appended to the front of the file
|
||||
encryptedbytes = append(salt[:], encryptedbytes...)
|
||||
err = ioutil.WriteFile(fps.profilefile, encryptedbytes, 0600)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// createKey derives a key from a password
|
||||
func createKey(password string) ([32]byte, [128]byte, error) {
|
||||
var salt [128]byte
|
||||
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
||||
log.Errorf("Cannot read from random: %v\n", err)
|
||||
return [32]byte{}, salt, err
|
||||
}
|
||||
dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
|
||||
|
||||
var dkr [32]byte
|
||||
copy(dkr[:], dk)
|
||||
return dkr, salt, nil
|
||||
}
|
||||
|
||||
//encryptProfile encrypts the cwtchPeer via the specified key.
|
||||
func encryptProfile(p peer.CwtchPeer, key [32]byte) ([]byte, error) {
|
||||
var nonce [24]byte
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
log.Errorf("Cannot read from random: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(p.GetProfile())
|
||||
encrypted := secretbox.Seal(nonce[:], []byte(bytes), &nonce, &key)
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
//decryptProfile decrypts the passed ciphertext into a cwtchPeer via the specified key.
|
||||
func decryptProfile(ciphertext []byte, key [32]byte) (*model.Profile, error) {
|
||||
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], ciphertext[:24])
|
||||
decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &key)
|
||||
if ok {
|
||||
cp := new(model.Profile)
|
||||
err := json.Unmarshal(decrypted, &cp)
|
||||
if err == nil {
|
||||
return cp, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("Failed to decrypt")
|
||||
}
|
||||
|
||||
// Load instantiates a cwtchPeer from the file store
|
||||
func (fps *fileProfileStore) Load() (peer.CwtchPeer, error) {
|
||||
encryptedbytes, err := ioutil.ReadFile(fps.profilefile)
|
||||
if err == nil {
|
||||
var dkr [32]byte
|
||||
//Separate the salt from the encrypted bytes, then generate the derived key
|
||||
salt, encryptedbytes := encryptedbytes[0:128], encryptedbytes[128:]
|
||||
dk := pbkdf2.Key([]byte(fps.password), salt, 4096, 32, sha3.New512)
|
||||
copy(dkr[:], dk)
|
||||
|
||||
profile, err := decryptProfile(encryptedbytes, dkr)
|
||||
if err == nil {
|
||||
return peer.FromProfile(profile), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// fileStore stores a cwtchPeer in an encrypted file
|
||||
type fileStore struct {
|
||||
filename string
|
||||
password string
|
||||
}
|
||||
|
||||
type FileStore interface {
|
||||
Save([]byte) error
|
||||
Load() ([]byte, error)
|
||||
}
|
||||
|
||||
// NewFileStore instantiates a fileStore given a filename and a password
|
||||
func NewFileStore(filename string, password string) FileStore {
|
||||
filestore := new(fileStore)
|
||||
filestore.password = password
|
||||
filestore.filename = filename
|
||||
return filestore
|
||||
}
|
||||
|
||||
// Save serializes a cwtchPeer to a file
|
||||
func (fps *fileStore) Save(data []byte) error {
|
||||
key, salt, _ := createKey(fps.password)
|
||||
encryptedbytes, err := encryptFileData(data, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the salt for the derived key is appended to the front of the file
|
||||
encryptedbytes = append(salt[:], encryptedbytes...)
|
||||
err = ioutil.WriteFile(fps.filename, encryptedbytes, 0600)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
//encryptFileData encrypts the cwtchPeer via the specified key.
|
||||
func encryptFileData(data []byte, key [32]byte) ([]byte, error) {
|
||||
var nonce [24]byte
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
log.Errorf("Cannot read from random: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encrypted := secretbox.Seal(nonce[:], data, &nonce, &key)
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
//decryptFile decrypts the passed ciphertext into a cwtchPeer via the specified key.
|
||||
func decryptFile(ciphertext []byte, key [32]byte) ([]byte, error) {
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], ciphertext[:24])
|
||||
decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &key)
|
||||
if ok {
|
||||
return decrypted, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Failed to decrypt")
|
||||
}
|
||||
|
||||
// Load instantiates a cwtchPeer from the file store
|
||||
func (fps *fileStore) Load() ([]byte, error) {
|
||||
encryptedbytes, err := ioutil.ReadFile(fps.filename)
|
||||
if err == nil {
|
||||
var dkr [32]byte
|
||||
//Separate the salt from the encrypted bytes, then generate the derived key
|
||||
salt, encryptedbytes := encryptedbytes[0:128], encryptedbytes[128:]
|
||||
dk := pbkdf2.Key([]byte(fps.password), salt, 4096, 32, sha3.New512)
|
||||
copy(dkr[:], dk)
|
||||
|
||||
data, err := decryptFile(encryptedbytes, dkr)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
func TestFileProfileStore(t *testing.T) {
|
||||
fileStore := CreateFileProfileStore(".test.json", "password")
|
||||
fileStore := NewFileStore(".test.json", "password")
|
||||
alice := peer.NewCwtchPeer("alice")
|
||||
fileStore.Save(alice)
|
||||
|
|
@ -1,11 +1,39 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/peer"
|
||||
"cwtch.im/cwtch/model"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type profileStore struct {
|
||||
fs FileStore
|
||||
}
|
||||
|
||||
// ProfileStore is an interface to managing the storage of Cwtch Profiles
|
||||
type ProfileStore interface {
|
||||
Save(cwtchPeer peer.CwtchPeer) error
|
||||
Load() (peer.CwtchPeer, error)
|
||||
Save(profile *model.Profile) error
|
||||
Load() (*model.Profile, error)
|
||||
}
|
||||
|
||||
func NewProfileStore(filename string, password string) ProfileStore {
|
||||
return &profileStore{NewFileStore(filename, password)}
|
||||
}
|
||||
|
||||
func (ps *profileStore) Save(profile *model.Profile) error {
|
||||
bytes, _ := json.Marshal(profile)
|
||||
return ps.fs.Save(bytes)
|
||||
}
|
||||
|
||||
// Load instantiates a cwtchPeer from the file store
|
||||
func (ps *profileStore) Load() (*model.Profile, error) {
|
||||
decrypted, err := ps.fs.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := new(model.Profile)
|
||||
err = json.Unmarshal(decrypted, &cp)
|
||||
if err == nil {
|
||||
return cp, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue