Merge pull request #132 from NicolasDP/master

Add Fast PBKDF2 for SHA1, SHA256 and SHA512
This commit is contained in:
Vincent Hanquez 2017-02-14 09:43:21 +00:00 committed by GitHub
commit e3ef0684f9
9 changed files with 627 additions and 4 deletions

View File

@ -8,17 +8,23 @@
-- Password Based Key Derivation Function 2 -- Password Based Key Derivation Function 2
-- --
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Crypto.KDF.PBKDF2 module Crypto.KDF.PBKDF2
( PRF ( PRF
, prfHMAC , prfHMAC
, Parameters(..) , Parameters(..)
, generate , generate
, fastPBKDF2_SHA1
, fastPBKDF2_SHA256
, fastPBKDF2_SHA512
) where ) where
import Data.Word import Data.Word
import Data.Bits import Data.Bits
import Foreign.Marshal.Alloc import Foreign.Marshal.Alloc
import Foreign.Ptr (plusPtr) import Foreign.Ptr (plusPtr, Ptr)
import Foreign.C.Types (CUInt(..), CInt(..), CSize(..))
import Crypto.Hash (HashAlgorithm) import Crypto.Hash (HashAlgorithm)
import qualified Crypto.MAC.HMAC as HMAC import qualified Crypto.MAC.HMAC as HMAC
@ -100,3 +106,70 @@ generate prf params password salt =
c = fromIntegral ((w `shiftR` 8) .&. 0xff) c = fromIntegral ((w `shiftR` 8) .&. 0xff)
d = fromIntegral (w .&. 0xff) d = fromIntegral (w .&. 0xff)
{-# NOINLINE generate #-} {-# NOINLINE generate #-}
fastPBKDF2_SHA1 :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out)
=> Parameters
-> password
-> salt
-> out
fastPBKDF2_SHA1 params password salt =
B.allocAndFreeze (outputLength params) $ \outPtr ->
B.withByteArray password $ \passPtr ->
B.withByteArray salt $ \saltPtr ->
c_cryptonite_fastpbkdf2_hmac_sha1
passPtr (fromIntegral $ B.length password)
saltPtr (fromIntegral $ B.length salt)
(fromIntegral $ iterCounts params)
outPtr (fromIntegral $ outputLength params)
fastPBKDF2_SHA256 :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out)
=> Parameters
-> password
-> salt
-> out
fastPBKDF2_SHA256 params password salt =
B.allocAndFreeze (outputLength params) $ \outPtr ->
B.withByteArray password $ \passPtr ->
B.withByteArray salt $ \saltPtr ->
c_cryptonite_fastpbkdf2_hmac_sha256
passPtr (fromIntegral $ B.length password)
saltPtr (fromIntegral $ B.length salt)
(fromIntegral $ iterCounts params)
outPtr (fromIntegral $ outputLength params)
fastPBKDF2_SHA512 :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out)
=> Parameters
-> password
-> salt
-> out
fastPBKDF2_SHA512 params password salt =
B.allocAndFreeze (outputLength params) $ \outPtr ->
B.withByteArray password $ \passPtr ->
B.withByteArray salt $ \saltPtr ->
c_cryptonite_fastpbkdf2_hmac_sha512
passPtr (fromIntegral $ B.length password)
saltPtr (fromIntegral $ B.length salt)
(fromIntegral $ iterCounts params)
outPtr (fromIntegral $ outputLength params)
foreign import ccall unsafe "cryptonite_pbkdf2.h cryptonite_fastpbkdf2_hmac_sha1"
c_cryptonite_fastpbkdf2_hmac_sha1 :: Ptr Word8 -> CSize
-> Ptr Word8 -> CSize
-> CUInt
-> Ptr Word8 -> CSize
-> IO ()
foreign import ccall unsafe "cryptonite_pbkdf2.h cryptonite_fastpbkdf2_hmac_sha256"
c_cryptonite_fastpbkdf2_hmac_sha256 :: Ptr Word8 -> CSize
-> Ptr Word8 -> CSize
-> CUInt
-> Ptr Word8 -> CSize
-> IO ()
foreign import ccall unsafe "cryptonite_pbkdf2.h cryptonite_fastpbkdf2_hmac_sha512"
c_cryptonite_fastpbkdf2_hmac_sha512 :: Ptr Word8 -> CSize
-> Ptr Word8 -> CSize
-> CUInt
-> Ptr Word8 -> CSize
-> IO ()

