diff --git a/Crypto/PBKDF.hs b/Crypto/PBKDF.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PBKDF.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE BangPatterns       #-}
+{-# LANGUAGE RecordWildCards    #-}
+
+module Crypto.PBKDF 
+    ( sha1PBKDF1
+    , sha256PBKDF1
+    , sha512PBKDF1
+    , sha1PBKDF2
+    , sha256PBKDF2
+    , sha512PBKDF2
+    ) where
+
+import           Crypto.PBKDF.Core
+import           Text.Bytedump
+
+
+-- | Password Based Key Derivation Functions:
+--   This module provides stock implementations of the PBKDF functions from
+--   RFC-2898 based on the SHA-1, SHA-256 and SHA-256 hash functions. Each
+--   function takes the password and salt as a string and returns a hex
+--   string. To work with ByteStrings and provide your own hash and
+--   psuedo-random use the Crypto.PBKDF.Core that are used to implement
+--   these functions.
+
+
+-- | SHA-based PBKDF1 functions
+
+sha1PBKDF1, sha256PBKDF1, sha512PBKDF1 
+    :: String   -- ^ the password (will be encoded with UTF-8) 
+    -> String   -- ^ the salt     (will be encoded with UTF-8)
+    -> Int      -- ^ the iteration count
+    -> String   -- ^ the result key as a hex string
+sha1PBKDF1   pw_s na_s c = dumpRawBS $ pbkdf1_ $ sha1PBKDF   pw_s na_s c 0
+sha256PBKDF1 pw_s na_s c = dumpRawBS $ pbkdf1_ $ sha256PBKDF pw_s na_s c 0
+sha512PBKDF1 pw_s na_s c = dumpRawBS $ pbkdf1_ $ sha512PBKDF pw_s na_s c 0
+
+
+-- | SHA-based PBKDF2 functions
+
+sha1PBKDF2, sha256PBKDF2, sha512PBKDF2
+    :: String   -- ^ the password (will be encoded with UTF-8) 
+    -> String   -- ^ the salt     (will be encoded with UTF-8)
+    -> Int      -- ^ the iteration count
+    -> Int      -- ^ the length of the key to be generated (in octets)
+    -> String   -- ^ the result key as a hex string
+
+sha1PBKDF2   pw_s na_s c dkLen = dumpRawBS $ pbkdf2_ $ sha1PBKDF   pw_s na_s c dkLen
+sha256PBKDF2 pw_s na_s c dkLen = dumpRawBS $ pbkdf2_ $ sha256PBKDF pw_s na_s c dkLen
+sha512PBKDF2 pw_s na_s c dkLen = dumpRawBS $ pbkdf2_ $ sha512PBKDF pw_s na_s c dkLen
diff --git a/Crypto/PBKDF/Core.hs b/Crypto/PBKDF/Core.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PBKDF/Core.hs
@@ -0,0 +1,137 @@
+{-# LANGUAGE BangPatterns       #-}
+{-# LANGUAGE RecordWildCards    #-}
+
+module Crypto.PBKDF.Core
+    ( sha1PBKDF
+    , sha256PBKDF
+    , sha512PBKDF
+    , pbkdf
+    , PBKDF(..)
+    , PRF(..)
+    , pbkdf1_
+    , pbkdf2_
+    ) where
+
+import qualified Data.Binary                    as B
+import           Data.Bits
+import qualified Data.ByteString                as BS
+import qualified Data.ByteString.UTF8           as BU
+import qualified Data.ByteString.Lazy           as BLC
+import qualified Crypto.Hash                    as CH
+import           Crypto.MAC.HMAC
+import           Data.Byteable
+
+
+-- | SHA-1 generator
+
+sha1PBKDF :: String -> String -> Int -> Int -> PBKDF
+sha1PBKDF =
+    pbkdf
+        PRF
+            { prf_hmac      = hmac sha1 64          -- 512-bit block
+            , prf_hash      = sha1                  -- SHA-1
+            , prf_hLen      = 20                    -- 160-bit hash
+            } 
+  where
+    sha1 = toBytes . (CH.hash :: BS.ByteString -> CH.Digest CH.SHA1)
+
+-- | SHA-256 generator
+
+sha256PBKDF :: String -> String -> Int -> Int -> PBKDF
+sha256PBKDF =
+    pbkdf
+        PRF
+            { prf_hmac      = hmac sha256 64        -- 512-bit block
+            , prf_hash      = sha256                -- SHA-256
+            , prf_hLen      = 32                    -- 256-bit hash
+            } 
+  where
+    sha256 = toBytes . (CH.hash :: BS.ByteString -> CH.Digest CH.SHA256)
+
+-- | SHA-512 generator
+
+sha512PBKDF :: String -> String -> Int -> Int -> PBKDF
+sha512PBKDF =
+    pbkdf
+        PRF
+            { prf_hmac      = hmac sha512 128       -- 1024-bit block
+            , prf_hash      = sha512                -- SHA-512
+            , prf_hLen      = 64                    -- 512-bit hash
+            } 
+  where
+    sha512 = toBytes . (CH.hash :: BS.ByteString -> CH.Digest CH.SHA512)
+
+-- | construct a PBKDF generator from a HMAC function
+
+pbkdf :: PRF -> String -> String -> Int -> Int -> PBKDF
+pbkdf prf pw_s na_s c dkLen =
+    PBKDF
+        { pbkdf_PRF    = prf
+        , pbkdf_P      = BU.fromString pw_s
+        , pbkdf_S      = BU.fromString na_s
+        , pbkdf_c      = c
+        , pbkdf_dkLen  = dkLen
+        }
+
+-- | PBKDF generator parameters
+
+data PBKDF
+    = PBKDF
+        { pbkdf_PRF    :: PRF              -- ^ the psuedo-random (i.e., HMAC) function
+        , pbkdf_P      :: BS.ByteString    -- ^ the password (will be UTF-8 encoded)
+        , pbkdf_S      :: BS.ByteString    -- ^ the salt     (will be UTF-8 encoded)
+        , pbkdf_c      :: Int              -- ^ iteration count for applying the HMAC
+        , pbkdf_dkLen  :: Int              -- ^ the length of the o/p derived key 
+        }
+
+-- | the HMAC function and its underlying HASH function
+
+data PRF
+    = PRF
+        { prf_hmac      :: BS.ByteString -> BS.ByteString -> BS.ByteString  -- ^ the PR/HMAC function
+        , prf_hash      :: BS.ByteString -> BS.ByteString                   -- ^ the underlying hash function
+        , prf_hLen      :: Int                                              -- ^ number of octets in o/p hash
+        } 
+
+
+-- | the pbkdf1_ core function
+
+pbkdf1_ :: PBKDF -> BS.ByteString
+pbkdf1_ PBKDF{..} = iterate_n pbkdf_c prf_hash $ pbkdf_P `BS.append` pbkdf_S
+  where
+    PRF{..} = pbkdf_PRF
+
+-- | the pbkdf2_ core function
+
+pbkdf2_ :: PBKDF -> BS.ByteString
+pbkdf2_ PBKDF{..} = BS.take pbkdf_dkLen $ BS.concat $ map f $ zip zbs ivs
+  where
+    f (zb,iv)   = snd $ itr zb $ pbkdf_S `BS.append` iv
+
+    itr zb msg  = iterate_n pbkdf_c g (msg,zb)
+
+    g (!u,!p)   = (u',BS.pack $ BS.zipWith xor p u')
+      where
+        u' = prf_hmac pbkdf_P u
+
+    r           = pbkdf_dkLen - (l - 1) * prf_hLen
+
+    l           = ceiling $ (fromIntegral pbkdf_dkLen :: Double) / fromIntegral prf_hLen
+    
+    zbs         = replicate l (mk_zb prf_hLen) ++ [mk_zb r]
+    
+    mk_zb sz    = BS.pack $ replicate sz 0
+
+    PRF{..}     = pbkdf_PRF
+
+    ivs = [ BS.pack $ drop (length os - 4) os | bno<-bnos, let os = BLC.unpack $ B.encode bno ]
+      where
+        bnos = [1..] :: [Int]
+
+-- iterate a function over an argument k times
+
+iterate_n :: Int -> (a->a) -> a -> a
+iterate_n !i f !x =
+    case i of
+      0 -> x
+      _ -> iterate_n (i-1) f $ f x
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2001-2005, Chris Dornan
+
+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.
+ 
+- The name of the authors may not be used to endorse or promote products
+derived from this software without specific prior written permission. 
+
+THIS SOFTWARE IS PROVIDED "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
+THE AUTHORS 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,5 @@
+pbkdf
+=====
+
+The Password Based Key Derivation Functions described in RFC 2898 with
+a test suite based on the test vectors published in RFC6070.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/pbkdf.cabal b/pbkdf.cabal
new file mode 100644
--- /dev/null
+++ b/pbkdf.cabal
@@ -0,0 +1,58 @@
+Name:                pbkdf
+Version:             1.0.0.0
+Synopsis:            Haskell implementation of the PBKDF functions from RFC-2898.
+Description:         The Password Based Key Derivation Functions described in RFC-2898 with a test suite to verify that it works with the test vectors published in RFC6070.
+Homepage:            https://github.com/cdornan/pbkdf
+License:             BSD3
+License-file:        LICENSE
+Author:              Chris Dornan
+Maintainer:          chris.dornan@irisconnect.com
+Copyright:           (C) Chris Dornan
+Category:            Cryptography
+Build-type:          Simple
+
+Cabal-version:       >=1.14
+
+Source-repository this
+    type:           git
+    location:       https://github.com/cdornan/pbkdf.git
+    tag:            1.0.0.0
+
+Source-repository head
+    type:           git
+    location:       https://github.com/cdornan/pbkdf.git
+
+Library
+    Exposed-modules:
+        Crypto.PBKDF
+        Crypto.PBKDF.Core
+
+    Build-depends:
+        base                >= 4.5   && < 5.0       ,
+        binary              >= 0.5                  ,
+        byteable            >= 0.1                  ,
+        bytedump            >= 1.0                  ,
+        bytestring          >= 0.9                  ,
+        cryptohash          >= 0.10                 ,
+        utf8-string         >= 0.3.7
+        
+    GHC-Options: -Wall
+
+    Default-Language: Haskell2010
+
+Test-Suite rfc-6070
+    type:          exitcode-stdio-1.0
+    main-is:       rfc6070.hs
+    Build-depends:
+        base                >= 4.5                  ,
+        binary              >= 0.5                  ,
+        byteable            >= 0.1                  ,
+        bytedump            >= 1.0                  ,
+        bytestring          >= 0.9                  ,
+        cryptohash          >= 0.10                 ,
+        pbkdf                                       ,
+        utf8-string         >= 0.3.7
+
+    GHC-Options: -Wall
+
+    Default-Language: Haskell2010
diff --git a/rfc6070.hs b/rfc6070.hs
new file mode 100644
--- /dev/null
+++ b/rfc6070.hs
@@ -0,0 +1,46 @@
+
+import           Crypto.PBKDF
+import           Text.Printf
+import           Control.Applicative
+import           System.Exit
+
+
+main :: IO ()
+main = 
+ do ok <- run_tests
+    case ok of
+      True  -> return ()
+      False -> exitWith $ ExitFailure 1
+
+run_tests :: IO Bool
+run_tests = and <$> mapM test test_vectors
+  where
+    test (pw,na,c,out) =
+     do putStr $ printf "%-30s %-40s %8d (%d) : " (show pw) (show na) c dkl
+        case ok of
+          True  -> putStrLn          "passed"
+          False -> putStrLn $ printf "FAILED (%s[%d])" t_out (length t_out)
+        return ok
+      where
+        ok    = t_out == out
+        t_out = sha1PBKDF2 pw na c dkl
+        dkl   = length out `div` 2
+
+-- RFC6070 test vectors
+
+test_vectors :: [(String,String,Int,String)]
+test_vectors = 
+    [ (,,,) "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"
+    ]
+
+--simple_test :: IO ()
+--simple_test = putStrLn $ sha1PBKDF2   "password" "salt" 4096  20
+
+--sha256_test :: IO ()
+--sha256_test = putStrLn $ sha256PBKDF2 "password" "salt" 1000000 32
+
