-- | -- Module : Crypto.KDF.Argon2 -- License : BSD-style -- Maintainer : Vincent Hanquez -- Stability : experimental -- Portability : unknown -- -- Argon2 hashing function (P-H-C winner) -- -- Recommended to use this module qualified -- -- File started from Argon2.hs, from Oliver Charles -- at https://github.com/ocharles/argon2 -- module Crypto.KDF.Argon2 ( Options(..) , TimeCost , MemoryCost , Parallelism , Variant(..) , Version(..) , defaultOptions -- * Hashing function , hash ) where import Crypto.Internal.ByteArray (ScrubbedBytes, ByteArray, ByteArrayAccess) import qualified Crypto.Internal.ByteArray as B import Crypto.Error import Control.Monad (when) import Data.Word import Foreign.C import Foreign.Ptr -- | Which variant of Argon2 to use. You should choose the variant that is most -- applicable to your intention to hash inputs. data Variant = Argon2d -- ^ Argon2d is faster than Argon2i and uses data-depending memory access, -- which makes it suitable for cryptocurrencies and applications with no -- threats from side-channel timing attacks. | Argon2i -- ^ Argon2i uses data-independent memory access, which is preferred -- for password hashing and password-based key derivation. Argon2i -- is slower as it makes more passes over the memory to protect from -- tradeoff attacks. | Argon2id -- ^ Argon2id is a hybrid of Argon2i and Argon2d, using a combination -- of data-depending and data-independent memory accesses, which gives -- some of Argon2i's resistance to side-channel cache timing attacks -- and much of Argon2d's resistance to GPU cracking attacks deriving (Eq,Ord,Read,Show,Enum,Bounded) -- | Which version of Argon2 to use data Version = Version10 | Version13 deriving (Eq,Ord,Read,Show,Enum,Bounded) -- | The time cost, which defines the amount of computation realized and therefore the execution time, given in number of iterations. -- -- 'FFI.ARGON2_MIN_TIME' <= 'hashIterations' <= 'FFI.ARGON2_MAX_TIME' type TimeCost = Word32 -- | The memory cost, which defines the memory usage, given in kibibytes. -- -- max 'FFI.ARGON2_MIN_MEMORY' (8 * 'hashParallelism') <= 'hashMemory' <= 'FFI.ARGON2_MAX_MEMORY' type MemoryCost = Word32 -- \ A parallelism degree, which defines the number of parallel threads. -- -- 'FFI.ARGON2_MIN_LANES' <= 'hashParallelism' <= 'FFI.ARGON2_MAX_LANES' && 'FFI.ARGON_MIN_THREADS' <= 'hashParallelism' <= 'FFI.ARGON2_MAX_THREADS' type Parallelism = Word32 -- | Parameters that can be adjusted to change the runtime performance of the -- hashing. data Options = Options { iterations :: !TimeCost , memory :: !MemoryCost , parallelism :: !Parallelism , variant :: !Variant -- ^ Which variant of Argon2 to use. , version :: !Version -- ^ Which version of Argon2 to use. } deriving (Eq,Ord,Read,Show) saltMinLength :: Int saltMinLength = 8 outputMinLength :: Int outputMinLength = 4 -- specification allows up to 2^32-1 but this is too big for a signed Int -- on a 32-bit architecture, so we limit tag length to 2^31-1 bytes outputMaxLength :: Int outputMaxLength = 0x7fffffff defaultOptions :: Options defaultOptions = Options { iterations = 1 , memory = 2 ^ (17 :: Int) , parallelism = 4 , variant = Argon2i , version = Version13 } hash :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out) => Options -> password -> salt -> Int -> CryptoFailable out hash options password salt outLen | saltLen < saltMinLength = CryptoFailed CryptoError_SaltTooSmall | outLen < outputMinLength = CryptoFailed CryptoError_OutputLengthTooSmall | outLen > outputMaxLength = CryptoFailed CryptoError_OutputLengthTooBig | otherwise = CryptoPassed $ B.allocAndFreeze outLen $ \out -> do res <- B.withByteArray password $ \pPass -> B.withByteArray salt $ \pSalt -> argon2_hash (iterations options) (memory options) (parallelism options) pPass (csizeOfInt passwordLen) pSalt (csizeOfInt saltLen) out (csizeOfInt outLen) (cOfVariant $ variant options) (cOfVersion $ version options) when (res /= 0) $ error "argon2: hash: internal error" where saltLen = B.length salt passwordLen = B.length password data Pass data Salt data HashOut type CVariant = CInt -- valid value is 0 (Argon2d), 1 (Argon2i) and 2 (Argon2id) type CVersion = CInt -- valid value is 0x10, 0x13 cOfVersion :: Version -> CVersion cOfVersion Version10 = 0x10 cOfVersion Version13 = 0x13 cOfVariant :: Variant -> CVariant cOfVariant Argon2d = 0 cOfVariant Argon2i = 1 cOfVariant Argon2id = 2 csizeOfInt :: Int -> CSize csizeOfInt = fromIntegral foreign import ccall unsafe "cryptonite_argon2_hash" argon2_hash :: Word32 -> Word32 -> Word32 -> Ptr Pass -> CSize -> Ptr Salt -> CSize -> Ptr HashOut -> CSize -> CVariant -> CVersion -> IO CInt