{-# LANGUAGE RecordWildCards #-}

module Passman.Engine.KeyDerivation.PBKDF2
    ( PBKDF2WithHmacSHA1(..)
    , PBKDF2WithHmacSHA256(..)
    ) where

import           Crypto.Hash.Algorithms             (HashAlgorithm, SHA1(..), SHA256(..))
import           Crypto.KDF.PBKDF2
import           Data.Semigroup                     ((<>))

import qualified Passman.Engine.ByteString          as B
import           Passman.Engine.Errors
import           Passman.Engine.KeyDerivation.Class
import           Passman.Engine.KeyDerivation.Internal

newtype PBKDF2WithHmacSHA1 = PBKDF2WithHmacSHA1 Int
    deriving (Show, Eq)

instance KDF PBKDF2WithHmacSHA1 where
    deriveKey (PBKDF2WithHmacSHA1 baseIterations) = deriveKey' SHA1 baseIterations
    maxSize (PBKDF2WithHmacSHA1 _) = Nothing

newtype PBKDF2WithHmacSHA256 = PBKDF2WithHmacSHA256 Int
    deriving (Show, Eq)

instance KDF PBKDF2WithHmacSHA256 where
    deriveKey (PBKDF2WithHmacSHA256 baseIterations) = deriveKey' SHA256 baseIterations
    maxSize (PBKDF2WithHmacSHA256 _) = Nothing


baseSalt :: B.ByteString
baseSalt = B.pack [ 0x78, 0x94, 0xad, 0xf8, 0x2f, 0x7b, 0x07, 0x11, 0x85, 0xf9, 0x44, 0xbe, 0x25, 0x3b, 0x16, 0x57 ]

deriveKey' :: HashAlgorithm a => a -> Int -> Parameters -> Key -> Salt -> Either EngineError B.ByteString
deriveKey' alg baseIterations Parameters{..} key salt = do
    checkRequest key' salt' iterCounts
    return $ generate prf params key' (baseSalt <> salt')
    where
        params = Parameters (baseIterations + iterCounts) outputLength
        prf = prfHMAC alg
        key' = unwrapKey key
        salt' = unwrapSalt salt

