aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Crypto/Macaroon.hs21
-rw-r--r--src/Crypto/Macaroon/Internal.hs6
-rw-r--r--src/Crypto/Macaroon/Verifier.hs141
-rw-r--r--src/Crypto/Macaroon/Verifier/Internal.hs78
4 files changed, 158 insertions, 88 deletions
diff --git a/src/Crypto/Macaroon.hs b/src/Crypto/Macaroon.hs
index bfcf8df..c9c8c21 100644
--- a/src/Crypto/Macaroon.hs
+++ b/src/Crypto/Macaroon.hs
@@ -23,6 +23,7 @@ module Crypto.Macaroon (
23 -- * Types 23 -- * Types
24 Macaroon 24 Macaroon
25 , Caveat 25 , Caveat
26 , Secret
26 , Key 27 , Key
27 , Location 28 , Location
28 , Sig 29 , Sig
@@ -33,9 +34,9 @@ module Crypto.Macaroon (
33 , caveats 34 , caveats
34 , signature 35 , signature
35 -- ** Caveats 36 -- ** Caveats
36 , caveatLoc 37 , cl
37 , caveatId 38 , cid
38 , caveatVId 39 , vid
39 40
40 -- * Create Macaroons 41 -- * Create Macaroons
41 , create 42 , create
@@ -54,23 +55,11 @@ import qualified Data.ByteString.Char8 as B8
54import Crypto.Macaroon.Internal 55import Crypto.Macaroon.Internal
55 56
56-- | Create a Macaroon from its key, identifier and location 57-- | Create a Macaroon from its key, identifier and location
57create :: Key -> Key -> Location -> Macaroon 58create :: Secret -> Key -> Location -> Macaroon
58create secret ident loc = MkMacaroon loc ident [] (toBytes (hmac derivedKey ident :: HMAC SHA256)) 59create secret ident loc = MkMacaroon loc ident [] (toBytes (hmac derivedKey ident :: HMAC SHA256))
59 where 60 where
60 derivedKey = toBytes (hmac "macaroons-key-generator" secret :: HMAC SHA256) 61 derivedKey = toBytes (hmac "macaroons-key-generator" secret :: HMAC SHA256)
61 62
62-- | Caveat target location
63caveatLoc :: Caveat -> Location
64caveatLoc = cl
65
66-- | Caveat identifier
67caveatId :: Caveat -> Key
68caveatId = cid
69
70-- | Caveat verification identifier
71caveatVId :: Caveat -> Key
72caveatVId = vid
73
74-- | Inspect a macaroon's contents. For debugging purposes. 63-- | Inspect a macaroon's contents. For debugging purposes.
75inspect :: Macaroon -> String 64inspect :: Macaroon -> String
76inspect = show 65inspect = show
diff --git a/src/Crypto/Macaroon/Internal.hs b/src/Crypto/Macaroon/Internal.hs
index 2f56512..d6e80d3 100644
--- a/src/Crypto/Macaroon/Internal.hs
+++ b/src/Crypto/Macaroon/Internal.hs
@@ -23,7 +23,11 @@ import qualified Data.ByteString.Char8 as B8
23import Data.Hex 23import Data.Hex
24import Data.List 24import Data.List
25 25
26-- |Type alias for Macaroons and Caveat keys and identifiers 26
27-- |Type alias for Macaroons secret keys
28type Secret = BS.ByteString
29
30-- |Type alias for Macaroons and Caveat and identifiers
27type Key = BS.ByteString 31type Key = BS.ByteString
28 32
29-- |Type alias for Macaroons and Caveat locations 33-- |Type alias for Macaroons and Caveat locations
diff --git a/src/Crypto/Macaroon/Verifier.hs b/src/Crypto/Macaroon/Verifier.hs
index 02cb448..a739437 100644
--- a/src/Crypto/Macaroon/Verifier.hs
+++ b/src/Crypto/Macaroon/Verifier.hs
@@ -1,5 +1,8 @@
1{-# LANGUAGE OverloadedStrings #-} 1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE RankNTypes #-} 2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE TypeSynonymInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
3{-| 6{-|
4Module : Crypto.Macaroon.Verifier 7Module : Crypto.Macaroon.Verifier
5Copyright : (c) 2015 Julien Tanguy 8Copyright : (c) 2015 Julien Tanguy
@@ -13,79 +16,75 @@ Portability : portable
13 16
14-} 17-}
15module Crypto.Macaroon.Verifier ( 18module Crypto.Macaroon.Verifier (
16 Verified(..) 19 verify
17 , CaveatVerifier 20 , ValidationError(ValidatorError, ParseError)
18 , (<???>) 21 -- , (.<), (.<=), (.==), (.>), (.>=)
19 , verifyMacaroon 22 -- , module Data.Attoparsec.ByteString.Char8
20 , verifySig
21 , verifyExact
22 , verifyFun
23 , module Data.Attoparsec.ByteString.Char8
24 , verifyCavs
25) where 23) where
26 24
27 25
28import Crypto.Hash 26import Control.Monad
27import Control.Monad.IO.Class
28import Data.Attoparsec.ByteString
29import Data.Attoparsec.ByteString.Char8
29import Data.Bool 30import Data.Bool
30import qualified Data.ByteString as BS 31import qualified Data.ByteString as BS
31import Data.Byteable 32import Data.Either.Combinators
32import Data.Foldable
33import Data.Function
34import Data.Maybe
35import Data.Traversable
36import Data.Attoparsec.ByteString
37import Data.Attoparsec.ByteString.Char8
38 33
39import Crypto.Macaroon.Internal 34import Crypto.Macaroon.Internal
35import Crypto.Macaroon.Verifier.Internal
36
37
38
39
40-- (.<) :: (MonadIO m, Ord a, Parsable a) => Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
41-- (.<) = verifyOpBool "Greater or equal" (<) "<"
42
43-- (.<=) :: (MonadIO m, Ord a, Parsable a) => Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
44-- (.<=) = verifyOpBool "Strictly greater" (<=) "<="
45
46-- (.==) :: (MonadIO m, Eq a, Parsable a) => Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
47-- (.==) = verifyOpBool "Not equal" (==) "="
48
49-- (.>) :: (MonadIO m, Ord a, Parsable a) => Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
50-- (.>) = verifyOpBool "Less or equal" (>) ">"
51
52-- (.>=) :: (MonadIO m, Ord a, Parsable a) => Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
53-- (.>=) = verifyOpBool "Strictly less" (>=) ">="
54
55-- | Verify a Macaroon's signature and caveats, given the corresponding Secret
56-- and verifiers.
57--
58-- A verifier is a function of type
59-- @'MonadIO' m => 'Caveat' -> m ('Maybe' ('Either' 'ValidatorError' 'Caveat'))@.
60--
61-- It should return:
62--
63-- * 'Nothing' if the caveat is not related to the verifier
64-- (for instance a time verifier is given an action caveat);
65-- * 'Just' ('Left' ('ParseError' reason)) if the verifier is related to the
66-- caveat, but failed to parse it completely;
67-- * 'Just' ('Left' ('ValidatorError' reason)) if the verifier is related to the
68-- caveat, parsed it and invalidated it;
69-- * 'Just' ('Right' '()') if the verifier has successfully verified the
70-- given caveat
71verify :: MonadIO m => Secret -> [Caveat -> m (Maybe (Either ValidationError ()))] -> Macaroon -> m (Either ValidationError Macaroon)
72verify secret verifiers m = join <$> forM (verifySig secret m) (verifyCavs verifiers)
73
74
75-- verifyOpBool :: MonadIO m => String -> Parser a -> (a -> a -> Bool) -> BS.ByteString -> Key -> m a -> Caveat -> m (Maybe (Either ValidationError Caveat))
76-- verifyOpBool err p f op k val = verifyParser k valueParser $ \s -> do
77-- expected <- val
78-- return $ bool (Left $ ValidatorError err) (Right Win) =<< f expected <$> mapLeft ParseError (parseOnly p s)
79-- where
80-- valueParser = string op *> skipSpace *> takeByteString
81
82-- verifyParser :: (MonadIO m) => Key -> Parser a -> (a -> m (Either ValidationError Win)) -> Caveat -> m (Maybe (Either ValidationError Caveat))
83-- verifyParser k p f c = case parseOnly keyParser . cid $ c of
84-- Left _ -> return Nothing
85-- Right bs -> Just <$> case parseOnly p bs of
86-- Left err -> return $ Left $ ParseError err
87-- Right a -> fmap (const c) <$> f a
88-- where
89-- keyParser = string k *> skipSpace *> takeByteString
40 90
41
42-- | Opaque datatype for now. Might need more explicit errors
43data Verified = Ok | Failed deriving (Show,Eq)
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)
65 where
66 hash s c = toBytes (hmac s (vid c `BS.append` cid c) :: HMAC SHA256)
67 derivedKey = toBytes (hmac "macaroons-key-generator" k :: HMAC SHA256)
68
69verifyMacaroon :: Key -> [CaveatVerifier] -> Macaroon -> Verified
70verifyMacaroon secret verifiers m = verifySig secret m `mappend` verifyCavs verifiers m
71
72
73verifyCavs :: [CaveatVerifier] -> Macaroon -> Verified
74verifyCavs verifiers m = foldMap (\c -> fromMaybe Failed $ foldMap (($ c) . vFun) verifiers) (caveats m)
75
76verifyExact :: (Eq a) => Key -> a -> Parser a -> Caveat -> Maybe Verified
77verifyExact k expected = verifyFun k (expected ==)
78
79verifyFun :: Key -> (a -> Bool) -> Parser a -> Caveat -> Maybe Verified
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/src/Crypto/Macaroon/Verifier/Internal.hs b/src/Crypto/Macaroon/Verifier/Internal.hs
new file mode 100644
index 0000000..2af55d3
--- /dev/null
+++ b/src/Crypto/Macaroon/Verifier/Internal.hs
@@ -0,0 +1,78 @@
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RankNTypes #-}
3{-|
4Module : Crypto.Macaroon.Verifier.Internal
5Copyright : (c) 2015 Julien Tanguy
6License : BSD3
7
8Maintainer : julien.tanguy@jhome.fr
9Stability : experimental
10Portability : portable
11
12
13
14-}
15module Crypto.Macaroon.Verifier.Internal where
16
17import Control.Monad
18import Control.Monad.IO.Class
19import Crypto.Hash
20import Data.Bool
21import Data.Byteable
22import qualified Data.ByteString as BS
23import Data.Either
24import Data.Either.Validation
25import Data.Foldable
26import Data.Maybe
27import Data.Monoid
28
29import Crypto.Macaroon.Internal
30
31-- | Type representing different validation errors.
32-- Only 'ParseError' and 'ValidatorError' are exported, 'SigMismatch' and
33-- 'NoVerifier' are used internally and should not be used by the user
34data ValidationError = SigMismatch -- ^ Signatures do not match
35 | NoVerifier -- ^ No verifier can handle a given caveat
36 | ParseError String -- ^ A verifier had a parse error
37 | ValidatorError String -- ^ A verifier failed
38 deriving (Show,Eq)
39
40-- | The 'Monoid' instance is written so 'SigMismatch' is an annihilator,
41-- and 'NoVerifier' is the identity element
42instance Monoid ValidationError where
43 mempty = NoVerifier
44 NoVerifier `mappend` e = e
45 e `mappend` NoVerifier = e
46 SigMismatch `mappend` _ = SigMismatch
47 _ `mappend` SigMismatch = SigMismatch
48 (ValidatorError e) `mappend` (ParseError _) = ValidatorError e
49 (ParseError _) `mappend` (ValidatorError e) = ValidatorError e
50
51-- | Check that the given macaroon has a correct signature
52verifySig :: Key -> Macaroon -> Either ValidationError Macaroon
53verifySig k m = bool (Left SigMismatch) (Right m) $
54 signature m == foldl' hash (toBytes (hmac derivedKey (identifier m) :: HMAC SHA256)) (caveats m)
55 where
56 hash s c = toBytes (hmac s (vid c `BS.append` cid c) :: HMAC SHA256)
57 derivedKey = toBytes (hmac "macaroons-key-generator" k :: HMAC SHA256)
58
59-- | Given a list of verifiers, verify each caveat of the given macaroon
60verifyCavs :: MonadIO m
61 => [Caveat -> m (Maybe (Either ValidationError ()))]
62 -> Macaroon
63 -> m (Either ValidationError Macaroon)
64verifyCavs verifiers m = gatherEithers <$> mapM validateCaveat (caveats m)
65 where
66 {-
67 - validateCaveat :: Caveat -> m (Validation String Caveat)
68 - We can use fromJust here safely since we use a `Just Failure` as a
69 - starting value for the foldM. We are guaranteed to have a `Just something`
70 - from it.
71 -}
72 validateCaveat c = fmap (const c) . fromJust <$> foldM (\res v -> mappend res . fmap eitherToValidation <$> v c) (defErr c) verifiers
73 -- defErr :: Caveat -> Maybe (Validation String Caveat)
74 defErr c = Just $ Failure NoVerifier
75 -- gatherEithers :: [Validation String Caveat] -> Either String Caveat
76 gatherEithers vs = case partitionEithers . map validationToEither $ vs of
77 ([],_) -> Right m
78 (errs,_) -> Left (mconcat errs)