[KDF] move PBKDF2 and Scrypt to not be pinned to ByteString

This commit is contained in:
Vincent Hanquez 2015-05-22 15:19:42 +01:00
parent 1dacb7fa94
commit 02956f9ef0
4 changed files with 40 additions and 35 deletions

View File

@ -17,47 +17,49 @@ module Crypto.KDF.PBKDF2
import Data.Word
import Data.Bits
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Foreign.Marshal.Alloc
import Foreign.Ptr (plusPtr)
import Crypto.Hash (HashAlgorithm)
import qualified Crypto.MAC.HMAC as HMAC
import Crypto.Internal.ByteArray (ByteArray)
import qualified Crypto.Internal.ByteArray as B (allocAndFreeze, convert, withByteArray)
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, Bytes)
import qualified Crypto.Internal.ByteArray as B
import Data.Memory.PtrMethods
-- | The PRF used for PBKDF2
type PRF = B.ByteString -- ^ the password parameters
-> B.ByteString -- ^ the content
-> B.ByteString -- ^ prf(password,content)
type PRF password =
password -- ^ the password parameters
-> Bytes -- ^ the content
-> Bytes -- ^ prf(password,content)
-- | PRF for PBKDF2 using HMAC with the hash algorithm as parameter
prfHMAC :: HashAlgorithm a
=> a -- ^ the Hash Algorithm to use with HMAC
-> PRF -- ^ the PRF functiont o use
prfHMAC :: (HashAlgorithm a, ByteArrayAccess password)
=> a
-> PRF password
prfHMAC alg k = hmacIncr alg (HMAC.initialize k)
where hmacIncr :: HashAlgorithm a => a -> HMAC.Context a -> (ByteString -> ByteString)
where hmacIncr :: HashAlgorithm a => a -> HMAC.Context a -> (Bytes -> Bytes)
hmacIncr _ !ctx = \b -> B.convert $ HMAC.finalize $ HMAC.update ctx b
-- | Parameters for PBKDF2
data Parameters = Parameters
{ password :: ByteString -- ^ Password (bytes encoded)
, salt :: ByteString -- ^ Salt (bytes encoded)
, iterCounts :: Int -- ^ the number of user-defined iterations for the algorithms. e.g. WPA2 uses 4000.
, outputLength :: Int -- ^ the number of bytes to generate out of PBKDF2
{ iterCounts :: Int -- ^ the number of user-defined iterations for the algorithms. e.g. WPA2 uses 4000.
, outputLength :: Int -- ^ the number of bytes to generate out of PBKDF2
}
-- | generate the pbkdf2 key derivation function from the output
generate :: ByteArray ba => PRF -> Parameters -> ba
generate prf params =
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 params)
!runPRF = prf password
!hLen = B.length $ runPRF B.empty
-- run the following f function on each complete chunk.
@ -76,21 +78,22 @@ generate prf params =
let uData = runPRF uprev
B.withByteArray uData $ \u -> memXor p p u hLen
applyMany (i-1) uData
applyMany (iterCounts params) (salt params `B.append` toBS iterNb)
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 0 _ = return ()
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 (i-1) uData
memSet tmp 0 hLen
applyMany (iterCounts params) (salt params `B.append` toBS iterNb)
applyMany (iterCounts params) (B.convert salt `B.append` toBS iterNb)
memCopy p tmp len
-- big endian encoding of Word32
toBS :: Word32 -> ByteString
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)

View File

@ -15,7 +15,6 @@ module Crypto.KDF.Scrypt
) where
import Data.Word
import Data.ByteString (ByteString)
import Foreign.Marshal.Alloc
import Foreign.Ptr (Ptr, plusPtr)
import Control.Monad (forM_)
@ -23,30 +22,33 @@ import Control.Monad (forM_)
import Crypto.Hash (SHA256(..))
import qualified Crypto.KDF.PBKDF2 as PBKDF2
import Crypto.Internal.Compat (popCount, unsafeDoIO)
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess)
import qualified Crypto.Internal.ByteArray as B
-- | Parameters for Scrypt
data Parameters = Parameters
{ password :: ByteString -- ^ Password (bytes encoded)
, salt :: ByteString -- ^ Salt (bytes encoded)
, n :: Word64 -- ^ Cpu/Memory cost ratio. must be a power of 2 greater than 1. also known as N.
, r :: Int -- ^ Must satisfy r * p < 2^30
, p :: Int -- ^ Must satisfy r * p < 2^30
, outputLength :: Int -- ^ the number of bytes to generate out of Scrypt
{ n :: Word64 -- ^ Cpu/Memory cost ratio. must be a power of 2 greater than 1. also known as N.
, r :: Int -- ^ Must satisfy r * p < 2^30
, p :: Int -- ^ Must satisfy r * p < 2^30
, outputLength :: Int -- ^ the number of bytes to generate out of Scrypt
}
foreign import ccall "cryptonite_scrypt_smix"
ccryptonite_scrypt_smix :: Ptr Word8 -> Word32 -> Word64 -> Ptr Word8 -> Ptr Word8 -> IO ()
-- | Generate the scrypt key derivation data
generate :: Parameters -> ByteString
generate params
generate :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray output)
=> Parameters
-> password
-> salt
-> output
generate params password salt
| r params * p params >= 0x40000000 =
error "Scrypt: invalid parameters: r and p constraint"
| popCount (n params) /= 1 =
error "Scrypt: invalid parameters: n not a power of 2"
| otherwise = unsafeDoIO $ do
let b = PBKDF2.generate prf (PBKDF2.Parameters (password params) (salt params) 1 intLen) :: B.Bytes
let b = PBKDF2.generate prf (PBKDF2.Parameters 1 intLen) password salt :: B.Bytes
newSalt <- B.copy b $ \bPtr ->
allocaBytesAligned (128*(fromIntegral $ n params)*(r params)) 8 $ \v ->
allocaBytesAligned (256*r params) 8 $ \xy -> do
@ -54,7 +56,7 @@ generate params
ccryptonite_scrypt_smix (bPtr `plusPtr` (i * 128 * (r params)))
(fromIntegral $ r params) (n params) v xy
return $ PBKDF2.generate prf (PBKDF2.Parameters (password params) newSalt 1 (outputLength params))
return $ PBKDF2.generate prf (PBKDF2.Parameters 1 (outputLength params)) password (newSalt :: B.Bytes)
where prf = PBKDF2.prfHMAC SHA256
intLen = p params * 128 * r params
{-# NOINLINE generate #-}

View File

@ -49,7 +49,7 @@ tests = testGroup "PBKDF2"
where katTests prf vects = map (toKatTest prf) $ zip is vects
toKatTest prf (i, ((pass, salt, iter, dkLen), output)) =
testCase (show i) (output @=? PBKDF2.generate prf (PBKDF2.Parameters pass salt iter dkLen))
testCase (show i) (output @=? PBKDF2.generate prf (PBKDF2.Parameters iter dkLen) pass salt)
is :: [Int]
is = [1..]

View File

@ -30,4 +30,4 @@ vectors =
tests = testGroup "Scrypt"
$ map toCase $ zip [(1::Int)..] vectors
where toCase (i, ((pass,salt,n,r,p,dklen), output)) =
testCase (show i) (output @=? Scrypt.generate (Scrypt.Parameters pass salt n r p dklen))
testCase (show i) (output @=? Scrypt.generate (Scrypt.Parameters n r p dklen) pass salt)