{-# LANGUAGE CPP                      #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CApiFFI                  #-}
-- |An interface to symmetric cipher algorithms.
module OpenSSL.EVP.Cipher
    ( Cipher
    , getCipherByName
    , getCipherNames

    , CryptoMode(..)

    , cipher
    , cipherBS
    , cipherLBS
    , cipherStrictLBS
    )
    where
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as L8
import Foreign
import Foreign.C
import OpenSSL.Objects
import OpenSSL.EVP.Internal

#if !MIN_VERSION_base(4,8,0)
import Data.Monoid
#endif

foreign import capi unsafe "openssl/evp.h EVP_get_cipherbyname"
        _get_cipherbyname :: CString -> IO (Ptr EVP_CIPHER)

-- |@'getCipherByName' name@ returns a symmetric cipher algorithm
-- whose name is @name@. If no algorithms are found, the result is
-- @Nothing@.
getCipherByName :: String -> IO (Maybe Cipher)
getCipherByName :: String -> IO (Maybe Cipher)
getCipherByName String
name
    = String -> (CString -> IO (Maybe Cipher)) -> IO (Maybe Cipher)
forall a. String -> (CString -> IO a) -> IO a
withCString String
name ((CString -> IO (Maybe Cipher)) -> IO (Maybe Cipher))
-> (CString -> IO (Maybe Cipher)) -> IO (Maybe Cipher)
forall a b. (a -> b) -> a -> b
$ \ CString
namePtr ->
      do ptr <- CString -> IO (Ptr EVP_CIPHER)
_get_cipherbyname CString
namePtr
         if ptr == nullPtr then
             return Nothing
           else
             return $ Just $ Cipher ptr

-- |@'getCipherNames'@ returns a list of name of symmetric cipher
-- algorithms.
getCipherNames :: IO [String]
getCipherNames :: IO [String]
getCipherNames = ObjNameType -> Bool -> IO [String]
getObjNames ObjNameType
CipherMethodType Bool
True

{- encrypt/decrypt ----------------------------------------------------------- -}

-- | Encrypt a lazy bytestring in a strict manner. Does not leak the keys.
cipherStrictLBS :: Cipher         -- ^ Cipher
                -> B8.ByteString  -- ^ Key
                -> B8.ByteString  -- ^ IV
                -> CryptoMode     -- ^ Encrypt\/Decrypt
                -> L8.ByteString  -- ^ Input
                -> IO L8.ByteString
cipherStrictLBS :: Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> ByteString
-> IO ByteString
cipherStrictLBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode ByteString
input =
    do ctx <- Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> IO CipherCtx
cipherInitBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode
       xs  <- cipherUpdateBS ctx `mapM` L8.toChunks input
       x   <- cipherFinalBS  ctx
       return $ L8.fromChunks (xs `mappend` [x])

-- |@'cipher'@ lazilly encrypts or decrypts a stream of data. The
-- input string doesn't necessarily have to be finite.
cipher :: Cipher     -- ^ algorithm to use
       -> String     -- ^ symmetric key
       -> String     -- ^ IV
       -> CryptoMode -- ^ operation
       -> String     -- ^ An input string to encrypt\/decrypt. Note
                     --   that the string must not contain any letters
                     --   which aren't in the range of U+0000 -
                     --   U+00FF.
       -> IO String  -- ^ the result string
{-# DEPRECATED cipher "Use cipherBS, cipherLBS or cipherStrictLBS." #-}
cipher :: Cipher -> String -> String -> CryptoMode -> String -> IO String
cipher Cipher
c String
key String
iv CryptoMode
mode String
input
    = (ByteString -> String) -> IO ByteString -> IO String
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> String
L8.unpack (IO ByteString -> IO String) -> IO ByteString -> IO String
forall a b. (a -> b) -> a -> b
$ Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> ByteString
-> IO ByteString
cipherLBS Cipher
c (String -> StrictByteString
B8.pack String
key) (String -> StrictByteString
B8.pack String
iv) CryptoMode
mode (String -> ByteString
L8.pack String
input)

-- |@'cipherBS'@ strictly encrypts or decrypts a chunk of data.
cipherBS :: Cipher           -- ^ algorithm to use
         -> B8.ByteString    -- ^ symmetric key
         -> B8.ByteString    -- ^ IV
         -> CryptoMode       -- ^ operation
         -> B8.ByteString    -- ^ input string to encrypt\/decrypt
         -> IO B8.ByteString -- ^ the result string
cipherBS :: Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> StrictByteString
-> IO StrictByteString
cipherBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode StrictByteString
input
    = do ctx <- Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> IO CipherCtx
cipherInitBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode
         cipherStrictly ctx input

-- |@'cipherLBS'@ lazilly encrypts or decrypts a stream of data. The
-- input string doesn't necessarily have to be finite.
cipherLBS :: Cipher           -- ^ algorithm to use
          -> B8.ByteString    -- ^ symmetric key
          -> B8.ByteString    -- ^ IV
          -> CryptoMode       -- ^ operation
          -> L8.ByteString    -- ^ input string to encrypt\/decrypt
          -> IO L8.ByteString -- ^ the result string
cipherLBS :: Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> ByteString
-> IO ByteString
cipherLBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode ByteString
input
    = do ctx <- Cipher
-> StrictByteString
-> StrictByteString
-> CryptoMode
-> IO CipherCtx
cipherInitBS Cipher
c StrictByteString
key StrictByteString
iv CryptoMode
mode
         cipherLazily ctx input