module Crypto.KDF.PBKDF2
( PRF
, prfHMAC
, Parameters(..)
, generate
) where
import Data.Word
import Data.Bits
import Foreign.Marshal.Alloc
import Foreign.Ptr (plusPtr)
import Crypto.Hash (HashAlgorithm)
import qualified Crypto.MAC.HMAC as HMAC
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, Bytes)
import qualified Crypto.Internal.ByteArray as B
import Data.Memory.PtrMethods
type PRF password =
password
-> Bytes
-> Bytes
prfHMAC :: (HashAlgorithm a, ByteArrayAccess password)
=> a
-> PRF password
prfHMAC alg k = hmacIncr alg (HMAC.initialize k)
where hmacIncr :: HashAlgorithm a => a -> HMAC.Context a -> (Bytes -> Bytes)
hmacIncr _ !ctx = \b -> B.convert $ HMAC.finalize $ HMAC.update ctx b
data Parameters = Parameters
{ iterCounts :: Int
, outputLength :: Int
}
generate :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray ba)
=> PRF password
-> Parameters
-> password
-> salt
-> ba
generate prf params password salt =
B.allocAndFreeze (outputLength params) $ \p -> do
memSet p 0 (outputLength params)
loop 1 (outputLength params) p
where
!runPRF = prf password
!hLen = B.length $ runPRF B.empty
loop iterNb len p
| len == 0 = return ()
| len < hLen = partial iterNb len p
| otherwise = do
let applyMany 0 _ = return ()
applyMany i uprev = do
let uData = runPRF uprev
B.withByteArray uData $ \u -> memXor p p u hLen
applyMany (i1) uData
applyMany (iterCounts params) (B.convert salt `B.append` toBS iterNb)
loop (iterNb+1) (len hLen) (p `plusPtr` hLen)
partial iterNb len p = allocaBytesAligned hLen 8 $ \tmp -> do
let applyMany :: Int -> Bytes -> IO ()
applyMany 0 _ = return ()
applyMany i uprev = do
let uData = runPRF uprev
B.withByteArray uData $ \u -> memXor tmp tmp u hLen
applyMany (i1) uData
memSet tmp 0 hLen
applyMany (iterCounts params) (B.convert salt `B.append` toBS iterNb)
memCopy p tmp len
toBS :: ByteArray ba => Word32 -> ba
toBS w = B.pack [a,b,c,d]
where a = fromIntegral (w `shiftR` 24)
b = fromIntegral ((w `shiftR` 16) .&. 0xff)
c = fromIntegral ((w `shiftR` 8) .&. 0xff)
d = fromIntegral (w .&. 0xff)