{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module E.Encrypt
(
Cipher(..)
, Decipher(..)
, Algs(..)
, algorithm
, encryptTem
, decryptTem
, EError(..)
) where
import Control.Monad.Trans.Either
import Data.Either.Combinators
import Data.Either.MoreCombinators
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as Map
import Data.Semigroup
import Data.Text (Text)
import E.Metadata
import E.Template
newtype Algs = Algs
{ unAlgs :: HashMap AlgName (Cipher, Decipher) }
instance Semigroup Algs where
Algs xs <> Algs ys = Algs (xs <> ys)
{-# INLINE (<>) #-}
instance Monoid Algs where
mappend = (<>)
{-# INLINE mappend #-}
mempty = Algs mempty
{-# INLINE mempty #-}
newtype Cipher = Cipher
{ runCipher :: Args -> PlainContent -> EitherT Text IO EncContent }
newtype Decipher = Decipher
{ runDecipher :: Args -> EncContent -> EitherT Text IO PlainContent }
algorithm :: AlgName -> Cipher -> Decipher -> Algs
algorithm n c d = Algs (Map.singleton n (c, d))
data EError
= AlgNotFound AlgName
| ValNotFound ValName
| DecryptingPlain ValName
| MetadataError MetadataError
| CipherError AlgName Text
| DecipherError AlgName Text
deriving (Eq,Show)
cipher :: Algs -> PlainValue -> EitherT EError IO EncValue
cipher algs (PlainValue _ alg args content) =
case Map.lookup alg (unAlgs algs) of
Just (Cipher enc, _) -> bimapEitherT (CipherError alg) (EncValue alg args) (enc args content)
Nothing -> left (AlgNotFound alg)
decipher :: ValName -> Algs -> EncValue -> EitherT EError IO PlainValue
decipher name algs (EncValue alg args content) =
case Map.lookup alg (unAlgs algs) of
Just (_, Decipher dec) -> bimapEitherT (DecipherError alg) (PlainValue name alg args) (dec args content)
Nothing -> left (AlgNotFound alg)
encryptTem :: Algs -> Metadata -> Tem -> EitherT EError IO (Tem, Metadata)
encryptTem e = et
where
et meta Nil = right (Nil, meta)
et meta (Txt text template) = do
(template', meta') <- et meta template
right (Txt text template', meta')
et meta (Val evp@(PlainValue name _ _ _) template) = do
ciphered <- cipher e evp
(template', meta') <- et meta template
meta'' <- hoistEither (mapLeft MetadataError (addCiphered name ciphered meta'))
right (Ref (ValRef name) template', meta'')
et meta (Ref eValueName template) = do
(template', meta') <- et meta template
pure (Ref eValueName template', meta')
decryptTem :: Algs -> Metadata -> Tem -> EitherT EError IO Tem
decryptTem e = dt
where
dt _ Nil = right Nil
dt meta (Txt text template) = right . Txt text =<< dt meta template
dt _ (Val (PlainValue name _ _ _) _) = left (DecryptingPlain name)
dt meta (Ref (ValRef name) template) = do
ciphered <- hoistEither (note (ValNotFound name) (getCiphered name meta))
PlainValue _ _ _ (PlainContent content) <- decipher name e ciphered
template' <- dt meta template
right (Txt content template')