module Botan.PwdHash where

import Botan.Low.PwdHash (PBKDFName(..))
import qualified Botan.Low.PwdHash as Low

import Botan.Hash
import Botan.MAC
import Botan.Prelude

-- NOTE: I suspect that there is an implicit random generator access going on
--  under the hood, so these are to be relegated to MonadRandomIO, even though
--  they do not explicitly require an RNG context

pwdhash :: PBKDF -> Int -> ByteString -> ByteString -> ByteString
pwdhash :: PBKDF -> Int -> ByteString -> ByteString -> ByteString
pwdhash PBKDF
algo Int
outLen ByteString
passphrase ByteString
salt = IO ByteString -> ByteString
forall a. IO a -> a
unsafePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
-> Int
-> Int
-> Int
-> Int
-> ByteString
-> ByteString
-> IO ByteString
Low.pwdhash
    ByteString
name
    Int
p1
    Int
p2
    Int
p3
    Int
outLen
    ByteString
passphrase
    ByteString
salt
    where
        name :: ByteString
name = PBKDF -> ByteString
pbkdfName PBKDF
algo
        (Int
p1,Int
p2,Int
p3) = PBKDF -> (Int, Int, Int)
pbkdfParams PBKDF
algo

-- TODO: ... -> (PBKDF, ByteString) once we have parsers for PBKDFName -> PBKDF (with default values)
pwdhashTimed :: PBKDFName -> Int -> Int -> ByteString -> ByteString -> (PBKDFParams,ByteString)
pwdhashTimed :: ByteString
-> Int
-> Int
-> ByteString
-> ByteString
-> ((Int, Int, Int), ByteString)
pwdhashTimed ByteString
algo Int
msec Int
outLen ByteString
passphrase ByteString
salt = IO ((Int, Int, Int), ByteString) -> ((Int, Int, Int), ByteString)
forall a. IO a -> a
unsafePerformIO (IO ((Int, Int, Int), ByteString) -> ((Int, Int, Int), ByteString))
-> IO ((Int, Int, Int), ByteString)
-> ((Int, Int, Int), ByteString)
forall a b. (a -> b) -> a -> b
$ do
    (Int
p1,Int
p2,Int
p3,ByteString
out) <- ByteString
-> Int
-> Int
-> ByteString
-> ByteString
-> IO (Int, Int, Int, ByteString)
Low.pwdhashTimed
        ByteString
algo
        Int
msec
        Int
outLen
        ByteString
passphrase
        ByteString
salt
    ((Int, Int, Int), ByteString) -> IO ((Int, Int, Int), ByteString)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ((Int
p1,Int
p2,Int
p3), ByteString
out)

-- NOTE: We have the correct parameters for Scrypt and the Argons
--  However, the order is not necessarily correct, and it may also
--  be inconsistent comparing pwdhash with pwdhashTimed.
-- Further investigation is needed
data PBKDF
    = PBKDF2 MAC Int        -- Iterations
    | Scrypt Int Int Int    -- N, r, p
    | Argon2d Int Int Int   -- Iterations, memory, parallelism
    | Argon2i Int Int Int   -- Iterations, memory, parallelism
    | Argon2id Int Int Int  -- Iterations, memory, parallelism
    | Bcrypt Int            -- Iterations
    | OpenPGP_S2K Hash Int  -- Iterations

pbkdfName :: PBKDF -> PBKDFName
pbkdfName :: PBKDF -> ByteString
pbkdfName (PBKDF2 MAC
m Int
_)      = ByteString -> ByteString
Low.pbkdf2 (MAC -> ByteString
macName MAC
m)
pbkdfName (Scrypt Int
n Int
r Int
p)    = ByteString
Low.Scrypt
pbkdfName (Argon2d Int
i Int
m Int
p)   = ByteString
Low.Argon2d
pbkdfName (Argon2i Int
i Int
m Int
p)   = ByteString
Low.Argon2i
pbkdfName (Argon2id Int
i Int
m Int
p)  = ByteString
Low.Argon2id
pbkdfName (Bcrypt Int
i)        = ByteString
Low.Bcrypt_PBKDF
pbkdfName (OpenPGP_S2K Hash
h Int
_) = ByteString -> ByteString
Low.openPGP_S2K (Hash -> ByteString
hashName Hash
h)

type PBKDFParams = (Int,Int,Int)

pbkdfParams :: PBKDF -> PBKDFParams
pbkdfParams :: PBKDF -> (Int, Int, Int)
pbkdfParams (PBKDF2 MAC
_ Int
i)        = (Int
i, Int
0, Int
0)
pbkdfParams (Scrypt Int
n Int
r Int
p)      = (Int
n, Int
r, Int
p)
pbkdfParams (Argon2d Int
i Int
m Int
p)     = (Int
i, Int
m, Int
p)
pbkdfParams (Argon2i Int
i Int
m Int
p)     = (Int
i, Int
m, Int
p)
pbkdfParams (Argon2id Int
i Int
m Int
p)    = (Int
i, Int
m, Int
p)
pbkdfParams (Bcrypt Int
i)          = (Int
i, Int
0, Int
0)
pbkdfParams (OpenPGP_S2K Hash
_ Int
i)   = (Int
i, Int
0, Int
0)

pbkdfParamsNone :: PBKDFParams
pbkdfParamsNone :: (Int, Int, Int)
pbkdfParamsNone = (Int
0,Int
0,Int
0)