pbkdf-1.1.0.0: Crypto/PBKDF/Core.hs
{-# 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<-[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