packages feed

fastpbkdf2 (empty) → 0.1.0.0

raw patch · 9 files changed

+830/−0 lines, 9 filesdep +basedep +base16-bytestringdep +bytestringsetup-changed

Dependencies added: base, base16-bytestring, bytestring, criterion, cryptonite, fastpbkdf2, pbkdf, tasty, tasty-hunit

Files

+ LICENSE view
@@ -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.
+ README.md view
@@ -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!
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Bench.hs view
@@ -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)+                ]+  ]
+ cbits/fastpbkdf2.c view
@@ -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);+}+
+ fastpbkdf2.cabal view
@@ -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
+ include/fastpbkdf2.h view
@@ -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
+ src/Crypto/KDF/PBKDF2.hs view
@@ -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 #-}
+ test/Main.hs view
@@ -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"+                        )+                      ]