{-# LANGUAGE ForeignFunctionInterface, CPP #-}
module Crypto.Cipher.RC4
(
RC4
, Ctx(..)
, initCtx
, generate
, combine
, encrypt
, decrypt
) where
import Data.Word
import Data.Byteable
import Foreign.Ptr
import Foreign.ForeignPtr
import System.IO.Unsafe
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import Control.Applicative ((<$>))
import Crypto.Cipher.Types
unsafeDoIO :: IO a -> a
#if __GLASGOW_HASKELL__ > 704
unsafeDoIO = unsafeDupablePerformIO
#else
unsafeDoIO = unsafePerformIO
#endif
newtype RC4 = RC4 Ctx
instance Byteable RC4 where
toBytes (RC4 (Ctx b)) = b
instance Cipher RC4 where
cipherInit key = RC4 (initCtx $ toBytes key)
cipherName _ = "RC4"
cipherKeySize _ = KeySizeRange 1 1024
instance StreamCipher RC4 where
streamCombine (RC4 ctx) b = (\(ctx2, r) -> (r, RC4 ctx2)) $ combine ctx b
newtype Ctx = Ctx B.ByteString
instance Show Ctx where
show _ = "RC4.Ctx"
foreign import ccall unsafe "rc4.h rc4_init"
c_rc4_init :: Ptr Word8
-> Word32
-> Ptr Ctx
-> IO ()
foreign import ccall unsafe "rc4.h rc4_combine"
c_rc4_combine :: Ptr Ctx
-> Ptr Word8
-> Word32
-> Ptr Word8
-> IO ()
withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a
withByteStringPtr b f = withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)
where (fptr, off, _) = B.toForeignPtr b
initCtx :: B.ByteString
-> Ctx
initCtx key = unsafeDoIO $
Ctx <$> (B.create 264 $ \ctx -> B.useAsCStringLen key $ \(keyPtr,keyLen) -> c_rc4_init (castPtr keyPtr) (fromIntegral keyLen) (castPtr ctx))
generate :: Ctx -> Int -> (Ctx, B.ByteString)
generate ctx len = combine ctx (B.replicate len 0)
combine :: Ctx
-> B.ByteString
-> (Ctx, B.ByteString)
combine (Ctx cctx) clearText = unsafeDoIO $
B.mallocByteString 264 >>= \dctx ->
B.mallocByteString len >>= \outfptr ->
withByteStringPtr clearText $ \clearPtr ->
withByteStringPtr cctx $ \srcCtx ->
withForeignPtr dctx $ \dstCtx -> do
withForeignPtr outfptr $ \outptr -> do
B.memcpy dstCtx srcCtx 264
c_rc4_combine (castPtr dstCtx) clearPtr (fromIntegral len) outptr
return $! (Ctx $! B.PS dctx 0 264, B.PS outfptr 0 len)
where len = B.length clearText
{-# DEPRECATED encrypt "use combine instead" #-}
{-# DEPRECATED decrypt "use combine instead" #-}
encrypt,decrypt :: Ctx -> B.ByteString -> (Ctx, B.ByteString)
encrypt = combine
decrypt = combine