aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJulien Tanguy <julien.tanguy@jhome.fr>2015-07-08 18:13:14 +0200
committerJulien Tanguy <julien.tanguy@jhome.fr>2015-07-08 19:16:46 +0200
commitc830f7c2cf925ce340f4097d76ea2a3bc94cb4a6 (patch)
tree21a98f3a5129c4ef8efbca8c9f6b1e76290d536f
parent1dcd44f350f9842027b498e89ae586275047a3d3 (diff)
downloadhmacaroons-c830f7c2cf925ce340f4097d76ea2a3bc94cb4a6.tar.gz
hmacaroons-c830f7c2cf925ce340f4097d76ea2a3bc94cb4a6.tar.zst
hmacaroons-c830f7c2cf925ce340f4097d76ea2a3bc94cb4a6.zip
Rewrite Verifier with Validation
-rw-r--r--default.nix8
-rw-r--r--hmacaroons.cabal2
-rw-r--r--src/Crypto/Macaroon/Verifier.hs73
-rw-r--r--test/Crypto/Macaroon/Verifier/Tests.hs59
4 files changed, 42 insertions, 100 deletions
diff --git a/default.nix b/default.nix
index d968974..bd725a9 100644
--- a/default.nix
+++ b/default.nix
@@ -1,6 +1,6 @@
1{ mkDerivation, attoparsec, base, base64-bytestring, byteable 1{ mkDerivation, attoparsec, base, base64-bytestring, byteable
2, bytestring, cereal, cryptohash, deepseq, hex, QuickCheck, stdenv 2, bytestring, cereal, cryptohash, deepseq, either, hex, QuickCheck
3, tasty, tasty-hunit, tasty-quickcheck 3, stdenv, tasty, tasty-hunit, tasty-quickcheck
4}: 4}:
5mkDerivation { 5mkDerivation {
6 pname = "hmacaroons"; 6 pname = "hmacaroons";
@@ -8,11 +8,11 @@ mkDerivation {
8 src = ./.; 8 src = ./.;
9 buildDepends = [ 9 buildDepends = [
10 attoparsec base base64-bytestring byteable bytestring cereal 10 attoparsec base base64-bytestring byteable bytestring cereal
11 cryptohash deepseq hex 11 cryptohash deepseq either hex
12 ]; 12 ];
13 testDepends = [ 13 testDepends = [
14 attoparsec base base64-bytestring byteable bytestring cereal 14 attoparsec base base64-bytestring byteable bytestring cereal
15 cryptohash hex QuickCheck tasty tasty-hunit tasty-quickcheck 15 cryptohash either hex QuickCheck tasty tasty-hunit tasty-quickcheck
16 ]; 16 ];
17 homepage = "https://github.com/jtanguy/hmacaroons"; 17 homepage = "https://github.com/jtanguy/hmacaroons";
18 description = "Haskell implementation of macaroons"; 18 description = "Haskell implementation of macaroons";
diff --git a/hmacaroons.cabal b/hmacaroons.cabal
index b70a984..3aa338a 100644
--- a/hmacaroons.cabal
+++ b/hmacaroons.cabal
@@ -86,6 +86,7 @@ benchmark bench
86 cereal >= 0.4, 86 cereal >= 0.4,
87 cryptohash >=0.11 && <0.12, 87 cryptohash >=0.11 && <0.12,
88 -- cipher-aes >=0.2 && <0.3, 88 -- cipher-aes >=0.2 && <0.3,
89 either >=4.4,
89 hex >= 0.1, 90 hex >= 0.1,
90 deepseq >= 1.1, 91 deepseq >= 1.1,
91 criterion >= 1.1 92 criterion >= 1.1
@@ -102,6 +103,7 @@ test-suite test
102 byteable >= 0.1 && <0.2, 103 byteable >= 0.1 && <0.2,
103 cereal >= 0.4, 104 cereal >= 0.4,
104 cryptohash >=0.11 && <0.12, 105 cryptohash >=0.11 && <0.12,
106 either >=4.4,
105 hex >= 0.1, 107 hex >= 0.1,
106 tasty >= 0.10, 108 tasty >= 0.10,
107 tasty-hunit >= 0.9, 109 tasty-hunit >= 0.9,
diff --git a/src/Crypto/Macaroon/Verifier.hs b/src/Crypto/Macaroon/Verifier.hs
index 02cb448..713a971 100644
--- a/src/Crypto/Macaroon/Verifier.hs
+++ b/src/Crypto/Macaroon/Verifier.hs
@@ -1,5 +1,5 @@
1{-# LANGUAGE OverloadedStrings #-} 1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RankNTypes #-} 2{-# LANGUAGE RankNTypes #-}
3{-| 3{-|
4Module : Crypto.Macaroon.Verifier 4Module : Crypto.Macaroon.Verifier
5Copyright : (c) 2015 Julien Tanguy 5Copyright : (c) 2015 Julien Tanguy
@@ -13,79 +13,50 @@ Portability : portable
13 13
14-} 14-}
15module Crypto.Macaroon.Verifier ( 15module Crypto.Macaroon.Verifier (
16 Verified(..) 16 Verifier
17 , CaveatVerifier
18 , (<???>)
19 , verifyMacaroon 17 , verifyMacaroon
20 , verifySig 18 , verifySig
21 , verifyExact 19 -- , verifyExact
22 , verifyFun 20 -- , verifyFun
23 , module Data.Attoparsec.ByteString.Char8 21 , module Data.Attoparsec.ByteString.Char8
24 , verifyCavs 22 , verifyCavs
25) where 23) where
26 24
27 25
28import Crypto.Hash 26import Crypto.Hash
27import Data.Attoparsec.ByteString
28import Data.Attoparsec.ByteString.Char8
29import Data.Bool 29import Data.Bool
30import qualified Data.ByteString as BS
31import Data.Byteable 30import Data.Byteable
31import qualified Data.ByteString as BS
32import Data.Either
33import Data.Either.Validation
32import Data.Foldable 34import Data.Foldable
33import Data.Function 35import Data.Function
34import Data.Maybe 36import Data.Maybe
35import Data.Traversable 37import Data.Traversable
36import Data.Attoparsec.ByteString
37import Data.Attoparsec.ByteString.Char8
38 38
39import Crypto.Macaroon.Internal 39import Crypto.Macaroon.Internal
40 40
41type Verifier = Caveat -> Maybe (Either String Caveat)
41 42
42-- | Opaque datatype for now. Might need more explicit errors 43verifySig :: Key -> Macaroon -> Either String Macaroon
43data Verified = Ok | Failed deriving (Show,Eq) 44verifySig k m = bool (Left "Signatures do not match") (Right m) $
44
45instance Monoid Verified where
46 mempty = Ok
47 mappend Ok Ok = Ok
48 mappend _ _ = Failed
49
50
51data CaveatVerifier = CV { vFun :: Caveat -> Maybe Verified , helpText :: String}
52
53instance Eq CaveatVerifier where
54 (==) = (==) `on` helpText
55
56instance Show CaveatVerifier where
57 show = helpText
58
59(<???>) :: (Caveat -> Maybe Verified) -> String -> CaveatVerifier
60f <???> t = CV f t
61
62verifySig :: Key -> Macaroon -> Verified
63verifySig k m = bool Failed Ok $
64 signature m == foldl' hash (toBytes (hmac derivedKey (identifier m) :: HMAC SHA256)) (caveats m) 45 signature m == foldl' hash (toBytes (hmac derivedKey (identifier m) :: HMAC SHA256)) (caveats m)
65 where 46 where
66 hash s c = toBytes (hmac s (vid c `BS.append` cid c) :: HMAC SHA256) 47 hash s c = toBytes (hmac s (vid c `BS.append` cid c) :: HMAC SHA256)
67 derivedKey = toBytes (hmac "macaroons-key-generator" k :: HMAC SHA256) 48 derivedKey = toBytes (hmac "macaroons-key-generator" k :: HMAC SHA256)
68 49
69verifyMacaroon :: Key -> [CaveatVerifier] -> Macaroon -> Verified 50verifyMacaroon :: Key -> [Verifier] -> Macaroon -> Either String Macaroon
70verifyMacaroon secret verifiers m = verifySig secret m `mappend` verifyCavs verifiers m 51verifyMacaroon secret verifiers m = verifySig secret m >>= verifyCavs verifiers
71
72 52
73verifyCavs :: [CaveatVerifier] -> Macaroon -> Verified 53verifyCavs :: [Verifier] -> Macaroon -> Either String Macaroon
74verifyCavs verifiers m = foldMap (\c -> fromMaybe Failed $ foldMap (($ c) . vFun) verifiers) (caveats m) 54verifyCavs verifiers m = case partitionEithers verifiedCaveats of
55 ([],_) -> Right m
56 (errs,_) -> Left (mconcat errs)
57 where
58 verifiedCaveats = map (\c -> defaultFail c $ foldMap (fmap eitherToValidation . ($c)) verifiers) $ caveats m
59 defaultFail c = maybe (Left ("No validation for this caveat: " ++ show c)) validationToEither
75 60
76verifyExact :: (Eq a) => Key -> a -> Parser a -> Caveat -> Maybe Verified
77verifyExact k expected = verifyFun k (expected ==)
78 61
79verifyFun :: Key -> (a -> Bool) -> Parser a -> Caveat -> Maybe Verified 62-- TODO: define API
80verifyFun key f parser cav = if key `BS.isPrefixOf` cid cav then
81 case parseOnly kvparser (cid cav) of
82 Right v -> (bool Failed Ok . f) <$> Just v
83 Left _ -> Just Failed
84 else Nothing
85 where
86 kvparser = do
87 key <- string key
88 skipSpace
89 string "="
90 skipSpace
91 parser <* endOfInput
diff --git a/test/Crypto/Macaroon/Verifier/Tests.hs b/test/Crypto/Macaroon/Verifier/Tests.hs
index 101fa26..4a9295f 100644
--- a/test/Crypto/Macaroon/Verifier/Tests.hs
+++ b/test/Crypto/Macaroon/Verifier/Tests.hs
@@ -16,7 +16,8 @@ import Data.List
16import qualified Data.ByteString.Char8 as B8 16import qualified Data.ByteString.Char8 as B8
17import Test.Tasty 17import Test.Tasty
18-- import Test.Tasty.HUnit 18-- import Test.Tasty.HUnit
19import Test.Tasty.QuickCheck 19import Test.Tasty.QuickCheck hiding (Success, Failure)
20import Data.Either
20 21
21import Crypto.Macaroon 22import Crypto.Macaroon
22import Crypto.Macaroon.Verifier 23import Crypto.Macaroon.Verifier
@@ -25,7 +26,6 @@ import Crypto.Macaroon.Instances
25 26
26tests :: TestTree 27tests :: TestTree
27tests = testGroup "Crypto.Macaroon.Verifier" [ sigs 28tests = testGroup "Crypto.Macaroon.Verifier" [ sigs
28 , firstParty
29 ] 29 ]
30 30
31{- 31{-
@@ -45,52 +45,21 @@ m2 = addFirstPartyCaveat "test = caveat" m
45m3 :: Macaroon 45m3 :: Macaroon
46m3 = addFirstPartyCaveat "value = 42" m2 46m3 = addFirstPartyCaveat "value = 42" m2
47 47
48exTC = verifyExact "test" "caveat" (many' letter_ascii) <???> "test = caveat" 48-- exTC = verifyExact "test" "caveat" (many' letter_ascii)
49exTZ = verifyExact "test" "bleh" (many' letter_ascii) <???> "test = bleh" 49-- exTZ = verifyExact "test" "bleh" (many' letter_ascii)
50exV42 = verifyExact "value" 42 decimal <???> "value = 42" 50-- exV42 = verifyExact "value" 42 decimal
51exV43 = verifyExact "value" 43 decimal <???> "value = 43" 51-- exV43 = verifyExact "value" 43 decimal
52 52
53funTCPre = verifyFun "test" ("cav" `isPrefixOf`) (many' letter_ascii) <???> "test startsWith cav" 53-- funTCPre = verifyFun "test" (string "test = " *> many' letter_ascii)
54funTV43lte = verifyFun "value" (<= 43) decimal <???> "value <= 43" 54-- (\e -> if "cav" `isPrefixOf` e then Right e else Left "Does not start with cav" )
55-- funTV43lte = verifyFun "value" (string "value = " *> decimal)
56-- (\v -> if v <= 43 then Right v else Left "Greater than 43")
55 57
56allvs = [exTC, exTZ, exV42, exV43, funTCPre, funTV43lte] 58-- allvs = [exTC, exTZ, exV42, exV43, funTCPre, funTV43lte]
57 59
58{- 60{-
59 - Tests 61 - Tests
60 -} 62 -}
61sigs = testProperty "Signatures" $ \sm -> verifySig (secret sm) (macaroon sm) == Ok 63sigs = testProperty "Signatures" $ \sm -> verifySig (secret sm) (macaroon sm) == Right (macaroon sm)
62 64
63firstParty = testGroup "First party caveats" [ 65-- TODO: Re-do tests
64 testGroup "Pure verifiers" [
65 testProperty "Zero caveat" $
66 forAll (sublistOf allvs) (\vs -> Ok == verifyCavs vs m)
67 , testProperty "One caveat" $
68 forAll (sublistOf allvs) (\vs -> disjoin [
69 Ok == verifyCavs vs m2 .&&. any (`elem` vs) [exTC,funTCPre] .&&. (exTZ `notElem` vs)
70 , Failed === verifyCavs vs m2
71 ])
72 , testProperty "Two Exact" $
73 forAll (sublistOf allvs) (\vs -> disjoin [
74 Ok == verifyCavs vs m3 .&&.
75 any (`elem` vs) [exTC,funTCPre] .&&. (exTZ `notElem` vs) .&&.
76 any (`elem` vs) [exV42,funTV43lte] .&&. (exV43 `notElem` vs)
77 , Failed === verifyCavs vs m3
78 ])
79 ]
80 , testGroup "Pure verifiers with sig" [
81 testProperty "Zero caveat" $
82 forAll (sublistOf allvs) (\vs -> Ok == verifyMacaroon sec vs m)
83 , testProperty "One caveat" $
84 forAll (sublistOf allvs) (\vs -> disjoin [
85 Ok == verifyMacaroon sec vs m2 .&&. any (`elem` vs) [exTC,funTCPre] .&&. (exTZ `notElem` vs)
86 , Failed === verifyMacaroon sec vs m2
87 ])
88 , testProperty "Two Exact" $
89 forAll (sublistOf allvs) (\vs -> disjoin [
90 Ok == verifyMacaroon sec vs m3 .&&.
91 any (`elem` vs) [exTC,funTCPre] .&&. (exTZ `notElem` vs) .&&.
92 any (`elem` vs) [exV42,funTV43lte] .&&. (exV43 `notElem` vs)
93 , Failed === verifyMacaroon sec vs m3
94 ])
95 ]
96 ]