paddle-0.1.0.0: src/Paddle/WebHook/Signature.hs
module Paddle.WebHook.Signature where
import Prelude ()
import qualified Prelude
import Protolude hiding (toS)
import Protolude.Conv
import qualified Data.Map.Strict as M
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as LBS
import qualified OpenSSL as SSL
import qualified OpenSSL.EVP.Digest as SSL
import qualified OpenSSL.EVP.Verify as SSL
import qualified Data.ByteString.Base64 as Base64
import Paddle.Env (Env(..))
type SignatureBody = M.Map Text [Text]
-- Given all fields in webhook request, validate against their signature
validateSignature :: (MonadIO m) => Env -> SignatureBody -> m (Either Text ())
validateSignature env rawFields =
let
fields = M.fromList $ map (\(k ,v) -> (toS k, toS (maybe "" identity (head v)))) (M.toList rawFields)
signature :: Either Prelude.String ByteString
signature = maybeToEither "Missing signature field." (M.lookup "p_signature" fields)
signatureDecode :: ByteString -> Either Prelude.String ByteString
signatureDecode = Base64.decode . toS
-- we need to verify the signature against the PHP-serialized request parameters (excluding p_signature)
-- here's a gist that does it in Swift: https://gist.github.com/drewmccormack/a51b18ffeda8f596a11a8623481344d8
serializeString str = "s:" <> BSB.intDec (BS.length str) <> ":\"" <> BSB.byteString str <> "\";"
serialize sig = (\x -> Right (x, sig)) <$> LBS.toStrict $ BSB.toLazyByteString $
"a:" <> BSB.intDec (M.size fields - 1) <> ":{" <>
foldMap (\( key, value ) -> serializeString key <> serializeString value) (M.toAscList $ M.delete "p_signature" fields)
<> "}"
in do
case signature >>= signatureDecode >>= serialize of
Left err -> return $ Left (toS err)
Right (serializedFields, sigBytes) -> liftIO $ SSL.withOpenSSL $ do
Just sha1 <- SSL.getDigestByName "SHA1"
verifyRes <- SSL.verifyBS sha1 sigBytes (pctxPubKey env) serializedFields
if (verifyRes == SSL.VerifySuccess)
then return $ Right ()
else return $ Left "Request has invalid signature"