cryptohash-sha256 0.11.7.2 → 0.11.100.0
raw patch · 5 files changed
+230/−102 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Crypto.Hash.SHA256: hmac :: ByteString -> ByteString -> ByteString
+ Crypto.Hash.SHA256: hmaclazy :: ByteString -> ByteString -> ByteString
Files
- cbits/sha256.c +117/−90
- changelog.md +6/−0
- cryptohash-sha256.cabal +3/−2
- src-tests/test-sha256.hs +58/−9
- src/Crypto/Hash/SHA256.hs +46/−1
cbits/sha256.c view
@@ -32,26 +32,28 @@ #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+/* 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 +#define ptr_uint32_aligned(ptr) (!((uintptr_t)(ptr) & 0x3))+ static inline uint32_t-ror32(uint32_t word, uint32_t shift)+ror32(const uint32_t word, const unsigned shift) {- // GCC usually transforms this into a 'ror'-insn+ /* GCC usually transforms this into a 'ror'-insn */ return (word >> shift) | (word << (32 - shift)); } static inline uint32_t-hs_htonl(uint32_t hl)+cpu_to_be32(const 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+ /* GCC usually transforms this into a bswap insn */ return ((hl & 0xff000000) >> 24) | ((hl & 0x00ff0000) >> 8) | ((hl & 0x0000ff00) << 8) |@@ -60,21 +62,21 @@ } static inline void-hs_htonl_array(uint32_t *dest, const uint32_t *src, unsigned wordcnt)+cpu_to_be32_array(uint32_t *dest, const uint32_t *src, unsigned wordcnt) { while (wordcnt--)- *dest++ = hs_htonl(*src++);+ *dest++ = cpu_to_be32(*src++); } static inline uint64_t-hs_htonll(uint64_t hll)+cpu_to_be64(const 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);+ return ((uint64_t)cpu_to_be32(hll & 0xffffffff) << 32LL) | cpu_to_be32(hll >> 32); #endif } @@ -82,31 +84,31 @@ 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;- ctx->h[3] = 0xa54ff53a;- ctx->h[4] = 0x510e527f;- ctx->h[5] = 0x9b05688c;- ctx->h[6] = 0x1f83d9ab;- ctx->h[7] = 0x5be0cd19;+ memset(ctx, 0, SHA256_CTX_SIZE);++ ctx->h[0] = 0x6a09e667;+ ctx->h[1] = 0xbb67ae85;+ ctx->h[2] = 0x3c6ef372;+ ctx->h[3] = 0xa54ff53a;+ ctx->h[4] = 0x510e527f;+ ctx->h[5] = 0x9b05688c;+ ctx->h[6] = 0x1f83d9ab;+ ctx->h[7] = 0x5be0cd19; } /* 232 times the cube root of the first 64 primes 2..311 */ static const uint32_t k[] = {- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,- 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,- 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,- 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,- 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,- 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; #define e0(x) (ror32(x, 2) ^ ror32(x,13) ^ ror32(x,22))@@ -115,89 +117,114 @@ #define s1(x) (ror32(x,17) ^ ror32(x,19) ^ (x >> 10)) static void-sha256_do_chunk(struct sha256_ctx *ctx, uint32_t buf[])+sha256_do_chunk_aligned(struct sha256_ctx *ctx, uint32_t w[]) {- uint32_t a, b, c, d, e, f, g, h, t1, t2;- int i;- uint32_t w[64];+ int i; - 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];+ for (i = 16; i < 64; i++)+ w[i] = s1(w[i - 2]) + w[i - 7] + s0(w[i - 15]) + w[i - 16]; - a = ctx->h[0]; b = ctx->h[1]; c = ctx->h[2]; d = ctx->h[3];- e = ctx->h[4]; f = ctx->h[5]; g = ctx->h[6]; h = ctx->h[7];+ uint32_t a = ctx->h[0];+ uint32_t b = ctx->h[1];+ uint32_t c = ctx->h[2];+ uint32_t d = ctx->h[3];+ uint32_t e = ctx->h[4];+ uint32_t f = ctx->h[5];+ uint32_t g = ctx->h[6];+ uint32_t h = ctx->h[7]; -#define R(a, b, c, d, e, f, g, h, k, w) \- t1 = h + e1(e) + (g ^ (e & (f ^ g))) + k + w; \- t2 = e0(a) + ((a & b) | (c & (a | b))); \- d += t1; \- h = t1 + t2;+#define R(a, b, c, d, e, f, g, h, k, w) \+ t1 = h + e1(e) + (g ^ (e & (f ^ g))) + k + w; \+ t2 = e0(a) + ((a & b) | (c & (a | b))); \+ d += t1; \+ h = t1 + t2; - for (i = 0; i < 64; i += 8) {- R(a, b, c, d, e, f, g, h, k[i + 0], w[i + 0]);- R(h, a, b, c, d, e, f, g, k[i + 1], w[i + 1]);- R(g, h, a, b, c, d, e, f, k[i + 2], w[i + 2]);- R(f, g, h, a, b, c, d, e, k[i + 3], w[i + 3]);- R(e, f, g, h, a, b, c, d, k[i + 4], w[i + 4]);- R(d, e, f, g, h, a, b, c, k[i + 5], w[i + 5]);- R(c, d, e, f, g, h, a, b, k[i + 6], w[i + 6]);- R(b, c, d, e, f, g, h, a, k[i + 7], w[i + 7]);- }+ for (i = 0; i < 64; i += 8) {+ uint32_t t1, t2; + R(a, b, c, d, e, f, g, h, k[i + 0], w[i + 0]);+ R(h, a, b, c, d, e, f, g, k[i + 1], w[i + 1]);+ R(g, h, a, b, c, d, e, f, k[i + 2], w[i + 2]);+ R(f, g, h, a, b, c, d, e, k[i + 3], w[i + 3]);+ R(e, f, g, h, a, b, c, d, k[i + 4], w[i + 4]);+ R(d, e, f, g, h, a, b, c, k[i + 5], w[i + 5]);+ R(c, d, e, f, g, h, a, b, k[i + 6], w[i + 6]);+ R(b, c, d, e, f, g, h, a, k[i + 7], w[i + 7]);+ }+ #undef R - ctx->h[0] += a; ctx->h[1] += b; ctx->h[2] += c; ctx->h[3] += d;- ctx->h[4] += e; ctx->h[5] += f; ctx->h[6] += g; ctx->h[7] += h;+ ctx->h[0] += a;+ ctx->h[1] += b;+ ctx->h[2] += c;+ ctx->h[3] += d;+ ctx->h[4] += e;+ ctx->h[5] += f;+ ctx->h[6] += g;+ ctx->h[7] += h; } -void-hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, size_t len)+static void+sha256_do_chunk(struct sha256_ctx *ctx, const uint8_t buf[]) {- size_t index, to_fill;+ uint32_t w[64]; /* only first 16 words are filled in */+ if (ptr_uint32_aligned(buf)) { /* aligned buf */+ cpu_to_be32_array(w, (const uint32_t *)buf, 16);+ } else { /* unaligned buf */+ memcpy(w, buf, 64);+#if !WORDS_BIGENDIAN+ cpu_to_be32_array(w, w, 16);+#endif+ }+ sha256_do_chunk_aligned(ctx, w);+} - /* check for partial buffer */- index = (size_t) (ctx->sz & 0x3f);- to_fill = 64 - index;+void+hs_cryptohash_sha256_update(struct sha256_ctx *ctx, const uint8_t *data, size_t len)+{+ size_t index = ctx->sz & 0x3f;+ const size_t to_fill = 64 - index; - ctx->sz += len;+ ctx->sz += len; - /* process partial buffer if there's enough data to make a block */- if (index && len >= to_fill) {- memcpy(ctx->buf + index, data, to_fill);- sha256_do_chunk(ctx, (uint32_t *) ctx->buf);- len -= to_fill;- data += to_fill;- index = 0;- }+ /* process partial buffer if there's enough data to make a block */+ if (index && len >= to_fill) {+ memcpy(ctx->buf + index, data, to_fill);+ sha256_do_chunk(ctx, ctx->buf);+ /* memset(ctx->buf, 0, 64); */+ len -= to_fill;+ data += to_fill;+ index = 0;+ } - /* process as much 64-block as possible */- for (; len >= 64; len -= 64, data += 64)- sha256_do_chunk(ctx, (uint32_t *) data);+ /* process as many 64-blocks as possible */+ while (len >= 64) {+ sha256_do_chunk(ctx, data);+ len -= 64;+ data += 64;+ } - /* append data into buf */- if (len)- memcpy(ctx->buf + index, data, len);+ /* append data into buf */+ if (len)+ memcpy(ctx->buf + index, data, len); } void hs_cryptohash_sha256_finalize (struct sha256_ctx *ctx, uint8_t *out) {- static uint8_t padding[64] = { 0x80, };- uint64_t bits;- uint32_t index, padlen;+ static const uint8_t padding[64] = { 0x80, }; - /* cpu -> big endian */- bits = hs_htonll(ctx->sz << 3);+ /* add padding and update data with it */+ uint64_t bits = cpu_to_be64(ctx->sz << 3); - /* pad out to 56 */- index = (uint32_t) (ctx->sz & 0x3f);- padlen = (index < 56) ? (56 - index) : ((64 + 56) - index);- hs_cryptohash_sha256_update(ctx, padding, padlen);+ /* pad out to 56 */+ const size_t index = ctx->sz & 0x3f;+ const size_t padlen = (index < 56) ? (56 - index) : ((64 + 56) - index);+ hs_cryptohash_sha256_update(ctx, padding, padlen); - /* append length */- hs_cryptohash_sha256_update(ctx, (uint8_t *) &bits, sizeof(bits));+ /* append length */+ hs_cryptohash_sha256_update(ctx, (uint8_t *) &bits, sizeof(bits)); - /* store to digest */- hs_htonl_array((uint32_t *) out, ctx->h, 8);+ /* output hash */+ cpu_to_be32_array((uint32_t *) out, ctx->h, 8); }
changelog.md view
@@ -1,3 +1,9 @@+## 0.11.100.0++ - new `hmac` and `hmaclazy` functions providing HMAC-SHA1+ computation conforming to RFC2104 and RFC4231+ - fix unaligned memory-accesses+ ## 0.11.7.2 - switch to 'safe' FFI for calls where overhead becomes neglible
cryptohash-sha256.cabal view
@@ -1,8 +1,9 @@ name: cryptohash-sha256-version: 0.11.7.2+version: 0.11.100.0 description: A practical incremental and one-pass, pure API to the <https://en.wikipedia.org/wiki/SHA-2 SHA-256 hash algorithm>+ (including <https://en.wikipedia.org/wiki/HMAC HMAC> support) with performance close to the fastest implementations available in other languages. . The implementation is made in C with a haskell FFI wrapper that hides the C implementation.@@ -46,7 +47,7 @@ hs-source-dirs: src exposed-modules: Crypto.Hash.SHA256 ghc-options: -Wall -fno-cse -O2- cc-options: -O3+ cc-options: -Wall -O3 c-sources: cbits/sha256.c include-dirs: cbits
src-tests/test-sha256.hs view
@@ -8,14 +8,14 @@ import qualified Data.ByteString.Base16 as B16 -- reference implementation-import qualified Data.Digest.Pure.SHA as REF+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+import Test.Tasty.QuickCheck as QC vectors :: [ByteString] vectors =@@ -64,7 +64,11 @@ , 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 ] runTest :: ByteString -> ByteString runTest = B16.encode . IUT.hash@@ -75,6 +79,10 @@ runTestLazy :: Int -> ByteString -> ByteString runTestLazy i = B16.encode . IUT.hashlazy . 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+ ---- xltest = testGroup "XL-vec"@@ -82,13 +90,39 @@ where vecXL = BL.fromChunks (replicate 16777216 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno") - splitB :: Int -> ByteString -> [ByteString]- splitB l b- | B.length b > l = b1 : splitB l b2- | otherwise = [b]+splitB :: Int -> ByteString -> [ByteString]+splitB l b+ | B.length b > l = b1 : splitB l b2+ | otherwise = [b]+ where+ (b1, b2) = B.splitAt l b+++rfc4231Vectors :: [(ByteString,ByteString,ByteString)]+rfc4231Vectors = -- (secrect,msg,mac)+ [ (rep 20 0x0b, "Hi There", x"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")+ , ("Jefe", "what do ya want for nothing?", x"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843")+ , (rep 20 0xaa, rep 50 0xdd, x"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe")+ , (B.pack [1..25], rep 50 0xcd, x"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b")+ , (rep 20 0x0c, "Test With Truncation", x"a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5")+ , (rep 131 0xaa, "Test Using Larger Than Block-Size Key - Hash Key First", x"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54")+ , (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+ rep n c = B.replicate n c++rfc4231Tests :: [TestTree]+rfc4231Tests = zipWith makeTest [1::Int ..] rfc4231Vectors+ where+ makeTest i (key, msg, mac) = testGroup ("vec"++show i) $+ [ testCase "hmac" (hex mac @=? hex (IUT.hmac key msg))+ , testCase "hmaclazy" (hex mac @=? hex (IUT.hmaclazy key lazymsg))+ ] where- (b1, b2) = B.splitAt l b+ lazymsg = BL.fromChunks . splitB 1 $ msg + hex = B16.encode -- define own 'foldl' here to avoid RULE rewriting to 'hashlazy' myfoldl' :: (b -> a -> b) -> b -> [a] -> b@@ -121,6 +155,8 @@ refImplTests = [ testProperty "hash" prop_hash , testProperty "hashlazy" prop_hashlazy+ , testProperty "hmac" prop_hmac+ , testProperty "hmaclazy" prop_hmaclazy ] where prop_hash (RandBS bs)@@ -129,12 +165,24 @@ prop_hashlazy (RandLBS bs) = ref_hashlazy bs == IUT.hashlazy bs + prop_hmac (RandBS k) (RandBS bs)+ = ref_hmac k bs == IUT.hmac k bs++ prop_hmaclazy (RandBS k) (RandLBS bs)+ = ref_hmaclazy k bs == IUT.hmaclazy k bs+ ref_hash :: ByteString -> ByteString- ref_hash = toStrict . REF.bytestringDigest . REF.sha256 . fromStrict+ ref_hash = ref_hashlazy . fromStrict ref_hashlazy :: BL.ByteString -> ByteString ref_hashlazy = toStrict . REF.bytestringDigest . REF.sha256 + ref_hmac :: ByteString -> ByteString -> ByteString+ ref_hmac secret = ref_hmaclazy secret . fromStrict++ ref_hmaclazy :: ByteString -> BL.ByteString -> ByteString+ ref_hmaclazy secret = toStrict . REF.bytestringDigest . REF.hmacSha256 (fromStrict secret)+ -- toStrict/fromStrict only available with bytestring-0.10 and later toStrict = B.concat . BL.toChunks fromStrict = BL.fromChunks . (:[])@@ -142,5 +190,6 @@ main :: IO () main = defaultMain $ testGroup "cryptohash-sha256" [ testGroup "KATs" katTests+ , testGroup "RFC4231" rfc4231Tests , testGroup "REF" refImplTests ]
src/Crypto/Hash/SHA256.hs view
@@ -63,7 +63,15 @@ -- package is recommended. , hash -- :: ByteString -> ByteString- , hashlazy -- :: ByteString -> ByteString+ , hashlazy -- :: L.ByteString -> ByteString++ -- ** 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 ) where import Prelude hiding (init)@@ -76,6 +84,7 @@ import Data.ByteString (ByteString) import Data.ByteString.Unsafe (unsafeUseAsCStringLen) import Data.ByteString.Internal (create, toForeignPtr, memcpy)+import Data.Bits (xor) import Data.Word import System.IO.Unsafe (unsafeDupablePerformIO) @@ -213,3 +222,39 @@ hashlazy :: L.ByteString -> ByteString hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr+++{-# NOINLINE hmac #-}+-- | Compute 32-byte <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible+-- HMAC-SHA1 digest for a strict bytestring message+--+-- @since 0.11.100.0+hmac :: ByteString -- ^ secret+ -> ByteString -- ^ message+ -> ByteString+hmac secret msg = hash $ B.append opad (hash $ B.append ipad msg)+ where+ opad = B.map (xor 0x5c) k'+ ipad = B.map (xor 0x36) k'++ k' = B.append kt pad+ kt = if B.length secret > 64 then hash secret else secret+ pad = B.replicate (64 - B.length kt) 0+++{-# NOINLINE hmaclazy #-}+-- | Compute 32-byte <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible+-- HMAC-SHA1 digest for a lazy bytestring message+--+-- @since 0.11.100.0+hmaclazy :: ByteString -- ^ secret+ -> L.ByteString -- ^ message+ -> ByteString+hmaclazy secret msg = hash $ B.append opad (hashlazy $ L.append ipad msg)+ where+ opad = B.map (xor 0x5c) k'+ ipad = L.fromChunks [B.map (xor 0x36) k']++ k' = B.append kt pad+ kt = if B.length secret > 64 then hash secret else secret+ pad = B.replicate (64 - B.length kt) 0