cryptohash-sha512 0.11.100.1 → 0.11.101.0
raw patch · 8 files changed
+485/−296 lines, 8 filesdep ~basedep ~base16-bytestringdep ~bytestringnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, base16-bytestring, bytestring, criterion, tasty, tasty-hunit, tasty-quickcheck
API changes (from Hackage documentation)
+ Crypto.Hash.SHA512: finalizeAndLength :: Ctx -> (ByteString, Word64)
+ Crypto.Hash.SHA512: hashlazyAndLength :: ByteString -> (ByteString, Word64)
+ Crypto.Hash.SHA512: hmaclazyAndLength :: ByteString -> ByteString -> (ByteString, Word64)
+ Crypto.Hash.SHA512: start :: ByteString -> Ctx
+ Crypto.Hash.SHA512: startlazy :: ByteString -> Ctx
Files
- cbits/sha512.c +0/−242
- cbits/sha512.h +233/−3
- changelog.md +15/−0
- cryptohash-sha512.cabal +19/−12
- src-tests/test-sha512.hs +52/−3
- src/Compat.hs +24/−0
- src/Crypto/Hash/SHA512.hs +81/−36
- src/Crypto/Hash/SHA512/FFI.hs +61/−0
− cbits/sha512.c
@@ -1,242 +0,0 @@-/*- * Copyright (C) 2006-2009 Vincent Hanquez <vincent@snarc.org>- * 2016 Herbert Valerio Riedel <hvr@gnu.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.- */--#include "sha512.h"--#include <assert.h>-#include <string.h>-#include <ghcautoconf.h>--#if defined(static_assert)-static_assert(sizeof(struct sha512_ctx) == SHA512_CTX_SIZE, "unexpected sha512_ctx size");-#else-/* poor man's pre-C11 _Static_assert */-typedef char static_assertion__unexpected_sha512_ctx_size[(sizeof(struct sha512_ctx) == SHA512_CTX_SIZE)?1:-1];-#endif--#define ptr_uint64_aligned(ptr) (!((uintptr_t)(ptr) & 0x7))--static inline uint64_t-ror64(const uint64_t word, const unsigned shift)-{- /* GCC usually transforms this into a 'ror'-insn */- return (word >> shift) | (word << (64 - shift));-}--static inline uint64_t-cpu_to_be64(const uint64_t hll)-{-#if WORDS_BIGENDIAN- return hll;-#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)- return __builtin_bswap64(hll);-#else- /* GCC & Clang usually transforms this into a bswap insn */- return ((hll & 0xff00000000000000) >> 56) |- ((hll & 0x00ff000000000000) >> 40) |- ((hll & 0x0000ff0000000000) >> 24) |- ((hll & 0x000000ff00000000) >> 8) |- ((hll & 0x00000000ff000000) << 8) |- ((hll & 0x0000000000ff0000) << 24) |- ((hll & 0x000000000000ff00) << 40) |- ((hll & 0x00000000000000ff) << 56);-#endif-}--static inline void-cpu_to_be64_array(uint64_t *dest, const uint64_t *src, unsigned wordcnt)-{- while (wordcnt--)- *dest++ = cpu_to_be64(*src++);-}--void-hs_cryptohash_sha512_init (struct sha512_ctx *ctx)-{- memset(ctx, 0, SHA512_CTX_SIZE);- - ctx->h[0] = 0x6a09e667f3bcc908ULL;- ctx->h[1] = 0xbb67ae8584caa73bULL;- ctx->h[2] = 0x3c6ef372fe94f82bULL;- ctx->h[3] = 0xa54ff53a5f1d36f1ULL;- ctx->h[4] = 0x510e527fade682d1ULL;- ctx->h[5] = 0x9b05688c2b3e6c1fULL;- ctx->h[6] = 0x1f83d9abfb41bd6bULL;- ctx->h[7] = 0x5be0cd19137e2179ULL;-}--/* 232 times the cube root of the first 64 primes 2..311 */-static const uint64_t k[] = {- 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,- 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,- 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,- 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,- 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,- 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,- 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,- 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,- 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,- 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,- 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,- 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,- 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,- 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,- 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,- 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,- 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,- 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,- 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,- 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,- 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,- 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,- 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,- 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,- 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,- 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,- 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL-};--#define e0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39))-#define e1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41))-#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7))-#define s1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6))--static void-sha512_do_chunk_aligned(struct sha512_ctx *ctx, uint64_t w[])-{- int i;- - for (i = 16; i < 80; i++)- w[i] = s1(w[i - 2]) + w[i - 7] + s0(w[i - 15]) + w[i - 16];-- uint64_t a = ctx->h[0];- uint64_t b = ctx->h[1];- uint64_t c = ctx->h[2];- uint64_t d = ctx->h[3];- uint64_t e = ctx->h[4];- uint64_t f = ctx->h[5];- uint64_t g = ctx->h[6];- uint64_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-- for (i = 0; i < 80; i += 8) {- uint64_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;-}--static void-sha512_do_chunk(struct sha512_ctx *ctx, const uint8_t buf[])-{- uint64_t w[80]; /* only first 16 words are filled in */- if (ptr_uint64_aligned(buf)) { /* aligned buf */- cpu_to_be64_array(w, (const uint64_t *)buf, 16);- } else { /* unaligned buf */- memcpy(w, buf, 128);-#if !WORDS_BIGENDIAN- cpu_to_be64_array(w, w, 16);-#endif- }- sha512_do_chunk_aligned(ctx, w);-}--void-hs_cryptohash_sha512_update(struct sha512_ctx *ctx, const uint8_t *data, size_t len)-{- size_t index = ctx->sz & 0x7f;- const size_t to_fill = 128 - index;-- ctx->sz += len;- // handle overflow- if (ctx->sz < len)- ctx->sz_hi++;-- /* process partial buffer if there's enough data to make a block */- if (index && len >= to_fill) {- memcpy(ctx->buf + index, data, to_fill);- sha512_do_chunk(ctx, ctx->buf);- /* memset(ctx->buf, 0, 128); */- len -= to_fill;- data += to_fill;- index = 0;- }-- /* process as many 128b-blocks as possible */- while (len >= 128) {- sha512_do_chunk(ctx, data);- len -= 128;- data += 128;- }-- /* append data into buf */- if (len)- memcpy(ctx->buf + index, data, len);-}--void-hs_cryptohash_sha512_finalize (struct sha512_ctx *ctx, uint8_t *out)-{- static const uint8_t padding[128] = { 0x80, };-- /* add padding and update data with it */- uint64_t bits[2];- bits[0] = cpu_to_be64((ctx->sz_hi << 3) | (ctx->sz >> 61));- bits[1] = cpu_to_be64(ctx->sz << 3);-- /* pad out to 112 */- const size_t index = ctx->sz & 0x7f;- const size_t padlen = (index < 112) ? (112 - index) : ((128 + 112) - index);- hs_cryptohash_sha512_update(ctx, padding, padlen);-- /* append length */- hs_cryptohash_sha512_update(ctx, (uint8_t *) bits, sizeof(bits));-- /* output hash */- cpu_to_be64_array((uint64_t *) out, ctx->h, 8);-}
cbits/sha512.h view
@@ -28,6 +28,9 @@ #include <stdint.h> #include <stddef.h>+#include <assert.h>+#include <string.h>+#include <ghcautoconf.h> struct sha512_ctx {@@ -41,8 +44,235 @@ #define SHA512_DIGEST_SIZE 64 #define SHA512_CTX_SIZE 208 -void hs_cryptohash_sha512_init (struct sha512_ctx *ctx);-void hs_cryptohash_sha512_update (struct sha512_ctx *ctx, const uint8_t *data, size_t len);-void hs_cryptohash_sha512_finalize (struct sha512_ctx *ctx, uint8_t *out);+static inline void hs_cryptohash_sha512_init (struct sha512_ctx *ctx);+static inline void hs_cryptohash_sha512_update (struct sha512_ctx *ctx, const uint8_t *data, size_t len);+static inline uint64_t hs_cryptohash_sha512_finalize (struct sha512_ctx *ctx, uint8_t *out);++#if defined(static_assert)+static_assert(sizeof(struct sha512_ctx) == SHA512_CTX_SIZE, "unexpected sha512_ctx size");+#else+/* poor man's pre-C11 _Static_assert */+typedef char static_assertion__unexpected_sha512_ctx_size[(sizeof(struct sha512_ctx) == SHA512_CTX_SIZE)?1:-1];+#endif++#define ptr_uint64_aligned(ptr) (!((uintptr_t)(ptr) & 0x7))++static inline uint64_t+ror64(const uint64_t word, const unsigned shift)+{+ /* GCC usually transforms this into a 'ror'-insn */+ return (word >> shift) | (word << (64 - shift));+}++static inline uint64_t+cpu_to_be64(const uint64_t hll)+{+#if WORDS_BIGENDIAN+ return hll;+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)+ return __builtin_bswap64(hll);+#else+ /* GCC & Clang usually transforms this into a bswap insn */+ return ((hll & 0xff00000000000000) >> 56) |+ ((hll & 0x00ff000000000000) >> 40) |+ ((hll & 0x0000ff0000000000) >> 24) |+ ((hll & 0x000000ff00000000) >> 8) |+ ((hll & 0x00000000ff000000) << 8) |+ ((hll & 0x0000000000ff0000) << 24) |+ ((hll & 0x000000000000ff00) << 40) |+ ((hll & 0x00000000000000ff) << 56);+#endif+}++static inline void+cpu_to_be64_array(uint64_t *dest, const uint64_t *src, unsigned wordcnt)+{+ while (wordcnt--)+ *dest++ = cpu_to_be64(*src++);+}++static inline void+hs_cryptohash_sha512_init (struct sha512_ctx *ctx)+{+ memset(ctx, 0, SHA512_CTX_SIZE);+ + ctx->h[0] = 0x6a09e667f3bcc908ULL;+ ctx->h[1] = 0xbb67ae8584caa73bULL;+ ctx->h[2] = 0x3c6ef372fe94f82bULL;+ ctx->h[3] = 0xa54ff53a5f1d36f1ULL;+ ctx->h[4] = 0x510e527fade682d1ULL;+ ctx->h[5] = 0x9b05688c2b3e6c1fULL;+ ctx->h[6] = 0x1f83d9abfb41bd6bULL;+ ctx->h[7] = 0x5be0cd19137e2179ULL;+}++/* 232 times the cube root of the first 64 primes 2..311 */+static const uint64_t k[] = {+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,+ 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,+ 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,+ 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,+ 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,+ 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,+ 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,+ 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,+ 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,+ 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,+ 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,+ 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,+ 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,+ 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL+};++#define e0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39))+#define e1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41))+#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7))+#define s1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6))++static void+sha512_do_chunk_aligned(struct sha512_ctx *ctx, uint64_t w[])+{+ int i;+ + for (i = 16; i < 80; i++)+ w[i] = s1(w[i - 2]) + w[i - 7] + s0(w[i - 15]) + w[i - 16];++ uint64_t a = ctx->h[0];+ uint64_t b = ctx->h[1];+ uint64_t c = ctx->h[2];+ uint64_t d = ctx->h[3];+ uint64_t e = ctx->h[4];+ uint64_t f = ctx->h[5];+ uint64_t g = ctx->h[6];+ uint64_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++ for (i = 0; i < 80; i += 8) {+ uint64_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;+}++static void+sha512_do_chunk(struct sha512_ctx *ctx, const uint8_t buf[])+{+ uint64_t w[80]; /* only first 16 words are filled in */+ if (ptr_uint64_aligned(buf)) { /* aligned buf */+ cpu_to_be64_array(w, (const uint64_t *)buf, 16);+ } else { /* unaligned buf */+ memcpy(w, buf, 128);+#if !WORDS_BIGENDIAN+ cpu_to_be64_array(w, w, 16);+#endif+ }+ sha512_do_chunk_aligned(ctx, w);+}++static inline void+hs_cryptohash_sha512_update(struct sha512_ctx *ctx, const uint8_t *data, size_t len)+{+ size_t index = ctx->sz & 0x7f;+ const size_t to_fill = 128 - index;++ ctx->sz += len;+ // handle overflow+ if (ctx->sz < len)+ ctx->sz_hi++;++ /* process partial buffer if there's enough data to make a block */+ if (index && len >= to_fill) {+ memcpy(ctx->buf + index, data, to_fill);+ sha512_do_chunk(ctx, ctx->buf);+ /* memset(ctx->buf, 0, 128); */+ len -= to_fill;+ data += to_fill;+ index = 0;+ }++ /* process as many 128b-blocks as possible */+ while (len >= 128) {+ sha512_do_chunk(ctx, data);+ len -= 128;+ data += 128;+ }++ /* append data into buf */+ if (len)+ memcpy(ctx->buf + index, data, len);+}++static inline uint64_t+hs_cryptohash_sha512_finalize (struct sha512_ctx *ctx, uint8_t *out)+{+ static const uint8_t padding[128] = { 0x80, };+ const uint64_t sz = ctx->sz;++ /* add padding and update data with it */+ uint64_t bits[2];+ bits[0] = cpu_to_be64((ctx->sz_hi << 3) | (ctx->sz >> 61));+ bits[1] = cpu_to_be64(ctx->sz << 3);++ /* pad out to 112 */+ const size_t index = ctx->sz & 0x7f;+ const size_t padlen = (index < 112) ? (112 - index) : ((128 + 112) - index);+ hs_cryptohash_sha512_update(ctx, padding, padlen);++ /* append length */+ hs_cryptohash_sha512_update(ctx, (uint8_t *) bits, sizeof(bits));++ /* output hash */+ cpu_to_be64_array((uint64_t *) out, ctx->h, 8);++ return sz;+}++static inline void+hs_cryptohash_sha512_hash (const uint8_t *data, size_t len, uint8_t *out)+{+ struct sha512_ctx ctx;++ hs_cryptohash_sha512_init(&ctx);++ hs_cryptohash_sha512_update(&ctx, data, len);++ hs_cryptohash_sha512_finalize(&ctx, out);+} #endif
changelog.md view
@@ -1,3 +1,18 @@+## 0.11.101.0++ - Add Eq instance for Ctx+ - add start and startlazy producing Ctx+ - Remove ineffective RULES+ - Declare `Crypto.Hash.SHA512` module `-XTrustworthy`+ - Convert to `CApiFFI`+ - Added `...AndLength` variants of hashing functions:++ - `finalizeAndLength`+ - `hashlazyAndLength`+ - `hmaclazyAndLength`++ - Minor optimizations in `hmac` and `hash`+ ## 0.11.100.1 - First public release
cryptohash-sha512.cabal view
@@ -1,5 +1,5 @@ name: cryptohash-sha512-version: 0.11.100.1+version: 0.11.101.0 description: A practical incremental and one-pass, pure API to the <https://en.wikipedia.org/wiki/SHA-2 SHA-512 hash algorithm>@@ -30,7 +30,14 @@ , GHC == 7.6.3 , GHC == 7.8.4 , GHC == 7.10.3- , GHC == 8.0.1+ , GHC == 8.0.2+ , GHC == 8.2.2+ , GHC == 8.4.4+ , GHC == 8.6.5+ , GHC == 8.8.4+ , GHC == 8.10.4+ , GHC == 9.0.1+ , GHC == 9.2.1 extra-source-files: cbits/sha512.h changelog.md@@ -41,14 +48,14 @@ library default-language: Haskell2010- build-depends: base >= 4.5 && < 4.10- , bytestring >= 0.9.2 && < 0.11+ build-depends: base >= 4.5 && < 4.17+ , bytestring >= 0.9.2 && < 0.12 hs-source-dirs: src exposed-modules: Crypto.Hash.SHA512+ other-modules: Crypto.Hash.SHA512.FFI Compat ghc-options: -Wall -fno-cse -O2- cc-options: -Wall -O3- c-sources: cbits/sha512.c+ cc-options: -Wall include-dirs: cbits test-suite test-sha512@@ -61,11 +68,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 >= 1.0.1.0 && < 1.1+ , SHA >= 1.6.4 && < 1.7+ , tasty >= 1.4 && < 1.5+ , tasty-quickcheck >= 0.10 && < 0.11+ , tasty-hunit >= 0.10 && < 0.11 benchmark bench-sha512 default-language: Haskell2010@@ -75,4 +82,4 @@ build-depends: cryptohash-sha512 , base , bytestring- , criterion == 1.1.*+ , criterion >= 1.5 && <1.6
src-tests/test-sha512.hs view
@@ -2,6 +2,7 @@ module Main (main) where +import Data.Word (Word64) import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL@@ -45,11 +46,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)@@ -64,25 +66,44 @@ , 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"@@ -90,6 +111,12 @@ 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@@ -109,7 +136,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"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58") ] where- x = fst.B16.decode+ x = B16.decodeLenient rep n c = B.replicate n c rfc4231Tests :: [TestTree]@@ -154,34 +181,56 @@ 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+ , testProperty "hmaclazyAndLength" prop_hmaclazyAndLength ] where 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_hashlazyAndLength (RandLBS bs)+ = ref_hashlazyAndLength bs == IUT.hashlazyAndLength bs++ prop_startlazy (RandLBS bs)+ = ref_hashlazy bs == (IUT.finalize $ IUT.startlazy 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 + prop_hmaclazyAndLength (RandBS k) (RandLBS bs)+ = ref_hmaclazyAndLength k bs == IUT.hmaclazyAndLength k bs+ ref_hash :: ByteString -> ByteString ref_hash = ref_hashlazy . fromStrict ref_hashlazy :: BL.ByteString -> ByteString ref_hashlazy = toStrict . REF.bytestringDigest . REF.sha512 + ref_hashlazyAndLength :: BL.ByteString -> (ByteString,Word64)+ ref_hashlazyAndLength x = (ref_hashlazy x, fromIntegral (BL.length x))+ 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.hmacSha512 (fromStrict secret)++ ref_hmaclazyAndLength :: ByteString -> BL.ByteString -> (ByteString,Word64)+ ref_hmaclazyAndLength secret msg = (ref_hmaclazy secret msg, fromIntegral (BL.length msg)) -- toStrict/fromStrict only available with bytestring-0.10 and later toStrict = B.concat . BL.toChunks
+ 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/SHA512.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE Trustworthy #-} -- | -- Module : Crypto.Hash.SHA512 -- License : BSD-style@@ -40,7 +41,11 @@ , 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@@ -64,6 +69,7 @@ , hash -- :: ByteString -> ByteString , hashlazy -- :: L.ByteString -> ByteString+ , hashlazyAndLength -- :: L.ByteString -> (ByteString,Word64) -- ** HMAC-SHA-512 --@@ -72,6 +78,7 @@ , hmac -- :: ByteString -> ByteString -> ByteString , hmaclazy -- :: ByteString -> L.ByteString -> ByteString+ , hmaclazyAndLength -- :: ByteString -> L.ByteString -> (ByteString,Word64) ) where import Prelude hiding (init)@@ -83,11 +90,14 @@ import qualified Data.ByteString as B import Data.ByteString (ByteString) import Data.ByteString.Unsafe (unsafeUseAsCStringLen)-import Data.ByteString.Internal (create, toForeignPtr, memcpy)+import Data.ByteString.Internal (create, toForeignPtr, memcpy, mallocByteString) import Data.Bits (xor) import Data.Word import System.IO.Unsafe (unsafeDupablePerformIO) +import Compat (constructBS)+import Crypto.Hash.SHA512.FFI+ -- | 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@@ -96,22 +106,6 @@ unsafeDoIO :: IO a -> a unsafeDoIO = unsafeDupablePerformIO --- | SHA-512 Context------ The context data is exactly 208 bytes long, however--- the data in the context is stored in host-endianness.------ The context data is made up of------ * Two 'Word64's representing the number of bytes already feed to hash algorithm so far (lower word first),------ * a 128-element 'Word8' buffer holding partial input-chunks, and finally------ * a 8-element 'Word64' array holding the current work-in-progress digest-value.------ Consequently, a SHA-512 digest as produced by 'hash', 'hashlazy', or 'finalize' is 64 bytes long.-newtype Ctx = Ctx ByteString- -- keep this synchronised with cbits/sha512.h {-# INLINE digestSize #-} digestSize :: Int@@ -121,18 +115,21 @@ sizeCtx :: Int sizeCtx = 208 -{-# 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 create' #-}+-- | Variant of 'create' which allows to return an argument+create' :: Int -> (Ptr Word8 -> IO a) -> IO (ByteString,a)+create' l f = do+ fp <- mallocByteString l+ x <- withForeignPtr fp $ \p -> f p+ let bs = constructBS fp l+ return $! x `seq` bs `seq` (bs,x)+ copyCtx :: Ptr Ctx -> Ptr Ctx -> IO () copyCtx dst src = memcpy (castPtr dst) (castPtr src) (fromIntegral sizeCtx) @@ -157,23 +154,17 @@ withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr) -foreign import ccall unsafe "sha512.h hs_cryptohash_sha512_init"- c_sha512_init :: Ptr Ctx -> IO ()--foreign import ccall unsafe "sha512.h hs_cryptohash_sha512_update"- c_sha512_update_unsafe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()--foreign import ccall safe "sha512.h hs_cryptohash_sha512_update"- c_sha512_update_safe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()- -- 'safe' call overhead neglible for 4KiB and more c_sha512_update :: Ptr Ctx -> Ptr Word8 -> CSize -> IO () c_sha512_update pctx pbuf sz | sz < 4096 = c_sha512_update_unsafe pctx pbuf sz | otherwise = c_sha512_update_safe pctx pbuf sz -foreign import ccall unsafe "sha512.h hs_cryptohash_sha512_finalize"- c_sha512_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()+-- 'safe' call overhead neglible for 4KiB and more+c_sha512_hash :: Ptr Word8 -> CSize -> Ptr Word8 -> IO ()+c_sha512_hash pbuf sz pout+ | sz < 4096 = c_sha512_hash_unsafe pbuf sz pout+ | otherwise = c_sha512_hash_safe pbuf sz pout updateInternalIO :: Ptr Ctx -> ByteString -> IO () updateInternalIO ptr d =@@ -182,6 +173,9 @@ finalizeInternalIO :: Ptr Ctx -> IO ByteString finalizeInternalIO ptr = create digestSize (c_sha512_finalize ptr) +finalizeInternalIO' :: Ptr Ctx -> IO (ByteString,Word64)+finalizeInternalIO' ptr = create' digestSize (c_sha512_finalize_len ptr)+ {-# NOINLINE init #-} -- | create a new hash context init :: Ctx@@ -211,19 +205,51 @@ | validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO | otherwise = error "SHA512.finalize: invalid Ctx" +{-# NOINLINE finalizeAndLength #-}+-- | Variant of 'finalize' also returning length of hashed content+--+-- @since 0.11.101.0+finalizeAndLength :: Ctx -> (ByteString,Word64)+finalizeAndLength ctx+ | validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO'+ | otherwise = error "SHA256.finalize: invalid Ctx"+ {-# NOINLINE hash #-} -- | hash a strict bytestring into a digest bytestring (64 bytes) hash :: ByteString -> ByteString-hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do- c_sha512_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr+-- hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do c_sha512_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr+hash d = unsafeDoIO $ unsafeUseAsCStringLen d $ \(cs, len) -> create digestSize (c_sha512_hash (castPtr cs) (fromIntegral len)) +{-# NOINLINE start #-}+-- | hash a strict bytestring into a Ctx+--+-- @since 0.11.101.0+start :: ByteString -> Ctx+start d = unsafeDoIO $ withCtxNew $ \ptr -> do+ c_sha512_init ptr >> updateInternalIO ptr d+ {-# NOINLINE hashlazy #-} -- | hash a lazy bytestring into a digest bytestring (64 bytes) hashlazy :: L.ByteString -> ByteString hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do c_sha512_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr +{-# NOINLINE hashlazyAndLength #-}+-- | Variant of 'hashlazy' which simultaneously computes the hash and length of a lazy bytestring.+--+-- @since 0.11.101.0+hashlazyAndLength :: L.ByteString -> (ByteString,Word64)+hashlazyAndLength l = unsafeDoIO $ withCtxNewThrow $ \ptr ->+ c_sha512_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO' ptr +{-# NOINLINE startlazy #-}+-- | hash a lazy bytestring into a Ctx+--+-- @since 0.11.101.0+startlazy :: L.ByteString -> Ctx+startlazy l = unsafeDoIO $ withCtxNew $ \ptr -> do+ c_sha512_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l)+ {-# NOINLINE hmac #-} -- | Compute 64-byte <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible -- HMAC-SHA-512 digest for a strict bytestring message@@ -254,6 +280,25 @@ 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 > 128 then hash secret else secret+ pad = B.replicate (128 - B.length kt) 0++-- | Variant of 'hmaclazy' which also returns length of message+--+-- @since 0.11.101.0+hmaclazyAndLength :: ByteString -- ^ secret+ -> L.ByteString -- ^ message+ -> (ByteString,Word64) -- ^ digest (64 bytes) and length of message+hmaclazyAndLength secret msg =+ (hash (B.append opad htmp), sz' - fromIntegral ipadLen)+ where+ (htmp, sz') = hashlazyAndLength (L.append ipad msg)++ opad = B.map (xor 0x5c) k'+ ipad = L.fromChunks [B.map (xor 0x36) k']+ ipadLen = B.length k' k' = B.append kt pad kt = if B.length secret > 128 then hash secret else secret
+ src/Crypto/Hash/SHA512/FFI.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE Unsafe #-}++-- Ugly hack to workaround https://ghc.haskell.org/trac/ghc/ticket/14452+{-# OPTIONS_GHC -O0+ -fdo-lambda-eta-expansion+ -fcase-merge+ -fstrictness+ -fno-omit-interface-pragmas+ -fno-ignore-interface-pragmas #-}++{-# OPTIONS_GHC -optc-Wall -optc-O3 #-}++-- |+-- Module : Crypto.Hash.SHA522.FFI+-- License : BSD-3+--+module Crypto.Hash.SHA512.FFI where++import Data.ByteString (ByteString)+import Data.Word+import Foreign.C.Types+import Foreign.Ptr++-- | SHA-512 Context+--+-- The context data is exactly 208 bytes long, however+-- the data in the context is stored in host-endianness.+--+-- The context data is made up of+--+-- * Two 'Word64's representing the number of bytes already feed to hash algorithm so far (lower word first),+--+-- * a 128-element 'Word8' buffer holding partial input-chunks, and finally+--+-- * a 8-element 'Word64' array holding the current work-in-progress digest-value.+--+-- Consequently, a SHA-512 digest as produced by 'hash', 'hashlazy', or 'finalize' is 64 bytes long.+newtype Ctx = Ctx ByteString+ deriving (Eq)++foreign import capi unsafe "sha512.h hs_cryptohash_sha512_init"+ c_sha512_init :: Ptr Ctx -> IO ()++foreign import capi unsafe "sha512.h hs_cryptohash_sha512_update"+ c_sha512_update_unsafe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()++foreign import capi safe "sha512.h hs_cryptohash_sha512_update"+ c_sha512_update_safe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()++foreign import capi unsafe "sha512.h hs_cryptohash_sha512_finalize"+ c_sha512_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()++foreign import capi unsafe "sha512.h hs_cryptohash_sha512_finalize"+ c_sha512_finalize_len :: Ptr Ctx -> Ptr Word8 -> IO Word64++foreign import capi unsafe "sha512.h hs_cryptohash_sha512_hash"+ c_sha512_hash_unsafe :: Ptr Word8 -> CSize -> Ptr Word8 -> IO ()++foreign import capi safe "sha512.h hs_cryptohash_sha512_hash"+ c_sha512_hash_safe :: Ptr Word8 -> CSize -> Ptr Word8 -> IO ()