{-# LANGUAGE Trustworthy #-}
module GitHub.Data.Webhooks.Secure
( PayloadNotSecure(..)
, isSecurePayload
, assertSecurePayload
) where
import Crypto.Hash.Algorithms (SHA1)
import Crypto.MAC.HMAC (HMAC(..), hmac)
import Control.Monad (unless)
import Control.Exception (Exception, throwIO)
import Data.ByteArray (convert, constEq)
import Data.Monoid ((<>))
import Data.ByteString (ByteString)
import Data.Text (Text)
import qualified Data.ByteString.Base16 as B16
import qualified Data.Text.Encoding as E
isSecurePayload
:: Text
-> Maybe Text
-> ByteString
-> Bool
isSecurePayload secret shaOpt payload = maybe False (constEq ourSig) theirSig
where
hexDigest = B16.encode . convert . hmacGetDigest
theirSig = E.encodeUtf8 <$> shaOpt
ourSig = "sha1=" <> hexDigest (hmac (E.encodeUtf8 secret) payload :: HMAC SHA1)
data PayloadNotSecure = PayloadNotSecure
instance Exception PayloadNotSecure
instance Show PayloadNotSecure where
showsPrec _ PayloadNotSecure = showString "the origin of this request may not originate from GitHub"
assertSecurePayload
:: Text
-> Maybe Text
-> ByteString
-> IO ()
assertSecurePayload secret shaOpt payload = do
let secure = isSecurePayload secret shaOpt payload
unless secure $! throwIO PayloadNotSecure