-- |
-- Module      : Crypto.KDF.Argon2
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- 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 (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 (Variant -> Variant -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Variant -> Variant -> Bool
$c/= :: Variant -> Variant -> Bool
== :: Variant -> Variant -> Bool
$c== :: Variant -> Variant -> Bool
Eq,Eq Variant
Variant -> Variant -> Bool
Variant -> Variant -> Ordering
Variant -> Variant -> Variant
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Variant -> Variant -> Variant
$cmin :: Variant -> Variant -> Variant
max :: Variant -> Variant -> Variant
$cmax :: Variant -> Variant -> Variant
>= :: Variant -> Variant -> Bool
$c>= :: Variant -> Variant -> Bool
> :: Variant -> Variant -> Bool
$c> :: Variant -> Variant -> Bool
<= :: Variant -> Variant -> Bool
$c<= :: Variant -> Variant -> Bool
< :: Variant -> Variant -> Bool
$c< :: Variant -> Variant -> Bool
compare :: Variant -> Variant -> Ordering
$ccompare :: Variant -> Variant -> Ordering
Ord,ReadPrec [Variant]
ReadPrec Variant
Int -> ReadS Variant
ReadS [Variant]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Variant]
$creadListPrec :: ReadPrec [Variant]
readPrec :: ReadPrec Variant
$creadPrec :: ReadPrec Variant
readList :: ReadS [Variant]
$creadList :: ReadS [Variant]
readsPrec :: Int -> ReadS Variant
$creadsPrec :: Int -> ReadS Variant
Read,Int -> Variant -> ShowS
[Variant] -> ShowS
Variant -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Variant] -> ShowS
$cshowList :: [Variant] -> ShowS
show :: Variant -> String
$cshow :: Variant -> String
showsPrec :: Int -> Variant -> ShowS
$cshowsPrec :: Int -> Variant -> ShowS
Show,Int -> Variant
Variant -> Int
Variant -> [Variant]
Variant -> Variant
Variant -> Variant -> [Variant]
Variant -> Variant -> Variant -> [Variant]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Variant -> Variant -> Variant -> [Variant]
$cenumFromThenTo :: Variant -> Variant -> Variant -> [Variant]
enumFromTo :: Variant -> Variant -> [Variant]
$cenumFromTo :: Variant -> Variant -> [Variant]
enumFromThen :: Variant -> Variant -> [Variant]
$cenumFromThen :: Variant -> Variant -> [Variant]
enumFrom :: Variant -> [Variant]
$cenumFrom :: Variant -> [Variant]
fromEnum :: Variant -> Int
$cfromEnum :: Variant -> Int
toEnum :: Int -> Variant
$ctoEnum :: Int -> Variant
pred :: Variant -> Variant
$cpred :: Variant -> Variant
succ :: Variant -> Variant
$csucc :: Variant -> Variant
Enum,Variant
forall a. a -> a -> Bounded a
maxBound :: Variant
$cmaxBound :: Variant
minBound :: Variant
$cminBound :: Variant
Bounded)

