2021-11-09 23:47:33 +00:00
|
|
|
package peer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"database/sql"
|
2021-11-18 23:43:58 +00:00
|
|
|
"errors"
|
2021-11-09 23:47:33 +00:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
2021-11-18 23:43:58 +00:00
|
|
|
func openEncryptedDatabase(profileDirectory string, password string, createIfNotExists bool) (*sql.DB, error) {
|
2021-11-09 23:47:33 +00:00
|
|
|
salt, err := ioutil.ReadFile(path.Join(profileDirectory, saltFile))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
key := createKey(password, salt)
|
|
|
|
dbPath := filepath.Join(profileDirectory, "db")
|
2021-11-18 23:43:58 +00:00
|
|
|
|
|
|
|
if !createIfNotExists {
|
|
|
|
if _, err := os.Stat(dbPath); errors.Is(err, os.ErrNotExist) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-09 23:47:33 +00:00
|
|
|
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")
|
2021-11-18 23:43:58 +00:00
|
|
|
db, err := openEncryptedDatabase(profileDirectory, password, true)
|
2021-11-09 23:47:33 +00:00
|
|
|
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")
|
|
|
|
|
2021-11-19 19:49:04 +00:00
|
|
|
cps, err := NewCwtchProfileStorage(db, profileDirectory)
|
2021-11-09 23:47:33 +00:00
|
|
|
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) {
|
|
|
|
|
2021-11-18 23:43:58 +00:00
|
|
|
log.Debugf("Creating Encrypted Database")
|
|
|
|
db, err := openEncryptedDatabase(profileDirectory, password, true)
|
2021-11-17 23:34:14 +00:00
|
|
|
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")
|
|
|
|
|
2021-11-19 19:49:04 +00:00
|
|
|
cps, err := NewCwtchProfileStorage(db, profileDirectory)
|
2021-11-17 23:34:14 +00:00
|
|
|
if err != nil {
|
|
|
|
db.Close()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cps, nil
|
|
|
|
}
|
|
|
|
|
2021-11-09 23:47:33 +00:00
|
|
|
// FromEncryptedDatabase constructs a Cwtch Profile from an existing Encrypted Database
|
|
|
|
func FromEncryptedDatabase(profileDirectory string, password string) (CwtchPeer, error) {
|
2021-11-18 23:43:58 +00:00
|
|
|
log.Infof("Loading Encrypted Profile: %v", profileDirectory)
|
|
|
|
db, err := openEncryptedDatabase(profileDirectory, password, false)
|
2021-11-09 23:47:33 +00:00
|
|
|
if db == nil || err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debugf("Initializing Profile from Encrypted Storage")
|
2021-11-19 19:49:04 +00:00
|
|
|
cps, err := NewCwtchProfileStorage(db, profileDirectory)
|
2021-11-09 23:47:33 +00:00
|
|
|
if err != nil {
|
|
|
|
db.Close()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return FromEncryptedStorage(cps), nil
|
|
|
|
}
|