hkdf

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

commit 93aaf3eafad677087ffbea6fc5c24a63cb9651ea
parent 3da63a199d294c6e7c4306786ea071c7f969f21d
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed, 11 Jun 2025 11:41:35 +0400

lib: total functions

Diffstat:
MREADME.md | 2+-
Mlib/Crypto/KDF/HMAC.hs | 8++++----
Mtest/Main.hs | 19+++++++------------
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