HsOpenSSL-0.3.1: OpenSSL/EVP/Cipher.hsc
{- -*- haskell -*- -}
-- #prune
-- |An interface to symmetric cipher algorithms.
#include "HsOpenSSL.h"
module OpenSSL.EVP.Cipher
( Cipher
, EVP_CIPHER -- private
, withCipherPtr -- private
, getCipherByName
, getCipherNames
, cipherIvLength -- private
, CipherCtx -- private
, EVP_CIPHER_CTX -- private
, newCtx -- private
, withCipherCtxPtr -- private
, CryptoMode(..)
, cipherStrictly -- private
, cipherLazily -- private
, cipher
, cipherBS
, cipherLBS
)
where
import Control.Monad
import Data.ByteString.Internal (createAndTrim)
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.ByteString.Lazy.Internal as L8Internal
import Foreign
import Foreign.C
import OpenSSL.Objects
import OpenSSL.Utils
import System.IO.Unsafe
{- EVP_CIPHER ---------------------------------------------------------------- -}
-- |@Cipher@ is an opaque object that represents an algorithm of
-- symmetric cipher.
newtype Cipher = Cipher (Ptr EVP_CIPHER)
data EVP_CIPHER
foreign import ccall unsafe "EVP_get_cipherbyname"
_get_cipherbyname :: CString -> IO (Ptr EVP_CIPHER)
foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_iv_length"
_iv_length :: Ptr EVP_CIPHER -> Int
withCipherPtr :: Cipher -> (Ptr EVP_CIPHER -> IO a) -> IO a
withCipherPtr (Cipher cipherPtr) f = f cipherPtr
-- |@'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 name
= withCString name $ \ namePtr ->
do ptr <- _get_cipherbyname 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 = getObjNames CipherMethodType True
cipherIvLength :: Cipher -> Int
cipherIvLength (Cipher cipherPtr) = _iv_length cipherPtr
{- EVP_CIPHER_CTX ------------------------------------------------------------ -}
newtype CipherCtx = CipherCtx (ForeignPtr EVP_CIPHER_CTX)
data EVP_CIPHER_CTX
foreign import ccall unsafe "EVP_CIPHER_CTX_init"
_ctx_init :: Ptr EVP_CIPHER_CTX -> IO ()
foreign import ccall unsafe "&EVP_CIPHER_CTX_cleanup"
_ctx_cleanup :: FunPtr (Ptr EVP_CIPHER_CTX -> IO ())
foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_CTX_block_size"
_ctx_block_size :: Ptr EVP_CIPHER_CTX -> Int
newCtx :: IO CipherCtx
newCtx = do ctx <- mallocForeignPtrBytes (#size EVP_CIPHER_CTX)
withForeignPtr ctx $ \ ctxPtr ->
_ctx_init ctxPtr
addForeignPtrFinalizer _ctx_cleanup ctx
return $ CipherCtx ctx
withCipherCtxPtr :: CipherCtx -> (Ptr EVP_CIPHER_CTX -> IO a) -> IO a
withCipherCtxPtr (CipherCtx ctx) = withForeignPtr ctx
{- encrypt/decrypt ----------------------------------------------------------- -}
-- |@CryptoMode@ represents instruction to 'cipher' and such like.
data CryptoMode = Encrypt | Decrypt
foreign import ccall unsafe "EVP_CipherInit"
_CipherInit :: Ptr EVP_CIPHER_CTX -> Ptr EVP_CIPHER -> CString -> CString -> Int -> IO Int
foreign import ccall unsafe "EVP_CipherUpdate"
_CipherUpdate :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr Int -> Ptr CChar -> Int -> IO Int
foreign import ccall unsafe "EVP_CipherFinal"
_CipherFinal :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr Int -> IO Int
cryptoModeToInt :: CryptoMode -> Int
cryptoModeToInt Encrypt = 1
cryptoModeToInt Decrypt = 0
cipherInit :: Cipher -> String -> String -> CryptoMode -> IO CipherCtx
cipherInit (Cipher c) key iv mode
= do ctx <- newCtx
withCipherCtxPtr ctx $ \ ctxPtr ->
withCString key $ \ keyPtr ->
withCString iv $ \ ivPtr ->
_CipherInit ctxPtr c keyPtr ivPtr (cryptoModeToInt mode)
>>= failIf (/= 1)
return ctx
cipherUpdateBS :: CipherCtx -> B8.ByteString -> IO B8.ByteString
cipherUpdateBS ctx inBS
= withCipherCtxPtr ctx $ \ ctxPtr ->
unsafeUseAsCStringLen inBS $ \ (inBuf, inLen) ->
createAndTrim (inLen + _ctx_block_size ctxPtr - 1) $ \ outBuf ->
alloca $ \ outLenPtr ->
_CipherUpdate ctxPtr (castPtr outBuf) outLenPtr inBuf inLen
>>= failIf (/= 1)
>> peek outLenPtr
cipherFinalBS :: CipherCtx -> IO B8.ByteString
cipherFinalBS ctx
= withCipherCtxPtr ctx $ \ ctxPtr ->
createAndTrim (_ctx_block_size ctxPtr) $ \ outBuf ->
alloca $ \ outLenPtr ->
_CipherFinal ctxPtr (castPtr outBuf) outLenPtr
>>= failIf (/= 1)
>> peek outLenPtr
-- |@'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
cipher c key iv mode input
= liftM L8.unpack $ cipherLBS c key iv mode $ L8.pack input
-- |@'cipherBS'@ strictly encrypts or decrypts a chunk of data.
cipherBS :: Cipher -- ^ algorithm to use
-> String -- ^ symmetric key
-> String -- ^ IV
-> CryptoMode -- ^ operation
-> B8.ByteString -- ^ input string to encrypt\/decrypt
-> IO B8.ByteString -- ^ the result string
cipherBS c key iv mode input
= do ctx <- cipherInit c key iv 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
-> String -- ^ symmetric key
-> String -- ^ IV
-> CryptoMode -- ^ operation
-> L8.ByteString -- ^ input string to encrypt\/decrypt
-> IO L8.ByteString -- ^ the result string
cipherLBS c key iv mode input
= do ctx <- cipherInit c key iv mode
cipherLazily ctx input
cipherStrictly :: CipherCtx -> B8.ByteString -> IO B8.ByteString
cipherStrictly ctx input
= do output' <- cipherUpdateBS ctx input
output'' <- cipherFinalBS ctx
return $ B8.append output' output''
cipherLazily :: CipherCtx -> L8.ByteString -> IO L8.ByteString
cipherLazily ctx (L8Internal.Empty) =
cipherFinalBS ctx >>= \ bs -> (return . L8.fromChunks) [bs]
cipherLazily ctx (L8Internal.Chunk x xs) = do
y <- cipherUpdateBS ctx x
ys <- unsafeInterleaveIO $
cipherLazily ctx xs
return $ L8Internal.Chunk y ys