1 {-# LANGUAGE OverloadedStrings #-}
3 Module : Crypto.Macaroon.Serializer.Base64
4 Copyright : (c) 2015 Julien Tanguy
7 Maintainer : julien.tanguy@jhome.fr
8 Stability : experimental
11 Base64 serializer/deserializer
14 module Crypto.Macaroon.Serializer.Base64 (
19 import Control.Applicative
21 import qualified Data.ByteString as BS
22 import qualified Data.ByteString.Base64.URL as B64
23 import qualified Data.ByteString.Char8 as B8
24 import Data.Attoparsec.ByteString
25 import qualified Data.Attoparsec.ByteString.Char8 as A8
34 import Crypto.Macaroon.Internal
37 -- | Serialize a macaroon in an URL-safe Base64 encoding
38 serialize :: Macaroon -> BS.ByteString
39 serialize m = B8.filter (/= '=') . B64.encode . runPut $ do
40 packetize "location" (location m)
41 packetize "identifier" (identifier m)
42 forM_ (caveats m) $ \c -> do
43 packetize "cid" (cid c)
44 unless (cl c == location m && vid c == BS.empty) $ do
45 packetize "vid" (vid c)
47 packetize "signature" (signature m)
49 packetize :: BS.ByteString -> BS.ByteString -> Put
50 packetize key dat = do
51 let size = 4 + 2 + BS.length key + BS.length dat
52 putByteString $ B8.map toLower . hex . encode $ (fromIntegral size :: Word16)
58 deserialize :: BS.ByteString -> Either String Macaroon
59 deserialize = parseOnly macaroon . B64.decodeLenient
62 macaroon :: Parser Macaroon
64 ps <- many packet <* endOfInput
65 let ([("location",l),("identifier",i)],ps') = splitAt 2 ps
66 let (caveats,sig) = splitAt (length ps' - 1) ps'
67 let [("signature",s)] = sig
68 return $ MkMacaroon l i (map (mkCaveat l) (groupBy splitCavs caveats)) s
70 mkCaveat _ [("cid",c),("vid",v),("cl",l)] = MkCaveat c v l
71 mkCaveat l [("cid",c)] = MkCaveat c BS.empty l
72 mkCaveat _ _ = error "Malformed caveat"
73 splitCavs _ ("cid",_) = False
76 packet :: Parser (BS.ByteString, BS.ByteString)
79 case A8.parseOnly (A8.hexadecimal :: Parser Word16) size of
82 bs <- A8.take (fromIntegral $ s - 4)
83 let (key, dat) = B8.break (== ' ') bs
84 return (key, B8.tail $ B8.init dat)