1 {-# LANGUAGE OverloadedStrings #-}
3 Module : Crypto.Macaroon
4 Copyright : (c) 2015 Julien Tanguy
7 Maintainer : julien.tanguy@jhome.fr
8 Stability : experimental
11 Pure haskell implementations of macaroons.
13 Warning: this implementation has not been audited by security experts.
14 Do not use in production
19 - Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud <http://research.google.com/pubs/pub41892.html>
20 - Time for better security in NoSQL <http://hackingdistributed.com/2014/11/23/macaroons-in-hyperdex>
22 module Crypto.Macaroon (
29 -- * Accessing functions
46 -- * Prepare Macaroons for transfer
50 import Crypto.Cipher.AES
54 import qualified Data.ByteString as BS
55 import qualified Data.ByteString.Base64.URL as B64
56 import qualified Data.ByteString.Char8 as B8
61 import Crypto.Macaroon.Internal
63 -- | Create a Macaroon from its key, identifier and location
64 create :: Key -> Key -> Location -> Macaroon
65 create secret ident loc = MkMacaroon loc ident [] (toBytes (hmac derivedKey ident :: HMAC SHA256))
67 derivedKey = toBytes (hmac "macaroons-key-generator" secret :: HMAC SHA256)
69 -- | Caveat target location
70 caveatLoc :: Caveat -> Location
73 -- | Caveat identifier
74 caveatId :: Caveat -> Key
77 -- | Caveat verification identifier
78 caveatVId :: Caveat -> Key
81 -- | Inspect a macaroon's contents. For debugging purposes.
82 inspect :: Macaroon -> String
85 -- | Serialize a macaroon in an URL-safe Base64 encoding
86 serialize :: Macaroon -> BS.ByteString
87 serialize m = B8.filter (/= '=') . B64.encode $ packets
89 packets = BS.concat [ putPacket "location" (location m)
90 , putPacket "identifier" (identifier m)
92 , putPacket "signature" (signature m)
94 caveatPackets = BS.concat $ map (cavPacket (location m)) (caveats m)
95 cavPacket loc c | cl c == loc && vid c == BS.empty = putPacket "cid" (cid c)
96 | otherwise = BS.concat [ putPacket "cid" (cid c)
97 , putPacket "vid" (vid c)
98 , putPacket "cl" (cl c)
100 putPacket key dat = BS.concat [
101 B8.map toLower . hex . encode $ (fromIntegral size :: Word16)
108 size = 4 + 2 + BS.length key + BS.length dat
113 -- | Add a first party Caveat to a Macaroon, with its identifier
114 addFirstPartyCaveat :: Key -> Macaroon -> Macaroon
115 addFirstPartyCaveat ident m = addCaveat (location m) ident BS.empty m
117 -- |Add a third party Caveat to a Macaroon, using its location, identifier and
119 addThirdPartyCaveat :: Key
124 addThirdPartyCaveat key cid loc m = addCaveat loc cid vid m
126 vid = encryptECB (initAES (signature m)) key