sha256

Pure Haskell SHA-256, HMAC-SHA256 (docs.ppad.tech/sha256).
git clone git://git.ppad.tech/sha256.git
Log | Files | Refs | README | LICENSE

commit 4716cd5b4e673e9cb66e4e5e427e5464a7c10977
parent 9bbe66af958ce48c8087108eaaf209d8faa055fc
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Jan 2026 11:01:29 +0400

release: v0.3.0

Diffstat:
MCHANGELOG | 5+++++
Mbench/Main.hs | 53+++++++++++++++++++++++++++--------------------------
Mlib/Crypto/Hash/SHA256/Internal.hs | 3+--
Mppad-sha256.cabal | 3+--
4 files changed, 34 insertions(+), 30 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,5 +1,10 @@ # Changelog +- 0.3.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.2.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 diff --git a/bench/Main.hs b/bench/Main.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module Main where @@ -14,30 +15,30 @@ main = defaultMain [ ] suite :: Benchmark -suite = env setup $ \ ~(bs, bl, mac0, mac1, macl0, macl1) -> - bgroup "ppad-sha256" [ - bgroup "SHA256 (32B input)" [ - bench "hash" $ whnf SHA256.hash bs - , bench "hash_lazy" $ whnf SHA256.hash_lazy bl - , bench "SHA.sha256" $ whnf SHA.sha256 bl +suite = + let !bs = BS.replicate 32 0 + !bl = BL.fromStrict bs + !mac0 = SHA256.hmac "key" "foo" + !mac1 = SHA256.hmac "key" "bar" + !mac2 = SHA256.hmac "key" "foo" + !macl0 = SHA256.hmac_lazy "key" "foo" + !macl1 = SHA256.hmac_lazy "key" "bar" + !macl2 = SHA256.hmac_lazy "key" "foo" + in bgroup "ppad-sha256" [ + bgroup "SHA256 (32B input)" [ + bench "hash" $ whnf SHA256.hash bs + , bench "hash_lazy" $ whnf SHA256.hash_lazy bl + , bench "SHA.sha256" $ whnf SHA.sha256 bl + ] + , bgroup "HMAC-SHA256 (32B input)" [ + bench "hmac" $ whnf (SHA256.hmac "key") bs + , bench "hmac_lazy" $ whnf (SHA256.hmac_lazy "key") bl + , bench "SHA.hmacSha256" $ whnf (SHA.hmacSha256 "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 + ] ] - , bgroup "HMAC-SHA256 (32B input)" [ - bench "hmac" $ whnf (SHA256.hmac "key") bs - , bench "hmac_lazy" $ whnf (SHA256.hmac_lazy "key") bl - , bench "SHA.hmacSha256" $ whnf (SHA.hmacSha256 "key") bl - ] - , bgroup "MAC comparison" [ - bench "hmac" $ nf (mac0 ==) mac1 - , bench "hmac_lazy" $ nf (macl0 ==) macl1 - ] - ] - where - setup = do - let bs_32B = BS.replicate 32 0 - bl_32B = BL.fromStrict bs_32B - mac0 = SHA256.hmac "key" "foo" - mac1 = SHA256.hmac "key" "bar" - macl0 = SHA256.hmac_lazy "key" "foo" - macl1 = SHA256.hmac_lazy "key" "bar" - pure (bs_32B, bl_32B, mac0, mac1, macl0, macl1) - diff --git a/lib/Crypto/Hash/SHA256/Internal.hs b/lib/Crypto/Hash/SHA256/Internal.hs @@ -33,7 +33,6 @@ module Crypto.Hash.SHA256.Internal ( , unsafe_padding ) where -import Control.DeepSeq (NFData(..)) import qualified Data.Bits as B import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BI @@ -59,7 +58,7 @@ import qualified GHC.Word (Word8(..)) -- >>> bs0 == bs1 -- don't do this -- False newtype MAC = MAC BS.ByteString - deriving newtype (Show, NFData) + deriving newtype Show instance Eq MAC where -- | A constant-time equality check for message authentication codes. diff --git a/ppad-sha256.cabal b/ppad-sha256.cabal @@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-sha256 -version: 0.2.5 +version: 0.3.0 synopsis: The SHA-256 and HMAC-SHA256 algorithms license: MIT license-file: LICENSE @@ -38,7 +38,6 @@ library build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 - , deepseq >= 1.4 && < 1.6 c-sources: cbits/sha256_arm.c if arch(aarch64)