status-go/services/shhext/db.go

174 lines
3.8 KiB
Go

package shhext
import (
"fmt"
"os"
sqliteDriver "github.com/mutecomm/go-sqlcipher" // We require go sqlcipher that overrides default implementation
"github.com/status-im/status-protocol-go/sqlite"
)
const exportDB = "SELECT sqlcipher_export('newdb')"
// The default number of kdf iterations in sqlcipher (from version 3.0.0)
// https://github.com/sqlcipher/sqlcipher/blob/fda4c68bb474da7e955be07a2b807bda1bb19bd2/CHANGELOG.md#300---2013-11-05
// https://www.zetetic.net/sqlcipher/sqlcipher-api/#kdf_iter
const defaultKdfIterationsNumber = 64000
// The reduced number of kdf iterations (for performance reasons) which is
// currently used for derivation of the database key
// https://github.com/status-im/status-go/pull/1343
// https://notes.status.im/i8Y_l7ccTiOYq09HVgoFwA
const KdfIterationsNumber = 3200
func migrateDBFile(oldPath string, newPath string, oldKey string, newKey string) error {
_, err := os.Stat(oldPath)
// No files, nothing to do
if os.IsNotExist(err) {
return nil
}
// Any other error, throws
if err != nil {
return err
}
if err := os.Rename(oldPath, newPath); err != nil {
return err
}
db, err := sqlite.OpenWithIter(newPath, oldKey, defaultKdfIterationsNumber, sqlite.MigrationConfig{})
if err != nil {
return err
}
keyString := fmt.Sprintf("PRAGMA rekey = '%s'", newKey)
if _, err = db.Exec(keyString); err != nil {
return err
}
return nil
}
// migrateDBKeyKdfIterations changes the number of kdf iterations executed
// during the database key derivation. This change is necessary because
// of performance reasons.
// https://github.com/status-im/status-go/pull/1343
// `sqlcipher_export` is used for migration, check out this link for details:
// https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlcipher_export
func migrateDBKeyKdfIterations(oldPath string, newPath string, key string) error {
_, err := os.Stat(oldPath)
// No files, nothing to do
if os.IsNotExist(err) {
return nil
}
// Any other error, throws
if err != nil {
return err
}
isEncrypted, err := sqliteDriver.IsEncrypted(oldPath)
if err != nil {
return err
}
// Nothing to do, move db to the next migration
if !isEncrypted {
return os.Rename(oldPath, newPath)
}
db, err := sqlite.OpenWithIter(oldPath, key, defaultKdfIterationsNumber, sqlite.MigrationConfig{})
if err != nil {
return err
}
attach := fmt.Sprintf(
"ATTACH DATABASE '%s' AS newdb KEY '%s'",
newPath,
key)
if _, err = db.Exec(attach); err != nil {
return err
}
changeKdfIter := fmt.Sprintf(
"PRAGMA newdb.kdf_iter = %d",
KdfIterationsNumber)
if _, err = db.Exec(changeKdfIter); err != nil {
return err
}
if _, err = db.Exec(exportDB); err != nil {
return err
}
if err = db.Close(); err != nil {
return err
}
return os.Remove(oldPath)
}
// encryptDatabase encrypts an unencrypted database with key
func encryptDatabase(oldPath string, newPath string, key string) error {
_, err := os.Stat(oldPath)
// No files, nothing to do
if os.IsNotExist(err) {
return nil
}
// Any other error, throws
if err != nil {
return err
}
isEncrypted, err := sqliteDriver.IsEncrypted(oldPath)
if err != nil {
return err
}
// Nothing to do, already encrypted
if isEncrypted {
return os.Rename(oldPath, newPath)
}
db, err := sqlite.OpenWithIter(oldPath, "", defaultKdfIterationsNumber, sqlite.MigrationConfig{})
if err != nil {
return err
}
attach := fmt.Sprintf(
"ATTACH DATABASE '%s' AS newdb KEY '%s'",
newPath,
key)
if _, err = db.Exec(attach); err != nil {
return err
}
changeKdfIter := fmt.Sprintf(
"PRAGMA newdb.kdf_iter = %d",
KdfIterationsNumber)
if _, err = db.Exec(changeKdfIter); err != nil {
return err
}
if _, err = db.Exec(exportDB); err != nil {
return err
}
if err = db.Close(); err != nil {
return err
}
return os.Remove(oldPath)
}