ppad-sha512 0.1.5 → 0.2.0
raw patch · 8 files changed
+100/−41 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Crypto.Hash.SHA512: MAC :: ByteString -> MAC
+ Crypto.Hash.SHA512: newtype MAC
- Crypto.Hash.SHA512: hmac :: ByteString -> ByteString -> ByteString
+ Crypto.Hash.SHA512: hmac :: ByteString -> ByteString -> MAC
- Crypto.Hash.SHA512: hmac_lazy :: ByteString -> ByteString -> ByteString
+ Crypto.Hash.SHA512: hmac_lazy :: ByteString -> ByteString -> MAC
Files
- CHANGELOG +5/−0
- bench/Main.hs +27/−17
- bench/Weight.hs +13/−8
- lib/Crypto/Hash/SHA512.hs +4/−3
- lib/Crypto/Hash/SHA512/Internal.hs +29/−0
- lib/Crypto/Hash/SHA512/Lazy.hs +3/−3
- ppad-sha512.cabal +1/−1
- test/Main.hs +18/−9
CHANGELOG view
@@ -1,5 +1,10 @@ # Changelog +- 0.2.0 (2026-01-10)+ * The HMAC functions now produce a value of type MAC, which is a+ newtype over a ByteString. The 'Eq' instance for MAC compares values+ in constant time.+ - 0.1.5 (2026-01-08) * We now check if the ARM cryptographic extensions are available, and, if so, use them to calculate hashes. If they're unavailable we
bench/Main.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module Main where@@ -14,22 +15,31 @@ ] suite :: Benchmark-suite = env setup $ \ ~(bs, bl) ->- bgroup "ppad-sha512" [- bgroup "SHA512 (32B input)" [- bench "hash" $ whnf SHA512.hash bs- , bench "hash_lazy" $ whnf SHA512.hash_lazy bl- , bench "SHA.sha512" $ whnf SHA.sha512 bl- ]- , bgroup "HMAC-SHA512 (32B input)" [- bench "hmac" $ whnf (SHA512.hmac "key") bs- , bench "hmac_lazy" $ whnf (SHA512.hmac_lazy "key") bl- , bench "SHA.hmacSha512" $ whnf (SHA.hmacSha512 "key") bl+suite =+ let !bs = BS.replicate 32 0+ !bl = BL.fromStrict bs+ !mac0 = SHA512.hmac "key" "foo"+ !mac1 = SHA512.hmac "key" "bar"+ !mac2 = SHA512.hmac "key" "foo"+ !macl0 = SHA512.hmac_lazy "key" "foo"+ !macl1 = SHA512.hmac_lazy "key" "bar"+ !macl2 = SHA512.hmac_lazy "key" "foo"+ in bgroup "ppad-sha512" [+ bgroup "SHA512 (32B input)" [+ bench "hash" $ whnf SHA512.hash bs+ , bench "hash_lazy" $ whnf SHA512.hash_lazy bl+ , bench "SHA.sha512" $ whnf SHA.sha512 bl+ ]+ , bgroup "HMAC-SHA512 (32B input)" [+ bench "hmac" $ whnf (SHA512.hmac "key") bs+ , bench "hmac_lazy" $ whnf (SHA512.hmac_lazy "key") bl+ , bench "SHA.hmacSha512" $ whnf (SHA.hmacSha512 "key") bl+ ]+ , bgroup "MAC comparison" [+ bench "hmac, unequal" $ whnf (mac0 ==) mac1+ , bench "hmac, equal" $ whnf (mac0 ==) mac2+ , bench "hmac_lazy, unequal" $ whnf (macl0 ==) macl1+ , bench "hmac_lazy, equal" $ whnf (macl0 ==) macl2+ ] ]- ]- where- setup = do- let bs_32B = BS.replicate 32 0- bl_32B = BL.fromStrict bs_32B- pure (bs_32B, bl_32B)
bench/Weight.hs view
@@ -1,13 +1,18 @@ {-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns -fno-warn-type-defaults #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module Main where +import Control.DeepSeq import qualified Crypto.Hash.SHA512 as SHA512 import qualified Data.ByteString as BS import Weigh +instance NFData SHA512.MAC where+ rnf (SHA512.MAC b) = rnf b+ -- note that 'weigh' doesn't work properly in a repl main :: IO () main = mainWith $ do@@ -21,10 +26,10 @@ !bs2 = BS.replicate 128 0 !bs3 = BS.replicate 12288 0 in wgroup "hash" $ do- func' "hash (32B input)" SHA512.hash bs0- func' "hash (64B input)" SHA512.hash bs1- func' "hash (128B input)" SHA512.hash bs2- func' "hash (12288B input)" SHA512.hash bs3+ func "hash (32B input)" SHA512.hash bs0+ func "hash (64B input)" SHA512.hash bs1+ func "hash (128B input)" SHA512.hash bs2+ func "hash (12288B input)" SHA512.hash bs3 hmac :: Weigh () hmac =@@ -34,8 +39,8 @@ !bs2 = BS.replicate 128 0 !bs3 = BS.replicate 12288 0 in wgroup "hmac" $ do- func' "hmac (32B input)" (SHA512.hmac key) bs0- func' "hmac (64B input)" (SHA512.hmac key) bs1- func' "hmac (128B input)" (SHA512.hmac key) bs2- func' "hmac (12288B input)" (SHA512.hmac key) bs3+ func "hmac (32B input)" (SHA512.hmac key) bs0+ func "hmac (64B input)" (SHA512.hmac key) bs1+ func "hmac (128B input)" (SHA512.hmac key) bs2+ func "hmac (12288B input)" (SHA512.hmac key) bs3
lib/Crypto/Hash/SHA512.hs view
@@ -22,6 +22,7 @@ , Lazy.hash_lazy -- * SHA512-based MAC functions+ , MAC(..) , hmac , Lazy.hmac_lazy ) where@@ -94,15 +95,15 @@ hmac :: BS.ByteString -- ^ key -> BS.ByteString -- ^ text- -> BS.ByteString+ -> MAC hmac mk@(BI.PS _ _ l) text | sha512_arm_available = let !inner = hash_arm_with ipad 128 text- in hash_arm (opad <> inner)+ in MAC (hash_arm (opad <> inner)) | otherwise = let !ipad_state = block_hash iv (prepare_schedule (parse_block ipad 0)) !inner = cat (process_with ipad_state 128 text)- in hash (opad <> inner)+ in MAC (hash (opad <> inner)) where !step1 = k <> BS.replicate (128 - lk) 0x00 !ipad = BS.map (B.xor 0x36) step1
lib/Crypto/Hash/SHA512/Internal.hs view
@@ -1,5 +1,7 @@ {-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RecordWildCards #-} -- |@@ -15,6 +17,8 @@ , Block(..) , Schedule(..) + , MAC(..)+ , iv , block_hash , prepare_schedule@@ -41,6 +45,31 @@ import Foreign.Marshal.Utils (copyBytes, fillBytes) import Foreign.Ptr (Ptr, plusPtr) import Foreign.Storable (poke)++-- MAC type ------------------------------------------------------------------++-- | A message authentication code.+--+-- Note that you should compare MACs for equality using the 'Eq'+-- instance, which performs the comparison in constant time, instead+-- of unwrapping and comparing the underlying 'ByteStrings'.+--+-- >>> let foo@(MAC bs0) = hmac key "hi"+-- >>> let bar@(MAC bs1) = hmac key "there"+-- >>> foo == bar -- do this+-- False+-- >>> bs0 == bs1 -- don't do this+-- False+newtype MAC = MAC BS.ByteString+ deriving newtype Show++instance Eq MAC where+ -- | A constant-time equality check for message authentication codes.+ --+ -- Runs in variable-time only for invalid inputs.+ (MAC a@(BI.PS _ _ la)) == (MAC b@(BI.PS _ _ lb))+ | la /= lb = False+ | otherwise = BS.foldl' (B..|.) 0 (BS.packZipWith B.xor a b) == 0 -- preliminary utils ---------------------------------------------------------
lib/Crypto/Hash/SHA512/Lazy.hs view
@@ -21,6 +21,7 @@ , hmac_lazy ) where +import Crypto.Hash.SHA512.Internal import qualified Data.Bits as B import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as BSB@@ -30,7 +31,6 @@ import qualified Data.ByteString.Lazy.Internal as BLI import Data.Word (Word64) import Foreign.ForeignPtr (plusForeignPtr)-import Crypto.Hash.SHA512.Internal -- preliminary utils @@ -136,7 +136,7 @@ hmac_lazy :: BS.ByteString -- ^ key -> BL.ByteString -- ^ text- -> BS.ByteString+ -> MAC hmac_lazy mk@(BI.PS _ _ l) text = let step1 = k <> BS.replicate (128 - lk) 0x00 step2 = BS.map (B.xor 0x36) step1@@ -144,7 +144,7 @@ step4 = hash_lazy step3 step5 = BS.map (B.xor 0x5C) step1 step6 = step5 <> step4- in hash step6+ in MAC (hash step6) where hash bs = cat (go iv (pad bs)) where go :: Registers -> BS.ByteString -> Registers
ppad-sha512.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-sha512-version: 0.1.5+version: 0.2.0 synopsis: The SHA-512 and HMAC-SHA512 algorithms license: MIT license-file: LICENSE
test/Main.hs view
@@ -46,7 +46,8 @@ let key = decodeLenient (TE.encodeUtf8 mt_key) msg = decodeLenient (TE.encodeUtf8 mt_msg) pec = decodeLenient (TE.encodeUtf8 mt_tag)- out = BS.take bytes (SHA512.hmac key msg)+ SHA512.MAC mac = SHA512.hmac key msg+ out = BS.take bytes mac if mt_result == "invalid" then assertBool "invalid" (pec /= out) else assertEqual mempty pec out@@ -83,13 +84,16 @@ , cmp_hmac "hmv3" hmv3_key hmv3_put hmv3_pec , cmp_hmac "hmv4" hmv4_key hmv4_put hmv4_pec , testCase "hmv5" $ do- let out = BS.take 32 $ B16.encode (SHA512.hmac hmv5_key hmv5_put)+ let SHA512.MAC mac = SHA512.hmac hmv5_key hmv5_put+ out = BS.take 32 $ B16.encode mac assertEqual mempty hmv5_pec out , testCase "hmv6" $ do- let out = B16.encode (SHA512.hmac hmv6_key hmv6_put)+ let SHA512.MAC mac = SHA512.hmac hmv6_key hmv6_put+ out = B16.encode mac assertEqual mempty hmv6_pec out , testCase "hmv7" $ do- let out = B16.encode (SHA512.hmac hmv7_key hmv7_put)+ let SHA512.MAC mac = SHA512.hmac hmv7_key hmv7_put+ out = B16.encode mac assertEqual mempty hmv7_pec out ] , testGroup "hmac_lazy" [@@ -99,15 +103,18 @@ , cmp_hmac_lazy "hmv4" hmv4_key hmv4_put hmv4_pec , testCase "hmv5" $ do let lut = BL.fromStrict hmv5_put- out = BS.take 32 $ B16.encode (SHA512.hmac_lazy hmv5_key lut)+ SHA512.MAC mac = SHA512.hmac_lazy hmv5_key lut+ out = BS.take 32 $ B16.encode mac assertEqual mempty hmv5_pec out , testCase "hmv6" $ do let lut = BL.fromStrict hmv6_put- out = B16.encode (SHA512.hmac_lazy hmv6_key lut)+ SHA512.MAC mac = SHA512.hmac_lazy hmv6_key lut+ out = B16.encode mac assertEqual mempty hmv6_pec out , testCase "hmv7" $ do let lut = BL.fromStrict hmv7_put- out = B16.encode (SHA512.hmac_lazy hmv7_key lut)+ SHA512.MAC mac = SHA512.hmac_lazy hmv7_key lut+ out = B16.encode mac assertEqual mempty hmv7_pec out ] ]@@ -229,12 +236,14 @@ cmp_hmac :: String -> BS.ByteString -> BS.ByteString -> BS.ByteString -> TestTree cmp_hmac msg key put pec = testCase msg $ do- let out = B16.encode (SHA512.hmac key put)+ let SHA512.MAC mac = SHA512.hmac key put+ out = B16.encode mac assertEqual mempty pec out cmp_hmac_lazy :: String -> BS.ByteString -> BS.ByteString -> BS.ByteString -> TestTree cmp_hmac_lazy msg key (BL.fromStrict -> put) pec = testCase msg $ do- let out = B16.encode (SHA512.hmac_lazy key put)+ let SHA512.MAC mac = SHA512.hmac_lazy key put+ out = B16.encode mac assertEqual mempty pec out