-- | Which version of Argon2 to use
data Version = Version10 | Version13
    deriving (Version -> Version -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Version -> Version -> Bool
$c/= :: Version -> Version -> Bool
== :: Version -> Version -> Bool
$c== :: Version -> Version -> Bool
Eq,Eq Version
Version -> Version -> Bool
Version -> Version -> Ordering
Version -> Version -> Version
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Version -> Version -> Version
$cmin :: Version -> Version -> Version
max :: Version -> Version -> Version
$cmax :: Version -> Version -> Version
>= :: Version -> Version -> Bool
$c>= :: Version -> Version -> Bool
> :: Version -> Version -> Bool
$c> :: Version -> Version -> Bool
<= :: Version -> Version -> Bool
$c<= :: Version -> Version -> Bool
< :: Version -> Version -> Bool
$c< :: Version -> Version -> Bool
compare :: Version -> Version -> Ordering
$ccompare :: Version -> Version -> Ordering
Ord,ReadPrec [Version]
ReadPrec Version
Int -> ReadS Version
ReadS [Version]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Version]
$creadListPrec :: ReadPrec [Version]
readPrec :: ReadPrec Version
$creadPrec :: ReadPrec Version
readList :: ReadS [Version]
$creadList :: ReadS [Version]
readsPrec :: Int -> ReadS Version
$creadsPrec :: Int -> ReadS Version
Read,Int -> Version -> ShowS
[Version] -> ShowS
Version -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Version] -> ShowS
$cshowList :: [Version] -> ShowS
show :: Version -> String
$cshow :: Version -> String
showsPrec :: Int -> Version -> ShowS
$cshowsPrec :: Int -> Version -> ShowS
Show,Int -> Version
Version -> Int
Version -> [Version]
Version -> Version
Version -> Version -> [Version]
Version -> Version -> Version -> [Version]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Version -> Version -> Version -> [Version]
$cenumFromThenTo :: Version -> Version -> Version -> [Version]
enumFromTo :: Version -> Version -> [Version]
$cenumFromTo :: Version -> Version -> [Version]
enumFromThen :: Version -> Version -> [Version]
$cenumFromThen :: Version -> Version -> [Version]
enumFrom :: Version -> [Version]
$cenumFrom :: Version -> [Version]
fromEnum :: Version -> Int
$cfromEnum :: Version -> Int
toEnum :: Int -> Version
$ctoEnum :: Int -> Version
pred :: Version -> Version
$cpred :: Version -> Version
succ :: Version -> Version
$csucc :: Version -> Version
Enum,Version
forall a. a -> a -> Bounded a
maxBound :: Version
$cmaxBound :: Version
minBound :: Version
$cminBound :: Version
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
    { Options -> TimeCost
iterations  :: !TimeCost
    , Options -> TimeCost
memory      :: !MemoryCost
    , Options -> TimeCost
parallelism :: !Parallelism
    , Options -> Variant
variant     :: !Variant     -- ^ Which variant of Argon2 to use.
    , Options -> Version
version     :: !Version     -- ^ Which version of Argon2 to use.
    }
    deriving (Options -> Options -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Options -> Options -> Bool
$c/= :: Options -> Options -> Bool
== :: Options -> Options -> Bool
$c== :: Options -> Options -> Bool
Eq,Eq Options
Options -> Options -> Bool
Options -> Options -> Ordering
Options -> Options -> Options
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Options -> Options -> Options
$cmin :: Options -> Options -> Options
max :: Options -> Options -> Options
$cmax :: Options -> Options -> Options
>= :: Options -> Options -> Bool
$c>= :: Options -> Options -> Bool
> :: Options -> Options -> Bool
$c> :: Options -> Options -> Bool
<= :: Options -> Options -> Bool
$c<= :: Options -> Options -> Bool
< :: Options -> Options -> Bool
$c< :: Options -> Options -> Bool
compare :: Options -> Options -> Ordering
$ccompare :: Options -> Options -> Ordering
Ord,ReadPrec [Options]
ReadPrec Options
Int -> ReadS Options
ReadS [Options]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Options]
$creadListPrec :: ReadPrec [Options]
readPrec :: ReadPrec Options
$creadPrec :: ReadPrec Options
readList :: ReadS [Options]
$creadList :: ReadS [Options]
readsPrec :: Int -> ReadS Options
$creadsPrec :: Int -> ReadS Options
Read,Int -> Options -> ShowS
[Options] -> ShowS
Options -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Options] -> ShowS
$cshowList :: [Options] -> ShowS
show :: Options -> String
$cshow :: Options -> String
showsPrec :: Int -> Options -> ShowS
$cshowsPrec :: Int -> Options -> ShowS
Show)

saltMinLength :: Int
saltMinLength :: Int
saltMinLength = Int
8

outputMinLength :: Int
outputMinLength :: Int
outputMinLength = Int
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 :: Int
outputMaxLength = Int
0x7fffffff

defaultOptions :: Options
defaultOptions :: Options
defaultOptions =
    Options { iterations :: TimeCost
iterations  = TimeCost
1
            , memory :: TimeCost
memory      = TimeCost
2 forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
17 :: Int)
            , parallelism :: TimeCost
