purr

password generation and secret sharing
Log | Files | Refs | README | LICENSE

commit 9efdc01828af6d56755ea381cb2708f3f8b288b6
parent b4bbf6e5a796d6dfc44ac0a052ec4949d2394927
Author: James Eversole <james@eversole.co>
Date:   Tue,  2 May 2023 21:26:40 -0500

Pure nix flake; replace crypto-simple with Saltine; update README and TODO

Diffstat:
APurr.cabal | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME | 6++----
MTODO | 3+--
Mflake.nix | 2--
Mpackage.yaml | 12++++++------
Dpurr.cabal | 106-------------------------------------------------------------------------------
Msrc/Feature/Sharing/SQLite.hs | 46+++++++++++++++++++++++++++-------------------
Msrc/Lib.hs | 2++
8 files changed, 144 insertions(+), 139 deletions(-)

diff --git a/Purr.cabal b/Purr.cabal @@ -0,0 +1,106 @@ +cabal-version: 1.12 + +name: purr +version: 0.3.0 +description: https://git.eversole.co/purr +author: James Eversole +maintainer: james@eversole.co +copyright: 2022 James Eversole +license: ISC +license-file: LICENSE +build-type: Simple +extra-source-files: + README + ChangeLog.md + +library + exposed-modules: + Core.Configuration + Core.HTTP + Core.SQLite + Core.Templates + Core.Types + Feature.Generation.HTTP + Feature.Generation.Links + Feature.Generation.Passwords + Feature.Generation.Shared + Feature.Generation.Templates + Feature.Sharing.HTTP + Feature.Sharing.SQLite + Feature.Sharing.Templates + Lib + other-modules: + Paths_Purr + hs-source-dirs: + src + default-extensions: + ConstraintKinds + DeriveGeneric + FlexibleContexts + FlexibleInstances + GeneralizedNewtypeDeriving + OverloadedStrings + ScopedTypeVariables + build-depends: + base >=4.7 + , base64-bytestring >=1.2.0.0 + , blaze-html >=0.9.1.0 + , bytestring >=0.10.12.1 + , containers >=0.6.4.1 + , dhall >=1.40 + , file-embed ==0.0.15.0 + , http-types >=0.12.3 + , iso8601-time >=0.1.5 + , mtl >=2.2.2 + , random >=1.2 + , saltine >=0.2.0.0 + , scotty >=0.12 + , shakespeare >=2.0.20 + , split >=0.2.3.4 + , sqlite-simple >=0.4.18.0 + , text >=1.2.5.0 + , time >=1.9 + , utf8-string ==1.0.2 + , wai-extra >=3.1.12.1 + , wai-middleware-static >=0.5 + default-language: Haskell2010 + +executable Purr-musl + main-is: Main.hs + other-modules: + Paths_Purr + hs-source-dirs: + app + default-extensions: + ConstraintKinds + DeriveGeneric + FlexibleContexts + FlexibleInstances + GeneralizedNewtypeDeriving + OverloadedStrings + ScopedTypeVariables + ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC + build-depends: + Purr + , base >=4.7 + , base64-bytestring >=1.2.0.0 + , blaze-html >=0.9.1.0 + , bytestring >=0.10.12.1 + , containers >=0.6.4.1 + , dhall >=1.40 + , file-embed ==0.0.15.0 + , http-types >=0.12.3 + , iso8601-time >=0.1.5 + , mtl >=2.2.2 + , random >=1.2 + , saltine >=0.2.0.0 + , scotty >=0.12 + , shakespeare >=2.0.20 + , split >=0.2.3.4 + , sqlite-simple >=0.4.18.0 + , text >=1.2.5.0 + , time >=1.9 + , utf8-string ==1.0.2 + , wai-extra >=3.1.12.1 + , wai-middleware-static >=0.5 + default-language: Haskell2010 diff --git a/README b/README @@ -27,12 +27,10 @@ DEPLOYMENT Use Nix with flakes enabled. Build binary and run natively: -nix build --impure && ./result/bin/Purr-musl +nix build && ./result/bin/Purr-musl Build and add Docker image to local registry: -nix build .#purrImage --impure && docker load < result - -I'll get rid of the "--impure" requirement ASAP. +nix build .#purrImage && docker load < result DEVELOPMENT & SUPPORT diff --git a/TODO b/TODO @@ -1,2 +1 @@ -- Replace crypto-simple dependency -- Make the Docker images much smaller again +- Make the Docker images much smaller diff --git a/flake.nix b/flake.nix @@ -20,8 +20,6 @@ in { packages.${packageName} = haskellPackages.callCabal2nix packageName self rec { - crypto-simple = - pkgs.haskell.lib.dontCheck haskellPackages.crypto-simple; }; packages.default = self.packages.${system}.${packageName}; diff --git a/package.yaml b/package.yaml @@ -33,14 +33,14 @@ dependencies: - blaze-html >= 0.9.1.0 - bytestring >= 0.10.12.1 - containers >= 0.6.4.1 -- crypto-simple >= 0.1.0.0 -- dhall >= 1.40 && < 1.41.2 +- dhall >= 1.40 - file-embed == 0.0.15.0 - http-types >= 0.12.3 - iso8601-time >= 0.1.5 - mtl >= 2.2.2 - random >= 1.2 -- scotty == 0.12 +- saltine >= 0.2.0.0 +- scotty >= 0.12 - shakespeare >= 2.0.20 - sqlite-simple >= 0.4.18.0 - split >= 0.2.3.4 @@ -61,9 +61,9 @@ executables: - -threaded - -rtsopts - -with-rtsopts=-N - - -static - - -optl-static - - -optl-pthread + #- -static + #- -optl-static + #- -optl-pthread - -fPIC dependencies: - Purr diff --git a/purr.cabal b/purr.cabal @@ -1,106 +0,0 @@ -cabal-version: 1.12 - -name: purr -version: 0.3.0 -description: https://git.eversole.co/purr -author: James Eversole -maintainer: james@eversole.co -copyright: 2022 James Eversole -license: ISC -license-file: LICENSE -build-type: Simple -extra-source-files: - README - ChangeLog.md - -library - exposed-modules: - Core.Configuration - Core.HTTP - Core.SQLite - Core.Templates - Core.Types - Feature.Generation.HTTP - Feature.Generation.Links - Feature.Generation.Passwords - Feature.Generation.Shared - Feature.Generation.Templates - Feature.Sharing.HTTP - Feature.Sharing.SQLite - Feature.Sharing.Templates - Lib - other-modules: - Paths_Purr - hs-source-dirs: - src - default-extensions: - ConstraintKinds - DeriveGeneric - FlexibleContexts - FlexibleInstances - GeneralizedNewtypeDeriving - OverloadedStrings - ScopedTypeVariables - build-depends: - base >=4.7 - , base64-bytestring >=1.2.0.0 - , blaze-html >=0.9.1.0 - , bytestring >=0.10.12.1 - , containers >=0.6.4.1 - , crypto-simple >=0.1.0.0 - , dhall >=1.40 - , file-embed ==0.0.15.0 - , http-types >=0.12.3 - , iso8601-time >=0.1.5 - , mtl >=2.2.2 - , random >=1.2 - , scotty >=0.12 - , shakespeare >=2.0.20 - , split >=0.2.3.4 - , sqlite-simple >=0.4.18.0 - , text >=1.2.5.0 - , time >=1.9 - , utf8-string ==1.0.2 - , wai-extra >=3.1.12.1 - , wai-middleware-static >=0.5 - default-language: Haskell2010 - -executable Purr-musl - main-is: Main.hs - other-modules: - Paths_Purr - hs-source-dirs: - app - default-extensions: - ConstraintKinds - DeriveGeneric - FlexibleContexts - FlexibleInstances - GeneralizedNewtypeDeriving - OverloadedStrings - ScopedTypeVariables - ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC - build-depends: - Purr - , base >=4.7 - , base64-bytestring >=1.2.0.0 - , blaze-html >=0.9.1.0 - , bytestring >=0.10.12.1 - , containers >=0.6.4.1 - , crypto-simple >=0.1.0.0 - , dhall >=1.40 - , file-embed ==0.0.15.0 - , http-types >=0.12.3 - , iso8601-time >=0.1.5 - , mtl >=2.2.2 - , random >=1.2 - , scotty >=0.12 - , shakespeare >=2.0.20 - , split >=0.2.3.4 - , sqlite-simple >=0.4.18.0 - , text >=1.2.5.0 - , time >=1.9 - , utf8-string ==1.0.2 - , wai-extra >=3.1.12.1 - , wai-middleware-static >=0.5 - default-language: Haskell2010 diff --git a/src/Feature/Sharing/SQLite.hs b/src/Feature/Sharing/SQLite.hs @@ -4,18 +4,19 @@ import Core.SQLite import Core.Types import Feature.Generation.Passwords (Password) -import Control.Monad.Reader (ask, lift, liftIO) -import Crypto.Simple.CBC (decrypt, encrypt) -import Data.List.Split (splitOn) -import Data.Maybe (listToMaybe) -import Data.Time.Clock.POSIX (getPOSIXTime) +import Control.Monad.Reader (ask, lift, liftIO) +import Data.List.Split (splitOn) +import Data.Maybe (listToMaybe, fromMaybe) +import Data.Time.Clock.POSIX (getPOSIXTime) import Database.SQLite.Simple -import qualified Data.ByteString.Base64 as B64 -import qualified Data.ByteString.Char8 as B -import qualified Data.Text as T -import qualified Data.Text.Encoding as ET -import qualified Data.Text.Lazy as LT +import qualified Crypto.Saltine.Core.SecretBox as Box +import qualified Crypto.Saltine.Internal.SecretBox as IBox +import qualified Data.ByteString.Base64 as B64 +import qualified Data.ByteString.Char8 as B +import qualified Data.Text as T +import qualified Data.Text.Encoding as ET +import qualified Data.Text.Lazy as LT findByLink :: String -> PurrAction (Maybe T.Text) findByLink link = do @@ -30,7 +31,8 @@ insertNewSecret :: T.Text -> Integer -> T.Text -> Integer -> PurrAction () insertNewSecret sec life link maxViews = do db <- dbPath key <- encKey - encSec <- liftIO $ encryptSecret key sec + nonce <- liftIO $ Box.newNonce + let encSec = encryptSecret key sec nonce conn <- liftIO $ open db time <- liftIO $ epochTime liftIO $ execute conn @@ -41,12 +43,10 @@ insertNewSecret sec life link maxViews = do readEncryptedSecret :: String -> [SecretEntry] -> PurrAction (Maybe T.Text) readEncryptedSecret key sec = do db <- dbPath + nonce <- liftIO $ Box.newNonce liftIO $ incViews sec db delete <- liftIO $ deleteExpiredSecret sec db - decKey <- liftIO ( sequence - $ decryptSecret key - <$> decodeSecret - <$> listToMaybe sec ) + let decKey = decryptSecret key nonce $ decodeSecret $ safeHead failedSecret sec if (delete) then return Nothing else return (ET.decodeLatin1 <$> decKey) @@ -83,11 +83,19 @@ encodeSecret b = ET.decodeUtf8 $ B64.encode b decodeSecret :: SecretEntry -> B.ByteString decodeSecret s = B64.decodeLenient $ ET.encodeUtf8 (secret s) -encryptSecret :: String -> T.Text -> IO B.ByteString -encryptSecret k s = encrypt (B.pack k) (ET.encodeUtf8 s) +encryptSecret :: String -> T.Text -> Box.Nonce -> B.ByteString +encryptSecret k s n = do + Box.secretbox (IBox.Key $ B.pack k) n (ET.encodeUtf8 s) -decryptSecret :: String -> B.ByteString -> IO B.ByteString -decryptSecret k b = decrypt (B.pack k) b +decryptSecret :: String -> Box.Nonce -> B.ByteString -> Maybe B.ByteString +decryptSecret k n b = Box.secretboxOpen (IBox.Key $ B.pack k) n b epochTime :: IO Integer epochTime = fmap round getPOSIXTime + +failedSecret :: SecretEntry +failedSecret = SecretEntry "fail" "fail" 0 0 0 0 + +safeHead :: a -> [a] -> a +safeHead x [] = x +safeHead x l = head l diff --git a/src/Lib.hs b/src/Lib.hs @@ -6,12 +6,14 @@ import qualified Core.SQLite as DB import Core.Types import Control.Monad.Reader (lift, liftIO, runReaderT) +import Crypto.Saltine (sodiumInit) import GHC.Natural (popCountNatural) import Prelude hiding (id) import Web.Scotty.Trans (scottyT) main :: IO () main = do + sodiumInit dhallConf <- liftIO Configuration.main DB.main (dbFile dhallConf) scottyT (applicationPort dhallConf) (flip runApp dhallConf) HTTP.app where