pbkdf

Pure Haskell password-based KDF (docs.ppad.tech/pbkdf).
git clone git://git.ppad.tech/pbkdf.git
Log | Files | Refs | README | LICENSE

commit 93af1a1af0ed7731c953ef4c415f05c1401bc094
parent 503902b710af487d85b6bae2792f6c240b85abe3
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed, 11 Jun 2025 11:49:26 +0400

lib: total functions

Diffstat:
MREADME.md | 2+-
Mlib/Crypto/KDF/PBKDF.hs | 14+++++++-------
Mtest/Main.hs | 19+++++++------------
3 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md @@ -21,7 +21,7 @@ A sample GHCi session: > > -- derive a 32-byte key from a secret > KDF.derive SHA256.hmac "my password" "my salt" 100 32 - "\"\NAKqxp\165S\t\212i\139\SUB(\132\176\204\224<\164\177\144\&1D\209\175\145\139[K\159h\205" + Just "\"\NAKqxp\165S\t\212i\139\SUB(\132\176\204\224<\164\177\144\&1D\209\175\145\139[K\159h\205" ``` ## Documentation diff --git a/lib/Crypto/KDF/PBKDF.hs b/lib/Crypto/KDF/PBKDF.hs @@ -21,6 +21,7 @@ module Crypto.KDF.PBKDF ( , derive )where +import Control.Monad (guard) import Data.Bits ((.>>.), (.&.)) import qualified Data.Bits as B import qualified Data.ByteString as BS @@ -70,7 +71,8 @@ xor = BS.packZipWith B.xor -- >>> import qualified Crypto.Hash.SHA256 as SHA256 -- >>> import qualified Data.ByteString as BS -- >>> import qualified Data.ByteString.Base16 as B16 --- >>> BS.take 16 (B16.encode (derive SHA256.hmac "passwd" "salt" 1 64)) +-- >>> let Just key = derive SHA256.hmac "passwd" "salt" 1 64 +-- >>> BS.take 16 (B16.encode key) -- "55ac046e56e3089f" derive :: HMAC -- ^ pseudo-random function (HMAC) @@ -78,12 +80,10 @@ derive -> BS.ByteString -- ^ salt -> Word64 -- ^ iteration count -> Word32 -- ^ bytelength of derived key (max 0xffff_ffff * hlen) - -> BS.ByteString -- ^ derived key -derive prf p s c dklen - | dklen > 0xffff_ffff * fi hlen = -- 2 ^ 32 - 1 - error "ppad-pbkdf (derive): derived key too long" - | otherwise = - loop mempty 1 + -> Maybe BS.ByteString -- ^ derived key +derive prf p s c dklen = do + guard (dklen <= 0xffff_ffff * fi hlen) + pure (loop mempty 1) where !hlen = BS.length (prf mempty mempty) !l = ceiling (fi dklen / fi hlen :: Double) :: Word32 diff --git a/test/Main.hs b/test/Main.hs @@ -3,11 +3,9 @@ module Main where -import Control.Exception import qualified Crypto.Hash.SHA256 as SHA256 import qualified Crypto.Hash.SHA512 as SHA512 import qualified Crypto.KDF.PBKDF as KDF -import qualified Data.ByteString as BS import qualified Data.Aeson as A import qualified Data.Text.IO as TIO import Test.Tasty @@ -48,16 +46,13 @@ execute h W.PbkdfTest {..} = testCase t_msg $ do cow = pt_iterationCount siz = pt_dkLen pec = pt_dk - if pt_result == "invalid" - then do - out <- try (pure $! KDF.derive hmac pas sal cow siz) - :: IO (Either ErrorCall BS.ByteString) - case out of - Left _ -> assertBool "invalid" True - Right o -> assertBool "invalid" (pec /= o) - else do - let out = KDF.derive hmac pas sal cow siz - assertEqual mempty pec out + case KDF.derive hmac pas sal cow siz of + Nothing + | pt_result == "invalid" -> assertBool "invalid" True + | otherwise -> assertFailure mempty + Just out + | pt_result == "invalid" -> assertBool "invalid" (pec /= out) + | otherwise -> assertEqual mempty pec out where hmac = case h of SHA256 -> SHA256.hmac