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 }