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  : 
    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"
 Given all fields in webhook request, validate against their signature
 we need to verify the signature against the PHP-serialized request parameters (excluding p_signature)
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]

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

  here 's a gist that does it in  : 
    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"