cryptohash-sha256 0.11.7.1 → 0.11.7.2
raw patch · 12 files changed
+492/−625 lines, 12 filesdep +SHAdep +base16-bytestringdep ~basedep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: SHA, base16-bytestring
Dependency ranges changed: base, bytestring
API changes (from Hackage documentation)
Files
- Bench/Bench.hs +0/−33
- Bench/BenchAPI.hs +0/−39
- Crypto/Hash/SHA256.hs +0/−184
- Tests/KAT.hs +0/−92
- cbits/bitfn.h +0/−240
- cbits/sha256.c +62/−12
- cbits/sha256.h +5/−3
- changelog.md +11/−0
- cryptohash-sha256.cabal +17/−22
- src-bench/bench-sha256.hs +36/−0
- src-tests/test-sha256.hs +146/−0
- src/Crypto/Hash/SHA256.hs +215/−0
− Bench/Bench.hs
@@ -1,33 +0,0 @@-{-# LANGUAGE BangPatterns #-}-import Criterion.Main-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L-import qualified Crypto.Hash.SHA256 as SHA256--hashmany (i,u,f) = f . foldl u i--allHashs =- [ ("SHA2-256",SHA256.hash, hashmany (SHA256.init,SHA256.update,SHA256.finalize))- ]--benchHash :: a -> (a -> B.ByteString) -> Benchmarkable-benchHash bs f = whnf f bs--withHashesFilter out f = map f $ filter (\(n,_,_) -> not (n `elem` out)) allHashs-withHashes f = map f allHashs--main = do- let !bs32 = B.replicate 32 0- !bs256 = B.replicate 256 0- !bs4096 = B.replicate 4096 0- !bs1M = B.replicate (1*1024*1024) 0- let !lbs64x256 = (map (const (B.replicate 64 0)) [0..3])- !lbs64x4096 = (map (const (B.replicate 64 0)) [0..63])- defaultMain- [ bgroup "hash-32b" (withHashes (\(name, f,_) -> bench name $ benchHash bs32 f))- , bgroup "hash-256b" (withHashes (\(name, f,_) -> bench name $ benchHash bs256 f))- , bgroup "hash-4Kb" (withHashes (\(name, f,_) -> bench name $ benchHash bs4096 f))- , bgroup "hash-1Mb" (withHashesFilter ["MD2"] (\(name, f,_) -> bench name $ benchHash bs1M f))- , bgroup "iuf-64x256" (withHashes (\(name, _,f) -> bench name $ benchHash lbs64x256 f))- , bgroup "iuf-64x4096" (withHashes (\(name, _,f) -> bench name $ benchHash lbs64x4096 f))- ]
− Bench/BenchAPI.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE BangPatterns #-}-import Criterion.Main-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L-import qualified Crypto.Hash.SHA256 as SHA256--sha256F = ( "SHA256"- , SHA256.hash- , SHA256.finalize . SHA256.update SHA256.init- )--main = do- let !bs32 = B.replicate 32 0- !bs256 = B.replicate 256 0- !bs4096 = B.replicate 4096 0- !bs1M = B.replicate (1*1024*1024) 0- let !lbs64x256 = (map (const (B.replicate 64 0)) [0..3])- !lbs64x4096 = (map (const (B.replicate 64 0)) [0..63])-- let (fname, fHash, fIncr) = sha256F- let benchName ty z = fname ++ "." ++ ty -- ++ " " ++ show z- defaultMain- [ bgroup "hash-0b"- [ bench (benchName "hash" 0) $ whnf fHash B.empty- , bench (benchName "incr" 0) $ whnf fIncr B.empty- ]- , bgroup "hash-32b"- [ bench (benchName "hash" 32) $ whnf fHash bs32- , bench (benchName "incr" 32) $ whnf fIncr bs32- ]- , bgroup "hash-256b"- [ bench (benchName "hash" 256) $ whnf fHash bs256- , bench (benchName "incr" 256) $ whnf fIncr bs256- ]- , bgroup "hash-4Kb"- [ bench (benchName "hash" 4096) $ whnf fHash bs4096- , bench (benchName "incr" 4096) $ whnf fIncr bs4096- ]- ]
− Crypto/Hash/SHA256.hs
@@ -1,184 +0,0 @@--- |--- Module : Crypto.Hash.SHA256--- License : BSD-style--- Maintainer : Herbert Valerio Riedel <hvr@gnu.org>--- Stability : stable--- Portability : unknown------ 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 bytestring- -- - '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-- -- * 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'- --- -- 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 -- :: ByteString -> ByteString- ) where--import Prelude hiding (init)-import Foreign.Ptr-import Foreign.ForeignPtr (withForeignPtr)-import Foreign.Storable-import Foreign.Marshal.Alloc-import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString as B-import Data.ByteString (ByteString)-import Data.ByteString.Unsafe (unsafeUseAsCStringLen)-import Data.ByteString.Internal (create, toForeignPtr)-import Data.Word-import System.IO.Unsafe (unsafeDupablePerformIO)---- | perform IO for hashes that do allocation and ffi.--- unsafeDupablePerformIO is used when possible as the--- computation is pure and the output is directly linked--- to the input. we also do not modify anything after it has--- been returned to the user.-unsafeDoIO :: IO a -> a-unsafeDoIO = unsafeDupablePerformIO---- | SHA-256 Context-newtype Ctx = Ctx ByteString--{-# INLINE digestSize #-}-digestSize :: Int-digestSize = 32--{-# INLINE sizeCtx #-}-sizeCtx :: Int-sizeCtx = 192--{-# RULES "digestSize" B.length (finalize init) = digestSize #-}-{-# RULES "hash" forall b. finalize (update init b) = hash b #-}-{-# RULES "hash.list1" forall b. finalize (updates init [b]) = hash b #-}-{-# RULES "hashmany" forall b. finalize (foldl update init b) = hashlazy (L.fromChunks b) #-}-{-# RULES "hashlazy" forall b. finalize (foldl update init $ L.toChunks b) = hashlazy b #-}--{-# INLINE withByteStringPtr #-}-withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a-withByteStringPtr b f =- withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)- where (fptr, off, _) = toForeignPtr b--{-# INLINE memcopy64 #-}-memcopy64 :: Ptr Word64 -> Ptr Word64 -> IO ()-memcopy64 dst src = mapM_ peekAndPoke [0..(24-1)]- where peekAndPoke i = peekElemOff src i >>= pokeElemOff dst i--withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx-withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx- where createCtx = create sizeCtx $ \dstPtr ->- withByteStringPtr ctxB $ \srcPtr -> do- memcopy64 (castPtr dstPtr) (castPtr srcPtr)- f (castPtr dstPtr)--withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a-withCtxThrow (Ctx ctxB) f =- allocaBytes sizeCtx $ \dstPtr ->- withByteStringPtr ctxB $ \srcPtr -> do- memcopy64 (castPtr dstPtr) (castPtr srcPtr)- f (castPtr dstPtr)--withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx-withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)--withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a-withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)--foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_init"- c_sha256_init :: Ptr Ctx -> IO ()--foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_update"- c_sha256_update :: Ptr Ctx -> Ptr Word8 -> Word32 -> IO ()--foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_finalize"- c_sha256_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()--updateInternalIO :: Ptr Ctx -> ByteString -> IO ()-updateInternalIO ptr d =- unsafeUseAsCStringLen d (\(cs, len) -> c_sha256_update ptr (castPtr cs) (fromIntegral len))--finalizeInternalIO :: Ptr Ctx -> IO ByteString-finalizeInternalIO ptr = create digestSize (c_sha256_finalize ptr)--{-# NOINLINE init #-}--- | init a context-init :: Ctx-init = unsafeDoIO $ withCtxNew $ c_sha256_init--{-# NOINLINE update #-}--- | update a context with a bytestring-update :: Ctx -> ByteString -> Ctx-update ctx d = unsafeDoIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d--{-# NOINLINE updates #-}--- | updates a context with multiples bytestring-updates :: Ctx -> [ByteString] -> Ctx-updates ctx d = unsafeDoIO $ withCtxCopy ctx $ \ptr -> mapM_ (updateInternalIO ptr) d--{-# NOINLINE finalize #-}--- | finalize the context into a digest bytestring-finalize :: Ctx -> ByteString-finalize ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO--{-# NOINLINE hash #-}--- | hash a strict bytestring into a digest bytestring-hash :: ByteString -> ByteString-hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do- c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr--{-# NOINLINE hashlazy #-}--- | hash a lazy bytestring into a digest bytestring-hashlazy :: L.ByteString -> ByteString-hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do- c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
− Tests/KAT.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-}-import Data.Char-import Data.Bits-import Data.Word-import Data.ByteString (ByteString)-import Data.Foldable (foldl')-import Data.Monoid (mconcat)-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC-import qualified Crypto.Hash.SHA256 as SHA256--import Test.Tasty-import Test.Tasty.QuickCheck-import Test.Tasty.HUnit--v0,v1,v2 :: ByteString-v0 = ""-v1 = "The quick brown fox jumps over the lazy dog"-v2 = "The quick brown fox jumps over the lazy cog"-vectors = [ v0, v1, v2 ]--instance Arbitrary ByteString where- arbitrary = B.pack `fmap` arbitrary--data HashFct = HashFct- { fctHash :: (B.ByteString -> B.ByteString)- , fctInc :: ([B.ByteString] -> B.ByteString) }--hashinc i u f = f . foldl u i--sha256Hash = HashFct { fctHash = SHA256.hash, fctInc = hashinc SHA256.init SHA256.update SHA256.finalize }---results :: [ (String, HashFct, [String]) ]-results = [- ("SHA256", sha256Hash, [- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",- "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592",- "e4c4d8f3bf76b692de791a173e05321150f7a345b46484fe427f6acc7ecc81be" ])- ]--hexalise s = concatMap (\c -> [ hex $ c `div` 16, hex $ c `mod` 16 ]) s- where hex i- | i >= 0 && i <= 9 = fromIntegral (ord '0') + i- | i >= 10 && i <= 15 = fromIntegral (ord 'a') + i - 10- | otherwise = 0--hexaliseB :: B.ByteString -> B.ByteString-hexaliseB = B.pack . hexalise . B.unpack--splitB :: Int -> ByteString -> [ByteString]-splitB l b =- if B.length b > l- then- let (b1, b2) = B.splitAt l b in- b1 : splitB l b2- else- [ b ]--showHash :: B.ByteString -> String-showHash = map (toEnum.fromEnum) . hexalise . B.unpack--runhash hash v = showHash $ (fctHash hash) $ v-runhashinc hash v = showHash $ (fctInc hash) $ v--makeTestAlg (name, hash, results) = testGroup name $ concatMap maketest (zip3 [0..] vectors results)- where- runtest :: ByteString -> String- runtest v = runhash hash v-- runtestinc :: Int -> ByteString -> String- runtestinc i v = runhashinc hash $ splitB i v-- maketest (i, v, r) =- [ testCase (show i ++ " one-pass") (r @=? runtest v)- , testCase (show i ++ " inc 1") (r @=? runtestinc 1 v)- , testCase (show i ++ " inc 2") (r @=? runtestinc 2 v)- , testCase (show i ++ " inc 3") (r @=? runtestinc 3 v)- , testCase (show i ++ " inc 4") (r @=? runtestinc 4 v)- , testCase (show i ++ " inc 5") (r @=? runtestinc 5 v)- , testCase (show i ++ " inc 9") (r @=? runtestinc 9 v)- , testCase (show i ++ " inc 16") (r @=? runtestinc 16 v)- ]--katTests :: [TestTree]-katTests = map makeTestAlg results---main = defaultMain $ testGroup "cryptohash"- [ testGroup "KATs" katTests- ]
− cbits/bitfn.h
@@ -1,240 +0,0 @@-/*- * Copyright (C) 2006-2009 Vincent Hanquez <vincent@snarc.org>- *- * Redistribution and use in source and binary forms, with or without- * modification, are permitted provided that the following conditions- * are met:- * 1. Redistributions of source code must retain the above copyright- * notice, this list of conditions and the following disclaimer.- * 2. Redistributions in binary form must reproduce the above copyright- * notice, this list of conditions and the following disclaimer in the- * documentation and/or other materials provided with the distribution.- *- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.- */--#ifndef BITFN_H-#define BITFN_H-#include <stdint.h>--#ifndef NO_INLINE_ASM-/**********************************************************/-# if (defined(__i386__))-# define ARCH_HAS_SWAP32-static inline uint32_t bitfn_swap32(uint32_t a)-{- asm ("bswap %0" : "=r" (a) : "0" (a));- return a;-}-/**********************************************************/-# elif (defined(__arm__))-# define ARCH_HAS_SWAP32-static inline uint32_t bitfn_swap32(uint32_t a)-{- uint32_t tmp = a;- asm volatile ("eor %1, %0, %0, ror #16\n"- "bic %1, %1, #0xff0000\n"- "mov %0, %0, ror #8\n"- "eor %0, %0, %1, lsr #8\n"- : "=r" (a), "=r" (tmp) : "0" (a), "1" (tmp));- return a;-}-/**********************************************************/-# elif defined(__x86_64__)-# define ARCH_HAS_SWAP32-# define ARCH_HAS_SWAP64-static inline uint32_t bitfn_swap32(uint32_t a)-{- asm ("bswap %0" : "=r" (a) : "0" (a));- return a;-}--static inline uint64_t bitfn_swap64(uint64_t a)-{- asm ("bswap %0" : "=r" (a) : "0" (a));- return a;-}--# endif-#endif /* NO_INLINE_ASM */-/**********************************************************/--#ifndef ARCH_HAS_ROL32-static inline uint32_t rol32(uint32_t word, uint32_t shift)-{- return (word << shift) | (word >> (32 - shift));-}-#endif--#ifndef ARCH_HAS_ROR32-static inline uint32_t ror32(uint32_t word, uint32_t shift)-{- return (word >> shift) | (word << (32 - shift));-}-#endif--#ifndef ARCH_HAS_ROL64-static inline uint64_t rol64(uint64_t word, uint32_t shift)-{- return (word << shift) | (word >> (64 - shift));-}-#endif--#ifndef ARCH_HAS_ROR64-static inline uint64_t ror64(uint64_t word, uint32_t shift)-{- return (word >> shift) | (word << (64 - shift));-}-#endif--#ifndef ARCH_HAS_SWAP32-static inline uint32_t bitfn_swap32(uint32_t a)-{- return (a << 24) | ((a & 0xff00) << 8) | ((a >> 8) & 0xff00) | (a >> 24);-}-#endif--#ifndef ARCH_HAS_ARRAY_SWAP32-static inline void array_swap32(uint32_t *d, uint32_t *s, uint32_t nb)-{- while (nb--)- *d++ = bitfn_swap32(*s++);-}-#endif--#ifndef ARCH_HAS_SWAP64-static inline uint64_t bitfn_swap64(uint64_t a)-{- return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |- (((uint64_t) bitfn_swap32((uint32_t) a)) << 32);-}-#endif--#ifndef ARCH_HAS_ARRAY_SWAP64-static inline void array_swap64(uint64_t *d, uint64_t *s, uint32_t nb)-{- while (nb--)- *d++ = bitfn_swap64(*s++);-}-#endif--#ifndef ARCH_HAS_MEMORY_ZERO-static inline void memory_zero(void *ptr, uint32_t len)-{- uint32_t *ptr32 = ptr;- uint8_t *ptr8;- int i;-- for (i = 0; i < len / 4; i++)- *ptr32++ = 0;- if (len % 4) {- ptr8 = (uint8_t *) ptr32;- for (i = len % 4; i >= 0; i--)- ptr8[i] = 0;- }-}-#endif--#ifndef ARCH_HAS_ARRAY_COPY32-static inline void array_copy32(uint32_t *d, uint32_t *s, uint32_t nb)-{- while (nb--) *d++ = *s++;-}-#endif--#ifndef ARCH_HAS_ARRAY_COPY64-static inline void array_copy64(uint64_t *d, uint64_t *s, uint32_t nb)-{- while (nb--) *d++ = *s++;-}-#endif--#ifdef __MINGW32__- # define LITTLE_ENDIAN 1234- # define BYTE_ORDER LITTLE_ENDIAN-#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)- # include <sys/endian.h>-#elif defined(__OpenBSD__) || defined(__SVR4)- # include <sys/types.h>-#elif defined(__APPLE__)- # include <machine/endian.h>-#elif defined( BSD ) && ( BSD >= 199103 )- # include <machine/endian.h>-#elif defined( __QNXNTO__ ) && defined( __LITTLEENDIAN__ )- # define LITTLE_ENDIAN 1234- # define BYTE_ORDER LITTLE_ENDIAN-#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )- # define BIG_ENDIAN 1234- # define BYTE_ORDER BIG_ENDIAN-#elif defined( _AIX )- # include <sys/machine.h>-#else- # include <endian.h>-#endif-/* big endian to cpu */-#if LITTLE_ENDIAN == BYTE_ORDER--# define be32_to_cpu(a) bitfn_swap32(a)-# define cpu_to_be32(a) bitfn_swap32(a)-# define le32_to_cpu(a) (a)-# define cpu_to_le32(a) (a)-# define be64_to_cpu(a) bitfn_swap64(a)-# define cpu_to_be64(a) bitfn_swap64(a)-# define le64_to_cpu(a) (a)-# define cpu_to_le64(a) (a)--# define cpu_to_le32_array(d, s, l) array_copy32(d, s, l)-# define le32_to_cpu_array(d, s, l) array_copy32(d, s, l)-# define cpu_to_be32_array(d, s, l) array_swap32(d, s, l)-# define be32_to_cpu_array(d, s, l) array_swap32(d, s, l)--# define cpu_to_le64_array(d, s, l) array_copy64(d, s, l)-# define le64_to_cpu_array(d, s, l) array_copy64(d, s, l)-# define cpu_to_be64_array(d, s, l) array_swap64(d, s, l)-# define be64_to_cpu_array(d, s, l) array_swap64(d, s, l)--# define ror32_be(a, s) rol32(a, s)-# define rol32_be(a, s) ror32(a, s)--# define ARCH_IS_LITTLE_ENDIAN--#elif BIG_ENDIAN == BYTE_ORDER--# define be32_to_cpu(a) (a)-# define cpu_to_be32(a) (a)-# define be64_to_cpu(a) (a)-# define cpu_to_be64(a) (a)-# define le64_to_cpu(a) bitfn_swap64(a)-# define cpu_to_le64(a) bitfn_swap64(a)-# define le32_to_cpu(a) bitfn_swap32(a)-# define cpu_to_le32(a) bitfn_swap32(a)--# define cpu_to_le32_array(d, s, l) array_swap32(d, s, l)-# define le32_to_cpu_array(d, s, l) array_swap32(d, s, l)-# define cpu_to_be32_array(d, s, l) array_copy32(d, s, l)-# define be32_to_cpu_array(d, s, l) array_copy32(d, s, l)--# define cpu_to_le64_array(d, s, l) array_swap64(d, s, l)-# define le64_to_cpu_array(d, s, l) array_swap64(d, s, l)-# define cpu_to_be64_array(d, s, l) array_copy64(d, s, l)-# define be64_to_cpu_array(d, s, l) array_copy64(d, s, l)--# define ror32_be(a, s) ror32(a, s)-# define rol32_be(a, s) rol32(a, s)--# define ARCH_IS_BIG_ENDIAN--#else-# error "endian not supported"-#endif--#endif /* !BITFN_H */
cbits/sha256.c view
@@ -23,15 +23,67 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <string.h> #include "sha256.h"-#include "bitfn.h" +#include <assert.h>+#include <string.h>+#include <ghcautoconf.h>++#if defined(static_assert)+static_assert(sizeof(struct sha256_ctx) == SHA256_CTX_SIZE, "unexpected sha256_ctx size");+#else+// poor man's pre-C11 _Static_assert+typedef char static_assertion__unexpected_sha256_ctx_size[(sizeof(struct sha256_ctx) == SHA256_CTX_SIZE)?1:-1];+#endif++static inline uint32_t+ror32(uint32_t word, uint32_t shift)+{+ // GCC usually transforms this into a 'ror'-insn+ return (word >> shift) | (word << (32 - shift));+}++static inline uint32_t+hs_htonl(uint32_t hl)+{+#if WORDS_BIGENDIAN+ return hl;+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)+ return __builtin_bswap32(hl);+#else+ // GCC usually transforms this into a bswap insn+ return ((hl & 0xff000000) >> 24) |+ ((hl & 0x00ff0000) >> 8) |+ ((hl & 0x0000ff00) << 8) |+ ( hl << 24);+#endif+}++static inline void+hs_htonl_array(uint32_t *dest, const uint32_t *src, unsigned wordcnt)+{+ while (wordcnt--)+ *dest++ = hs_htonl(*src++);+}++static inline uint64_t+hs_htonll(uint64_t hll)+{+#if WORDS_BIGENDIAN+ return hll;+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)+ return __builtin_bswap64(hll);+#else+ return ((uint64_t)hs_htonl(hll & 0xFFFFFFFF) << 32LL) | hs_htonl(hll >> 32);+#endif+}++ void hs_cryptohash_sha256_init (struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx));-+ ctx->h[0] = 0x6a09e667; ctx->h[1] = 0xbb67ae85; ctx->h[2] = 0x3c6ef372;@@ -69,7 +121,7 @@ int i; uint32_t w[64]; - cpu_to_be32_array(w, buf, 16);+ hs_htonl_array(w, buf, 16); for (i = 16; i < 64; i++) w[i] = s1(w[i - 2]) + w[i - 7] + s0(w[i - 15]) + w[i - 16]; @@ -100,12 +152,12 @@ } void-hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, uint32_t len)+hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, size_t len) {- uint32_t index, to_fill;+ size_t index, to_fill; /* check for partial buffer */- index = (uint32_t) (ctx->sz & 0x3f);+ index = (size_t) (ctx->sz & 0x3f); to_fill = 64 - index; ctx->sz += len;@@ -133,11 +185,10 @@ { static uint8_t padding[64] = { 0x80, }; uint64_t bits;- uint32_t i, index, padlen;- uint32_t *p = (uint32_t *) out;+ uint32_t index, padlen; /* cpu -> big endian */- bits = cpu_to_be64(ctx->sz << 3);+ bits = hs_htonll(ctx->sz << 3); /* pad out to 56 */ index = (uint32_t) (ctx->sz & 0x3f);@@ -148,6 +199,5 @@ hs_cryptohash_sha256_update(ctx, (uint8_t *) &bits, sizeof(bits)); /* store to digest */- for (i = 0; i < 8; i++)- p[i] = cpu_to_be32(ctx->h[i]);+ hs_htonl_array((uint32_t *) out, ctx->h, 8); }
cbits/sha256.h view
@@ -27,19 +27,21 @@ #define CRYPTOHASH_SHA256_H #include <stdint.h>+#include <stddef.h> struct sha256_ctx { uint64_t sz;- uint8_t buf[128];+ uint8_t buf[64]; uint32_t h[8]; }; +/* keep this synchronised with 'digestSize'/'sizeCtx' in SHA256.hs */ #define SHA256_DIGEST_SIZE 32-#define SHA256_CTX_SIZE sizeof(struct sha256_ctx)+#define SHA256_CTX_SIZE 104 void hs_cryptohash_sha256_init (struct sha256_ctx *ctx);-void hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, uint32_t len);+void hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, size_t len); void hs_cryptohash_sha256_finalize (struct sha256_ctx *ctx, uint8_t *out); #endif
+ changelog.md view
@@ -0,0 +1,11 @@+## 0.11.7.2++ - switch to 'safe' FFI for calls where overhead becomes neglible+ - removed inline assembly in favour of portable C constructs+ - fix 32bit length overflow bug in `hash` function+ - fix inaccurate context-size+ - add context-size verification to incremental API operations++## 0.11.7.1++ - first version forked off `cryptohash-0.11.7` release
cryptohash-sha256.cabal view
@@ -1,5 +1,5 @@ name: cryptohash-sha256-version: 0.11.7.1+version: 0.11.7.2 description: A practical incremental and one-pass, pure API to the <https://en.wikipedia.org/wiki/SHA-2 SHA-256 hash algorithm>@@ -7,7 +7,7 @@ . The implementation is made in C with a haskell FFI wrapper that hides the C implementation. .- NOTE: This package has been forked off @cryptohash-0.11.7@ because the @cryptohash@ has been+ NOTE: This package has been forked off @cryptohash-0.11.7@ because the @cryptohash@ package has been deprecated and so this package continues to satisfy the need for a lightweight package providing the SHA256 hash algorithm without any dependencies on packages other than @base@ and @bytestring@.@@ -31,8 +31,8 @@ , GHC == 7.10.3 , GHC == 8.0.1 -extra-source-files: cbits/bitfn.h- cbits/sha256.h+extra-source-files: cbits/sha256.h+ changelog.md source-repository head type: git@@ -43,39 +43,34 @@ build-depends: base >= 4.5 && < 4.10 , bytestring >= 0.9.2 && < 0.11 + hs-source-dirs: src exposed-modules: Crypto.Hash.SHA256 ghc-options: -Wall -fno-cse -O2 cc-options: -O3 c-sources: cbits/sha256.c include-dirs: cbits -test-suite test-kat+test-suite test-sha256 default-language: Haskell2010 type: exitcode-stdio-1.0- hs-source-dirs: Tests- main-is: KAT.hs+ hs-source-dirs: src-tests+ main-is: test-sha256.hs+ ghc-options: -Wall -threaded build-depends: cryptohash-sha256 , base , bytestring- , tasty == 0.11.*- , tasty-quickcheck == 0.8.*- , tasty-hunit == 0.9.* -benchmark bench-hashes- default-language: Haskell2010- type: exitcode-stdio-1.0- main-is: Bench.hs- hs-source-dirs: Bench- build-depends: cryptohash-sha256- , base- , bytestring- , criterion == 1.1.*+ , base16-bytestring >= 0.1.1 && < 0.2+ , SHA >= 1.6.4 && < 1.7+ , tasty == 0.11.*+ , tasty-quickcheck == 0.8.*+ , tasty-hunit == 0.9.* -benchmark bench-api+benchmark bench-sha256 default-language: Haskell2010 type: exitcode-stdio-1.0- main-is: BenchAPI.hs- hs-source-dirs: Bench+ main-is: bench-sha256.hs+ hs-source-dirs: src-bench build-depends: cryptohash-sha256 , base , bytestring
+ src-bench/bench-sha256.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE BangPatterns #-}++import Criterion.Main+import qualified Crypto.Hash.SHA256 as SHA256+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)+ where+ bs = 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)++ , L.length lbs64x256 `seq` bench "lbs64x256" (whnf SHA256.hashlazy lbs64x256)+ , L.length lbs64x4096 `seq` bench "lbs64x4096" (whnf SHA256.hashlazy lbs64x4096)+ ]+ ]
+ src-tests/test-sha256.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Base16 as B16++-- reference implementation+import qualified Data.Digest.Pure.SHA as REF++-- implementation under test+import qualified Crypto.Hash.SHA256 as IUT++import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck as QC++vectors :: [ByteString]+vectors =+ [ ""+ , "The quick brown fox jumps over the lazy dog"+ , "The quick brown fox jumps over the lazy cog"+ , "abc"+ , "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"+ , "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"+ , B.replicate 1000000 0x61+ ]++answers :: [ByteString]+answers = map (B.filter (/= 0x20))+ [ "e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855"+ , "d7a8fbb3 07d78094 69ca9abc b0082e4f 8d5651e4 6d3cdb76 2d02d0bf 37c9e592"+ , "e4c4d8f3 bf76b692 de791a17 3e053211 50f7a345 b46484fe 427f6acc 7ecc81be"+ , "ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad"+ , "248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1"+ , "cf5b16a7 78af8380 036ce59e 7b049237 0b249b11 e8f07a51 afac4503 7afee9d1"+ , "cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0"+ ]++ansXLTest :: ByteString+ansXLTest = B.filter (/= 0x20)+ "50e72a0e 26442fe2 552dc393 8ac58658 228c0cbf b1d2ca87 2ae43526 6fcd055e"++katTests :: [TestTree]+katTests+ | length vectors == length answers = map makeTest (zip3 [1::Int ..] vectors answers) ++ [xltest]+ | otherwise = error "vectors/answers length mismatch"+ where+ makeTest (i, v, r) = testGroup ("vec"++show i) $+ [ 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)+ , testCase "inc-4" (r @=? runTestInc 4 v)+ , testCase "inc-5" (r @=? runTestInc 5 v)+ , testCase "inc-7" (r @=? runTestInc 7 v)+ , testCase "inc-8" (r @=? runTestInc 8 v)+ , testCase "inc-9" (r @=? runTestInc 9 v)+ , testCase "inc-16" (r @=? runTestInc 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)+ ]++ runTest :: ByteString -> ByteString+ runTest = B16.encode . IUT.hash++ 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++ ----++ xltest = testGroup "XL-vec"+ [ testCase "inc" (ansXLTest @=? (B16.encode . IUT.hashlazy) vecXL) ]+ where+ vecXL = BL.fromChunks (replicate 16777216 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")++ splitB :: Int -> ByteString -> [ByteString]+ splitB l b+ | B.length b > l = b1 : splitB l b2+ | otherwise = [b]+ where+ (b1, b2) = B.splitAt l b+++-- define own 'foldl' here to avoid RULE rewriting to 'hashlazy'+myfoldl' :: (b -> a -> b) -> b -> [a] -> b+myfoldl' f z0 xs0 = lgo z0 xs0+ where+ lgo z [] = z+ lgo z (x:xs) = let z' = f z x+ in z' `seq` lgo z' xs++newtype RandBS = RandBS { unRandBS :: ByteString }+newtype RandLBS = RandLBS BL.ByteString++instance Arbitrary RandBS where+ arbitrary = fmap (RandBS . B.pack) arbitrary+ shrink (RandBS x) = fmap RandBS (go x)+ where+ go bs = zipWith B.append (B.inits bs) (tail $ B.tails bs)++instance Show RandBS where+ show (RandBS x) = "RandBS {len=" ++ show (B.length x)++"}"++instance Arbitrary RandLBS where+ arbitrary = fmap (RandLBS . BL.fromChunks . map unRandBS) arbitrary++instance Show RandLBS where+ show (RandLBS x) = "RandLBS {len=" ++ show (BL.length x) ++ ", chunks=" ++ show (length $ BL.toChunks x)++"}"+++refImplTests :: [TestTree]+refImplTests =+ [ testProperty "hash" prop_hash+ , testProperty "hashlazy" prop_hashlazy+ ]+ where+ prop_hash (RandBS bs)+ = ref_hash bs == IUT.hash bs++ prop_hashlazy (RandLBS bs)+ = ref_hashlazy bs == IUT.hashlazy bs++ ref_hash :: ByteString -> ByteString+ ref_hash = toStrict . REF.bytestringDigest . REF.sha256 . fromStrict++ ref_hashlazy :: BL.ByteString -> ByteString+ ref_hashlazy = toStrict . REF.bytestringDigest . REF.sha256++ -- toStrict/fromStrict only available with bytestring-0.10 and later+ toStrict = B.concat . BL.toChunks+ fromStrict = BL.fromChunks . (:[])++main :: IO ()+main = defaultMain $ testGroup "cryptohash-sha256"+ [ testGroup "KATs" katTests+ , testGroup "REF" refImplTests+ ]
+ src/Crypto/Hash/SHA256.hs view
@@ -0,0 +1,215 @@+-- |+-- Module : Crypto.Hash.SHA256+-- License : BSD-style+-- Maintainer : Herbert Valerio Riedel <hvr@gnu.org>+-- Stability : stable+-- Portability : unknown+--+-- 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++ -- * 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'+ --+ -- 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 -- :: ByteString -> ByteString+ ) where++import Prelude hiding (init)+import Foreign.C.Types+import Foreign.Ptr+import Foreign.ForeignPtr (withForeignPtr)+import Foreign.Marshal.Alloc+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as B+import Data.ByteString (ByteString)+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)+import Data.ByteString.Internal (create, toForeignPtr, memcpy)+import Data.Word+import System.IO.Unsafe (unsafeDupablePerformIO)++-- | perform IO for hashes that do allocation and ffi.+-- unsafeDupablePerformIO is used when possible as the+-- computation is pure and the output is directly linked+-- to the input. we also do not modify anything after it has+-- been returned to the user.+unsafeDoIO :: IO a -> a+unsafeDoIO = unsafeDupablePerformIO++-- | SHA-256 Context+--+-- The context data is exactly 104 bytes long, however+-- the data in the context is stored in host-endianness.+--+-- The context data is made up of+--+-- * a 'Word64' representing the number of bytes already feed to hash algorithm so far,+--+-- * a 64-element 'Word8' buffer holding partial input-chunks, and finally+--+-- * a 8-element 'Word32' array holding the current work-in-progress digest-value.+--+-- Consequently, a SHA-256 digest as produced by 'hash', 'hashlazy', or 'finalize' is 32 bytes long.+newtype Ctx = Ctx ByteString++-- keep this synchronised with cbits/sha256.h+{-# INLINE digestSize #-}+digestSize :: Int+digestSize = 32++{-# INLINE sizeCtx #-}+sizeCtx :: Int+sizeCtx = 104++{-# RULES "digestSize" B.length (finalize init) = digestSize #-}+{-# RULES "hash" forall b. finalize (update init b) = hash b #-}+{-# RULES "hash.list1" forall b. finalize (updates init [b]) = hash b #-}+{-# RULES "hashmany" forall b. finalize (foldl update init b) = hashlazy (L.fromChunks b) #-}+{-# RULES "hashlazy" forall b. finalize (foldl update init $ L.toChunks b) = hashlazy b #-}++{-# INLINE withByteStringPtr #-}+withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a+withByteStringPtr b f =+ withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)+ where (fptr, off, _) = toForeignPtr b++copyCtx :: Ptr Ctx -> Ptr Ctx -> IO ()+copyCtx dst src = memcpy (castPtr dst) (castPtr src) (fromIntegral sizeCtx)++withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx+withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx+ where+ createCtx = create sizeCtx $ \dstPtr ->+ withByteStringPtr ctxB $ \srcPtr -> do+ copyCtx (castPtr dstPtr) (castPtr srcPtr)+ f (castPtr dstPtr)++withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a+withCtxThrow (Ctx ctxB) f =+ allocaBytes sizeCtx $ \dstPtr ->+ withByteStringPtr ctxB $ \srcPtr -> do+ copyCtx (castPtr dstPtr) (castPtr srcPtr)+ f (castPtr dstPtr)++withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx+withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)++withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a+withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)++foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_init"+ c_sha256_init :: Ptr Ctx -> IO ()++foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_update"+ c_sha256_update_unsafe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()++foreign import ccall safe "sha256.h hs_cryptohash_sha256_update"+ c_sha256_update_safe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()++-- 'safe' call overhead neglible for 4KiB and more+c_sha256_update :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()+c_sha256_update pctx pbuf sz+ | sz < 4096 = c_sha256_update_unsafe pctx pbuf sz+ | otherwise = c_sha256_update_safe pctx pbuf sz++foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_finalize"+ c_sha256_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()++updateInternalIO :: Ptr Ctx -> ByteString -> IO ()+updateInternalIO ptr d =+ unsafeUseAsCStringLen d (\(cs, len) -> c_sha256_update ptr (castPtr cs) (fromIntegral len))++finalizeInternalIO :: Ptr Ctx -> IO ByteString+finalizeInternalIO ptr = create digestSize (c_sha256_finalize ptr)++{-# NOINLINE init #-}+-- | create a new hash context+init :: Ctx+init = unsafeDoIO $ withCtxNew $ c_sha256_init++validCtx :: Ctx -> Bool+validCtx (Ctx b) = B.length b == sizeCtx++{-# NOINLINE update #-}+-- | update a context with a bytestring+update :: Ctx -> ByteString -> Ctx+update ctx d+ | validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d+ | otherwise = error "SHA256.update: invalid Ctx"++{-# NOINLINE updates #-}+-- | updates a context with multiple bytestrings+updates :: Ctx -> [ByteString] -> Ctx+updates ctx d+ | validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> mapM_ (updateInternalIO ptr) d+ | otherwise = error "SHA256.updates: invalid Ctx"++{-# NOINLINE finalize #-}+-- | finalize the context into a digest bytestring (32 bytes)+finalize :: Ctx -> ByteString+finalize ctx+ | validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO+ | otherwise = error "SHA256.finalize: invalid Ctx"++{-# NOINLINE hash #-}+-- | hash a strict bytestring into a digest bytestring (32 bytes)+hash :: ByteString -> ByteString+hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do+ c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr++{-# NOINLINE hashlazy #-}+-- | hash a lazy bytestring into a digest bytestring (32 bytes)+hashlazy :: L.ByteString -> ByteString+hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do+ c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr