cryptohash-sha256 0.11.101.0 → 0.11.102.0
raw patch · 8 files changed
+294/−44 lines, 8 filesdep +cryptohash-sha256-puredep ~SHAdep ~basedep ~base16-bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: cryptohash-sha256-pure
Dependency ranges changed: SHA, base, base16-bytestring, bytestring, criterion, tasty, tasty-hunit, tasty-quickcheck
API changes (from Hackage documentation)
+ Crypto.Hash.SHA256: start :: ByteString -> Ctx
+ Crypto.Hash.SHA256: startlazy :: ByteString -> Ctx
Files
- changelog.md +7/−0
- cryptohash-sha256.cabal +41/−21
- src-bench/bench-sha256.hs +63/−17
- src-pure/Crypto/Hash/SHA256.hs +93/−0
- src-tests/test-sha256.hs +48/−4
- src/Compat.hs +24/−0
- src/Crypto/Hash/SHA256.hs +17/−2
- src/Crypto/Hash/SHA256/FFI.hs +1/−0
changelog.md view
@@ -1,3 +1,10 @@+## 0.11.102.0++ - Add Eq instance for Ctx+ - Add start and startlazy producing Ctx+ - Introduce new `use-cbits` cabal flag in order to+ add support for FFI-less `cryptohash-sha256-pure` pass-thru+ ## 0.11.101.0 - Add `hkdf` function providing HKDF-SHA256 conforming to RFC5869
cryptohash-sha256.cabal view
@@ -1,6 +1,6 @@-cabal-version: 1.12+cabal-version: 2.0 name: cryptohash-sha256-version: 0.11.101.0+version: 0.11.102.0 synopsis: Fast, pure and practical SHA-256 implementation description: {@@ -53,7 +53,11 @@ , GHC == 7.8.4 , GHC == 7.10.3 , GHC == 8.0.2- , GHC == 8.2.1+ , GHC == 8.2.2+ , GHC == 8.4.4+ , GHC == 8.6.5+ , GHC == 8.8.3+ , GHC == 8.10.1 extra-source-files: cbits/hs_sha256.h changelog.md@@ -67,35 +71,49 @@ manual: True default: False +flag use-cbits+ description: Use fast optimized C routines via FFI; if flag is disabled falls back to non-FFI Haskell optimized implementation.+ manual: True+ default: True+ library default-language: Haskell2010- other-extensions: BangPatterns- CApiFFI- Trustworthy- Unsafe - build-depends: base >= 4.5 && < 4.11- , bytestring >= 0.9.2 && < 0.11- ghc-options: -Wall - hs-source-dirs: src+ build-depends: base >= 4.5 && < 4.15+ exposed-modules: Crypto.Hash.SHA256- other-modules: Crypto.Hash.SHA256.FFI- include-dirs: cbits + if flag(use-cbits)+ build-depends: bytestring ^>= 0.9.2 || ^>= 0.10.0 || ^>= 0.11.0++ other-extensions: BangPatterns+ CApiFFI+ CPP+ Trustworthy+ Unsafe++ hs-source-dirs: src+ other-modules: Crypto.Hash.SHA256.FFI+ Compat+ include-dirs: cbits+ else+ hs-source-dirs: src-pure+ build-depends: cryptohash-sha256-pure ^>= 0.1.0+ executable sha256sum+ default-language: Haskell2010 hs-source-dirs: src-exe main-is: sha256sum.hs ghc-options: -Wall -threaded if flag(exe)- default-language: Haskell2010 other-extensions: RecordWildCards build-depends: cryptohash-sha256 , base , bytestring - , base16-bytestring >= 0.1.1 && < 0.2+ , base16-bytestring ^>= 0.1.1 || ^>= 1.0.0 else buildable: False @@ -110,11 +128,11 @@ , base , bytestring - , base16-bytestring >= 0.1.1 && < 0.2- , SHA >= 1.6.4 && < 1.7- , tasty == 0.11.*- , tasty-quickcheck == 0.8.*- , tasty-hunit == 0.9.*+ , base16-bytestring ^>= 0.1.1 || ^>= 1.0.0+ , SHA ^>= 1.6.4+ , tasty ^>= 1.1+ , tasty-quickcheck ^>= 0.10+ , tasty-hunit ^>= 0.10 benchmark bench-sha256 default-language: Haskell2010@@ -123,6 +141,8 @@ main-is: bench-sha256.hs hs-source-dirs: src-bench build-depends: cryptohash-sha256+ , cryptohash-sha256-pure ^>= 0.1.0+ , SHA ^>= 1.6.4 , base , bytestring- , criterion == 1.1.*+ , criterion ^>= 1.5
src-bench/bench-sha256.hs view
@@ -1,36 +1,82 @@ {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE PackageImports #-} import Criterion.Main-import qualified Crypto.Hash.SHA256 as SHA256++import qualified "cryptohash-sha256" Crypto.Hash.SHA256 as IUT+import qualified "cryptohash-sha256-pure" Crypto.Hash.SHA256.Legacy as Pure+import qualified Data.Digest.Pure.SHA as REF+ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L -benchSize :: Int -> Benchmark-benchSize sz = bs `seq` bench msg (whnf SHA256.hash bs)+benchSize'IUT :: Int -> Benchmark+benchSize'IUT sz = bs `seq` bench msg (whnf IUT.hash bs) where bs = B.replicate sz 0 msg = "bs-" ++ show sz +benchSize'Pure :: Int -> Benchmark+benchSize'Pure sz = bs `seq` bench msg (whnf Pure.hash bs)+ where+ bs = B.replicate sz 0+ msg = "bs-" ++ show sz++benchSize'REF :: Int -> Benchmark+benchSize'REF sz = bs `seq` bench msg (whnf REF.sha256 bs)+ where+ bs = L.fromStrict (B.replicate sz 0)+ msg = "bs-" ++ show sz++ main :: IO () main = do let !lbs64x256 = L.fromChunks $ replicate 4 (B.replicate 64 0) !lbs64x4096 = L.fromChunks $ replicate 64 (B.replicate 64 0) defaultMain [ bgroup "cryptohash-sha256"- [ benchSize 0- , benchSize 8- , benchSize 32- , benchSize 64- , benchSize 128- , benchSize 256- , benchSize 1024- , benchSize 4096- , benchSize (128*1024)- , benchSize (1024*1024)- , benchSize (2*1024*1024)- , benchSize (4*1024*1024)+ [ benchSize'IUT 0+ , benchSize'IUT 32+ , benchSize'IUT 64+ , benchSize'IUT 128+ , benchSize'IUT 1024+ , benchSize'IUT 4096+ , benchSize'IUT (32*1024)+ , benchSize'IUT (128*1024)+ , benchSize'IUT (1024*1024)+ , benchSize'IUT (2*1024*1024)+ , benchSize'IUT (4*1024*1024) - , L.length lbs64x256 `seq` bench "lbs64x256" (whnf SHA256.hashlazy lbs64x256)- , L.length lbs64x4096 `seq` bench "lbs64x4096" (whnf SHA256.hashlazy lbs64x4096)+ , L.length lbs64x256 `seq` bench "lbs64x256" (whnf IUT.hashlazy lbs64x256)+ , L.length lbs64x4096 `seq` bench "lbs64x4096" (whnf IUT.hashlazy lbs64x4096)+ ]+ , bgroup "cryptohash-sha256-pure"+ [ benchSize'Pure 0+ , benchSize'Pure 32+ , benchSize'Pure 64+ , benchSize'Pure 128+ , benchSize'Pure 1024+ , benchSize'Pure 4096+ , benchSize'Pure (32*1024)+ , benchSize'Pure (128*1024)+ , benchSize'Pure (1024*1024)+ , benchSize'Pure (2*1024*1024)+ , benchSize'Pure (4*1024*1024)++ , L.length lbs64x256 `seq` bench "lbs64x256" (whnf Pure.hashlazy lbs64x256)+ , L.length lbs64x4096 `seq` bench "lbs64x4096" (whnf Pure.hashlazy lbs64x4096)+ ]+ , bgroup "SHA"+ [ benchSize'REF 0+ , benchSize'REF 32+ , benchSize'REF 64+ , benchSize'REF 128+ , benchSize'REF 1024+ , benchSize'REF 4096+ , benchSize'REF (32*1024)+ , benchSize'REF (128*1024)+ , benchSize'REF (1024*1024)+ , benchSize'REF (2*1024*1024)+ , benchSize'REF (4*1024*1024) ] ]
+ src-pure/Crypto/Hash/SHA256.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE Trustworthy #-}++-- |+-- Module : Crypto.Hash.SHA256+-- License : BSD-3+-- Maintainer : Herbert Valerio Riedel <hvr@gnu.org>+-- Stability : stable+--+-- A module containing <https://en.wikipedia.org/wiki/SHA-2 SHA-256> bindings+--+module Crypto.Hash.SHA256+ (++ -- * Incremental API+ --+ -- | This API is based on 4 different functions, similar to the+ -- lowlevel operations of a typical hash:+ --+ -- - 'init': create a new hash context+ -- - 'update': update non-destructively a new hash context with a strict bytestring+ -- - 'updates': same as update, except that it takes a list of strict bytestrings+ -- - 'finalize': finalize the context and returns a digest bytestring.+ --+ -- all those operations are completely pure, and instead of+ -- changing the context as usual in others language, it+ -- re-allocates a new context each time.+ --+ -- Example:+ --+ -- > import qualified Data.ByteString+ -- > import qualified Crypto.Hash.SHA256 as SHA256+ -- >+ -- > main = print digest+ -- > where+ -- > digest = SHA256.finalize ctx+ -- > ctx = foldl SHA256.update ctx0 (map Data.ByteString.pack [ [1,2,3], [4,5,6] ])+ -- > ctx0 = SHA256.init++ Ctx(..)+ , init -- :: Ctx+ , update -- :: Ctx -> ByteString -> Ctx+ , updates -- :: Ctx -> [ByteString] -> Ctx+ , finalize -- :: Ctx -> ByteString+ , finalizeAndLength -- :: Ctx -> (ByteString,Word64)+ , start -- :: ByteString -> Ct+ , startlazy -- :: L.ByteString -> Ctx++ -- * Single Pass API+ --+ -- | This API use the incremental API under the hood to provide+ -- the common all-in-one operations to create digests out of a+ -- 'ByteString' and lazy 'L.ByteString'.+ --+ -- - 'hash': create a digest ('init' + 'update' + 'finalize') from a strict 'ByteString'+ -- - 'hashlazy': create a digest ('init' + 'update' + 'finalize') from a lazy 'L.ByteString'+ -- - 'hashlazyAndLength': create a digest ('init' + 'update' + 'finalizeAndLength') from a lazy 'L.ByteString'+ --+ -- Example:+ --+ -- > import qualified Data.ByteString+ -- > import qualified Crypto.Hash.SHA256 as SHA256+ -- >+ -- > main = print $ SHA256.hash (Data.ByteString.pack [0..255])+ --+ -- __NOTE__: The returned digest is a binary 'ByteString'. For+ -- converting to a base16/hex encoded digest the+ -- <https://hackage.haskell.org/package/base16-bytestring base16-bytestring>+ -- package is recommended.++ , hash -- :: ByteString -> ByteString+ , hashlazy -- :: L.ByteString -> ByteString+ , hashlazyAndLength -- :: L.ByteString -> (ByteString,Int64)++ -- ** HMAC-SHA-256+ --+ -- | <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible+ -- <https://en.wikipedia.org/wiki/HMAC HMAC>-SHA-256 digests++ , hmac -- :: ByteString -> ByteString -> ByteString+ , hmaclazy -- :: ByteString -> L.ByteString -> ByteString+ , hmaclazyAndLength -- :: ByteString -> L.ByteString -> (ByteString,Word64)++ -- ** HKDF-SHA-256+ --+ -- | <https://tools.ietf.org/html/rfc5869 RFC5869>-compatible+ -- <https://en.wikipedia.org/wiki/HKDF HKDF>-SHA-256 key derivation function++ , hkdf+ ) where++import Prelude ()+import Crypto.Hash.SHA256.Legacy
src-tests/test-sha256.hs view
@@ -1,10 +1,14 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-} module Main (main) where import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Base16 as B16+-- before bytestring-0.10 instance IsString ByteString+-- was imported only from Char8 module+import Data.ByteString.Char8 () import qualified Data.ByteString.Lazy as BL import Data.Word @@ -46,11 +50,12 @@ katTests :: [TestTree] katTests- | length vectors == length answers = map makeTest (zip3 [1::Int ..] vectors answers) ++ [xltest]+ | length vectors == length answers = map makeTest (zip3 [1::Int ..] vectors answers) ++ [xltest, xltest'] | otherwise = error "vectors/answers length mismatch" where makeTest (i, v, r) = testGroup ("vec"++show i) $ [ testCase "one-pass" (r @=? runTest v)+ , testCase "one-pass'" (r @=? runTest' v) , testCase "inc-1" (r @=? runTestInc 1 v) , testCase "inc-2" (r @=? runTestInc 2 v) , testCase "inc-3" (r @=? runTestInc 3 v)@@ -65,25 +70,43 @@ , testCase "lazy-7" (r @=? runTestLazy 7 v) , testCase "lazy-8" (r @=? runTestLazy 8 v) , testCase "lazy-16" (r @=? runTestLazy 16 v)+ , testCase "lazy-1'" (r @=? runTestLazy' 1 v)+ , testCase "lazy-2'" (r @=? runTestLazy' 2 v)+ , testCase "lazy-7'" (r @=? runTestLazy' 7 v)+ , testCase "lazy-8'" (r @=? runTestLazy' 8 v)+ , testCase "lazy-16'" (r @=? runTestLazy' 16 v) ] ++ [ testCase "lazy-63u" (r @=? runTestLazyU 63 v) | B.length v > 63 ] ++ [ testCase "lazy-65u" (r @=? runTestLazyU 65 v) | B.length v > 65 ] ++ [ testCase "lazy-97u" (r @=? runTestLazyU 97 v) | B.length v > 97 ] ++- [ testCase "lazy-131u" (r @=? runTestLazyU 131 v) | B.length v > 131 ]+ [ testCase "lazy-131u" (r @=? runTestLazyU 131 v) | B.length v > 131] +++ [ testCase "lazy-63u'" (r @=? runTestLazyU' 63 v) | B.length v > 63 ] +++ [ testCase "lazy-65u'" (r @=? runTestLazyU' 65 v) | B.length v > 65 ] +++ [ testCase "lazy-97u'" (r @=? runTestLazyU' 97 v) | B.length v > 97 ] +++ [ testCase "lazy-131u'" (r @=? runTestLazyU' 131 v) | B.length v > 131 ] runTest :: ByteString -> ByteString runTest = B16.encode . IUT.hash + runTest' :: ByteString -> ByteString+ runTest' = B16.encode . IUT.finalize . IUT.start+ runTestInc :: Int -> ByteString -> ByteString runTestInc i = B16.encode . IUT.finalize . myfoldl' IUT.update IUT.init . splitB i runTestLazy :: Int -> ByteString -> ByteString runTestLazy i = B16.encode . IUT.hashlazy . BL.fromChunks . splitB i + runTestLazy' :: Int -> ByteString -> ByteString+ runTestLazy' i = B16.encode . IUT.finalize . IUT.startlazy . BL.fromChunks . splitB i+ -- force unaligned md5-blocks runTestLazyU :: Int -> ByteString -> ByteString runTestLazyU i = B16.encode . IUT.hashlazy . BL.fromChunks . map B.copy . splitB i + runTestLazyU' :: Int -> ByteString -> ByteString+ runTestLazyU' i = B16.encode . IUT.finalize . IUT.startlazy . BL.fromChunks . map B.copy . splitB i+ ---- xltest = testGroup "XL-vec"@@ -91,6 +114,11 @@ where vecXL = BL.fromChunks (replicate 16777216 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno") + xltest' = testGroup "XL-vec'"+ [ testCase "inc'" (ansXLTest @=? (B16.encode . IUT.finalize . IUT.startlazy) vecXL) ]+ where+ vecXL = BL.fromChunks (replicate 16777216 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")+ splitB :: Int -> ByteString -> [ByteString] splitB l b | B.length b > l = b1 : splitB l b2@@ -98,7 +126,15 @@ where (b1, b2) = B.splitAt l b +-- Minor hack to paper over backward incompat changes introduced in base16-bytestring-1.0+class B16DecRes a where b16DecRes :: a -> ByteString+instance B16DecRes (Either String ByteString) where b16DecRes = either error id+instance B16DecRes (ByteString, ByteString) where b16DecRes = fst +b16decode :: ByteString -> ByteString+b16decode = b16DecRes . B16.decode++ rfc4231Vectors :: [(ByteString,ByteString,ByteString)] rfc4231Vectors = -- (secrect,msg,mac) [ (rep 20 0x0b, "Hi There", x"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")@@ -110,7 +146,7 @@ , (rep 131 0xaa, "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", x"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2") ] where- x = fst.B16.decode+ x = b16decode rep n c = B.replicate n c rfc4231Tests :: [TestTree]@@ -137,7 +173,7 @@ , ( 42, rep 22 0x0b, "", "", x"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8") ] where- x = fst.B16.decode+ x = b16decode rep n c = B.replicate n c rfc5869Tests :: [TestTree]@@ -178,7 +214,9 @@ refImplTests :: [TestTree] refImplTests = [ testProperty "hash" prop_hash+ , testProperty "start" prop_start , testProperty "hashlazy" prop_hashlazy+ , testProperty "startlazy" prop_startlazy , testProperty "hashlazyAndLength" prop_hashlazyAndLength , testProperty "hmac" prop_hmac , testProperty "hmaclazy" prop_hmaclazy@@ -188,8 +226,14 @@ prop_hash (RandBS bs) = ref_hash bs == IUT.hash bs + prop_start (RandBS bs)+ = ref_hash bs == (IUT.finalize $ IUT.start bs)+ prop_hashlazy (RandLBS bs) = ref_hashlazy bs == IUT.hashlazy bs++ prop_startlazy (RandLBS bs)+ = ref_hashlazy bs == (IUT.finalize $ IUT.startlazy bs) prop_hashlazyAndLength (RandLBS bs) = ref_hashlazyAndLength bs == IUT.hashlazyAndLength bs
+ src/Compat.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE Trustworthy #-}++-- |+-- Module : Compat+-- License : BSD-3+-- Maintainer : Herbert Valerio Riedel <hvr@gnu.org>+-- Stability : stable+--+-- Compat layer to reduce code exposure to CPP to a bare minimum+--+module Compat (constructBS) where++import Foreign.ForeignPtr (ForeignPtr)+import Data.Word (Word8)+import Data.ByteString.Internal (ByteString (..))++-- | Directly construct a 'ByteString', unsafely+constructBS :: ForeignPtr Word8 -> Int -> ByteString+#if MIN_VERSION_bytestring(0,11,0)+constructBS = BS+#else+constructBS = \fp -> PS fp 0+#endif
src/Crypto/Hash/SHA256.hs view
@@ -43,6 +43,8 @@ , updates -- :: Ctx -> [ByteString] -> Ctx , finalize -- :: Ctx -> ByteString , finalizeAndLength -- :: Ctx -> (ByteString,Word64)+ , start -- :: ByteString -> Ct+ , startlazy -- :: L.ByteString -> Ctx -- * Single Pass API --@@ -90,7 +92,7 @@ import Data.Bits (xor) import Data.ByteString (ByteString) import qualified Data.ByteString as B-import Data.ByteString.Internal (ByteString (PS), create,+import Data.ByteString.Internal (create, createAndTrim, mallocByteString, memcpy, toForeignPtr) import qualified Data.ByteString.Lazy as L@@ -103,6 +105,7 @@ import Prelude hiding (init) import System.IO.Unsafe (unsafeDupablePerformIO) +import Compat (constructBS) import Crypto.Hash.SHA256.FFI -- | perform IO for hashes that do allocation and ffi.@@ -134,7 +137,7 @@ create' l f = do fp <- mallocByteString l x <- withForeignPtr fp $ \p -> f p- let bs = PS fp 0 l+ let bs = constructBS fp l return $! x `seq` bs `seq` (bs,x) copyCtx :: Ptr Ctx -> Ptr Ctx -> IO ()@@ -228,11 +231,23 @@ -- hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr hash d = unsafeDoIO $ unsafeUseAsCStringLen d $ \(cs, len) -> create digestSize (c_sha256_hash (castPtr cs) (fromIntegral len)) +{-# NOINLINE start #-}+-- | hash a strict bytestring into a Ctx+start :: ByteString -> Ctx+start d = unsafeDoIO $ withCtxNew $ \ptr -> c_sha256_init ptr >> updateInternalIO ptr d++ {-# NOINLINE hashlazy #-} -- | hash a lazy bytestring into a digest bytestring (32 bytes) hashlazy :: L.ByteString -> ByteString hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr++{-# NOINLINE startlazy #-}+-- | hash a lazy bytestring into a Ctx+startlazy :: L.ByteString -> Ctx+startlazy l = unsafeDoIO $ withCtxNew $ \ptr ->+ c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) {-# NOINLINE hashlazyAndLength #-} -- | Variant of 'hashlazy' which simultaneously computes the hash and length of a lazy bytestring.
src/Crypto/Hash/SHA256/FFI.hs view
@@ -38,6 +38,7 @@ -- -- Consequently, a SHA-256 digest as produced by 'hash', 'hashlazy', or 'finalize' is 32 bytes long. newtype Ctx = Ctx ByteString+ deriving (Eq) foreign import capi unsafe "hs_sha256.h hs_cryptohash_sha256_init" c_sha256_init :: Ptr Ctx -> IO ()