-- |
-- Module      : Crypto.Store.CMS.PEM
-- License     : BSD-style
-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
-- Stability   : experimental
-- Portability : unknown
--
-- PEM serialization and deserialization of CMS 'ContentInfo'.
module Crypto.Store.CMS.PEM
    ( readCMSFile
    , readCMSFileFromMemory
    , berToContentInfo
    , pemToContentInfo
    , writeCMSFile
    , writeCMSFileToMemory
    , contentInfoToDER
    , contentInfoToPEM
    ) where

import qualified Data.ByteString as B
import           Data.Maybe (catMaybes)

import Crypto.Store.CMS.Info
import Crypto.Store.CMS.Util
import Crypto.Store.Error
import Crypto.Store.PEM


-- Reading from PEM format

-- | Read content info elements from a PEM file.
readCMSFile :: FilePath -> IO [ContentInfo]
readCMSFile :: String -> IO [ContentInfo]
readCMSFile String
path = [PEM] -> [ContentInfo]
accumulate forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [PEM]
readPEMs String
path

-- | Read content info elements from a bytearray in PEM format.
readCMSFileFromMemory :: B.ByteString -> [ContentInfo]
readCMSFileFromMemory :: ByteString -> [ContentInfo]
readCMSFileFromMemory = forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall a b. a -> b -> a
const []) [PEM] -> [ContentInfo]
accumulate forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String [PEM]
pemParseBS

accumulate :: [PEM] -> [ContentInfo]
accumulate :: [PEM] -> [ContentInfo]
accumulate = forall a. [Maybe a] -> [a]
catMaybes forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (forall a b c. (a -> b -> c) -> b -> a -> c
flip [Maybe ContentInfo] -> PEM -> [Maybe ContentInfo]
pemToContentInfo) []

-- | Read a content info from a bytearray in BER format.
berToContentInfo :: B.ByteString -> Either StoreError ContentInfo
berToContentInfo :: ByteString -> Either StoreError ContentInfo
berToContentInfo = forall obj.
ParseASN1Object [ASN1Event] obj =>
ByteString -> Either StoreError obj
decodeASN1Object

-- | Read a content info from a 'PEM' element and add it to the accumulator
-- list.
pemToContentInfo :: [Maybe ContentInfo] -> PEM -> [Maybe ContentInfo]
pemToContentInfo :: [Maybe ContentInfo] -> PEM -> [Maybe ContentInfo]
pemToContentInfo [Maybe ContentInfo]
acc PEM
pem
    | PEM -> String
pemName PEM
pem forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
names = ByteString -> [Maybe ContentInfo]
decode (PEM -> ByteString
pemContent PEM
pem)
    | Bool
otherwise                = forall a. Maybe a
Nothing forall a. a -> [a] -> [a]
: [Maybe ContentInfo]
acc
  where
    names :: [String]
names = [ String
"CMS", String
"PKCS7" ]
    decode :: ByteString -> [Maybe ContentInfo]
decode ByteString
bs =
        case ByteString -> Either StoreError ContentInfo
berToContentInfo ByteString
bs of
            Left StoreError
_ -> forall a. Maybe a
Nothing forall a. a -> [a] -> [a]
: [Maybe ContentInfo]
acc
            Right ContentInfo
info -> forall a. a -> Maybe a
Just ContentInfo
info forall a. a -> [a] -> [a]
: [Maybe ContentInfo]
acc


-- Writing to PEM format

-- | Write content info elements to a PEM file.
writeCMSFile :: FilePath -> [ContentInfo] -> IO ()
writeCMSFile :: String -> [ContentInfo] -> IO ()
writeCMSFile String
path = String -> ByteString -> IO ()
B.writeFile String
path forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ContentInfo] -> ByteString
writeCMSFileToMemory

-- | Write content info elements to a bytearray in PEM format.
writeCMSFileToMemory :: [ContentInfo] -> B.ByteString
writeCMSFileToMemory :: [ContentInfo] -> ByteString
writeCMSFileToMemory = [PEM] -> ByteString
pemsWriteBS forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map ContentInfo -> PEM
contentInfoToPEM

-- | Generate a bytearray in DER format for a content info.
contentInfoToDER :: ContentInfo -> B.ByteString
contentInfoToDER :: ContentInfo -> ByteString
contentInfoToDER = forall obj. ProduceASN1Object ASN1P obj => obj -> ByteString
encodeASN1Object

-- | Generate PEM for a content info.
contentInfoToPEM :: ContentInfo -> PEM
contentInfoToPEM :: ContentInfo -> PEM
contentInfoToPEM ContentInfo
info = PEM { pemName :: String
pemName = String
"CMS", pemHeader :: [(String, ByteString)]
pemHeader = [], pemContent :: ByteString
pemContent = ByteString
bs}
  where bs :: ByteString
bs = ContentInfo -> ByteString
contentInfoToDER ContentInfo
info