]> git.immae.eu Git - github/fretlink/hmacaroons.git/blame - src/Crypto/Macaroon/Serializer/Base64.hs
Haddock markup
[github/fretlink/hmacaroons.git] / src / Crypto / Macaroon / Serializer / Base64.hs
CommitLineData
a20c77f8
JT
1{-# LANGUAGE OverloadedStrings #-}
2{-|
3Module : Crypto.Macaroon.Serializer.Base64
4Copyright : (c) 2015 Julien Tanguy
5License : BSD3
6
7Maintainer : julien.tanguy@jhome.fr
8Stability : experimental
9Portability : portable
10
11Base64 serializer/deserializer
12
13-}
14module Crypto.Macaroon.Serializer.Base64 (
15 serialize
16 , deserialize
17 ) where
18
b1f77d7e
JT
19import Control.Applicative
20import Control.Monad
21import Crypto.Macaroon.Internal
22import Data.Attoparsec.ByteString
a20c77f8
JT
23import qualified Data.Attoparsec.ByteString.Char8 as A8
24import Data.Bits
b1f77d7e
JT
25import qualified Data.ByteString as BS
26import qualified Data.ByteString.Base64.URL as B64
27import qualified Data.ByteString.Char8 as B8
a20c77f8
JT
28import Data.Char
29import Data.Hex
30import Data.Int
31import Data.List
32import Data.Maybe
a20c77f8 33import Data.Serialize
b1f77d7e 34import Data.Word
a20c77f8
JT
35
36
37-- | Serialize a macaroon in an URL-safe Base64 encoding
38serialize :: Macaroon -> BS.ByteString
39serialize 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)
46 packetize "cl" (cl c)
47 packetize "signature" (signature m)
48
49packetize :: BS.ByteString -> BS.ByteString -> Put
50packetize key dat = do
51 let size = 4 + 2 + BS.length key + BS.length dat
52 putByteString $ B8.map toLower . hex . encode $ (fromIntegral size :: Word16)
53 putByteString key
54 putByteString " "
55 putByteString dat
56 putByteString "\n"
57
7986de7c 58-- | Deserialize a macaroon from a base64url-encoded ByteString
a20c77f8
JT
59deserialize :: BS.ByteString -> Either String Macaroon
60deserialize = parseOnly macaroon . B64.decodeLenient
61
62
63macaroon :: Parser Macaroon
64macaroon = do
65 ps <- many packet <* endOfInput
66 let ([("location",l),("identifier",i)],ps') = splitAt 2 ps
67 let (caveats,sig) = splitAt (length ps' - 1) ps'
68 let [("signature",s)] = sig
69 return $ MkMacaroon l i (map (mkCaveat l) (groupBy splitCavs caveats)) s
70 where
71 mkCaveat _ [("cid",c),("vid",v),("cl",l)] = MkCaveat c v l
72 mkCaveat l [("cid",c)] = MkCaveat c BS.empty l
73 mkCaveat _ _ = error "Malformed caveat"
74 splitCavs _ ("cid",_) = False
75 splitCavs _ _ = True
76
77packet :: Parser (BS.ByteString, BS.ByteString)
78packet = do
79 size <- A8.take 4
80 case A8.parseOnly (A8.hexadecimal :: Parser Word16) size of
81 Left e -> fail e
82 Right s -> do
83 bs <- A8.take (fromIntegral $ s - 4)
84 let (key, dat) = B8.break (== ' ') bs
85 return (key, B8.tail $ B8.init dat)
86