module Codec.Encryption
(Cipher(..)
,Word128(..)
,Word192(..)
,pkcs5
,unpkcs5
,module Codec.Encryption.Ciphers
) where
import Foreign hiding (unsafePerformIO)
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 !Word64 !Word64 deriving (Show,Eq)
data Word192 = Word192 !Word64 !Word64 !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`
(\(_ :: IOError) -> 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
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 (8len) (fromIntegral (8len))
in bs `BS.append` pad
unpkcs5 :: ByteString -> Maybe ByteString
unpkcs5 bs = let
len = BS.length bs
pchar = BS.last bs
(str,pad) = BS.splitAt (lenfromIntegral 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 ()) -> (CULong -> (IO CUInt))))
foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_setiv2"
gcry_cipher_setiv2 :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CULong -> (IO CUInt))))
foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_encrypt"
gcry_cipher_encrypt :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CULong -> ((Ptr ()) -> (CULong -> (IO CUInt))))))
foreign import ccall unsafe "Codec/Encryption.chs.h gcry_cipher_decrypt"
gcry_cipher_decrypt :: ((Ptr (CipherHandle)) -> ((Ptr ()) -> (CULong -> ((Ptr ()) -> (CULong -> (IO CUInt))))))