parallelism = TimeCost
4
            , variant :: Variant
variant     = Variant
Argon2i
            , version :: Version
version     = Version
Version13
            }

hash :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out)
     => Options
     -> password
     -> salt
     -> Int
     -> CryptoFailable out
hash :: forall password salt out.
(ByteArrayAccess password, ByteArrayAccess salt, ByteArray out) =>
Options -> password -> salt -> Int -> CryptoFailable out
hash Options
options password
password salt
salt Int
outLen
    | Int
saltLen forall a. Ord a => a -> a -> Bool
< Int
saltMinLength  = forall a. CryptoError -> CryptoFailable a
CryptoFailed CryptoError
CryptoError_SaltTooSmall
    | Int
outLen forall a. Ord a => a -> a -> Bool
< Int
outputMinLength = forall a. CryptoError -> CryptoFailable a
CryptoFailed CryptoError
CryptoError_OutputLengthTooSmall
    | Int
outLen forall a. Ord a => a -> a -> Bool
> Int
outputMaxLength = forall a. CryptoError -> CryptoFailable a
CryptoFailed CryptoError
CryptoError_OutputLengthTooBig
    | Bool
otherwise                = forall a. a -> CryptoFailable a
CryptoPassed forall a b. (a -> b) -> a -> b
$ forall a p. ByteArray a => Int -> (Ptr p -> IO ()) -> a
B.allocAndFreeze Int
outLen forall a b. (a -> b) -> a -> b
$ \Ptr HashOut
out -> do
        CVariant
res <- forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
B.withByteArray password
password forall a b. (a -> b) -> a -> b
$ \Ptr Pass
pPass ->
               forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
B.withByteArray salt
salt     forall a b. (a -> b) -> a -> b
$ \Ptr Salt
pSalt ->
                    TimeCost
-> TimeCost
-> TimeCost
-> Ptr Pass
-> CSize
-> Ptr Salt
-> CSize
-> Ptr HashOut
-> CSize
-> CVariant
-> CVariant
-> IO CVariant
argon2_hash (Options -> TimeCost
iterations Options
options)
                                (Options -> TimeCost
memory Options
options)
                                (Options -> TimeCost
parallelism Options
options)
                                Ptr Pass
pPass
                                (Int -> CSize
csizeOfInt Int
passwordLen)
                                Ptr Salt
pSalt
                                (Int -> CSize
csizeOfInt Int
saltLen)
                                Ptr HashOut
out
                                (Int -> CSize
csizeOfInt Int
outLen)
                                (Variant -> CVariant
cOfVariant forall a b. (a -> b) -> a -> b
$ Options -> Variant
variant Options
options)
                                (Version -> CVariant
cOfVersion forall a b. (a -> b) -> a -> b
$ Options -> Version
version Options
options)
        forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (CVariant
res forall a. Eq a => a -> a -> Bool
/= CVariant
0) forall a b. (a -> b) -> a -> b
$ forall a. HasCallStack => String -> a
error String
"argon2: hash: internal error"
  where
    saltLen :: Int
saltLen = forall ba. ByteArrayAccess ba => ba -> Int
B.length salt
salt
    passwordLen :: Int
passwordLen = forall ba. ByteArrayAccess ba => ba -> Int
B.length password
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 :: Version -> CVariant
cOfVersion Version
Version10 = CVariant
0x10
cOfVersion Version
Version13 = CVariant
0x13

cOfVariant :: Variant -> CVariant
cOfVariant :: Variant -> CVariant
cOfVariant Variant
Argon2d  = CVariant
0
cOfVariant Variant
Argon2i  = CVariant
1
cOfVariant Variant
Argon2id = CVariant
2

csizeOfInt :: Int -> CSize
csizeOfInt :: Int -> CSize
csizeOfInt = forall a b. (Integral a, Num b) => a -> b
fromIntegral

foreign import ccall unsafe "crypton_argon2_hash"
    argon2_hash :: Word32 -> Word32 -> Word32
                -> Ptr Pass -> CSize
                -> Ptr Salt -> CSize
                -> Ptr HashOut -> CSize
                -> CVariant
                -> CVersion
                -> IO CInt