diff --git a/cabal.project b/cabal.project index fab9495dc..bccf2e365 100644 --- a/cabal.project +++ b/cabal.project @@ -1,6 +1,17 @@ packages: . +-- packages: . ../direct-sqlcipher ../sqlcipher-simple source-repository-package type: git location: https://github.com/simplex-chat/aeson.git tag: 3eb66f9a68f103b5f1489382aad89f5712a64db7 + +source-repository-package + type: git + location: https://github.com/simplex-chat/direct-sqlcipher.git + tag: 477955063df65a2776c2a958b656ff359b76374d + +source-repository-package + type: git + location: https://github.com/simplex-chat/sqlcipher-simple.git + tag: 0738c7957e971b84a2a156d297596206b948c4f6 diff --git a/package.yaml b/package.yaml index ac686a874..6c765a766 100644 --- a/package.yaml +++ b/package.yaml @@ -38,7 +38,7 @@ dependencies: - cryptonite >= 0.27 && < 0.30 - cryptostore == 0.2.* - data-default == 0.7.* - - direct-sqlite == 2.3.* + - direct-sqlcipher == 2.3.* - directory == 1.3.* - filepath == 1.4.* - http-types == 0.12.* @@ -56,7 +56,7 @@ dependencies: - random >= 1.1 && < 1.3 - simple-logger == 0.1.* - socks == 0.6.* - - sqlite-simple == 0.4.* + - sqlcipher-simple == 0.4.* - stm == 2.5.* - template-haskell == 2.16.* - text == 1.2.* diff --git a/simplexmq.cabal b/simplexmq.cabal index c185f10cc..44ee69951 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -114,7 +114,7 @@ library , cryptonite >=0.27 && <0.30 , cryptostore ==0.2.* , data-default ==0.7.* - , direct-sqlite ==2.3.* + , direct-sqlcipher ==2.3.* , directory ==1.3.* , filepath ==1.4.* , generic-random >=1.3 && <1.5 @@ -131,7 +131,7 @@ library , random >=1.1 && <1.3 , simple-logger ==0.1.* , socks ==0.6.* - , sqlite-simple ==0.4.* + , sqlcipher-simple ==0.4.* , stm ==2.5.* , template-haskell ==2.16.* , text ==1.2.* @@ -175,7 +175,7 @@ executable ntf-server , cryptonite >=0.27 && <0.30 , cryptostore ==0.2.* , data-default ==0.7.* - , direct-sqlite ==2.3.* + , direct-sqlcipher ==2.3.* , directory ==1.3.* , filepath ==1.4.* , generic-random >=1.3 && <1.5 @@ -193,7 +193,7 @@ executable ntf-server , simple-logger ==0.1.* , simplexmq , socks ==0.6.* - , sqlite-simple ==0.4.* + , sqlcipher-simple ==0.4.* , stm ==2.5.* , template-haskell ==2.16.* , text ==1.2.* @@ -237,7 +237,7 @@ executable smp-agent , cryptonite >=0.27 && <0.30 , cryptostore ==0.2.* , data-default ==0.7.* - , direct-sqlite ==2.3.* + , direct-sqlcipher ==2.3.* , directory ==1.3.* , filepath ==1.4.* , generic-random >=1.3 && <1.5 @@ -255,7 +255,7 @@ executable smp-agent , simple-logger ==0.1.* , simplexmq , socks ==0.6.* - , sqlite-simple ==0.4.* + , sqlcipher-simple ==0.4.* , stm ==2.5.* , template-haskell ==2.16.* , text ==1.2.* @@ -299,7 +299,7 @@ executable smp-server , cryptonite >=0.27 && <0.30 , cryptostore ==0.2.* , data-default ==0.7.* - , direct-sqlite ==2.3.* + , direct-sqlcipher ==2.3.* , directory ==1.3.* , filepath ==1.4.* , generic-random >=1.3 && <1.5 @@ -317,7 +317,7 @@ executable smp-server , simple-logger ==0.1.* , simplexmq , socks ==0.6.* - , sqlite-simple ==0.4.* + , sqlcipher-simple ==0.4.* , stm ==2.5.* , template-haskell ==2.16.* , text ==1.2.* @@ -378,7 +378,7 @@ test-suite smp-server-test , cryptonite >=0.27 && <0.30 , cryptostore ==0.2.* , data-default ==0.7.* - , direct-sqlite ==2.3.* + , direct-sqlcipher ==2.3.* , directory ==1.3.* , filepath ==1.4.* , generic-random >=1.3 && <1.5 @@ -398,7 +398,7 @@ test-suite smp-server-test , simple-logger ==0.1.* , simplexmq , socks ==0.6.* - , sqlite-simple ==0.4.* + , sqlcipher-simple ==0.4.* , stm ==2.5.* , template-haskell ==2.16.* , text ==1.2.* diff --git a/src/Simplex/Messaging/Agent/Env/SQLite.hs b/src/Simplex/Messaging/Agent/Env/SQLite.hs index 5e85097d9..03cb3ba7b 100644 --- a/src/Simplex/Messaging/Agent/Env/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Env/SQLite.hs @@ -65,6 +65,7 @@ data AgentConfig = AgentConfig connIdBytes :: Int, tbqSize :: Natural, dbFile :: FilePath, + dbKey :: String, yesToMigrations :: Bool, smpCfg :: ProtocolClientConfig, ntfCfg :: ProtocolClientConfig, @@ -108,6 +109,7 @@ defaultAgentConfig = connIdBytes = 12, tbqSize = 64, dbFile = "smp-agent.db", + dbKey = "", yesToMigrations = False, smpCfg = defaultClientConfig {defaultTransport = (show defaultSMPPort, transport @TLS)}, ntfCfg = defaultClientConfig {defaultTransport = ("443", transport @TLS)}, @@ -139,9 +141,9 @@ data Env = Env } newSMPAgentEnv :: (MonadUnliftIO m, MonadRandom m) => AgentConfig -> m Env -newSMPAgentEnv config@AgentConfig {dbFile, yesToMigrations} = do +newSMPAgentEnv config@AgentConfig {dbFile, dbKey, yesToMigrations} = do idsDrg <- newTVarIO =<< drgNew - store <- liftIO $ createSQLiteStore dbFile Migrations.app yesToMigrations + store <- liftIO $ createSQLiteStore dbFile dbKey Migrations.app yesToMigrations clientCounter <- newTVarIO 0 randomServer <- newTVarIO =<< liftIO newStdGen ntfSupervisor <- atomically . newNtfSubSupervisor $ tbqSize config diff --git a/src/Simplex/Messaging/Agent/Store/SQLite.hs b/src/Simplex/Messaging/Agent/Store/SQLite.hs index 25316fa1a..a3e1aa10a 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite.hs @@ -152,11 +152,11 @@ data SQLiteStore = SQLiteStore dbNew :: Bool } -createSQLiteStore :: FilePath -> [Migration] -> Bool -> IO SQLiteStore -createSQLiteStore dbFilePath migrations yesToMigrations = do +createSQLiteStore :: FilePath -> String -> [Migration] -> Bool -> IO SQLiteStore +createSQLiteStore dbFilePath dbKey migrations yesToMigrations = do let dbDir = takeDirectory dbFilePath createDirectoryIfMissing False dbDir - st <- connectSQLiteStore dbFilePath + st <- connectSQLiteStore dbFilePath dbKey checkThreadsafe st migrateSchema st migrations yesToMigrations pure st @@ -192,24 +192,27 @@ confirmOrExit s = do ok <- getLine when (map toLower ok /= "y") exitFailure -connectSQLiteStore :: FilePath -> IO SQLiteStore -connectSQLiteStore dbFilePath = do +connectSQLiteStore :: FilePath -> String -> IO SQLiteStore +connectSQLiteStore dbFilePath dbKey = do dbNew <- not <$> doesFileExist dbFilePath - dbConnection <- newTMVarIO =<< connectDB dbFilePath + dbConnection <- newTMVarIO =<< connectDB dbFilePath dbKey pure SQLiteStore {dbFilePath, dbConnection, dbNew} -connectDB :: FilePath -> IO DB.Connection -connectDB path = do - dbConn <- DB.open path - SQLite3.exec (DB.connectionHandle dbConn) . fromQuery $ +connectDB :: FilePath -> String -> IO DB.Connection +connectDB path key = do + db <- DB.open path + let exec = SQLite3.exec $ DB.connectionHandle db + -- TODO escape key + unless (null key) . exec $ "PRAGMA key = '" <> T.pack key <> "';" + exec . fromQuery $ [sql| PRAGMA foreign_keys = ON; -- PRAGMA trusted_schema = OFF; PRAGMA secure_delete = ON; PRAGMA auto_vacuum = FULL; |] - -- _printPragmas dbConn path - pure dbConn + -- _printPragmas db path + pure db -- _printPragmas :: DB.Connection -> FilePath -> IO () -- _printPragmas db path = do diff --git a/stack.yaml b/stack.yaml index 29af9bcdb..38ec75bf4 100644 --- a/stack.yaml +++ b/stack.yaml @@ -48,6 +48,12 @@ extra-deps: - time-compat-1.9.6.1@sha256:42d8f2e08e965e1718917d54ad69e1d06bd4b87d66c41dc7410f59313dba4ed1,5033 - github: simplex-chat/aeson commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7 + # - ../direct-sqlcipher + - github: simplex-chat/direct-sqlcipher + commit: 477955063df65a2776c2a958b656ff359b76374d + # - ../sqlcipher-simple + - github: simplex-chat/sqlcipher-simple + commit: 0738c7957e971b84a2a156d297596206b948c4f6 # - ../hs-tls/core # - github: simplex-chat/hs-tls # commit: f6cc753611f80af300401cfae63846e9d7c40d9e diff --git a/tests/AgentTests/SQLiteTests.hs b/tests/AgentTests/SQLiteTests.hs index 6a79fdea2..84ae3e1aa 100644 --- a/tests/AgentTests/SQLiteTests.hs +++ b/tests/AgentTests/SQLiteTests.hs @@ -43,7 +43,7 @@ withStore2 = before connect2 . after (removeStore . fst) connect2 :: IO (SQLiteStore, SQLiteStore) connect2 = do s1 <- createStore - s2 <- connectSQLiteStore (dbFilePath s1) + s2 <- connectSQLiteStore (dbFilePath s1) "" pure (s1, s2) createStore :: IO SQLiteStore @@ -51,7 +51,7 @@ createStore = do -- Randomize DB file name to avoid SQLite IO errors supposedly caused by asynchronous -- IO operations on multiple similarly named files; error seems to be environment specific r <- randomIO :: IO Word32 - createSQLiteStore (testDB <> show r) Migrations.app True + createSQLiteStore (testDB <> show r) "" Migrations.app True removeStore :: SQLiteStore -> IO () removeStore db = do diff --git a/tests/AgentTests/SchemaDump.hs b/tests/AgentTests/SchemaDump.hs index 03baa28b2..43c67a332 100644 --- a/tests/AgentTests/SchemaDump.hs +++ b/tests/AgentTests/SchemaDump.hs @@ -20,7 +20,7 @@ schemaDumpTest = testVerifySchemaDump :: IO () testVerifySchemaDump = do - void $ createSQLiteStore testDB Migrations.app False + void $ createSQLiteStore testDB "" Migrations.app False void $ readCreateProcess (shell $ "touch " <> schema) "" savedSchema <- readFile schema savedSchema `seq` pure ()