commit 93af1a1af0ed7731c953ef4c415f05c1401bc094
parent 503902b710af487d85b6bae2792f6c240b85abe3
Author: Jared Tobin <jared@jtobin.io>
Date: Wed, 11 Jun 2025 11:49:26 +0400
lib: total functions
Diffstat:
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