packages feed

stripe-signature 1.0.0.10 → 1.0.0.12

raw patch · 4 files changed

+76/−40 lines, 4 filesdep +cryptohash-sha256dep −cryptonitedep −memoryPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: cryptohash-sha256

Dependencies removed: cryptonite, memory

API changes (from Hackage documentation)

- Stripe.Signature: digest :: WebhookSecretKey -> Natural -> ByteString -> HMAC SHA256
+ Stripe.Signature: digest :: WebhookSecretKey -> Natural -> ByteString -> ByteString

Files

changelog.md view
@@ -29,3 +29,9 @@ Support GHC 9.0, `cryptonite` 0.29, `bytestring` 0.11, `memory` 0.16  Switch `base16-bytestring` version from 0.1 to 1.0++## 1.0.0.12 - 2022-01-08++Drop `cryptonite` and `memory` dependencies; HMAC is now done using the `cryptohash-sha256` package instead++Add some tests for the `isSigValid` function
library/Stripe/Signature.hs view
@@ -1,5 +1,3 @@-{-# OPTIONS_GHC -Wall #-}- {- | https://stripe.com/docs/webhooks/signatures#verify-manually -}  module Stripe.Signature@@ -19,12 +17,8 @@ -- bytestring import Data.ByteString (ByteString) --- cryptonite-import Crypto.Hash     (SHA256)-import Crypto.MAC.HMAC as HMAC---- memory-import qualified Data.ByteArray+-- cryptohash-sha256+import qualified Crypto.Hash.SHA256  -- stripe-concepts import Stripe.Concepts (WebhookSecretKey (..))@@ -36,13 +30,13 @@  isSigValid :: Sig -> WebhookSecretKey -> ByteString -> Bool isSigValid x secret body =-    Data.List.any (Data.ByteArray.eq correctDigest) (sigV1 x)+    Data.List.any ((==) correctDigest) (sigV1 x)   where     correctDigest = digest secret (sigTime x) body -digest :: WebhookSecretKey -> Natural -> ByteString -> HMAC SHA256+digest :: WebhookSecretKey -> Natural -> ByteString -> ByteString digest (WebhookSecretKey secret) time body =-    HMAC.hmac secret (signedPayload time body)+    Crypto.Hash.SHA256.hmac secret (signedPayload time body)  signedPayload :: Natural -> ByteString -> ByteString signedPayload time body =
stripe-signature.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0  name: stripe-signature-version: 1.0.0.10+version: 1.0.0.12  synopsis: Verification of Stripe webhook signatures category: Web@@ -24,8 +24,6 @@ license-file: license.txt  build-type: Simple-tested-with: GHC==8.2.2, GHC==8.4.4, GHC==8.6.3, GHC==8.8.1, GHC==8.10.1,-             GHC==9.0.1  extra-source-files:     changelog.md@@ -33,6 +31,7 @@ library     hs-source-dirs: library     default-language: Haskell2010+    ghc-options: -Wall      exposed-modules:         Stripe.Signature@@ -42,9 +41,7 @@           || ^>= 4.15       , base16-bytestring ^>= 1.0       , bytestring ^>= 0.10 || ^>= 0.11-      , cryptonite ^>= 0.25 || ^>= 0.26 || ^>= 0.27 || ^>= 0.28-                || ^>= 0.29-      , memory ^>= 0.14 || ^>= 0.15 || ^>= 0.16+      , cryptohash-sha256 ^>= 0.11.101       , stripe-concepts ^>= 1.0       , text ^>= 1.2 @@ -53,11 +50,13 @@     hs-source-dirs: test     type: exitcode-stdio-1.0     main-is: test.hs-    ghc-options: -threaded+    ghc-options: -Wall      build-depends:         base ^>= 4.10 || ^>= 4.11 || ^>= 4.12 || ^>= 4.13 || ^>= 4.14           || ^>= 4.15+      , base16-bytestring ^>= 1.0       , bytestring ^>= 0.10 || ^>= 0.11+      , stripe-concepts ^>= 1.0       , stripe-signature       , text ^>= 1.2
test/test.hs view
@@ -1,40 +1,77 @@-{-# OPTIONS_GHC -Wall #-}- import Stripe.Signature  -- base import Control.Monad   (unless)+import Data.Foldable   (for_) import Data.Word       (Word8) import Numeric.Natural (Natural)-import System.Exit     (die)+import System.Exit     (exitFailure) +-- base16-bytestring+import qualified Data.ByteString.Base16 as Base16+ -- bytestring import qualified Data.ByteString+import qualified Data.ByteString.Char8 +-- stripe-concepts+import Stripe.Concepts (WebhookSecretKey (..))+ -- text import qualified Data.Text -test_string :: String-test_string =-    "t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e\-    \77a0e56ff536d0ce8e108d8bd,v0=6ffbb59b2300aae63f27240606\-    \9a9788598b792a944a07aba816edb039989a39"--test_time :: Natural-test_time = 1492774577--test_v1Bytes :: [Word8]-test_v1Bytes =-    [82,87,168,105,231,236,235,237,163,42,255,166,44,220,163,250-    ,81,202,215,231,122,14,86,255,83,109,12,232,225,8,216,189]+data Failure = Failure String  main :: IO () main =+    unless (null failures) $ do+        for_ failures $ \(Failure x) -> putStrLn ("🔥 " ++ x)+        exitFailure++failures :: [Failure]+failures = parseFailures ++ validationFailures++parseFailures :: [Failure]+parseFailures =     case parseSig (Data.Text.pack test_string) of-        Nothing -> die "Signature parsing failed"+        Nothing -> [Failure "Signature parsing failed"]         Just sig ->-          do-            unless (sigTime sig == test_time) $-                die ("sigTime: " ++ show (sigTime sig))-            unless ((Data.ByteString.unpack <$> sigV1 sig) == [test_v1Bytes]) $-                die ("sigV1: " ++ show (Data.ByteString.unpack <$> sigV1 sig))+            case sigTime sig == test_time of+                True -> []+                False -> [Failure $ "sigTime: " ++ show (sigTime sig)]+            +++            case (Data.ByteString.unpack <$> sigV1 sig) == [test_v1Bytes] of+                True -> []+                False -> [Failure $ "sigV1: " ++ show (Data.ByteString.unpack <$> sigV1 sig)]++  where+    test_string :: String+    test_string =+        "t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e\+        \77a0e56ff536d0ce8e108d8bd,v0=6ffbb59b2300aae63f27240606\+        \9a9788598b792a944a07aba816edb039989a39"++    test_time :: Natural+    test_time = 1492774577++    test_v1Bytes :: [Word8]+    test_v1Bytes =+        [82,87,168,105,231,236,235,237,163,42,255,166,44,220,163,250+        ,81,202,215,231,122,14,86,255,83,109,12,232,225,8,216,189]++validationFailures :: [Failure]+validationFailures =+    case Base16.decode (Data.ByteString.Char8.pack "f89c0a0e96fa6304a7ebd5ecaf13bc1beb859577cd7cafb7bdbc43c3bc2f6afa") of+        Left _ -> [Failure "Digest decoding failed"]+        Right d ->+            case isSigValid Sig{ sigTime = 123, sigV1 = [d] } (WebhookSecretKey (Data.ByteString.Char8.pack "secret")) (Data.ByteString.Char8.pack "hello") of+                True -> []+                False -> [Failure "isSigValid rejects a valid signature"]+            +++            case isSigValid Sig{ sigTime = 123, sigV1 = [d] } (WebhookSecretKey (Data.ByteString.Char8.pack "secret")) (Data.ByteString.Char8.pack "hello!") of+                False -> []+                True -> [Failure "isSigValid accepts an invalid signature"]+            +++            case isSigValid Sig{ sigTime = 123, sigV1 = [Data.ByteString.Char8.pack "whatever", d] } (WebhookSecretKey (Data.ByteString.Char8.pack "secret")) (Data.ByteString.Char8.pack "hello") of+                True -> []+                False -> [Failure "isSigValid should accept if *any* of the sigs are valid"]