From 941745d8a089614b5e444337d868bb9e181782bf Mon Sep 17 00:00:00 2001 From: Felix Paulusma Date: Mon, 9 Mar 2026 16:38:21 +0100 Subject: [PATCH 1/2] password: crypton-1.1.0 adjustment * avoiding conversion to 'Bytes' because of new 'ram' dependency. Doesn't have certain instances. --- password/password.cabal | 2 +- password/src/Data/Password/Argon2.hs | 13 ++++++------- password/src/Data/Password/Bcrypt.hs | 18 +++++++----------- password/src/Data/Password/Internal.hs | 15 +-------------- password/src/Data/Password/PBKDF2.hs | 22 ++++++++-------------- password/src/Data/Password/Scrypt.hs | 13 ++++++------- 6 files changed, 29 insertions(+), 54 deletions(-) diff --git a/password/password.cabal b/password/password.cabal index 5ef7740..ff61f13 100644 --- a/password/password.cabal +++ b/password/password.cabal @@ -126,7 +126,7 @@ library cryptonite >= 0.15.1 && < 0.31 else build-depends: - crypton >= 0.31 && < 1.1 + crypton >= 0.31 && < 1.2 test-suite doctests type: diff --git a/password/src/Data/Password/Argon2.hs b/password/src/Data/Password/Argon2.hs index 6917478..5cbc514 100644 --- a/password/src/Data/Password/Argon2.hs +++ b/password/src/Data/Password/Argon2.hs @@ -86,7 +86,7 @@ import Crypto.KDF.Argon2 as Argon2 (Options (..), Variant (..), Version (..), ha #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif -import Data.ByteArray (Bytes, constEq, convert) +import Data.ByteArray (constEq) import Data.ByteString as B (ByteString, length) import Data.ByteString.Base64 (encodeBase64) import Data.Maybe (fromMaybe) @@ -94,6 +94,7 @@ import Data.Maybe (fromMaybe) import Data.Semigroup ((<>)) #endif import Data.Text (Text) +import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T (dropWhileEnd, intercalate, split, splitAt, stripPrefix) import Data.Word (Word32) @@ -102,7 +103,6 @@ import Data.Password.Internal ( from64, readT, showT, - toBytes, unsafePad64, ) import Data.Password.Types ( @@ -232,14 +232,13 @@ hashPasswordWithSalt params@Argon2Params{..} s@(Salt salt) pass = -- | Only for internal use hashPasswordWithSalt' :: Argon2Params -> Salt Argon2 -> Password -> ByteString hashPasswordWithSalt' Argon2Params{..} (Salt salt) pass = - convert (argon2Hash :: Bytes) - where - argon2Hash = throwCryptoError $ + throwCryptoError $ Argon2.hash options - (toBytes $ unsafeShowPassword pass) - (convert salt :: Bytes) + (encodeUtf8 $ unsafeShowPassword pass) + salt $ fromIntegral argon2OutputLength + where options = Argon2.Options { iterations = argon2TimeCost, memory = argon2MemoryCost, diff --git a/password/src/Data/Password/Bcrypt.hs b/password/src/Data/Password/Bcrypt.hs index 5de4d5b..9ec72ec 100644 --- a/password/src/Data/Password/Bcrypt.hs +++ b/password/src/Data/Password/Bcrypt.hs @@ -86,8 +86,8 @@ module Data.Password.Bcrypt ( import Control.Monad (guard) import Control.Monad.IO.Class (MonadIO(liftIO)) import Crypto.KDF.BCrypt as Bcrypt (bcrypt, validatePassword) -import Data.ByteArray (Bytes, convert) import qualified Data.Text as T +import Data.Text.Encoding (encodeUtf8, decodeUtf8) import Text.Read (readMaybe) import Data.Password.Types ( @@ -97,11 +97,7 @@ import Data.Password.Types ( , unsafeShowPassword , Salt(..) ) -import Data.Password.Internal ( - PasswordCheck(..) - , fromBytes - , toBytes - ) +import Data.Password.Internal (PasswordCheck(..)) import qualified Data.Password.Internal (newSalt) @@ -169,9 +165,9 @@ hashPasswordWithSalt hashPasswordWithSalt cost (Salt salt) pass = let hash = Bcrypt.bcrypt cost - (convert salt :: Bytes) - (toBytes $ unsafeShowPassword pass) - in PasswordHash $ fromBytes hash + salt + (encodeUtf8 $ unsafeShowPassword pass) + in PasswordHash $ decodeUtf8 hash -- | Hash a password using the /bcrypt/ algorithm with the given cost. -- @@ -214,8 +210,8 @@ hashPasswordWithParams cost pass = liftIO $ do checkPassword :: Password -> PasswordHash Bcrypt -> PasswordCheck checkPassword pass (PasswordHash passHash) = if Bcrypt.validatePassword - (toBytes $ unsafeShowPassword pass) - (toBytes passHash) + (encodeUtf8 $ unsafeShowPassword pass) + (encodeUtf8 passHash) then PasswordCheckSuccess else PasswordCheckFail diff --git a/password/src/Data/Password/Internal.hs b/password/src/Data/Password/Internal.hs index 7ba5c73..eb9fb26 100644 --- a/password/src/Data/Password/Internal.hs +++ b/password/src/Data/Password/Internal.hs @@ -14,8 +14,6 @@ module Data.Password.Internal ( PasswordCheck(..) , newSalt -- * Utility - , toBytes - , fromBytes , from64 , unsafePad64 , readT @@ -26,7 +24,6 @@ module Data.Password.Internal ( import Control.Monad.IO.Class (MonadIO (liftIO)) import Crypto.Random (getRandomBytes) -import Data.ByteArray (Bytes, convert) import Data.ByteString (ByteString) #if MIN_VERSION_base64(1,0,0) import Data.ByteString.Base64 (decodeBase64Untyped) @@ -44,7 +41,7 @@ import Data.Text as T ( unpack, ) import Data.Password.Types (Salt(..)) -import Data.Text.Encoding (decodeUtf8, encodeUtf8) +import Data.Text.Encoding (encodeUtf8) import Text.Read (readMaybe) -- $setup @@ -83,16 +80,6 @@ data PasswordCheck -- hashed password. deriving (Eq, Read, Show) --- | Converting 'Text' to 'Bytes' -toBytes :: Text -> Bytes -toBytes = convert . encodeUtf8 -{-# INLINE toBytes #-} - --- | Converting 'Bytes' to 'Text' -fromBytes :: Bytes -> Text -fromBytes = decodeUtf8 . convert -{-# INLINE fromBytes #-} - -- | Decodes a base64 'Text' to a regular 'ByteString' (if possible) from64 :: Text -> Maybe ByteString #if MIN_VERSION_base64(1,0,0) diff --git a/password/src/Data/Password/PBKDF2.hs b/password/src/Data/Password/PBKDF2.hs index 709c375..8712934 100644 --- a/password/src/Data/Password/PBKDF2.hs +++ b/password/src/Data/Password/PBKDF2.hs @@ -85,13 +85,14 @@ import Crypto.KDF.PBKDF2 as PBKDF2 #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif -import Data.ByteArray (ByteArray, ByteArrayAccess, Bytes, constEq, convert) +import Data.ByteArray (constEq) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encodeBase64) import qualified Data.ByteString.Char8 as C8 (length) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T (intercalate, pack, split, stripPrefix) +import Data.Text.Encoding (encodeUtf8) import Data.Word (Word32) import Data.Password.Types ( @@ -101,12 +102,7 @@ import Data.Password.Types ( , unsafeShowPassword , Salt(..) ) -import Data.Password.Internal ( - PasswordCheck(..) - , from64 - , readT - , toBytes - ) +import Data.Password.Internal (PasswordCheck(..), from64, readT) import qualified Data.Password.Internal (newSalt) @@ -209,13 +205,12 @@ hashPasswordWithSalt params@PBKDF2Params{..} s@(Salt salt) pass = -- | Only for internal use hashPasswordWithSalt' :: PBKDF2Params -> Salt PBKDF2 -> Password -> ByteString hashPasswordWithSalt' PBKDF2Params{..} (Salt salt) pass = - convert (pbkdf2Hash :: Bytes) - where - pbkdf2Hash = algToFunc + algToFunc pbkdf2Algorithm params - (toBytes $ unsafeShowPassword pass) - (convert salt :: Bytes) + (encodeUtf8 $ unsafeShowPassword pass) + salt + where params = PBKDF2.Parameters { PBKDF2.iterCounts = fromIntegral pbkdf2Iterations, PBKDF2.outputLength = fromIntegral $ maxOutputLength pbkdf2Algorithm pbkdf2OutputLength @@ -324,8 +319,7 @@ textToAlg = \case _ -> Nothing -- Which function to use, based on the given algorithm -algToFunc :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray hash) - => PBKDF2Algorithm -> PBKDF2.Parameters -> password -> salt -> hash +algToFunc :: PBKDF2Algorithm -> PBKDF2.Parameters -> ByteString -> ByteString -> ByteString algToFunc = \case PBKDF2_MD5 -> PBKDF2.generate (PBKDF2.prfHMAC Crypto.MD5) PBKDF2_SHA1 -> PBKDF2.fastPBKDF2_SHA1 diff --git a/password/src/Data/Password/Scrypt.hs b/password/src/Data/Password/Scrypt.hs index 6e80be9..abdfc2a 100644 --- a/password/src/Data/Password/Scrypt.hs +++ b/password/src/Data/Password/Scrypt.hs @@ -78,12 +78,13 @@ import Crypto.KDF.Scrypt as Scrypt (Parameters(..), generate) #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif -import Data.ByteArray (Bytes, constEq, convert) +import Data.ByteArray (constEq) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encodeBase64) import qualified Data.ByteString.Char8 as C8 (length) import Data.Maybe (fromMaybe) import qualified Data.Text as T (intercalate, split) +import Data.Text.Encoding (encodeUtf8) import Data.Word (Word32) import Data.Password.Types ( @@ -98,7 +99,6 @@ import Data.Password.Internal ( , from64 , readT , showT - , toBytes ) import qualified Data.Password.Internal (newSalt) @@ -207,12 +207,11 @@ hashPasswordWithSalt params@ScryptParams{..} s@(Salt salt) pass = -- | Only for internal use hashPasswordWithSalt' :: ScryptParams -> Salt Scrypt -> Password -> ByteString hashPasswordWithSalt' ScryptParams{..} (Salt salt) pass = - convert (scryptHash :: Bytes) - where - scryptHash = Scrypt.generate + Scrypt.generate params - (toBytes $ unsafeShowPassword pass) - (convert salt :: Bytes) + (encodeUtf8 $ unsafeShowPassword pass) + salt + where params = Scrypt.Parameters { n = 2 ^ scryptRounds, r = fromIntegral scryptBlockSize, From 7b05f6d413382a7bc1d786b81a7212a0a666066e Mon Sep 17 00:00:00 2001 From: Felix Paulusma Date: Fri, 10 Apr 2026 16:34:34 +0200 Subject: [PATCH 2/2] password: bumped version, changed memory -> ram dependency and added to ChangeLog --- password/ChangeLog.md | 10 +++++++++- password/password.cabal | 24 +++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/password/ChangeLog.md b/password/ChangeLog.md index 464cca7..ae2d938 100644 --- a/password/ChangeLog.md +++ b/password/ChangeLog.md @@ -1,12 +1,20 @@ # Changelog for `password` +## 3.1.1.0 + +- Support `crypton` dependency to include `^>= 1.1.0`. +- Removed converting to and from `Bytes`, especially since `ram >= 0.21.0` + (a dependency of `crypton >= 1.1`) has it as a newtype over `ByteString`. + Thanks to [@Vlix](https://github.com/Vlix) + [#91](https://github.com/cdepillabout/password/pull/91) + ## 3.1.0.2 - Added reference to [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) package in modules and README. ## 3.1.0.1 -- Redo the conditionals in the `password.cabal` file so that the scrypt +- Redo the conditionals in the `password.cabal` file so that the `scrypt` library is only included as a test dependency on `x86_64`. This generally shouldn't affect users of the `password` library. Thanks to [@sternenseemann](https://github.com/sternenseemann) diff --git a/password/password.cabal b/password/password.cabal index ff61f13..8687be1 100644 --- a/password/password.cabal +++ b/password/password.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: password -version: 3.1.0.2 +version: 3.1.1.0 category: Security synopsis: Hashing and checking of passwords description: @@ -79,6 +79,13 @@ flag scrypt default: True manual: True +flag memory + description: Compile with [memory] dependency instead of [ram] + default: False + -- This should NOT be manual, so that constraint solvers + -- can figure out which version of 'crypton' is used. + manual: False + custom-setup setup-depends: base < 5 @@ -113,7 +120,6 @@ library base >= 4.9 && < 5 , base64 >= 0.3 && < 1.1 , bytestring >= 0.9 && < 0.13 - , memory < 1 , password-types < 2 , template-haskell >= 2.2 && < 3 , text >= 1.2.2 && < 3 @@ -121,12 +127,20 @@ library -Wall default-language: Haskell2010 + -- 'cryptonite' always means 'memory' if flag(cryptonite) build-depends: - cryptonite >= 0.15.1 && < 0.31 + cryptonite >= 0.15.1 && < 0.31, + memory < 1 else - build-depends: - crypton >= 0.31 && < 1.2 + if flag(memory) + build-depends: + crypton >= 0.31 && < 1.1, + memory < 1 + else + build-depends: + crypton >= 1.1 && < 1.2, + ram < 1 test-suite doctests type: