-- |
-- Verification of incomming webhook payloads, as described at
-- <https://developer.github.com/webhooks/securing/>

module GitHub.Data.Webhooks.Validate (
  isValidPayload
) where

import GitHub.Internal.Prelude
import Prelude ()

import Crypto.Hash.SHA1 (hmac)
import Data.ByteString  (ByteString)

import qualified Data.ByteString.Base16 as Hex
import qualified Data.Text.Encoding     as TE

-- | Validates a given payload against a given HMAC hexdigest using a given
-- secret.
-- Returns 'True' iff the given hash is non-empty and it's a valid signature of
-- the payload.
isValidPayload
  :: Text           -- ^ the secret
  -> Maybe Text     -- ^ the hash provided by the remote party
                    -- in @X-Hub-Signature@ (if any),
                    -- including the 'sha1=...' prefix
  -> ByteString     -- ^ the body
  -> Bool
isValidPayload :: Text -> Maybe Text -> ByteString -> Bool
isValidPayload Text
secret Maybe Text
shaOpt ByteString
payload = Bool -> (ByteString -> Bool) -> Maybe ByteString -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (ByteString
sign ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
==) Maybe ByteString
shaOptBS
  where
    shaOptBS :: Maybe ByteString
shaOptBS = Text -> ByteString
TE.encodeUtf8 (Text -> ByteString) -> Maybe Text -> Maybe ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
shaOpt
    hexDigest :: ByteString -> ByteString
hexDigest = ByteString -> ByteString
Hex.encode
    hm :: ByteString
hm = ByteString -> ByteString -> ByteString
hmac (Text -> ByteString
TE.encodeUtf8 Text
secret) ByteString
payload
    sign :: ByteString
sign = ByteString
"sha1=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString -> ByteString
hexDigest ByteString
hm