From d6216f1aa45b479578c9e4bd977b5887181ca8e3 Mon Sep 17 00:00:00 2001 From: "Roman Volosovskyi :: Darkviolet Lightgreen Halcyon" Date: Fri, 11 Jan 2019 12:12:23 +0200 Subject: [PATCH] [slow sign in] Reduce number of kdf iterations for PDF database key (#1343) `kdf_iter` parameter is reduced to 3200. This change is done because of performance reasons, currently key derivation is too slow on some mobile devices. The number of iterations before this commit is 64000, default value in `sqlcipher` from version `3.0.0`. https://github.com/sqlcipher/sqlcipher/blob/fda4c68bb474da7e955be07a2b807bda1bb19bd2/CHANGELOG.md#300---2013-11-05 Implementation: `sqlcipher_export` is used for migration, check out the link below for details https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlcipher_export --- services/shhext/chat/sql_lite_persistence.go | 80 +++++++++++++++++++- services/shhext/service.go | 8 +- 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/services/shhext/chat/sql_lite_persistence.go b/services/shhext/chat/sql_lite_persistence.go index 60aefbefc..be9ab0645 100644 --- a/services/shhext/chat/sql_lite_persistence.go +++ b/services/shhext/chat/sql_lite_persistence.go @@ -21,6 +21,17 @@ import ( // A safe max number of rows const maxNumberOfRows = 100000000 +// 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 + // SQLLitePersistence represents a persistence service tied to an SQLite database type SQLLitePersistence struct { db *sql.DB @@ -70,7 +81,7 @@ func MigrateDBFile(oldPath string, newPath string, oldKey string, newKey string) return err } - db, err := openDB(newPath, oldKey) + db, err := openDB(newPath, oldKey, defaultKdfIterationsNumber) if err != nil { return err } @@ -85,7 +96,66 @@ func MigrateDBFile(oldPath string, newPath string, oldKey string, newKey string) } -func openDB(path string, key string) (*sql.DB, error) { +// 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 + } + + db, err := openDB(oldPath, key, defaultKdfIterationsNumber) + 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 + } + + exportDB := "SELECT sqlcipher_export('newdb')" + + if _, err = db.Exec(exportDB); err != nil { + return err + } + + if err = db.Close(); err != nil { + return err + } + + if err = os.Remove(oldPath); err != nil { + return err + } + + return nil + +} + +func openDB(path string, key string, kdfIter int) (*sql.DB, error) { db, err := sql.Open("sqlite3", path) if err != nil { return nil, err @@ -104,7 +174,9 @@ func openDB(path string, key string) (*sql.DB, error) { return nil, err } - if _, err = db.Exec("PRAGMA cypher_page_size=4096"); err != nil { + kdfString := fmt.Sprintf("PRAGMA kdf_iter = '%d'", kdfIter) + + if _, err = db.Exec(kdfString); err != nil { return nil, err } return db, nil @@ -136,7 +208,7 @@ func (s *SQLLitePersistence) GetSessionStorage() dr.SessionStorage { // Open opens a file at the specified path func (s *SQLLitePersistence) Open(path string, key string) error { - db, err := openDB(path, key) + db, err := openDB(path, key, kdfIterationsNumber) if err != nil { return err } diff --git a/services/shhext/service.go b/services/shhext/service.go index 92cb0b2c0..5cce3898e 100644 --- a/services/shhext/service.go +++ b/services/shhext/service.go @@ -129,6 +129,7 @@ func (s *Service) InitProtocol(address string, password string) error { v0Path := filepath.Join(s.dataDir, fmt.Sprintf("%x.db", address)) v1Path := filepath.Join(s.dataDir, fmt.Sprintf("%s.db", s.installationID)) v2Path := filepath.Join(s.dataDir, fmt.Sprintf("%s.v2.db", s.installationID)) + v3Path := filepath.Join(s.dataDir, fmt.Sprintf("%s.v3.db", s.installationID)) if err := chat.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil { return err @@ -141,7 +142,12 @@ func (s *Service) InitProtocol(address string, password string) error { os.Remove(v2Path) } - persistence, err := chat.NewSQLLitePersistence(v2Path, hashedPassword) + if err := chat.MigrateDBKeyKdfIterations(v2Path, v3Path, hashedPassword); err != nil { + os.Remove(v2Path) + os.Remove(v3Path) + } + + persistence, err := chat.NewSQLLitePersistence(v3Path, hashedPassword) if err != nil { return err }