cwtch/peer/storage.go

165 lines
4.4 KiB
Go
Raw Normal View History

package peer
import (
"crypto/rand"
"database/sql"
"fmt"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/sha3"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
)
const versionFile = "VERSION"
const version = "2"
const saltFile = "SALT"
// CreateKeySalt derives a key and salt from a password: returns key, salt, err
func CreateKeySalt(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
}
// createKey derives a key from a password and salt
func createKey(password string, salt []byte) [32]byte {
dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha3.New512)
var dkr [32]byte
copy(dkr[:], dk)
return dkr
}
func initV2Directory(directory, password string) ([32]byte, [128]byte, error) {
os.Mkdir(directory, 0700)
key, salt, err := CreateKeySalt(password)
if err != nil {
log.Errorf("Could not create key for profile store from password: %v\n", err)
return [32]byte{}, [128]byte{}, err
}
if err = ioutil.WriteFile(path.Join(directory, versionFile), []byte(version), 0600); err != nil {
log.Errorf("Could not write version file: %v", err)
return [32]byte{}, [128]byte{}, err
}
if err = ioutil.WriteFile(path.Join(directory, saltFile), salt[:], 0600); err != nil {
log.Errorf("Could not write salt file: %v", err)
return [32]byte{}, [128]byte{}, err
}
return key, salt, nil
}
func openEncryptedDatabase(profileDirectory string, password string) (*sql.DB, error) {
salt, err := ioutil.ReadFile(path.Join(profileDirectory, saltFile))
if err != nil {
return nil, err
}
key := createKey(password, salt)
dbPath := filepath.Join(profileDirectory, "db")
dbname := fmt.Sprintf("%v?_pragma_key=x'%x'&_pragma_cipher_page_size=8192", dbPath, key)
db, err := sql.Open("sqlite3", dbname)
if err != nil {
log.Errorf("could not open encrypted database", err)
return nil, err
}
return db, nil
}
// CreateEncryptedStorePeer creates a *new* Cwtch Profile backed by an encrypted datastore
func CreateEncryptedStorePeer(profileDirectory string, name string, password string) (CwtchPeer, error) {
log.Debugf("Initializing Encrypted Storage Directory")
_, _, err := initV2Directory(profileDirectory, password)
if err != nil {
return nil, err
}
log.Debugf("Opening Encrypted Database")
db, err := openEncryptedDatabase(profileDirectory, password)
if db == nil || err != nil {
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
}
log.Debugf("Initializing Database")
err = initializeDatabase(db)
if err != nil {
db.Close()
return nil, err
}
log.Debugf("Creating Cwtch Profile Backed By Encrypted Database")
cps, err := NewCwtchProfileStorage(db)
if err != nil {
db.Close()
return nil, err
}
return NewProfileWithEncryptedStorage(name, cps), nil
}
2021-11-17 23:34:14 +00:00
// CreateEncryptedStore creates a encrypted datastore
func CreateEncryptedStore(profileDirectory string, password string) (*CwtchProfileStorage, error) {
log.Debugf("Initializing Encrypted Storage Directory")
_, _, err := initV2Directory(profileDirectory, password)
if err != nil {
return nil, err
}
log.Debugf("Opening Encrypted Database")
db, err := openEncryptedDatabase(profileDirectory, password)
if db == nil || err != nil {
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
}
log.Debugf("Initializing Database")
err = initializeDatabase(db)
if err != nil {
db.Close()
return nil, err
}
log.Debugf("Creating Cwtch Profile Backed By Encrypted Database")
cps, err := NewCwtchProfileStorage(db)
if err != nil {
db.Close()
return nil, err
}
return cps, nil
}
// FromEncryptedDatabase constructs a Cwtch Profile from an existing Encrypted Database
func FromEncryptedDatabase(profileDirectory string, password string) (CwtchPeer, error) {
log.Debugf("Loading Encrypted Profile")
db, err := openEncryptedDatabase(profileDirectory, password)
if db == nil || err != nil {
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
}
log.Debugf("Initializing Profile from Encrypted Storage")
cps, err := NewCwtchProfileStorage(db)
if err != nil {
db.Close()
return nil, err
}
return FromEncryptedStorage(cps), nil
}