diff --git a/cbits/sha512.c b/cbits/sha512.c
deleted file mode 100644
--- a/cbits/sha512.c
+++ /dev/null
@@ -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);
-}
diff --git a/cbits/sha512.h b/cbits/sha512.h
--- a/cbits/sha512.h
+++ b/cbits/sha512.h
@@ -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
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -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
diff --git a/cryptohash-sha512.cabal b/cryptohash-sha512.cabal
--- a/cryptohash-sha512.cabal
+++ b/cryptohash-sha512.cabal
@@ -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
diff --git a/src-tests/test-sha512.hs b/src-tests/test-sha512.hs
--- a/src-tests/test-sha512.hs
+++ b/src-tests/test-sha512.hs
@@ -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
diff --git a/src/Compat.hs b/src/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/Compat.hs
@@ -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
diff --git a/src/Crypto/Hash/SHA512.hs b/src/Crypto/Hash/SHA512.hs
--- a/src/Crypto/Hash/SHA512.hs
+++ b/src/Crypto/Hash/SHA512.hs
@@ -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
diff --git a/src/Crypto/Hash/SHA512/FFI.hs b/src/Crypto/Hash/SHA512/FFI.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Hash/SHA512/FFI.hs
@@ -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 ()
