diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+[![Build Status](https://travis-ci.org/adinapoli/fastpbkdf2-hs.svg?branch=master)](https://travis-ci.org/adinapoli/fastpbkdf2-hs)
+[![Build status](https://ci.appveyor.com/api/projects/status/3bkcrxd1m4xngerc?svg=true)](https://ci.appveyor.com/project/adinapoli/fastpbkdf2-hs)
+[![Coverage Status](https://coveralls.io/repos/github/adinapoli/fastpbkdf2-hs/badge.svg?branch=master)](https://coveralls.io/github/adinapoli/fastpbkdf2-hs?branch=master)
+
+## fastpbkdf2-hs
+
+Haskell bindings to the [fastpbkdf2](https://github.com/ctz/fastpbkdf2) library. This is currently (Dec 2016) the
+*fastest* Haskell library for computing the `pbkdf2`, outperforming competitors by several order of magnitude (unsuprisingly,
+as it hooks into some C code!):
+
+```
+benchmarking sha1/fastpbkdf2
+time                 4.948 ms   (4.850 ms .. 5.040 ms)
+                     0.998 R²   (0.997 R² .. 0.999 R²)
+mean                 4.767 ms   (4.707 ms .. 4.825 ms)
+std dev              186.5 μs   (152.3 μs .. 244.2 μs)
+variance introduced by outliers: 20% (moderately inflated)
+
+benchmarking sha1/cryptonite
+time                 29.61 ms   (29.04 ms .. 30.03 ms)
+                     0.999 R²   (0.997 R² .. 1.000 R²)
+mean                 29.82 ms   (29.47 ms .. 30.40 ms)
+std dev              916.0 μs   (527.1 μs .. 1.579 ms)
+
+benchmarking sha1/pbkdf2
+time                 8.032 s    (7.899 s .. 8.248 s)
+                     1.000 R²   (1.000 R² .. 1.000 R²)
+mean                 7.941 s    (7.911 s .. 7.995 s)
+std dev              47.08 ms   (0.0 s .. 48.71 ms)
+variance introduced by outliers: 19% (moderately inflated)
+```
+
+### Installation
+
+This library depends from `OpenSSL`. I have tried to make this self-contained, but the
+crypto layer of OpenSSL (or BoringSSL) requires some fine-tuned ASM code generated during
+the build process. Porting everything over in a customised `Build.hs` would have been too
+much pain, but PR are super welcome!
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE PackageImports #-}
+module Main where
+
+import              Criterion.Main
+import              Crypto.Hash.Algorithms as Crypto
+import "cryptonite" Crypto.KDF.PBKDF2 as Crypto
+import "fastpbkdf2" Crypto.KDF.PBKDF2 as Fast
+import              Crypto.PBKDF.ByteString as CD
+import              Data.ByteString as B
+
+data Library = FastPBKDF2
+             | Cryptonite
+             | PBKDF2
+
+encryptBench :: Library -> ByteString -> ByteString
+encryptBench FastPBKDF2 input = Fast.fastpbkdf2_hmac_sha1 input "salt" 10000 32
+encryptBench Cryptonite input = Crypto.generate (Crypto.prfHMAC Crypto.SHA1) (Crypto.Parameters 10000 32) input ("salt" :: ByteString)
+encryptBench PBKDF2     input = CD.sha1PBKDF2 input "salt" 10000 32
+
+main :: IO ()
+main = defaultMain [
+  bgroup "sha1" [ bench "fastpbkdf2"  $ whnf (encryptBench FastPBKDF2) (B.pack $ Prelude.replicate 100000 0x0)
+                , bench "cryptonite"  $ whnf (encryptBench Cryptonite) (B.pack $ Prelude.replicate 100000 0x0)
+                , bench "pbkdf2"      $ whnf (encryptBench PBKDF2)     (B.pack $ Prelude.replicate 100000 0x0)
+                ]
+  ]
diff --git a/cbits/fastpbkdf2.c b/cbits/fastpbkdf2.c
new file mode 100644
--- /dev/null
+++ b/cbits/fastpbkdf2.c
@@ -0,0 +1,399 @@
+/*
+ * fast-pbkdf2 - Optimal PBKDF2-HMAC calculation
+ * Written in 2015 by Joseph Birr-Pixton <jpixton@gmail.com>
+ *
+ * To the extent possible under law, the author(s) have dedicated all
+ * copyright and related and neighboring rights to this software to the
+ * public domain worldwide. This software is distributed without any
+ * warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication
+ * along with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "fastpbkdf2.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+
+/* --- MSVC doesn't support C99 --- */
+#ifdef _MSC_VER
+#define restrict
+#define _Pragma __pragma
+#endif
+
+/* --- Common useful things --- */
+#define MIN(a, b) ((a) > (b)) ? (b) : (a)
+
+static inline void write32_be(uint32_t n, uint8_t out[4])
+{
+#if defined(__GNUC__) && __GNUC__ >= 4 && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  *(uint32_t *)(out) = __builtin_bswap32(n);
+#else
+  out[0] = (n >> 24) & 0xff;
+  out[1] = (n >> 16) & 0xff;
+  out[2] = (n >> 8) & 0xff;
+  out[3] = n & 0xff;
+#endif
+}
+
+static inline void write64_be(uint64_t n, uint8_t out[8])
+{
+#if defined(__GNUC__) &&  __GNUC__ >= 4 && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  *(uint64_t *)(out) = __builtin_bswap64(n);
+#else
+  write32_be((n >> 32) & 0xffffffff, out);
+  write32_be(n & 0xffffffff, out + 4);
+#endif
+}
+
+/* --- Optional OpenMP parallelisation of consecutive blocks --- */
+#ifdef WITH_OPENMP
+# define OPENMP_PARALLEL_FOR _Pragma("omp parallel for")
+#else
+# define OPENMP_PARALLEL_FOR
+#endif
+
+/* Prepare block (of blocksz bytes) to contain md padding denoting a msg-size
+ * message (in bytes).  block has a prefix of used bytes.
+ *
+ * Message length is expressed in 32 bits (so suitable for sha1, sha256, sha512). */
+static inline void md_pad(uint8_t *block, size_t blocksz, size_t used, size_t msg)
+{
+  memset(block + used, 0, blocksz - used - 4);
+  block[used] = 0x80;
+  block += blocksz - 4;
+  write32_be((uint32_t) (msg * 8), block);
+}
+
+/* Internal function/type names for hash-specific things. */
+#define HMAC_CTX(_name) HMAC_ ## _name ## _ctx
+#define HMAC_INIT(_name) HMAC_ ## _name ## _init
+#define HMAC_UPDATE(_name) HMAC_ ## _name ## _update
+#define HMAC_FINAL(_name) HMAC_ ## _name ## _final
+
+#define PBKDF2_F(_name) pbkdf2_f_ ## _name
+#define PBKDF2(_name) pbkdf2_ ## _name
+
+/* This macro expands to decls for the whole implementation for a given
+ * hash function.  Arguments are:
+ *
+ * _name like 'sha1', added to symbol names
+ * _blocksz block size, in bytes
+ * _hashsz digest output, in bytes
+ * _ctx hash context type
+ * _init hash context initialisation function
+ *    args: (_ctx *c)
+ * _update hash context update function
+ *    args: (_ctx *c, const void *data, size_t ndata)
+ * _final hash context finish function
+ *    args: (void *out, _ctx *c)
+ * _xform hash context raw block update function
+ *    args: (_ctx *c, const void *data)
+ * _xcpy hash context raw copy function (only need copy hash state)
+ *    args: (_ctx * restrict out, const _ctx *restrict in)
+ * _xtract hash context state extraction
+ *    args: args (_ctx *restrict c, uint8_t *restrict out)
+ * _xxor hash context xor function (only need xor hash state)
+ *    args: (_ctx *restrict out, const _ctx *restrict in)
+ *
+ * The resulting function is named PBKDF2(_name).
+ */
+#define DECL_PBKDF2(_name, _blocksz, _hashsz, _ctx,                           \
+                    _init, _update, _xform, _final, _xcpy, _xtract, _xxor)    \
+  typedef struct {                                                            \
+    _ctx inner;                                                               \
+    _ctx outer;                                                               \
+  } HMAC_CTX(_name);                                                          \
+                                                                              \
+  static inline void HMAC_INIT(_name)(HMAC_CTX(_name) *ctx,                   \
+                                      const uint8_t *key, size_t nkey)        \
+  {                                                                           \
+    /* Prepare key: */                                                        \
+    uint8_t k[_blocksz];                                                      \
+                                                                              \
+    /* Shorten long keys. */                                                  \
+    if (nkey > _blocksz)                                                      \
+    {                                                                         \
+      _init(&ctx->inner);                                                     \
+      _update(&ctx->inner, key, nkey);                                        \
+      _final(k, &ctx->inner);                                                 \
+                                                                              \
+      key = k;                                                                \
+      nkey = _hashsz;                                                         \
+    }                                                                         \
+                                                                              \
+    /* Standard doesn't cover case where blocksz < hashsz. */                 \
+    assert(nkey <= _blocksz);                                                 \
+                                                                              \
+    /* Right zero-pad short keys. */                                          \
+    if (k != key)                                                             \
+      memcpy(k, key, nkey);                                                   \
+    if (_blocksz > nkey)                                                      \
+      memset(k + nkey, 0, _blocksz - nkey);                                   \
+                                                                              \
+    /* Start inner hash computation */                                        \
+    uint8_t blk_inner[_blocksz];                                              \
+    uint8_t blk_outer[_blocksz];                                              \
+                                                                              \
+    for (size_t i = 0; i < _blocksz; i++)                                     \
+    {                                                                         \
+      blk_inner[i] = 0x36 ^ k[i];                                             \
+      blk_outer[i] = 0x5c ^ k[i];                                             \
+    }                                                                         \
+                                                                              \
+    _init(&ctx->inner);                                                       \
+    _update(&ctx->inner, blk_inner, sizeof blk_inner);                        \
+                                                                              \
+    /* And outer. */                                                          \
+    _init(&ctx->outer);                                                       \
+    _update(&ctx->outer, blk_outer, sizeof blk_outer);                        \
+  }                                                                           \
+                                                                              \
+  static inline void HMAC_UPDATE(_name)(HMAC_CTX(_name) *ctx,                 \
+                                        const void *data, size_t ndata)       \
+  {                                                                           \
+    _update(&ctx->inner, data, ndata);                                        \
+  }                                                                           \
+                                                                              \
+  static inline void HMAC_FINAL(_name)(HMAC_CTX(_name) *ctx,                  \
+                                       uint8_t out[_hashsz])                  \
+  {                                                                           \
+    _final(out, &ctx->inner);                                                 \
+    _update(&ctx->outer, out, _hashsz);                                       \
+    _final(out, &ctx->outer);                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  /* --- PBKDF2 --- */                                                        \
+  static inline void PBKDF2_F(_name)(const HMAC_CTX(_name) *startctx,         \
+                                     uint32_t counter,                        \
+                                     const uint8_t *salt, size_t nsalt,       \
+                                     uint32_t iterations,                     \
+                                     uint8_t *out)                            \
+  {                                                                           \
+    uint8_t countbuf[4];                                                      \
+    write32_be(counter, countbuf);                                            \
+                                                                              \
+    /* Prepare loop-invariant padding block. */                               \
+    uint8_t Ublock[_blocksz];                                                 \
+    md_pad(Ublock, _blocksz, _hashsz, _blocksz + _hashsz);                    \
+                                                                              \
+    /* First iteration:                                                       \
+     *   U_1 = PRF(P, S || INT_32_BE(i))                                      \
+     */                                                                       \
+    HMAC_CTX(_name) ctx = *startctx;                                          \
+    HMAC_UPDATE(_name)(&ctx, salt, nsalt);                                    \
+    HMAC_UPDATE(_name)(&ctx, countbuf, sizeof countbuf);                      \
+    HMAC_FINAL(_name)(&ctx, Ublock);                                          \
+    _ctx result = ctx.outer;                                                  \
+                                                                              \
+    /* Subsequent iterations:                                                 \
+     *   U_c = PRF(P, U_{c-1})                                                \
+     */                                                                       \
+    for (uint32_t i = 1; i < iterations; i++)                                 \
+    {                                                                         \
+      /* Complete inner hash with previous U */                               \
+      _xcpy(&ctx.inner, &startctx->inner);                                    \
+      _xform(&ctx.inner, Ublock);                                             \
+      _xtract(&ctx.inner, Ublock);                                            \
+      /* Complete outer hash with inner output */                             \
+      _xcpy(&ctx.outer, &startctx->outer);                                    \
+      _xform(&ctx.outer, Ublock);                                             \
+      _xtract(&ctx.outer, Ublock);                                            \
+      _xxor(&result, &ctx.outer);                                             \
+    }                                                                         \
+                                                                              \
+    /* Reform result into output buffer. */                                   \
+    _xtract(&result, out);                                                    \
+  }                                                                           \
+                                                                              \
+  static inline void PBKDF2(_name)(const uint8_t *pw, size_t npw,             \
+                     const uint8_t *salt, size_t nsalt,                       \
+                     uint32_t iterations,                                     \
+                     uint8_t *out, size_t nout)                               \
+  {                                                                           \
+    assert(iterations);                                                       \
+    assert(out && nout);                                                      \
+                                                                              \
+    /* Starting point for inner loop. */                                      \
+    HMAC_CTX(_name) ctx;                                                      \
+    HMAC_INIT(_name)(&ctx, pw, npw);                                          \
+                                                                              \
+    /* How many blocks do we need? */                                         \
+    uint32_t blocks_needed = (uint32_t)(nout + _hashsz - 1) / _hashsz;        \
+                                                                              \
+    OPENMP_PARALLEL_FOR                                                       \
+    for (uint32_t counter = 1; counter <= blocks_needed; counter++)           \
+    {                                                                         \
+      uint8_t block[_hashsz];                                                 \
+      PBKDF2_F(_name)(&ctx, counter, salt, nsalt, iterations, block);         \
+                                                                              \
+      size_t offset = (counter - 1) * _hashsz;                                \
+      size_t taken = MIN(nout - offset, _hashsz);                             \
+      memcpy(out + offset, block, taken);                                     \
+    }                                                                         \
+  }
+
+static inline void sha1_extract(SHA_CTX *restrict ctx, uint8_t *restrict out)
+{
+  write32_be(ctx->h0, out);
+  write32_be(ctx->h1, out + 4);
+  write32_be(ctx->h2, out + 8);
+  write32_be(ctx->h3, out + 12);
+  write32_be(ctx->h4, out + 16);
+}
+
+static inline void sha1_cpy(SHA_CTX *restrict out, const SHA_CTX *restrict in)
+{
+  out->h0 = in->h0;
+  out->h1 = in->h1;
+  out->h2 = in->h2;
+  out->h3 = in->h3;
+  out->h4 = in->h4;
+}
+
+static inline void sha1_xor(SHA_CTX *restrict out, const SHA_CTX *restrict in)
+{
+  out->h0 ^= in->h0;
+  out->h1 ^= in->h1;
+  out->h2 ^= in->h2;
+  out->h3 ^= in->h3;
+  out->h4 ^= in->h4;
+}
+
+DECL_PBKDF2(sha1,
+            SHA_CBLOCK,
+            SHA_DIGEST_LENGTH,
+            SHA_CTX,
+            SHA1_Init,
+            SHA1_Update,
+            SHA1_Transform,
+            SHA1_Final,
+            sha1_cpy,
+            sha1_extract,
+            sha1_xor)
+
+static inline void sha256_extract(SHA256_CTX *restrict ctx, uint8_t *restrict out)
+{
+  write32_be(ctx->h[0], out);
+  write32_be(ctx->h[1], out + 4);
+  write32_be(ctx->h[2], out + 8);
+  write32_be(ctx->h[3], out + 12);
+  write32_be(ctx->h[4], out + 16);
+  write32_be(ctx->h[5], out + 20);
+  write32_be(ctx->h[6], out + 24);
+  write32_be(ctx->h[7], out + 28);
+}
+
+static inline void sha256_cpy(SHA256_CTX *restrict out, const SHA256_CTX *restrict in)
+{
+  out->h[0] = in->h[0];
+  out->h[1] = in->h[1];
+  out->h[2] = in->h[2];
+  out->h[3] = in->h[3];
+  out->h[4] = in->h[4];
+  out->h[5] = in->h[5];
+  out->h[6] = in->h[6];
+  out->h[7] = in->h[7];
+}
+
+static inline void sha256_xor(SHA256_CTX *restrict out, const SHA256_CTX *restrict in)
+{
+  out->h[0] ^= in->h[0];
+  out->h[1] ^= in->h[1];
+  out->h[2] ^= in->h[2];
+  out->h[3] ^= in->h[3];
+  out->h[4] ^= in->h[4];
+  out->h[5] ^= in->h[5];
+  out->h[6] ^= in->h[6];
+  out->h[7] ^= in->h[7];
+}
+
+DECL_PBKDF2(sha256,
+            SHA256_CBLOCK,
+            SHA256_DIGEST_LENGTH,
+            SHA256_CTX,
+            SHA256_Init,
+            SHA256_Update,
+            SHA256_Transform,
+            SHA256_Final,
+            sha256_cpy,
+            sha256_extract,
+            sha256_xor)
+
+static inline void sha512_extract(SHA512_CTX *restrict ctx, uint8_t *restrict out)
+{
+  write64_be(ctx->h[0], out);
+  write64_be(ctx->h[1], out + 8);
+  write64_be(ctx->h[2], out + 16);
+  write64_be(ctx->h[3], out + 24);
+  write64_be(ctx->h[4], out + 32);
+  write64_be(ctx->h[5], out + 40);
+  write64_be(ctx->h[6], out + 48);
+  write64_be(ctx->h[7], out + 56);
+}
+
+static inline void sha512_cpy(SHA512_CTX *restrict out, const SHA512_CTX *restrict in)
+{
+  out->h[0] = in->h[0];
+  out->h[1] = in->h[1];
+  out->h[2] = in->h[2];
+  out->h[3] = in->h[3];
+  out->h[4] = in->h[4];
+  out->h[5] = in->h[5];
+  out->h[6] = in->h[6];
+  out->h[7] = in->h[7];
+}
+
+static inline void sha512_xor(SHA512_CTX *restrict out, const SHA512_CTX *restrict in)
+{
+  out->h[0] ^= in->h[0];
+  out->h[1] ^= in->h[1];
+  out->h[2] ^= in->h[2];
+  out->h[3] ^= in->h[3];
+  out->h[4] ^= in->h[4];
+  out->h[5] ^= in->h[5];
+  out->h[6] ^= in->h[6];
+  out->h[7] ^= in->h[7];
+}
+
+DECL_PBKDF2(sha512,
+            SHA512_CBLOCK,
+            SHA512_DIGEST_LENGTH,
+            SHA512_CTX,
+            SHA512_Init,
+            SHA512_Update,
+            SHA512_Transform,
+            SHA512_Final,
+            sha512_cpy,
+            sha512_extract,
+            sha512_xor)
+
+void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw,
+                          const uint8_t *salt, size_t nsalt,
+                          uint32_t iterations,
+                          uint8_t *out, size_t nout)
+{
+  PBKDF2(sha1)(pw, npw, salt, nsalt, iterations, out, nout);
+}
+
+void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
+                            const uint8_t *salt, size_t nsalt,
+                            uint32_t iterations,
+                            uint8_t *out, size_t nout)
+{
+  PBKDF2(sha256)(pw, npw, salt, nsalt, iterations, out, nout);
+}
+
+void fastpbkdf2_hmac_sha512(const uint8_t *pw, size_t npw,
+                            const uint8_t *salt, size_t nsalt,
+                            uint32_t iterations,
+                            uint8_t *out, size_t nout)
+{
+  PBKDF2(sha512)(pw, npw, salt, nsalt, iterations, out, nout);
+}
+
diff --git a/fastpbkdf2.cabal b/fastpbkdf2.cabal
new file mode 100644
--- /dev/null
+++ b/fastpbkdf2.cabal
@@ -0,0 +1,68 @@
+name:                fastpbkdf2
+version:             0.1.0.0
+synopsis:            Haskell bindings to the fastpbkdf2 C library
+description:         Please see README.md
+homepage:            https://github.com/adinapoli/fastpbkdf2-hs#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Alfredo Di Napoli
+maintainer:          alfredo.dinapoli@gmail.com
+copyright:           2016 Alfredo Di Napoli
+category:            Cryptography
+build-type:          Simple
+cabal-version:       >=1.10
+extra-source-files:  README.md
+                     include/fastpbkdf2.h
+
+library
+  hs-source-dirs:      src
+  c-sources: cbits/fastpbkdf2.c
+  cc-options:    -ffast-math -std=gnu99
+  include-dirs:        include
+  exposed-modules:     Crypto.KDF.PBKDF2
+  build-depends:       base >= 4.6 && < 5
+                     , bytestring >= 0.10.4.0
+  default-language:    Haskell2010
+  if os(mingw32) || os(windows)
+    extra-libraries: eay32, ssl32
+  else
+    if os(osx)
+      include-dirs: /usr/local/opt/openssl/include
+      extra-lib-dirs: /usr/local/opt/openssl/lib
+    else
+      if arch(x86_64)
+        cpp-options: -D__x86_64__
+      if arch(i386)
+        cpp-options: -D__i386__
+    extra-libraries: crypto
+
+test-suite fastpbkdf2-hs-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Main.hs
+  build-depends:       base
+                     , base16-bytestring
+                     , bytestring
+                     , fastpbkdf2 -any
+                     , tasty
+                     , tasty-hunit
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+benchmark fastpbkdf2-bench
+  type: exitcode-stdio-1.0
+  main-is: Bench.hs
+  hs-source-dirs: bench
+  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2 -threaded -rtsopts -with-rtsopts=-N1 -with-rtsopts=-s -with-rtsopts=-qg
+  build-depends:
+      base >=4.6 && <5
+    , bytestring >= 0.10.4.0
+    , criterion
+    , fastpbkdf2
+    , cryptonite
+    , pbkdf
+  default-language: Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/adinapoli/fastpbkdf2-hs
diff --git a/include/fastpbkdf2.h b/include/fastpbkdf2.h
new file mode 100644
--- /dev/null
+++ b/include/fastpbkdf2.h
@@ -0,0 +1,71 @@
+/*
+ * fastpbkdf2 - Faster PBKDF2-HMAC calculation
+ * Written in 2015 by Joseph Birr-Pixton <jpixton@gmail.com>
+ *
+ * To the extent possible under law, the author(s) have dedicated all
+ * copyright and related and neighboring rights to this software to the
+ * public domain worldwide. This software is distributed without any
+ * warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication
+ * along with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef FASTPBKDF2_H
+#define FASTPBKDF2_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Calculates PBKDF2-HMAC-SHA1.
+ *
+ *  @p npw bytes at @p pw are the password input.
+ *  @p nsalt bytes at @p salt are the salt input.
+ *  @p iterations is the PBKDF2 iteration count and must be non-zero.
+ *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
+ *
+ *  This function cannot fail; it does not report errors.
+ */
+void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw,
+                          const uint8_t *salt, size_t nsalt,
+                          uint32_t iterations,
+                          uint8_t *out, size_t nout);
+
+/** Calculates PBKDF2-HMAC-SHA256.
+ *
+ *  @p npw bytes at @p pw are the password input.
+ *  @p nsalt bytes at @p salt are the salt input.
+ *  @p iterations is the PBKDF2 iteration count and must be non-zero.
+ *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
+ *
+ *  This function cannot fail; it does not report errors.
+ */
+void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
+                            const uint8_t *salt, size_t nsalt,
+                            uint32_t iterations,
+                            uint8_t *out, size_t nout);
+
+/** Calculates PBKDF2-HMAC-SHA512.
+ *
+ *  @p npw bytes at @p pw are the password input.
+ *  @p nsalt bytes at @p salt are the salt input.
+ *  @p iterations is the PBKDF2 iteration count and must be non-zero.
+ *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
+ *
+ *  This function cannot fail; it does not report errors.
+ */
+void fastpbkdf2_hmac_sha512(const uint8_t *pw, size_t npw,
+                            const uint8_t *salt, size_t nsalt,
+                            uint32_t iterations,
+                            uint8_t *out, size_t nout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/Crypto/KDF/PBKDF2.hs b/src/Crypto/KDF/PBKDF2.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/KDF/PBKDF2.hs
@@ -0,0 +1,91 @@
+
+module Crypto.KDF.PBKDF2 (
+    fastpbkdf2_hmac_sha1
+  , fastpbkdf2_hmac_sha256
+  , fastpbkdf2_hmac_sha512
+  ) where
+
+import Data.ByteString
+import Data.ByteString.Unsafe
+import Data.Monoid
+import Foreign.C.Types
+import Foreign.ForeignPtr
+import Foreign.Ptr
+import System.IO.Unsafe
+
+foreign import ccall "fastpbkdf2_hmac_sha1" c_fastpbkdf2_hmac_sha1 :: Signature
+
+foreign import ccall "fastpbkdf2_hmac_sha256" c_fastpbkdf2_hmac_sha256 :: Signature
+
+foreign import ccall "fastpbkdf2_hmac_sha512" c_fastpbkdf2_hmac_sha512 :: Signature
+
+type Signature = Ptr CChar -> CSize -> Ptr CChar -> CSize -> CInt -> Ptr CChar -> CSize -> IO ()
+
+fastpbkdf2_fn :: Signature
+              -> ByteString
+              -- ^ The user key (e.g. a password)
+              -> ByteString
+              -- ^ The salt
+              -> Int
+              -- ^ The iteration count
+              -> Int
+              -- ^ The length (in bytes) of the output
+              -> ByteString
+fastpbkdf2_fn fn password salt iterations keyLen = unsafeDupablePerformIO $ do
+  withPositive (iterations, keyLen) $ do
+    let outSize = CSize (fromIntegral keyLen)
+    outForeignPtr <- mallocForeignPtrBytes keyLen
+    withForeignPtr outForeignPtr $ \ptrOut -> do
+      unsafeUseAsCStringLen password $ \(passwordPtr, passwordSizeInt) -> do
+        unsafeUseAsCStringLen salt   $ \(saltPtr, saltSizeInt) -> do
+          let passwordSize = CSize (fromIntegral passwordSizeInt)
+          let saltSize     = CSize (fromIntegral saltSizeInt)
+          let iters        = CInt (fromIntegral iterations)
+          fn passwordPtr passwordSize saltPtr saltSize iters ptrOut outSize
+          unsafePackCStringLen (ptrOut, keyLen)
+{-# NOINLINE fastpbkdf2_fn #-}
+
+withPositive :: (Int, Int) -> IO a -> IO a
+withPositive (iter, keyLen) action
+  | iter   <= 0 = error $ "fastpbkdf2: iteration count must be > 0 (it was " <> show iter <> ")"
+  | keyLen <= 0 = error $ "fastpbkdf2: key length must be > 0 (it was " <> show keyLen <> ")"
+  | otherwise = action
+
+--------------------------------------------------------------------------------
+fastpbkdf2_hmac_sha1 :: ByteString
+                     -- ^ The user key (e.g. a password)
+                     -> ByteString
+                     -- ^ The salt
+                     -> Int
+                     -- ^ The iteration count
+                     -> Int
+                     -- ^ The length (in bytes) of the output
+                     -> ByteString
+fastpbkdf2_hmac_sha1 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha1
+{-# INLINE fastpbkdf2_hmac_sha1 #-}
+
+--------------------------------------------------------------------------------
+fastpbkdf2_hmac_sha256 :: ByteString
+                     -- ^ The user key (e.g. a password)
+                     -> ByteString
+                     -- ^ The salt
+                     -> Int
+                     -- ^ The iteration count
+                     -> Int
+                     -- ^ The length (in bytes) of the output
+                     -> ByteString
+fastpbkdf2_hmac_sha256 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha256
+{-# INLINE fastpbkdf2_hmac_sha256 #-}
+
+--------------------------------------------------------------------------------
+fastpbkdf2_hmac_sha512 :: ByteString
+                     -- ^ The user key (e.g. a password)
+                     -> ByteString
+                     -- ^ The salt
+                     -> Int
+                     -- ^ The iteration count
+                     -> Int
+                     -- ^ The length (in bytes) of the output
+                     -> ByteString
+fastpbkdf2_hmac_sha512 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha512
+{-# INLINE fastpbkdf2_hmac_sha512 #-}
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Main where
+
+import Control.Exception (evaluate, try, SomeException)
+import Crypto.KDF.PBKDF2
+import Data.ByteString as B
+import Data.ByteString.Base16 (decode)
+import Data.ByteString.Char8 as C8
+import Data.Either (isLeft, isRight)
+import Data.Monoid
+import Test.Tasty
+import Test.Tasty.HUnit
+
+----------------------------------------------------------------------
+unhex :: ByteString -> ByteString
+unhex = fst . decode
+
+----------------------------------------------------------------------
+type CryptoFn = ByteString -> ByteString -> Int -> Int -> ByteString
+
+----------------------------------------------------------------------
+testVectors :: CryptoFn -> [(C8.ByteString, C8.ByteString,Int,ByteString)] -> Assertion
+testVectors _ [] = return ()
+testVectors fn ((input, salt, iter, expected_hex):xs) = do
+  let actual = fn input salt iter (floor ((fromIntegral $ B.length expected_hex) / fromIntegral 2))
+  actual @?= (unhex expected_hex)
+  testVectors fn xs
+
+----------------------------------------------------------------------
+testInvalidIterationNumber :: Assertion
+testInvalidIterationNumber = do
+  (res :: Either SomeException ByteString) <- try $ evaluate (fastpbkdf2_hmac_sha1 "password" "salt" 0 32)
+  isLeft res @?= True
+
+----------------------------------------------------------------------
+testInvalidKeyLen :: Assertion
+testInvalidKeyLen = do
+  (res :: Either SomeException ByteString) <- try $ evaluate (fastpbkdf2_hmac_sha1 "password" "salt" 1 (-1))
+  isLeft res @?= True
+
+----------------------------------------------------------------------
+testEmptyPassword :: Assertion
+testEmptyPassword = do
+  (res :: Either SomeException ByteString) <- try $ evaluate (fastpbkdf2_hmac_sha1 "" "salt" 1 32)
+  isRight res @?= True
+
+----------------------------------------------------------------------
+testEmptySalt :: Assertion
+testEmptySalt = do
+  (res :: Either SomeException ByteString) <- try $ evaluate (fastpbkdf2_hmac_sha1 "password" "" 1 32)
+  isRight res @?= True
+
+----------------------------------------------------------------------
+main :: IO ()
+main = do
+  defaultMainWithIngredients defaultIngredients $ testGroup "all tests" [
+    testGroup "Test Vectors" $ [
+        testCase "RFC6070 SHA1 Test Vectors"   (testVectors fastpbkdf2_hmac_sha1   test_vectors_sha1)
+      , testCase "RFC6070 SHA256 Test Vectors" (testVectors fastpbkdf2_hmac_sha256 test_vectors_sha256)
+      , testCase "(Partial) RFC6070 SHA512 Test Vectors" (testVectors fastpbkdf2_hmac_sha512 test_vectors_sha512)
+      ]
+    , testGroup "Edge cases" $ [
+        testCase "Invalid iteration number" testInvalidIterationNumber
+      , testCase "Invalid key length" testInvalidKeyLen
+      , testCase "Empty password" testEmptyPassword
+      , testCase "Empty salt" testEmptySalt
+      ]
+    ]
+
+-- RFC6070 test vectors
+test_vectors_sha1 :: [(C8.ByteString,C8.ByteString,Int,ByteString)]
+test_vectors_sha1 = [ ("password", "salt", 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6")
+                    , ("password", "salt", 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957")
+                    , ("password", "salt", 4096, "4b007901b765489abead49d926f721d065a429c1")
+                    , ("password", "salt", 16777216, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984")
+                    , ( "passwordPASSWORDpassword"
+                      , "saltSALTsaltSALTsaltSALTsaltSALTsalt"
+                      , 4096
+                      , "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038")
+                    , ("pass\0word", "sa\0lt", 4096, "56fa6aa75548099dcc37d7f03425e0c3")
+                    ]
+
+test_vectors_sha256 :: [(C8.ByteString,C8.ByteString,Int,ByteString)]
+test_vectors_sha256 = [ ("password", "salt", 1, "120fb6cffcf8b32c43e7225256c4f837a86548c9")
+                    , ("password", "salt", 2, "ae4d0c95af6b46d32d0adff928f06dd02a303f8e")
+                    , ("password", "salt", 4096, "c5e478d59288c841aa530db6845c4c8d962893a0")
+                    , ("password", "salt", 16777216, "cf81c66fe8cfc04d1f31ecb65dab4089f7f179e8")
+                    , ( "passwordPASSWORDpassword"
+                      , "saltSALTsaltSALTsaltSALTsaltSALTsalt"
+                      , 4096
+                      , "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c")
+                    , ("pass\0word", "sa\0lt", 4096, "89b69d0516f829893c696226650a8687")
+                    ]
+
+test_vectors_sha512 :: [(C8.ByteString,C8.ByteString,Int,ByteString)]
+test_vectors_sha512 = [ ("password", "salt", 1, "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470"
+                                             <> "a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce"
+                        )
+                      , ("password", "salt", 2, "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f"
+                                             <> "7840edce4fef5a82be67335c77a6068e04112754f27ccf4e"
+                        )
+                      ]
