diff --git a/Data/CryptoHash/MD2.hs b/Data/CryptoHash/MD2.hs
--- a/Data/CryptoHash/MD2.hs
+++ b/Data/CryptoHash/MD2.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "md2.h md2_init"
 	c_md2_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/MD4.hs b/Data/CryptoHash/MD4.hs
--- a/Data/CryptoHash/MD4.hs
+++ b/Data/CryptoHash/MD4.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "md4.h md4_init"
 	c_md4_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/MD5.hs b/Data/CryptoHash/MD5.hs
--- a/Data/CryptoHash/MD5.hs
+++ b/Data/CryptoHash/MD5.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "md5.h md5_init"
 	c_md5_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/RIPEMD160.hs b/Data/CryptoHash/RIPEMD160.hs
--- a/Data/CryptoHash/RIPEMD160.hs
+++ b/Data/CryptoHash/RIPEMD160.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "ripemd.h ripemd160_init"
 	c_ripemd160_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/SHA1.hs b/Data/CryptoHash/SHA1.hs
--- a/Data/CryptoHash/SHA1.hs
+++ b/Data/CryptoHash/SHA1.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "sha1.h sha1_init"
 	c_sha1_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/SHA224.hs b/Data/CryptoHash/SHA224.hs
--- a/Data/CryptoHash/SHA224.hs
+++ b/Data/CryptoHash/SHA224.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "sha256.h sha224_init"
 	c_sha224_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/SHA256.hs b/Data/CryptoHash/SHA256.hs
--- a/Data/CryptoHash/SHA256.hs
+++ b/Data/CryptoHash/SHA256.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "sha256.h sha256_init"
 	c_sha256_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/SHA384.hs b/Data/CryptoHash/SHA384.hs
--- a/Data/CryptoHash/SHA384.hs
+++ b/Data/CryptoHash/SHA384.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "sha512.h sha384_init"
 	c_sha384_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/SHA512.hs b/Data/CryptoHash/SHA512.hs
--- a/Data/CryptoHash/SHA512.hs
+++ b/Data/CryptoHash/SHA512.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "sha512.h sha512_init"
 	c_sha512_init :: Ptr Ctx -> IO ()
