diff --git a/Benchmarks/Benchmarks.hs b/Benchmarks/Benchmarks.hs
new file mode 100644
--- /dev/null
+++ b/Benchmarks/Benchmarks.hs
@@ -0,0 +1,34 @@
+module Main where
+
+import Criterion.Main
+
+import Crypto.Cipher.RC4
+import qualified Data.ByteString as B
+import Control.DeepSeq
+
+instance NFData Ctx where
+    rnf (Ctx c) = c `seq` ()
+
+main = defaultMain
+    [ bgroup "init"
+        [ bench "1"    $ whnf initCtx b1
+        , bench "8"    $ whnf initCtx b8
+        , bench "32"   $ whnf initCtx b32
+        , bench "64"   $ whnf initCtx b64
+        , bench "256"  $ whnf initCtx b256
+        ]
+    , bgroup "encrypt"
+        [ bench "8"    $ nf (encrypt ctx) b8
+        , bench "32"   $ nf (encrypt ctx) b32
+        , bench "64"   $ nf (encrypt ctx) b64
+        , bench "256"  $ nf (encrypt ctx) b256
+        , bench "1024" $ nf (encrypt ctx) b1024
+        ]
+    ]
+    where b1    = B.replicate 1 0xf7
+          b8    = B.replicate 8 0xf7
+          b32   = B.replicate 32 0xf7
+          b64   = B.replicate 64 0x7f
+          b256  = B.replicate 256 0x7f
+          b1024 = B.replicate 1024 0x7f
+          ctx   = initCtx $ B.pack [1..10]
diff --git a/Crypto/Cipher/RC4.hs b/Crypto/Cipher/RC4.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/RC4.hs
@@ -0,0 +1,91 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+-- |
+-- Module      : Crypto.Cipher.RC4
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : stable
+-- Portability : Good
+--
+-- Initial FFI implementation by Peter White <peter@janrain.com>
+--
+-- Reorganized and simplified to have an opaque context.
+--
+module Crypto.Cipher.RC4
+    ( Ctx(..)
+    , initCtx
+    , generate
+    , combine
+    , encrypt
+    , decrypt
+    ) where
+
+import Data.Word
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import System.IO.Unsafe (unsafeDupablePerformIO)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B
+import Control.Applicative ((<$>))
+
+----------------------------------------------------------------------
+
+-- | The encryption context for RC4
+newtype Ctx = Ctx B.ByteString
+
+instance Show Ctx where
+    show _ = "RC4.Ctx"
+
+-- | C Call for initializing the encryptor
+foreign import ccall unsafe "rc4.h rc4_init"
+    c_rc4_init :: Ptr Word8 -- ^ The rc4 key
+               -> Word32    -- ^ The key length
+               -> Ptr Ctx   -- ^ The context
+               -> IO ()
+
+foreign import ccall unsafe "rc4.h rc4_combine"
+    c_rc4_combine :: Ptr Ctx        -- ^ Pointer to the permutation
+                  -> Ptr Word8      -- ^ Pointer to the clear text
+                  -> Word32         -- ^ Length of the clear text
+                  -> Ptr Word8      -- ^ Output buffer
+                  -> 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
+
+-- | RC4 context initialization.
+--
+-- seed the context with an initial key. the key size need to be
+-- adequate otherwise security takes a hit.
+initCtx :: B.ByteString -- ^ The key
+        -> Ctx          -- ^ The RC4 context with the key mixed in
+initCtx key = unsafeDupablePerformIO $
+    Ctx <$> (B.create 264 $ \ctx -> B.useAsCStringLen key $ \(keyPtr,keyLen) -> c_rc4_init (castPtr keyPtr) (fromIntegral keyLen) (castPtr ctx))
+
+-- | generate the next len bytes of the rc4 stream without combining
+-- it to anything.
+generate :: Ctx -> Int -> (Ctx, B.ByteString)
+generate ctx len = combine ctx (B.replicate len 0)
+
+-- | RC4 xor combination of the rc4 stream with an input
+combine :: Ctx                 -- ^ rc4 context
+        -> B.ByteString        -- ^ input
+        -> (Ctx, B.ByteString) -- ^ new rc4 context, and the output
+combine (Ctx cctx) clearText = unsafeDupablePerformIO $
+    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
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2008-2012 Vincent Hanquez <vincent@snarc.org>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Tests/Tests.hs b/Tests/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests/Tests.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Control.Applicative
+import Control.Monad
+
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+import qualified Crypto.Cipher.RC4 as RC4
+
+-- taken from wikipedia pages
+kats :: [ (B.ByteString, B.ByteString, B.ByteString) ]
+kats =
+    [   ("Key"
+        ,"Plaintext"
+        ,B.pack [0xBB,0xF3,0x16,0xE8,0xD9,0x40,0xAF,0x0A,0xD3]
+        )
+    ,   ("Wiki"
+        ,"pedia"
+        ,B.pack [0x10,0x21,0xBF,0x04,0x20]
+        )
+    ,   ("Secret"
+        ,"Attack at dawn"
+        ,B.pack [0x45,0xA0,0x1F,0x64,0x5F,0xC3,0x5B,0x38,0x35,0x52,0x54,0x4B,0x9B,0xF5]
+        )
+    ]
+
+runKat (key,plainText,cipherText) =
+       snd (RC4.encrypt ctx plainText)  == cipherText
+    && snd (RC4.decrypt ctx cipherText) == plainText
+    where ctx = RC4.initCtx $ key
+
+katToTestProperty (kat, i) = testProperty ("KAT " ++ show i) (runKat kat)
+
+data RC4Unit = RC4Unit B.ByteString B.ByteString
+    deriving (Show)
+
+instance Arbitrary RC4Unit where
+    arbitrary = RC4Unit <$> generateKey <*> generatePlaintext
+
+generateKey = choose (1, 284) >>= \sz -> (B.pack <$> replicateM sz arbitrary)
+generatePlaintext = choose (0,324) >>= \sz -> (B.pack <$> replicateM sz arbitrary)
+
+runOp f1 f2 (RC4Unit key plainText) =
+    let ctx = RC4.initCtx key
+     in (snd $ f2 ctx $ snd $ f1 ctx plainText) == plainText
+
+tests =
+    [ testGroup "KAT-RC4" $ map katToTestProperty $ zip kats [0..]
+    , testGroup "id"
+        [ testProperty "encrypt.decrypt" (runOp RC4.decrypt RC4.encrypt)
+        , testProperty "decrypt.encrypt" (runOp RC4.encrypt RC4.decrypt)
+        ]
+    ]
+
+main = defaultMain tests
diff --git a/cbits/rc4.c b/cbits/rc4.c
new file mode 100644
--- /dev/null
+++ b/cbits/rc4.c
@@ -0,0 +1,63 @@
+/* initial implementation by
+ *   Peter White <peter@janrain.com>
+ */
+
+/* C Standard includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+/* Local include */
+#include "rc4.h"
+
+/* Swap array elements i=State[i] and b=State[j]. */
+static void swap(uint8_t *i, uint8_t *j)
+{
+	uint8_t temp;
+
+	temp = *i;
+	*i = *j;
+	*j = temp;
+}
+
+/* Key scheduling algorithm. Swap array elements based on the key. */
+void rc4_init(uint8_t *key, uint32_t keylen, struct rc4_ctx *ctx)
+{
+	uint32_t i, j;
+	memset(ctx, 0, sizeof(struct rc4_ctx));
+
+	/* initialize context */
+	for (i = 0; i < 256; i++)
+		ctx->state[i] = i;
+	for (i = j = 0; i < 256; i++) {
+		j = (j + ctx->state[i] + key[i % keylen]) % 256;
+		swap(&ctx->state[i], &ctx->state[j]);
+	}
+}
+
+/* Combine the stream generated by the rc4 with some input */
+void rc4_combine(struct rc4_ctx *ctx, uint8_t *input, uint32_t len, uint8_t *output)
+{
+	uint32_t i = ctx->i;
+	uint32_t j = ctx->j;
+	uint32_t m;
+	uint8_t si, sj;
+
+	/* The RC4 algorithm */
+	for (m = 0; m < len; m++) {
+		i = (i + 1) & 0xff;
+		si = ctx->state[i];
+		j = (j + si) & 0xff;
+		sj = ctx->state[j];
+		/* swap(&state[i], &state[j]); */
+		ctx->state[i] = sj;
+		ctx->state[j] = si;
+		/* Xor the key stream value into the input */
+		*output++ = *input++ ^ (ctx->state[(si + sj) & 0xff]);
+	}
+
+	/* Output new S-box indices */
+	ctx->i = i;
+	ctx->j = j;
+}
diff --git a/cbits/rc4.h b/cbits/rc4.h
new file mode 100644
--- /dev/null
+++ b/cbits/rc4.h
@@ -0,0 +1,14 @@
+#ifndef RC4_H
+# define RC4_H
+
+struct rc4_ctx
+{
+	uint8_t state[256];
+	uint32_t i;
+	uint32_t j;
+};
+
+void rc4_init(uint8_t * key, uint32_t keylen, struct rc4_ctx *ctx);
+void rc4_combine(struct rc4_ctx *ctx, uint8_t *input, uint32_t len, uint8_t *output);
+
+#endif
diff --git a/cipher-rc4.cabal b/cipher-rc4.cabal
new file mode 100644
--- /dev/null
+++ b/cipher-rc4.cabal
@@ -0,0 +1,49 @@
+Name:                cipher-rc4
+Version:             0.1.0
+Description:         Fast RC4 cipher implementation
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          Vincent Hanquez <vincent@snarc.org>
+Synopsis:            Fast RC4 cipher implementation
+Category:            Cryptography
+Build-Type:          Simple
+Homepage:            http://github.com/vincenthz/hs-cipher-rc4
+Cabal-Version:       >=1.8
+Extra-Source-Files:  Tests/*.hs
+                     cbits/*.h
+
+Library
+  Build-Depends:     base >= 4 && < 5
+                   , bytestring
+                   , vector
+  Exposed-modules:   Crypto.Cipher.RC4
+  ghc-options:       -Wall
+  C-sources:         cbits/rc4.c
+
+Test-Suite test-cipher-rc4
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    Tests
+  Main-Is:           Tests.hs
+  Build-depends:     base >= 4 && < 5
+                   , cipher-rc4
+                   , bytestring
+                   , QuickCheck >= 2
+                   , test-framework >= 0.3.3
+                   , test-framework-quickcheck2 >= 0.2.9
+
+Benchmark bench-cipher-rc4
+  hs-source-dirs:    Benchmarks
+  Main-Is:           Benchmarks.hs
+  type:              exitcode-stdio-1.0
+  Build-depends:     base >= 4 && < 5
+                   , bytestring
+                   , cipher-rc4
+                   , deepseq
+                   , criterion
+                   , mtl
+
+source-repository head
+  type:     git
+  location: git://github.com/vincenthz/hs-cipher-rc4
