{-# LANGUAGE OverloadedStrings #-}

module Authorize.Macaroon.Crypto
    ( createSignature
    , updateSignature
    , encryptKey
    , decryptKey
    , bindForRequest
    , deriveKey
    ) where

import           Crypto.Hash                       (SHA256)
import           Crypto.MAC.HMAC                   (HMAC, hmac)
import qualified Crypto.Saltine.Class              as Nacl
import           Crypto.Saltine.Core.SecretBox     (newNonce, secretbox,
                                                    secretboxOpen)
import qualified Crypto.Saltine.Internal.ByteSizes as Sizes
import           Data.ByteArray                    (ByteArray, ByteArrayAccess,
                                                    convert)
import           Data.ByteString                   (ByteString)
import qualified Data.ByteString                   as BS

import           Authorize.Macaroon.Types          (Key (..), KeyId (..),
                                                    MacaroonId (..),
                                                    Signature (..))


createSignature :: Key -> MacaroonId -> Signature
createSignature k m = Signature $ keyedHash k m


updateSignature :: Signature -> Maybe KeyId -> ByteString -> Signature
updateSignature s kid c = Signature $ maybe (keyedHash s) (keyedPairHash s) kid c


encryptKey :: Signature -> Key -> IO KeyId
encryptKey (Signature s) (Key k) = do
    n   <- newNonce
    key <- maybe err return $ Nacl.decode s
    return . KeyId $ Nacl.encode n <> secretbox key n (convert k)
    where
    err = error "Unable to decode key"


decryptKey :: Signature -> KeyId -> Maybe Key
decryptKey (Signature s) (KeyId kid) = do
    n   <- Nacl.decode nonceBytes
    key <- Nacl.decode s
    Key . convert <$> secretboxOpen key n ct
    where
    (nonceBytes, ct) = BS.splitAt Sizes.secretBoxNonce kid


bindForRequest :: Signature -> Signature -> Signature
bindForRequest = keyedPairHash zeroKey
    where
    zeroKey = BS.replicate 32 0x0


hmac256 :: (ByteArrayAccess k, ByteArrayAccess x) => k -> x -> HMAC SHA256
hmac256 = hmac


deriveKey :: Key -> Key
deriveKey (Key k) = Key . convert $ hmac256 tag k
    where
    tag :: ByteString
    tag = "macaroons-key-generator"


keyedHash
    :: (ByteArrayAccess k, ByteArrayAccess b, ByteArray c)
    => k -> b -> c
keyedHash k = convert . hmac256 k


keyedPairHash
    :: ( ByteArrayAccess k
       , ByteArrayAccess b
       , ByteArrayAccess c
       , ByteArray d
       , Monoid d
       )
    => k -> b -> c -> d
keyedPairHash k x y
    = keyedHash k (keyedHash k x <> keyedHash k y :: ByteString)