[KDF] move PBKDF2 and Scrypt to not be pinned to ByteString
This commit is contained in:
parent
1dacb7fa94
commit
02956f9ef0
@ -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)
|
||||
|
||||
@ -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 #-}
|
||||
|
||||
@ -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..]
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user