commit 93aaf3eafad677087ffbea6fc5c24a63cb9651ea
parent 3da63a199d294c6e7c4306786ea071c7f969f21d
Author: Jared Tobin <jared@jtobin.io>
Date: Wed, 11 Jun 2025 11:41:35 +0400
lib: total functions
Diffstat:
3 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/README.md b/README.md
@@ -20,7 +20,7 @@ A sample GHCi session:
> import qualified Crypto.Hash.SHA256 as SHA256
>
> -- derive a 32-byte key from a secret
- > KDF.derive SHA256.hmac "my salt" "my optional info" 32 "my secret input"
+ > Just (KDF.derive SHA256.hmac "my salt" "my optional info" 32 "my secret input")
"\EM\232\v\140\202\230\f2:\221n\221\209\233\US\209>\174_!\138\255\\C\150\237^X\226\tt\252"
```
diff --git a/lib/Crypto/KDF/HMAC.hs b/lib/Crypto/KDF/HMAC.hs
@@ -66,10 +66,10 @@ expand
-> BS.ByteString -- ^ optional context and application-specific info
-> Word64 -- ^ bytelength of output keying material
-> BS.ByteString -- ^ pseudorandom key
- -> BS.ByteString -- ^ output keying material
+ -> Maybe BS.ByteString -- ^ output keying material
expand (HMACEnv hmac hashlen) info (fi -> len) prk
- | len > 255 * hashlen = error "ppad-hkdf (expand): invalid outlength"
- | otherwise = BS.take len (go (1 :: Int) mempty mempty)
+ | len > 255 * hashlen = Nothing
+ | otherwise = pure (BS.take len (go (1 :: Int) mempty mempty))
where
n = ceiling ((fi len :: Double) / (fi hashlen :: Double)) :: Int
go !j t !tl
@@ -95,7 +95,7 @@ derive
-> BS.ByteString -- ^ optional context and application-specific info
-> Word64 -- ^ bytelength of output keying material (<= 255 * hashlen)
-> BS.ByteString -- ^ input keying material
- -> BS.ByteString -- ^ output keying material
+ -> Maybe BS.ByteString -- ^ output keying material
derive hmac salt info len = expand env info len . extract env salt where
env = HMACEnv hmac (fi (BS.length (hmac mempty mempty)))
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.HMAC as KDF
-import qualified Data.ByteString as BS
import qualified Data.Aeson as A
import qualified Data.Text.IO as TIO
import Test.Tasty
@@ -50,16 +48,13 @@ execute h W.HkdfTest {..} = testCase t_msg $ do
inf = ht_info
siz = ht_size
pec = ht_okm
- if ht_result == "invalid"
- then do
- out <- try (pure $! KDF.derive hmac sal inf siz ikm)
- :: 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 sal inf siz ikm
- assertEqual mempty pec out
+ case KDF.derive hmac sal inf siz ikm of
+ Nothing
+ | ht_result == "invalid" -> assertBool "invalid" True
+ | otherwise -> assertFailure "failed"
+ Just out
+ | ht_result == "invalid" -> assertBool "invalid" (pec /= out)
+ | otherwise -> assertEqual mempty pec out
where
hmac = case h of
SHA256 -> SHA256.hmac