diff --git a/Data/CryptoHash/Skein256.hs b/Data/CryptoHash/Skein256.hs
new file mode 100644
--- /dev/null
+++ b/Data/CryptoHash/Skein256.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module      : Data.CryptoHash.Skein256
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- A module containing Skein256 bindings
+--
+module Data.CryptoHash.Skein256 (
+	Ctx(..),
+
+	-- * Incremental hashing Functions
+	init,      -- :: Int -> Ctx
+	update,    -- :: Ctx -> ByteString -> Ctx
+	finalize,  -- :: Ctx -> ByteString
+
+	-- * Single Pass hashing
+	hash,      -- :: Int -> ByteString -> ByteString
+	hashlazy   -- :: Int -> ByteString -> ByteString
+	) where
+
+import Prelude hiding (init)
+import Foreign
+import Foreign.C.String
+import Foreign.C.Types
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString (ByteString)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
+
+data Ctx = Ctx !ByteString
+
+sizeCtx :: Int
+sizeCtx = 100
+
+instance Storable Ctx where
+	sizeOf _    = sizeCtx
+	alignment _ = 16
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
+
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
+
+poke_hashlen :: Ptr Ctx -> IO Int
+poke_hashlen ptr = do
+	let iptr = castPtr ptr :: Ptr CUInt
+	a <- peek iptr
+	return $ fromIntegral a
+
+foreign import ccall unsafe "skein256.h skein256_init"
+	c_skein256_init :: Ptr Ctx -> CUInt -> IO ()
+
+foreign import ccall "skein256.h skein256_update"
+	c_skein256_update :: Ptr Ctx -> CString -> Word32 -> IO ()
+
+foreign import ccall unsafe "skein256.h skein256_finalize"
+	c_skein256_finalize :: Ptr Ctx -> CString -> IO ()
+
+allocInternal :: (Ptr Ctx -> IO a) -> IO a
+allocInternal = alloca
+
+allocInternalFrom :: Ctx -> (Ptr Ctx -> IO a) -> IO a
+allocInternalFrom ctx f = allocInternal $ \ptr -> (poke ptr ctx >> f ptr)
+
+updateInternalIO :: Ptr Ctx -> ByteString -> IO ()
+updateInternalIO ptr d =
+	unsafeUseAsCStringLen d (\(cs, len) -> c_skein256_update ptr cs (fromIntegral len))
+
+finalizeInternalIO :: Ptr Ctx -> IO ByteString
+finalizeInternalIO ptr = do
+	digestSize <- fmap (\x -> (x + 7) `shiftR` 3) $ poke_hashlen ptr
+	allocaBytes digestSize (\cs -> c_skein256_finalize ptr cs >> B.packCStringLen (cs, digestSize))
+
+{-# NOINLINE init #-}
+-- | init a context
+init :: Int -> Ctx
+init hashlen = unsafePerformIO $ allocInternal $ \ptr -> do (c_skein256_init ptr (fromIntegral hashlen) >> peek ptr)
+
+{-# NOINLINE update #-}
+-- | update a context with a bytestring
+update :: Ctx -> ByteString -> Ctx
+update ctx d = unsafePerformIO $ allocInternalFrom ctx $ \ptr -> do updateInternalIO ptr d >> peek ptr
+
+{-# NOINLINE finalize #-}
+-- | finalize the context into a digest bytestring
+finalize :: Ctx -> ByteString
+finalize ctx = unsafePerformIO $ allocInternalFrom ctx $ \ptr -> do finalizeInternalIO ptr
+
+{-# NOINLINE hash #-}
+-- | hash a strict bytestring into a digest bytestring
+hash :: Int -> ByteString -> ByteString
+hash hashlen d = unsafePerformIO $ allocInternal $ \ptr -> do
+	c_skein256_init ptr (fromIntegral hashlen) >> updateInternalIO ptr d >> finalizeInternalIO ptr
+
+{-# NOINLINE hashlazy #-}
+-- | hash a lazy bytestring into a digest bytestring
+hashlazy :: Int -> L.ByteString -> ByteString
+hashlazy hashlen l = unsafePerformIO $ allocInternal $ \ptr -> do
+	c_skein256_init ptr (fromIntegral hashlen) >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
diff --git a/Data/CryptoHash/Skein512.hs b/Data/CryptoHash/Skein512.hs
--- a/Data/CryptoHash/Skein512.hs
+++ b/Data/CryptoHash/Skein512.hs
@@ -29,8 +29,8 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
 data Ctx = Ctx !ByteString
 
@@ -40,28 +40,15 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = do
-		mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> do
-			mapM_ (\i -> do
-				f <- peek (ptr `plusPtr` i) :: IO Word8
-				poke (bptr `plusPtr` i) f
-				) [0..(sizeCtx-1)]
-			)
-		return $ Ctx b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 poke_hashlen :: Ptr Ctx -> IO Int
 poke_hashlen ptr = do
-	a <- peek (ptr `plusPtr` 3)
-	b <- peek (ptr `plusPtr` 2)
-	c <- peek (ptr `plusPtr` 1)
-	d <- peek (ptr `plusPtr` 0)
-	return (sl a 24 .|. sl b 16 .|. sl c 8 .|. sl d 0)
-	where
-		sl :: Word8 -> Int -> Int
-		sl a r = (fromIntegral a) `shiftL` r
+	let iptr = castPtr ptr :: Ptr CUInt
+	a <- peek iptr
+	return $ fromIntegral a
 
 foreign import ccall unsafe "skein512.h skein512_init"
 	c_skein512_init :: Ptr Ctx -> CUInt -> IO ()
diff --git a/Data/CryptoHash/Tiger.hs b/Data/CryptoHash/Tiger.hs
--- a/Data/CryptoHash/Tiger.hs
+++ b/Data/CryptoHash/Tiger.hs
@@ -28,10 +28,10 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafeIndex)
-import Data.ByteString.Internal (create)
+import Data.ByteString.Unsafe (unsafeUseAsCString, unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, memcpy)
 
-data Ctx = Ctx ByteString
+data Ctx = Ctx !ByteString
 
 digestSize :: Int
 sizeCtx :: Int
@@ -42,14 +42,9 @@
 instance Storable Ctx where
 	sizeOf _    = sizeCtx
 	alignment _ = 16
-	poke ptr (Ctx b) = mapM_ (\i -> poke (ptr `plusPtr` i) (unsafeIndex b i)) [0..(sizeCtx-1)]
+	poke ptr (Ctx b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (fromIntegral sizeCtx))
 
-	peek ptr = do
-		b <- create sizeCtx (\bptr -> mapM_ (\i -> do
-			f <- peek (ptr `plusPtr` i) :: IO Word8
-			poke (bptr `plusPtr` i) f
-			) [0..(sizeCtx-1)])
-		return $ Ctx $! b
+	peek ptr = create sizeCtx (\bptr -> memcpy bptr (castPtr ptr) (fromIntegral sizeCtx)) >>= return . Ctx
 
 foreign import ccall unsafe "tiger.h tiger_init"
 	c_tiger_init :: Ptr Ctx -> IO ()
diff --git a/Tests.hs b/Tests.hs
--- a/Tests.hs
+++ b/Tests.hs
@@ -13,6 +13,7 @@
 import qualified Data.CryptoHash.SHA512 as SHA512
 import qualified Data.CryptoHash.RIPEMD160 as RIPEMD160
 import qualified Data.CryptoHash.Tiger as Tiger
+import qualified Data.CryptoHash.Skein256 as Skein256
 import qualified Data.CryptoHash.Skein512 as Skein512
 
 v0 = ""
@@ -37,6 +38,7 @@
 ripemd160Hash = HashFct { fctHash = RIPEMD160.hash, fctInc = hashinc RIPEMD160.init RIPEMD160.update RIPEMD160.finalize }
 tigerHash = HashFct { fctHash = Tiger.hash, fctInc = hashinc Tiger.init Tiger.update Tiger.finalize }
 
+skein256Hash x = HashFct { fctHash = Skein256.hash x, fctInc = hashinc (Skein256.init x) Skein256.update Skein256.finalize }
 skein512Hash x = HashFct { fctHash = Skein512.hash x, fctInc = hashinc (Skein512.init x) Skein512.update Skein512.finalize }
 
 results :: [ (String, HashFct, [String]) ]
@@ -81,6 +83,14 @@
 		"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3",
 		"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075",
 		"a8f04b0f7201a0d728101c9d26525b31764a3493fcd8458f" ])
+	, ("Skein256-160", skein256Hash 160, [
+		"2ab89f14cfb3a5cb4655379386c42df7a45ccaf7",
+		"c86ef8411dc1deb008a3c175091691b74643631e",
+		"2775e79484bc087b58eb78d977c143a3029471be" ])
+	, ("Skein256-256", skein256Hash 256, [
+		"0b04103b828cddaebcf592ac845ecafd5887f61230a755406d38d85376e1ae08",
+		"a7e63c4dc73d0cb77184e319ebb6f69b73bfc8b945c1b371fafd01223a2ade1c",
+		"2174ca46353601a53826b430b52e76fca51bb0419f7a20ac7ffd53c4c448fa51" ])
 	, ("Skein512-160", skein512Hash 160, [
 		"b034bcc065b01b0c486159b3dba3e03aa52fdd70",
 		"9709e7d913bc3eee240e1f302840a0da9d9acc48",
diff --git a/cbits/skein.h b/cbits/skein.h
new file mode 100644
--- /dev/null
+++ b/cbits/skein.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006-2010 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef CRYPTOHASH_SKEIN_H
+#define CRYPTOHASH_SKEIN_H
+
+#define SKEIN_VERSION 1ULL
+#define SKEIN_IDSTRING 0x33414853ULL
+
+/*
+t0 0-63 || t1 64-127
+0-95      96-111      112-118    119     120-125  126    127
+Position  reserved    TreeLevel  BitPad  Type     First  Final
+*/
+#define FLAG_FIRST   (1ULL << 62)
+#define FLAG_FINAL   (1ULL << 63)
+#define FLAG_TYPE(x) (((uint64_t) ((x) & 0x3f)) << 56)
+
+#define TYPE_KEY     0x00
+#define TYPE_CFG     0x04
+#define TYPE_MSG     0x30
+#define TYPE_OUT     0x3f
+
+#define SET_TYPE(ctx, ty) ctx->t0 = 0; ctx->t1 = (ty)
+
+#endif
diff --git a/cbits/skein256.c b/cbits/skein256.c
new file mode 100644
--- /dev/null
+++ b/cbits/skein256.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2006-2010 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <string.h>
+#include "skein.h"
+#include "skein256.h"
+#include "bitfn.h"
+
+static const uint8_t K256_0[2] = { 14, 16, };
+static const uint8_t K256_1[2] = { 52, 57, };
+static const uint8_t K256_2[2] = { 23, 40, };
+static const uint8_t K256_3[2] = {  5, 37, };
+static const uint8_t K256_4[2] = { 25, 33, };
+static const uint8_t K256_5[2] = { 46, 12, };
+static const uint8_t K256_6[2] = { 58, 22, };
+static const uint8_t K256_7[2] = { 32, 32, };
+
+static inline void skein256_do_chunk(struct skein256_ctx *ctx, uint64_t *buf, uint32_t len)
+{
+	uint64_t x[4];
+	uint64_t ts[3];
+	uint64_t ks[4+1];
+
+	ks[4] = 0x5555555555555555;
+	ks[0] = ctx->h[0]; ks[4] ^= ctx->h[0];
+	ks[1] = ctx->h[1]; ks[4] ^= ctx->h[1];
+	ks[2] = ctx->h[2]; ks[4] ^= ctx->h[2];
+	ks[3] = ctx->h[3]; ks[4] ^= ctx->h[3];
+
+	ts[0] = ctx->t0;
+	ts[1] = ctx->t1;
+
+	ts[0] += len;
+
+	ts[2] = ts[0] ^ ts[1];
+
+#define INJECTKEY(r) \
+	x[0] += ks[((r)+0) % (4+1)];                   \
+	x[1] += ks[((r)+1) % (4+1)] + ts[((r)+0) % 3]; \
+	x[2] += ks[((r)+2) % (4+1)] + ts[((r)+1) % 3]; \
+	x[3] += ks[((r)+3) % (4+1)] + (r)
+
+#define ROUND(a,b,c,d,k) \
+	x[a] += x[b]; x[b] = rol64(x[b],k[0]); x[b] ^= x[a]; \
+	x[c] += x[d]; x[d] = rol64(x[d],k[1]); x[d] ^= x[c];
+
+#define PASS(i) \
+	ROUND(0,1,2,3,K256_0); \
+	ROUND(0,3,2,1,K256_1); \
+	ROUND(0,1,2,3,K256_2); \
+	ROUND(0,3,2,1,K256_3); \
+	INJECTKEY((i*2) + 1);          \
+	ROUND(0,1,2,3,K256_4); \
+	ROUND(0,3,2,1,K256_5); \
+	ROUND(0,1,2,3,K256_6); \
+	ROUND(0,3,2,1,K256_7); \
+	INJECTKEY((i*2) + 2)
+
+	x[0] = le64_to_cpu(buf[0]) + ks[0];
+	x[1] = le64_to_cpu(buf[1]) + ks[1] + ts[0];
+	x[2] = le64_to_cpu(buf[2]) + ks[2] + ts[1];
+	x[3] = le64_to_cpu(buf[3]) + ks[3];
+
+	/* 9 pass of 8 rounds = 72 rounds */
+	PASS(0);
+	PASS(1);
+	PASS(2);
+	PASS(3);
+	PASS(4);
+	PASS(5);
+	PASS(6);
+	PASS(7);
+	PASS(8);
+
+	ts[1] &= ~FLAG_FIRST;
+	ctx->t0 = ts[0];
+	ctx->t1 = ts[1];
+
+	ctx->h[0] = x[0] ^ cpu_to_le64(buf[0]);
+        ctx->h[1] = x[1] ^ cpu_to_le64(buf[1]);
+        ctx->h[2] = x[2] ^ cpu_to_le64(buf[2]);
+        ctx->h[3] = x[3] ^ cpu_to_le64(buf[3]);
+}
+
+void skein256_init(struct skein256_ctx *ctx, uint32_t hashlen)
+{
+	uint64_t buf[4];
+	memset(ctx, 0, sizeof(*ctx));
+
+	ctx->hashlen = hashlen;
+	SET_TYPE(ctx, FLAG_FIRST | FLAG_FINAL | FLAG_TYPE(TYPE_CFG));
+	
+	memset(buf, '\0', sizeof(buf));
+	buf[0] = cpu_to_le64((SKEIN_VERSION << 32) | SKEIN_IDSTRING);
+	buf[1] = cpu_to_le64(hashlen);
+	buf[2] = 0; /* tree info, not implemented */
+	skein256_do_chunk(ctx, buf, 4*8);
+
+	SET_TYPE(ctx, FLAG_FIRST | FLAG_TYPE(TYPE_MSG));
+}
+
+void skein256_update(struct skein256_ctx *ctx, uint8_t *data, uint32_t len)
+{
+	uint32_t to_fill;
+
+	to_fill = 32 - ctx->bufindex;
+
+	if (ctx->bufindex == 32) {
+		skein256_do_chunk(ctx, (uint64_t *) ctx->buf, 32);
+		ctx->bufindex = 0;
+	}
+
+	/* process partial buffer if there's enough data to make a block */
+	if (ctx->bufindex && len >= to_fill) {
+		memcpy(ctx->buf + ctx->bufindex, data, to_fill);
+		skein256_do_chunk(ctx, (uint64_t *) ctx->buf, 32);
+		len -= to_fill;
+		data += to_fill;
+		ctx->bufindex = 0;
+	}
+
+	/* process as much 32-block as possible except the last one in case we finalize */
+	for (; len > 32; len -= 32, data += 32)
+		skein256_do_chunk(ctx, (uint64_t *) data, 32);
+
+	/* append data into buf */
+	if (len) {
+		memcpy(ctx->buf + ctx->bufindex, data, len);
+		ctx->bufindex += len;
+	}
+}
+
+void skein256_finalize(struct skein256_ctx *ctx, uint8_t *out)
+{
+	uint32_t outsize;
+	uint64_t *p = (uint64_t *) out;
+	uint64_t x[4];
+	int i, j, n;
+
+	ctx->t1 |= FLAG_FINAL;
+	/* if buf is not complete pad with 0 bytes */
+	if (ctx->bufindex < 32)
+		memset(ctx->buf + ctx->bufindex, '\0', 32 - ctx->bufindex);
+	skein256_do_chunk(ctx, (uint64_t *) ctx->buf, ctx->bufindex);
+
+	memset(ctx->buf, '\0', 32);
+
+	/* make sure we have a 8 bit rounded value */
+	outsize = (ctx->hashlen + 7) >> 3;
+
+	/* backup h[0--4] */
+	for (j = 0; j < 4; j++)
+		x[j] = ctx->h[j];
+	/* threefish in counter mode, 0 for 1st 64 bytes, 1 for 2nd 64 bytes, .. */
+	for (i = 0; i*32 < outsize; i++) {
+		*((uint64_t *) ctx->buf) = cpu_to_le64(i);
+		SET_TYPE(ctx, FLAG_FIRST | FLAG_FINAL | FLAG_TYPE(TYPE_OUT));
+		skein256_do_chunk(ctx, (uint64_t *) ctx->buf, sizeof(uint64_t));
+
+		n = outsize - i * 32;
+		if (n >= 32) n = 32;
+
+		/* FIXME should be little endian array copy ? */
+		memcpy(out + i*32, ctx->h, n);
+
+		/* restore h[0--4] */
+		for (j = 0; j < 4; j++)
+			ctx->h[j] = x[j];
+	}
+}
diff --git a/cbits/skein256.h b/cbits/skein256.h
new file mode 100644
--- /dev/null
+++ b/cbits/skein256.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2006-2010 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef CRYPTOHASH_SKEIN256_H
+#define CRYPTOHASH_SKEIN256_H
+
+#include <stdint.h>
+
+struct skein256_ctx
+{
+	uint32_t hashlen;
+	uint32_t bufindex;
+	uint8_t  buf[32];
+	uint64_t h[4];
+	uint64_t t0;
+	uint64_t t1;
+};
+
+#define SKEIN256_CTX_SIZE		sizeof(struct skein256_ctx)
+
+void skein256_init(struct skein256_ctx *ctx, uint32_t hashlen);
+void skein256_update(struct skein256_ctx *ctx, uint8_t *data, uint32_t len);
+void skein256_finalize(struct skein256_ctx *ctx, uint8_t *out);
+
+#endif
diff --git a/cbits/skein512.c b/cbits/skein512.c
--- a/cbits/skein512.c
+++ b/cbits/skein512.c
@@ -23,28 +23,10 @@
  */
 
 #include <string.h>
+#include "skein.h"
 #include "skein512.h"
 #include "bitfn.h"
 
-#define SKEIN_VERSION 1ULL
-#define SKEIN_IDSTRING 0x33414853ULL
-
-/*
-t0 0-63 || t1 64-127
-0-95      96-111      112-118    119     120-125  126    127
-Position  reserved    TreeLevel  BitPad  Type     First  Final
-*/
-#define FLAG_FIRST   (1ULL << 62)
-#define FLAG_FINAL   (1ULL << 63)
-#define FLAG_TYPE(x) (((uint64_t) ((x) & 0x3f)) << 56)
-
-#define TYPE_KEY     0x00
-#define TYPE_CFG     0x04
-#define TYPE_MSG     0x30
-#define TYPE_OUT     0x3f
-
-#define SET_TYPE(ctx, ty) ctx->t0 = 0; ctx->t1 = (ty)
-
 static const uint8_t K512_0[4] = { 46, 36, 19, 37, };
 static const uint8_t K512_1[4] = { 33, 27, 14, 42, };
 static const uint8_t K512_2[4] = { 17, 49, 36, 39, };
@@ -144,7 +126,7 @@
 	uint64_t buf[8];
 	memset(ctx, 0, sizeof(*ctx));
 
-	ctx->hashlen = cpu_to_le64(hashlen);
+	ctx->hashlen = hashlen;
 	SET_TYPE(ctx, FLAG_FIRST | FLAG_FINAL | FLAG_TYPE(TYPE_CFG));
 	
 	memset(buf, '\0', sizeof(buf));
@@ -162,8 +144,13 @@
 
 	to_fill = 64 - ctx->bufindex;
 
+	if (ctx->bufindex == 64) {
+		skein512_do_chunk(ctx, (uint64_t *) ctx->buf, 64);
+		ctx->bufindex = 0;
+	}
+
 	/* process partial buffer if there's enough data to make a block */
-	if ((ctx->bufindex & 0x3f) && len >= to_fill) {
+	if (ctx->bufindex && len >= to_fill) {
 		memcpy(ctx->buf + ctx->bufindex, data, to_fill);
 		skein512_do_chunk(ctx, (uint64_t *) ctx->buf, 64);
 		len -= to_fill;
@@ -198,7 +185,7 @@
 	memset(ctx->buf, '\0', 64);
 
 	/* make sure we have a 8 bit rounded value */
-	outsize = (le64_to_cpu(ctx->hashlen) + 7) >> 3;
+	outsize = (ctx->hashlen + 7) >> 3;
 
 	/* backup h[0--7] */
 	for (j = 0; j < 8; j++)
diff --git a/cryptohash.cabal b/cryptohash.cabal
--- a/cryptohash.cabal
+++ b/cryptohash.cabal
@@ -1,5 +1,5 @@
 Name:                cryptohash
-Version:             0.5
+Version:             0.5.1
 Description:
     A collection of crypto hashes, with a practical incremental and one-pass, pure APIs,
     with performance close to the fastest implementations available in others languages.
@@ -10,7 +10,7 @@
 License-file:        LICENSE
 Author:              Vincent Hanquez
 Maintainer:          vincent@snarc.org
-Synopsis:            collection of crypto hashes, fast, pure and pratical
+Synopsis:            collection of crypto hashes, fast, pure and practical
 Category:            Data, Cryptography
 Build-Type:          Simple
 Cabal-Version:       >=1.6
@@ -19,7 +19,8 @@
 extra-source-files:
   cbits/bitfn.h cbits/md2.h cbits/md4.h cbits/md5.h
   cbits/ripemd.h cbits/sha1.h cbits/sha256.h cbits/sha512.h
-  cbits/tiger.h cbits/skein512.h
+  cbits/skein.h cbits/skein256.h cbits/skein512.h
+  cbits/tiger.h
 
 Flag test
   Description:       Build unit test
@@ -37,6 +38,7 @@
                      Data.CryptoHash.MD4
                      Data.CryptoHash.MD5
                      Data.CryptoHash.RIPEMD160
+                     Data.CryptoHash.Skein256
                      Data.CryptoHash.Skein512
                      Data.CryptoHash.Tiger
   ghc-options:       -Wall -O2 -optc-O3 -fno-cse
@@ -47,6 +49,7 @@
                      cbits/md4.c
                      cbits/md5.c
                      cbits/ripemd.c
+                     cbits/skein256.c
                      cbits/skein512.c
                      cbits/tiger.c
   Include-Dirs:      cbits
@@ -61,6 +64,7 @@
                      cbits/md4.c
                      cbits/md5.c
                      cbits/ripemd.c
+                     cbits/skein256.c
                      cbits/skein512.c
                      cbits/tiger.c
   if flag(test)
