pbkdf-1.1.1.0: Crypto/PBKDF/Core.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RecordWildCards #-}
module Crypto.PBKDF.Core
( sha1PBKDF
, sha1PBKDF'
, sha256PBKDF
, sha256PBKDF'
, sha512PBKDF
, 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
-- | make a SHA-1 parameter blocks (String edition)
sha1PBKDF :: String -> String -> Int -> Int -> PBKDF
sha1PBKDF pw na = sha1PBKDF' (BU.fromString pw) (BU.fromString na)
-- | make a SHA-1 parameter blocks (ByteString edition)
sha1PBKDF' :: BS.ByteString -> BS.ByteString -> 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)
-- | make a SHA-256 parameter blocks (String edition)
sha256PBKDF :: String -> String -> Int -> Int -> PBKDF
sha256PBKDF pw na = sha256PBKDF' (BU.fromString pw) (BU.fromString na)
-- | make a SHA-256 parameter blocks (ByteString edition)
sha256PBKDF' :: BS.ByteString -> BS.ByteString -> 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)
-- | make a SHA-512 parameter blocks (String edition)
sha512PBKDF :: String -> String -> Int -> Int -> PBKDF
sha512PBKDF pw na = sha512PBKDF' (BU.fromString pw) (BU.fromString na)
-- | make a SHA-512 parameter blocks (ByteString edition)
sha512PBKDF' :: BS.ByteString -> BS.ByteString -> 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 parameter block for the key generators (String edition)
pbkdf :: PRF -> String -> String -> Int -> Int -> PBKDF
pbkdf prf pw_s na_s c dkLen =
PBKDF prf (BU.fromString pw_s) (BU.fromString na_s) c dkLen
-- | the parameter block for the key generators
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
}
-- | contains the HMAC function and its underlying HASH function, along with
-- the size of the hashes it generates
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 key derivation 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 key derivation 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<-[1..] :: [Int],
let os = BLC.unpack $ B.encode bno ]
-- 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