59
benchs/PBKDF2.hs Normal file
View File

@ -0,0 +1,59 @@
{-# 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 Data.ByteString as B
password :: ByteString
password = "password"
salt :: ByteString
salt = "salt"
runBench :: Int
-> (ByteString -> ByteString -> ByteString)
-> (ByteString -> ByteString -> ByteString)
-> (ByteString -> ByteString -> ByteString)
-> Benchmark
runBench iter cryptonite fastCryptonite fastBinding =
bgroup (show iter)
[ bench "cryptonite" $ whnf (cryptonite password) salt
, bench "cryptonite-fast" $ whnf (fastCryptonite password) salt
, bench "fastpbkdf2-hs" $ whnf (fastBinding password) salt
]
makeBench :: (Parameters -> ByteString -> ByteString -> ByteString)
-> (Parameters -> ByteString -> ByteString -> ByteString)
-> (ByteString -> ByteString -> Int -> Int -> ByteString)
-> [Benchmark]
makeBench cryptonite fastCryptonite fastBinding =
[ runBench 1
(cryptonite (Parameters 1 32))
(fastCryptonite (Parameters 1 32))
(\p s -> fastBinding p s 1 32)
, runBench 10000
(cryptonite (Parameters 10000 32))
(fastCryptonite (Parameters 10000 32))
(\p s -> fastBinding p s 10000 32)
]
main :: IO ()
main = defaultMain
[ bgroup "SHA1" $ makeBench
(Crypto.generate (Crypto.prfHMAC Crypto.SHA1))
(Crypto.fastPBKDF2_SHA1)
(Fast.fastpbkdf2_hmac_sha1)
, bgroup "SHA256" $ makeBench
(Crypto.generate (Crypto.prfHMAC Crypto.SHA256))
(Crypto.fastPBKDF2_SHA256)
(Fast.fastpbkdf2_hmac_sha256)
, bgroup "SHA512" $ makeBench
(Crypto.generate (Crypto.prfHMAC Crypto.SHA512))
(Crypto.fastPBKDF2_SHA512)
(Fast.fastpbkdf2_hmac_sha512)
]

419
cbits/cryptonite_pbkdf2.c Normal file
View File

@ -0,0 +1,419 @@
/*
* fast-pbkdf2 - Optimal PBKDF2-HMAC calculation
* Written in 2015 by Joseph Birr-Pixton <jpixton@gmail.com>
* Ported to cryptonite in 2017 by Nicolas Di Prima <nicolas@primetype.co.uk>
*
* 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 <assert.h>
#include <string.h>
#include "cryptonite_pbkdf2.h"
#include "cryptonite_bitfn.h"
#include "cryptonite_sha1.h"
#include "cryptonite_sha256.h"
#include "cryptonite_sha512.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(&ctx->inner, k); \
\
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(&ctx->inner, out); \
_update(&ctx->outer, out, _hashsz); \
_final(&ctx->outer, out); \
} \
\
\
/* --- 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(struct sha1_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);
}
static inline void sha1_cpy(struct sha1_ctx *restrict out, const struct sha1_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];
}
static inline void sha1_xor(struct sha1_ctx *restrict out, const struct sha1_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];
}
void cryptonite_sha1_transform(struct sha1_ctx* ctx, uint8_t block[SHA1_BLOCK_SIZE])
{
cryptonite_sha1_update(ctx, block, SHA1_BLOCK_SIZE);
}
DECL_PBKDF2(sha1,
SHA1_BLOCK_SIZE,
SHA1_DIGEST_SIZE,
struct sha1_ctx,
cryptonite_sha1_init,
cryptonite_sha1_update,
cryptonite_sha1_transform,
cryptonite_sha1_finalize,
sha1_cpy,
sha1_extract,
sha1_xor);
static inline void sha256_extract(struct 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(struct sha256_ctx *restrict out, const struct 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(struct sha256_ctx *restrict out, const struct 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];
}
void cryptonite_sha256_transform(struct sha256_ctx* ctx, uint8_t block[SHA256_BLOCK_SIZE])
{
cryptonite_sha256_update(ctx, block, SHA256_BLOCK_SIZE);
}
DECL_PBKDF2(sha256,
SHA256_BLOCK_SIZE,
SHA256_DIGEST_SIZE,
struct sha256_ctx,
cryptonite_sha256_init,
cryptonite_sha256_update,
cryptonite_sha256_transform,
cryptonite_sha256_finalize,
sha256_cpy,
sha256_extract,
sha256_xor);
static inline void sha512_extract(struct 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(struct sha512_ctx *restrict out, const struct 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(struct sha512_ctx *restrict out, const struct 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];
}
void cryptonite_sha512_transform(struct sha512_ctx* ctx, uint8_t block[SHA512_BLOCK_SIZE])
{
cryptonite_sha512_update(ctx, block, SHA512_BLOCK_SIZE);
}
DECL_PBKDF2(sha512,
SHA512_BLOCK_SIZE,
SHA512_DIGEST_SIZE,
struct sha512_ctx,
cryptonite_sha512_init,
cryptonite_sha512_update,
cryptonite_sha512_transform,
cryptonite_sha512_finalize,
sha512_cpy,
sha512_extract,
sha512_xor);
void cryptonite_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 cryptonite_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 cryptonite_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);
}

31
cbits/cryptonite_pbkdf2.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef CRYPTONITE_PBKDF2_H_
#define CRYPTONITE_PBKDF2_H_
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void cryptonite_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
);
void cryptonite_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
);
void cryptonite_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

View File

@ -26,10 +26,12 @@
#include <stdint.h> #include <stdint.h>
# define SHA1_BLOCK_SIZE 64
struct sha1_ctx struct sha1_ctx
{ {
uint64_t sz; uint64_t sz;
uint8_t buf[64]; uint8_t buf[SHA1_BLOCK_SIZE];
uint32_t h[5]; uint32_t h[5];
}; };

View File

@ -27,6 +27,8 @@
#include <stdint.h> #include <stdint.h>
#define SHA256_BLOCK_SIZE 64
struct sha256_ctx struct sha256_ctx
{ {
uint64_t sz; uint64_t sz;

View File

@ -26,10 +26,12 @@
#include <stdint.h> #include <stdint.h>
# define SHA512_BLOCK_SIZE 128
struct sha512_ctx struct sha512_ctx
{ {
uint64_t sz[2]; uint64_t sz[2];
uint8_t buf[128]; uint8_t buf[SHA512_BLOCK_SIZE];
uint64_t h[8]; uint64_t h[8];
}; };

View File

@ -240,6 +240,7 @@ Library
, cbits/cryptonite_tiger.c , cbits/cryptonite_tiger.c
, cbits/cryptonite_whirlpool.c , cbits/cryptonite_whirlpool.c
, cbits/cryptonite_scrypt.c , cbits/cryptonite_scrypt.c
, cbits/cryptonite_pbkdf2.c
include-dirs: cbits cbits/ed25519 include-dirs: cbits cbits/ed25519
if arch(x86_64) if arch(x86_64)

View File

@ -3,7 +3,7 @@
-- from <http://www.ietf.org/rfc/rfc6070.txt> -- from <http://www.ietf.org/rfc/rfc6070.txt>
module KAT_PBKDF2 (tests) where module KAT_PBKDF2 (tests) where
import Crypto.Hash (SHA1(..), SHA256(..)) import Crypto.Hash (SHA1(..), SHA256(..), SHA512(..))
import qualified Crypto.KDF.PBKDF2 as PBKDF2 import qualified Crypto.KDF.PBKDF2 as PBKDF2
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
@ -42,14 +42,48 @@ vectors_hmac_sha256 =
) )
] ]
vectors_hmac_sha512 :: [ (VectParams, ByteString) ]
vectors_hmac_sha512 =
[ ( ("password", "salt", 1, 32)
, "\x86\x7f\x70\xcf\x1a\xde\x02\xcf\xf3\x75\x25\x99\xa3\xa5\x3d\xc4\xaf\x34\xc7\xa6\x69\x81\x5a\xe5\xd5\x13\x55\x4e\x1c\x8c\xf2\x52"
)
, ( ("password", "salt", 2, 32)
, "\xe1\xd9\xc1\x6a\xa6\x81\x70\x8a\x45\xf5\xc7\xc4\xe2\x15\xce\xb6\x6e\x01\x1a\x2e\x9f\x00\x40\x71\x3f\x18\xae\xfd\xb8\x66\xd5\x3c"
)
, ( ("password", "salt", 4096, 32)
, "\xd1\x97\xb1\xb3\x3d\xb0\x14\x3e\x01\x8b\x12\xf3\xd1\xd1\x47\x9e\x6c\xde\xbd\xcc\x97\xc5\xc0\xf8\x7f\x69\x02\xe0\x72\xf4\x57\xb5"
)
, ( ("passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 1, 72)
, "n\x23\xf2\x76\x38\x08\x4b\x0f\x7e\xa1\x73\x4e\x0d\x98\x41\xf5\x5d\xd2\x9e\xa6\x0a\x83\x44\x66\xf3\x39\x6b\xac\x80\x1f\xac\x1e\xeb\x63\x80\x2f\x03\xa0\xb4\xac\xd7\x60\x3e\x36\x99\xc8\xb7\x44\x37\xbe\x83\xff\x01\xad\x7f\x55\xda\xc1\xef\x60\xf4\xd5\x64\x80\xc3\x5e\xe6\x8f\xd5\x2c\x69\x36"
)
]
tests = testGroup "PBKDF2" tests = testGroup "PBKDF2"
[ testGroup "KATs-HMAC-SHA1" (katTests (PBKDF2.prfHMAC SHA1) vectors_hmac_sha1) [ testGroup "KATs-HMAC-SHA1" (katTests (PBKDF2.prfHMAC SHA1) vectors_hmac_sha1)
, testGroup "KATs-HMAC-SHA1 (fast)" (katTestFastPBKDF2_SHA1 vectors_hmac_sha1)
, testGroup "KATs-HMAC-SHA256" (katTests (PBKDF2.prfHMAC SHA256) vectors_hmac_sha256) , testGroup "KATs-HMAC-SHA256" (katTests (PBKDF2.prfHMAC SHA256) vectors_hmac_sha256)
, testGroup "KATs-HMAC-SHA256 (fast)" (katTestFastPBKDF2_SHA256 vectors_hmac_sha256)
, testGroup "KATs-HMAC-SHA512" (katTests (PBKDF2.prfHMAC SHA512) vectors_hmac_sha512)
, testGroup "KATs-HMAC-SHA512 (fast)" (katTestFastPBKDF2_SHA512 vectors_hmac_sha512)
] ]
where katTests prf vects = map (toKatTest prf) $ zip is vects where katTests prf vects = map (toKatTest prf) $ zip is vects
toKatTest prf (i, ((pass, salt, iter, dkLen), output)) = toKatTest prf (i, ((pass, salt, iter, dkLen), output)) =
testCase (show i) (output @=? PBKDF2.generate prf (PBKDF2.Parameters iter dkLen) pass salt) testCase (show i) (output @=? PBKDF2.generate prf (PBKDF2.Parameters iter dkLen) pass salt)
katTestFastPBKDF2_SHA1 = map toKatTestFastPBKDF2_SHA1 . zip is
toKatTestFastPBKDF2_SHA1 (i, ((pass, salt, iter, dkLen), output)) =
testCase (show i) (output @=? PBKDF2.fastPBKDF2_SHA1 (PBKDF2.Parameters iter dkLen) pass salt)
katTestFastPBKDF2_SHA256 = map toKatTestFastPBKDF2_SHA256 . zip is
toKatTestFastPBKDF2_SHA256 (i, ((pass, salt, iter, dkLen), output)) =
testCase (show i) (output @=? PBKDF2.fastPBKDF2_SHA256 (PBKDF2.Parameters iter dkLen) pass salt)
katTestFastPBKDF2_SHA512 = map toKatTestFastPBKDF2_SHA512 . zip is
toKatTestFastPBKDF2_SHA512 (i, ((pass, salt, iter, dkLen), output)) =
testCase (show i) (output @=? PBKDF2.fastPBKDF2_SHA512 (PBKDF2.Parameters iter dkLen) pass salt)
is :: [Int] is :: [Int]
is = [1..] is = [1..]