-- GENERATED by C->Haskell Compiler, version 0.16.0 Crystal Seed, 24 Jan 2009 (Haskell)
-- Edit the ORIGNAL .chs file instead!


{-# LINE 1 "./Codec/Encryption.chs" #-}
module Codec.Encryption
	(Cipher(..)
	,Word128(..)
	,Word192(..)
	,pkcs5
	,unpkcs5
	,module Codec.Encryption.Ciphers
	) where


import Foreign
import Foreign.C.Types
import Prelude hiding (catch)
import Control.Exception (catch)
import Data.Bits
import Data.ByteString(ByteString,useAsCStringLen)
import qualified Data.ByteString as BS
import Data.ByteString.Internal(create)
import Data.List(foldl')
import System.IO.Unsafe(unsafePerformIO)
import Codec.Encryption.Ciphers

data Word128 = Word128 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 deriving (Show,Eq)
data Word192 = Word192 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 deriving (Show,Eq)

instance Num Word128 where
	fromInteger num = let
		v1 = fromInteger (shiftR num 64)
		v2 = fromInteger num
		in Word128 v1 v2

instance Num Word192 where
	fromInteger num = let
		v1 = fromInteger (shiftR num 128)
		v2 = fromInteger (shiftR num  64)
		v3 = fromInteger num
		in Word192 v1 v2 v3

class (Show ciph,Show mode,CipherType ciph,CipherMode mode,CipherDatum key,CipherDatum iv) =>
	Cipher ciph mode key iv | ciph mode -> key iv where
	encrypt :: ciph -> mode -> key -> iv -> ByteString -> ByteString
	encrypt ct cm ck ci str = unsafePerformIO $ do
		ciph <- cipherOpen ct cm []
		setKey ciph ck
		setIV ciph ci
		cipherEncrypt ciph str
	decrypt :: ciph -> mode -> key -> iv -> ByteString -> Maybe ByteString
	decrypt ct cm ck ci str = unsafePerformIO ((do
		ciph <- cipherOpen ct cm []
		setKey ciph ck
		setIV ciph ci
		cipherDecrypt ciph str >>= return.Just) `catch` (\_ -> return Nothing))

class CipherDatum k where
	setKey :: CipherHandle -> k -> IO ()
	setIV  :: CipherHandle -> k -> IO ()

instance CipherDatum Word64 where
	setKey = cipherSetKey
	setIV = cipherSetIV

instance CipherDatum () where
	setKey _ _ = return ()
	setIV  _ _ = return ()

instance CipherDatum Word128 where
	setKey = cipherSetKey 
	setIV = cipherSetIV

instance CipherDatum Word192 where
	setKey = cipherSetKey 
	setIV = cipherSetIV

instance Storable Word192 where
	sizeOf _ = 24
	alignment _ = 4
	peek ptr = do
		v1 <- peek (castPtr ptr)
		v2 <- peek (castPtr (plusPtr ptr 8))
		v3 <- peek (castPtr (plusPtr ptr 16))
		return (Word192 v1 v2 v3)
	poke ptr (Word192 v1 v2 v3) = do
		poke (castPtr ptr) v1
		poke (castPtr (plusPtr ptr 8)) v2
		poke (castPtr (plusPtr ptr 16)) v3

instance Storable Word128 where
	sizeOf _ = 16
	alignment _ = 4
	peek ptr = do
		v1 <- peek (castPtr ptr)
		v2 <- peek (castPtr (plusPtr ptr 8))
		return (Word128 v1 v2)
	poke ptr (Word128 v1 v2) = do
		poke (castPtr ptr) v1
		poke (castPtr (plusPtr ptr 8)) v2

instance Cipher CipherDES ModeECB Word64 ()
instance Cipher CipherDES ModeCBC Word64 Word64
instance Cipher Cipher3DES ModeECB Word192 ()
instance Cipher Cipher3DES ModeCBC Word192 Word192
instance Cipher CipherCast5 ModeECB Word128 ()
instance Cipher CipherBlowfish ModeECB Word128 ()
-- ...

newtype CipherHandle = CipherHandle (ForeignPtr (CipherHandle))
withCipherHandle (CipherHandle fptr) = withForeignPtr fptr
{-# LINE 108 "./Codec/Encryption.chs" #-}

flags :: [CUInt] -> CUInt
flags = foldl' (.|.) 0

foreign import ccall unsafe "gcrypt.h &gcry_cipher_close" gcry_cipher_close :: FunPtr (Ptr CipherHandle -> IO ())

cipherOpen :: (Show tp,Show md,CipherType tp,CipherMode md) =>
	tp -> md -> [CipherFlag] -> IO CipherHandle
cipherOpen ct cm flg = alloca $ \hand -> do
	res <- gcry_cipher_open hand (cipherTypeToC ct) (cipherModeToC cm) (flags (map flagToC flg))
	if res /= 0
		then error ("Failed to open cipher "++show ct++" with mode "++show cm++" and flags "++show flg)
		else (do
			fp <- newForeignPtr gcry_cipher_close =<< peek hand
			return $ CipherHandle fp)

cipherSetKey :: Storable a => CipherHandle -> a -> IO ()
cipherSetKey hand dat = with dat $ \ptr -> cipherSetKey' hand (castPtr ptr) (sizeOf dat)

cipherSetKey' :: CipherHandle -> Ptr () -> Int -> IO ()
cipherSetKey' hand addr len = withCipherHandle hand $ \ptr -> do
		res <- gcry_cipher_setkey2 ptr addr (fromIntegral len)
		if res /= 0
			then error "Failed to set key"
			else return ()

cipherSetIV :: Storable a => CipherHandle -> a -> IO ()
cipherSetIV hand dat = with dat $ \ptr -> cipherSetIV' hand (castPtr ptr) (sizeOf dat)

cipherSetIV' :: CipherHandle -> Ptr () -> Int -> IO ()
cipherSetIV' hand addr len = withCipherHandle hand $ \ptr -> do
	res <- gcry_cipher_setiv2 ptr addr (fromIntegral len)
	if res /= 0
		then error "Failed to set IV"
		else return ()

cipherEncrypt :: CipherHandle -> ByteString -> IO ByteString
cipherEncrypt hand str = withCipherHandle hand $ \ptr ->
	useAsCStringLen str $ \(strp,len) ->
	create len $ \optr -> do
		res <- gcry_cipher_encrypt ptr (castPtr optr) (fromIntegral len) (castPtr strp) (fromIntegral len)
		if res /= 0
			then error "Failed to encrypt data"
			else return ()

cipherDecrypt :: CipherHandle -> ByteString -> IO ByteString
cipherDecrypt hand str = withCipherHandle hand $ \ptr ->
	useAsCStringLen str $ \(strp,len) ->
	create len $ \optr -> do
		res <- gcry_cipher_decrypt ptr (castPtr optr) (fromIntegral len) (castPtr strp) (fromIntegral len)
		if res /= 0
			then error "Failed to decrypt data"
			else return ()

pkcs5 :: ByteString -> ByteString
pkcs5 bs = let
	len = (BS.length bs) `mod` 8
	pad = BS.replicate (8-len) (fromIntegral (8-len))
	in bs `BS.append` pad

unpkcs5 :: ByteString -> Maybe ByteString
unpkcs5 bs = let
	len = BS.length bs
	pchar = BS.last bs
	(str,pad) = BS.splitAt (len-fromIntegral pchar) bs
	in if BS.all (==pchar) pad
		then Just str
		else Nothing

foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_open"
  gcry_cipher_open :: ((Ptr (Ptr (CipherHandle))) -> (CInt -> (CInt -> (CUInt -> (IO CUInt)))))

foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_setkey2"
  gcry_cipher_setkey2 :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CUInt -> (IO CUInt))))

foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_setiv2"
  gcry_cipher_setiv2 :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CUInt -> (IO CUInt))))

foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_encrypt"
  gcry_cipher_encrypt :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CUInt -> ((Ptr ()) -> (CUInt -> (IO CUInt))))))

foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_decrypt"
  gcry_cipher_decrypt :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CUInt -> ((Ptr ()) -> (CUInt -> (IO CUInt))))))