diff --git a/Crypto/Threefish.hs b/Crypto/Threefish.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
+-- | 256 and 512 bit variants of the Threefish block cipher used as the
+--   foundation of the Skein hash function.
+module Crypto.Threefish (  
+    Block256 (..), Threefish256, Key256,
+    Block512 (..), Threefish512, Key512,
+    Tweak (..), parseHex, readHex, defaultTweak,
+    Threefish (..)
+) where
+import Crypto.Threefish.Threefish256 as TF256
+import Crypto.Threefish.Threefish512 as TF512
+import Crypto.Threefish.Common as Common
+import Data.Serialize
+
+class Serialize a => Threefish a b | a -> b where
+  -- | Create a Threefish key using a custom tweak value.
+  threefishKey :: Tweak -> a -> b
+  -- | Encrypt a block using the given key and tweak value.
+  threefishEncrypt :: a -> Tweak -> a -> a
+  -- | Decrypt a block using the given key and tweak value.
+  threefishDecrypt :: a -> Tweak -> a -> a
+
+instance Threefish Block256 Threefish256 where
+  threefishKey = Threefish256
+  threefishEncrypt = encrypt256
+  threefishDecrypt = decrypt256
+
+instance Threefish Block512 Threefish512 where
+  threefishKey = Threefish512
+  threefishEncrypt = encrypt512
+  threefishDecrypt = decrypt512
diff --git a/Crypto/Threefish/Common.hs b/Crypto/Threefish/Common.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Common.hs
@@ -0,0 +1,73 @@
+-- | Misc. shared constants and functions.
+module Crypto.Threefish.Common (
+    Tweak (..), showBytes, keyConst, e2m, defaultTweak, parseHex, readHex
+  ) where
+import Numeric (showHex)
+import Data.Word
+import Data.Bits
+import Data.Serialize
+import Control.Applicative
+import qualified Data.ByteString as BS
+import Data.Char
+import Data.Default
+
+hexDigit :: Char -> Maybe Word8
+hexDigit c | c >= '0' && c <= '9' = Just $ fromIntegral $ ord c - ord '0'
+           | c >= 'a' && c <= 'f' = Just $ fromIntegral $ ord c - ord 'a' + 10
+           | c >= 'A' && c <= 'F' = Just $ fromIntegral $ ord c - ord 'A' + 10
+           | otherwise            = Nothing
+
+-- | Parses a string of hexadecimal digits into a ByteString.
+parseHex :: String -> Maybe BS.ByteString
+parseHex = go []
+  where
+    go :: [Word8] -> String -> Maybe BS.ByteString
+    go bytes (h:l:xs) = do
+      h' <- hexDigit h
+      l' <- hexDigit l
+      go (((h' `shiftL` 4) .|. l') : bytes) xs
+    go bytes [] =
+      Just (BS.pack (reverse bytes))
+    go _ _ =
+      Nothing
+
+-- | Show a little endian Word64 as a string of bytes.
+showBytes :: Word64 -> String
+showBytes =
+    go (8 :: Int)
+  where
+    sr = shiftR
+    go 0 _ = ""
+    go n w = showHex ((w`sr`4).&.15) $ showHex (w.&.15) $ go (n-1) (w`sr`8)
+
+-- | Key constant for Threefish.
+keyConst :: Word64
+keyConst = 0x1BD11BDAA9FC1A22
+
+-- | Default tweak when Threefish is used in CBC, CTR, etc. modes.
+defaultTweak :: Tweak
+defaultTweak = Tweak 0 0
+
+-- | Turn an Either computation into its Maybe counterpart.
+e2m :: Either a b -> Maybe b
+e2m (Right x) = Just x
+e2m _         = Nothing
+
+-- | Threefish tweak value. Please see the Skein specification for info on
+--   how to use this.
+data Tweak = Tweak {-# UNPACK #-} !Word64
+                   {-# UNPACK #-} !Word64
+
+instance Serialize Tweak where
+  put (Tweak low high) = putWord64le low >> putWord64le high
+  get = Tweak <$> getWord64le <*> getWord64le
+
+instance Show Tweak where
+  show (Tweak low high) = showBytes low ++ showBytes high
+
+instance Default Tweak where
+  def = defaultTweak
+
+-- | Read any deserializable type from a hex string.
+readHex :: Serialize a => String -> Maybe a
+readHex s = parseHex s >>= e2m . decode
diff --git a/Crypto/Threefish/Mix.hs b/Crypto/Threefish/Mix.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Mix.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE BangPatterns #-}
+module Crypto.Threefish.Mix where
+import Data.Word
+import Data.Bits
+
+{-# INLINE mix #-}
+mix :: Word64 -> Word64 -> Int -> (Word64, Word64)
+mix !a !b !r =
+  case a + b of
+    a' -> (a', rotateL b r `xor` a')
+
+{-# INLINE mixKey #-}
+mixKey :: Word64 -> Word64 -> Int -> Word64 -> Word64 -> (Word64, Word64)
+mixKey !a !b !r !k0 !k1 =
+    (a', rotateL b' r `xor` a')
+  where
+    !b' = b + k1
+    !a' = a + b' + k0
+
+{-# INLINE unmix #-}
+unmix :: Word64 -> Word64 -> Int -> (Word64, Word64)
+unmix !a !b !r =
+  case rotateR (b `xor` a) r of
+    b' -> (a - b', b')
+
+{-# INLINE unmixKey #-}
+unmixKey :: Word64 -> Word64 -> Int -> Word64 -> Word64 -> (Word64, Word64)
+unmixKey !a !b !r !k0 !k1 =
+    (a', b' - k1)
+  where
+    !b' = rotateR (b `xor` a) r
+    !a' = a - (b' + k0)
diff --git a/Crypto/Threefish/Random.hs b/Crypto/Threefish/Random.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Random.hs
@@ -0,0 +1,71 @@
+-- | Skein 256 as a PRNG.
+module Crypto.Threefish.Random (
+    SkeinGen, Block256 (..), Random (..), RandomGen (..),
+    newSkeinGen, mkSkeinGen, mkSkeinGenEx, randomBytes
+  ) where
+import Crypto.Threefish.Skein
+import System.Random
+import System.Entropy
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe
+import Foreign.Storable (sizeOf, peek)
+import Foreign.Ptr (castPtr)
+import Data.Serialize
+
+emptyKey :: Key256
+emptyKey = Block256 BS.empty
+
+-- | Default amount of random bytes to buffer.
+defaultSkeinGenPoolSize :: Int
+defaultSkeinGenPoolSize = 256
+
+-- | Skein-based PRNG as defined in the Skein 1.3 paper.
+data SkeinGen = SkeinGen {
+    sgState    :: Block256,
+    sgPool     :: BS.ByteString,
+    sgPoolSize :: Int
+  }
+
+instance RandomGen SkeinGen where
+  next g =
+    case randomBytes (sizeOf (0::Int)) g of
+      (bs, g') -> (unsafePerformIO $ unsafeUseAsCString bs $ peek . castPtr, g')
+  split g =
+    case BS.splitAt 32 (fst $ randomBytes 64 g) of
+      (a, b) -> (mkSkeinGenEx (sgPoolSize g) (Block256 a),
+                 mkSkeinGenEx (sgPoolSize g) (Block256 b))
+
+-- | Create a new Skein PRNG from the system's entropy pool.
+newSkeinGen :: IO SkeinGen
+newSkeinGen =
+  (mkSkeinGenEx defaultSkeinGenPoolSize . Block256) `fmap` getEntropy 32
+
+-- | Create a Skein PRNG from a seed.
+mkSkeinGen :: Serialize a => a -> SkeinGen
+mkSkeinGen = mkSkeinGenEx defaultSkeinGenPoolSize . Block256 . encode
+
+-- | Create a Skein PRNG with a custom pool size. Larger pool sizes give faster
+--   random data, but obviously take up more memory. Pool size is preserved
+--   across splits.
+mkSkeinGenEx :: Int -> Block256 -> SkeinGen
+mkSkeinGenEx poolsize (Block256 seed) = SkeinGen {
+    sgState    = skein (BS.replicate 32 0 `BS.append` seed),
+    sgPool     = BS.empty,
+    sgPoolSize = poolsize
+  }
+
+-- | Generate n random bytes using the given generator.
+randomBytes :: Int -> SkeinGen -> (BS.ByteString, SkeinGen)
+randomBytes nbytes (SkeinGen (Block256 state) pool poolsize)
+  | BS.length pool >= nbytes =
+    case BS.splitAt nbytes pool of
+      (output, rest) -> (output, SkeinGen (Block256 state) rest poolsize)
+  | otherwise =
+      (BS.append pool out, SkeinGen (Block256 state') pool' poolsize)
+  where
+    -- Use all of the output to avoid making unnecessary calls
+    nbytes' = 32 + max (nbytes + (32-(nbytes`rem`32))) poolsize
+    bytes = hash256 nbytes' emptyKey emptyKey state
+    (state', buffer) = BS.splitAt 32 bytes
+    (out, pool') = BS.splitAt (nbytes - BS.length pool) buffer
diff --git a/Crypto/Threefish/Skein.hs b/Crypto/Threefish/Skein.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Skein.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE BangPatterns, OverloadedStrings, ForeignFunctionInterface #-}
+-- | 256 and 512 bit Skein. Supports "normal" hashing and Skein-MAC.
+module Crypto.Threefish.Skein (
+    Skein (..), Block256 (..), Block512 (..), Key256, Key512, Nonce256,
+    hash256, hash512
+  ) where
+import qualified Data.ByteString as BS
+import Crypto.Threefish.Threefish256
+import Crypto.Threefish.Threefish512
+import Crypto.Threefish.UBI
+import Data.Bits
+import Data.Serialize
+import Data.Word
+import Data.ByteString.Unsafe
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import System.IO.Unsafe
+
+foreign import ccall "hash256" c_hash256 :: Ptr Word64 -- ^ Key (nullPtr for none)
+                                         -> Ptr Word64 -- ^ Nonce (nullPtr for none)
+                                         -> Word64     -- ^ Message length
+                                         -> Ptr Word64 -- ^ Message
+                                         -> Int        -- ^ Size of output, in bytes
+                                         -> Ptr Word64 -- ^ Output blocks pointer
+                                         -> IO ()
+
+class Skein a where
+  -- | Calculate the Skein-MAC of a message.
+  skeinMAC :: a -> BS.ByteString -> a
+  -- | Calculate the Skein checksum of a message.
+  skein :: BS.ByteString -> a
+
+type Nonce256 = Block256
+
+-- | Hash a message using a particular key. For normal hashing, use an empty
+--   ByteString; for Skein-MAC, use the MAC key.
+hash256 :: Int -> Key256 -> Nonce256 -> BS.ByteString -> BS.ByteString
+hash256 outlen (Block256 key) (Block256 nonce) !msg =
+    unsafePerformIO $
+      withKey $ \k -> do
+        withNonce $ \n -> do
+          unsafeUseAsCString msg $ \b -> do
+            out <- mallocForeignPtrArray (outblocks*32)
+            withForeignPtr out $ \out' -> do
+              c_hash256 k n len (castPtr b) outlen out'
+              BS.packCStringLen (castPtr out', outlen)
+  where
+    outblocks =
+      case outlen `quotRem` 32 of
+        (blocks, 0) -> blocks
+        (blocks, _) -> blocks+1
+    !len = fromIntegral $ BS.length msg
+    withKey f | BS.length key == 32 = unsafeUseAsCString key (f . castPtr)
+                | otherwise         = f nullPtr
+    withNonce f | BS.length nonce == 32 = unsafeUseAsCString nonce (f . castPtr)
+                | otherwise             = f nullPtr
+
+{-# INLINE skein256 #-}
+-- | Hash a message using 256 bit Skein.
+skein256 :: BS.ByteString -> Block256
+skein256 = Block256 . hash256 32 (Block256 "") (Block256 "")
+
+{-# INLINE skeinMAC256 #-}
+-- | Create a 256 bit Skein-MAC.
+skeinMAC256 :: Key256 -> BS.ByteString -> Block256
+skeinMAC256 key = Block256 . hash256 32 key (Block256 "")
+
+instance Skein Block256 where
+  skeinMAC = skeinMAC256
+  skein    = skein256
+
+
+
+---------------------
+-- 512 bit version --
+---------------------
+
+config512 :: Block512
+config512 = Block512 0x0000000133414853 512 0 0 0 0 0 0
+
+{-# INLINE xb512 #-}
+xb512 :: Block512 -> Block512 -> Block512
+xb512 (Block512 x1 x2 x3 x4 x5 x6 x7 x8)
+      (Block512 y1 y2 y3 y4 y5 y6 y7 y8) =
+  Block512 (x1 `xor` y1) (x2 `xor` y2) (x3 `xor` y3) (x4 `xor` y4)
+           (x5 `xor` y5) (x6 `xor` y6) (x7 `xor` y7) (x8 `xor` y8)
+
+-- | Initial state for Skein512
+init512 :: Key512 -> Block512
+init512 key = fst $ processBlock512 32 key configTweak config512
+
+zero512 :: Block512
+zero512 = Block512 0 0 0 0 0 0 0 0
+
+{-# INLINE processBlock512 #-}
+-- | Process a single block of Skein 512. Call on Threefish, XOR the cryptotext
+--   with the plaintext and update the tweak.
+processBlock512 :: Word64 -> Key512 -> Tweak -> Block512 -> (Key512, Tweak)
+processBlock512 !len !key !tweak !block =
+    (encrypt512 key tweak' block `xb512` block, setFirst False tweak')
+  where
+    !tweak' = addBytes len tweak
+
+-- | Hash a message using a particular key. For normal hashing, use all zeroes;
+--   for Skein-MAC, use the MAC key.
+hash512 :: Key512 -> BS.ByteString -> Block512
+hash512 !firstkey !bs =
+    case flip runGet bs' $ go len (init512 firstkey) (newTweak Message) of
+      Right x -> x
+      Left _  -> error "hash512 failed to get output bytes - impossible!"
+  where
+    !len = BS.length bs
+    !lastLen = case len `rem` 64 of 0 -> 64 ; n -> n
+    !lastLenW64 = fromIntegral lastLen
+    !bs' = BS.append bs (BS.replicate (64-lastLen) 0)
+    go !n !key !tweak
+      | n > 64 = do
+        block <- get
+        let (block', tweak') = processBlock512 64 key tweak block
+        go (n-64) block' tweak'
+      | otherwise = do
+        block <- get
+        let tweak' = setLast True tweak
+            (block', _) = processBlock512 lastLenW64 key tweak' block
+            finalTweak = setLast True $ newTweak Output
+            (b,_) = processBlock512 8 block' finalTweak zero512
+        return b
+
+{-# INLINE skein512 #-}
+-- | Hash a message using 512 bit Skein.
+skein512 :: BS.ByteString -> Block512
+skein512 = hash512 zero512
+
+{-# INLINE skeinMAC512 #-}
+-- | Create a 512 bit Skein-MAC.
+skeinMAC512 :: Key512 -> BS.ByteString -> Block512
+skeinMAC512 =
+  hash512 . fst . processBlock512 64 zero512 (setLast True $ newTweak Key)
+
+instance Skein Block512 where
+  skeinMAC = skeinMAC512
+  skein    = skein512
diff --git a/Crypto/Threefish/Threefish256.hs b/Crypto/Threefish/Threefish256.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Threefish256.hs
@@ -0,0 +1,91 @@
+{-# LANGUAGE BangPatterns, ForeignFunctionInterface #-}
+-- | 256 bit Threefish.
+module Crypto.Threefish.Threefish256 (
+    Block256 (..), Key256, Threefish256 (..),
+    encrypt256, decrypt256, readBlock256, Tweak (..)
+  ) where
+import Data.Word
+import Crypto.Threefish.Common
+import Data.Serialize
+import Control.Applicative
+import Crypto.Classes
+import Data.Tagged
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import Foreign.Storable
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import System.IO.Unsafe
+
+foreign import ccall "encrypt256" c_encrypt256 :: Ptr Word64
+                                               -> Word64
+                                               -> Word64
+                                               -> Ptr Word64
+                                               -> Ptr Word64
+                                               -> IO ()
+
+foreign import ccall "decrypt256" c_decrypt256 :: Ptr Word64
+                                               -> Word64
+                                               -> Word64
+                                               -> Ptr Word64
+                                               -> Ptr Word64
+                                               -> IO ()
+
+newtype Block256 = Block256 BS.ByteString deriving Eq
+type Key256 = Block256
+
+instance Show Block256 where
+  show (Block256 bs) =
+    case readBlock256 bs 0 of
+      (a, b, c, d) -> showBytes a ++ showBytes b ++ showBytes c ++ showBytes d
+
+instance Serialize Block256 where
+  put (Block256 bs) = putByteString bs
+  get = Block256 <$> getBytes 32
+
+instance Serialize Threefish256 where
+  put (Threefish256 tweak key) = put tweak >> put key
+  get = Threefish256 <$> get <*> get
+
+-- | 256 bit Threefish block cipher.
+data Threefish256 = Threefish256 !Tweak !Key256
+
+{-# INLINE readBlock256 #-}
+readBlock256 :: BS.ByteString -> Int -> (Word64, Word64, Word64, Word64)
+readBlock256 bs off = unsafePerformIO . unsafeUseAsCString bs $ \ptr -> do
+  a <- peekElemOff (castPtr ptr) off
+  b <- peekElemOff (castPtr ptr) (off+1)
+  c <- peekElemOff (castPtr ptr) (off+2)
+  d <- peekElemOff (castPtr ptr) (off+3)
+  return $! (a, b, c, d)
+
+instance BlockCipher Threefish256 where
+  blockSize = Tagged 256
+  keyLength = Tagged 256
+  encryptBlock (Threefish256 tweak key) block =
+    case encrypt256 key tweak (Block256 block) of
+      Block256 out -> out
+  decryptBlock (Threefish256 tweak key) block =
+    case decrypt256 key tweak (Block256 block) of
+      Block256 out -> out
+  buildKey bs | BS.length bs /= 32 = Nothing
+              | otherwise          = Just (Threefish256 defaultTweak
+                                                        (Block256 bs))
+
+decrypt256 :: Key256 -> Tweak -> Block256 -> Block256
+decrypt256 (Block256 key) (Tweak t0 t1) (Block256 block) =
+  unsafePerformIO $ unsafeUseAsCString key $ \k ->
+                    unsafeUseAsCString block $ \b -> do
+                      out <- mallocForeignPtrArray 4
+                      withForeignPtr out $ \out' -> do
+                        c_decrypt256 (castPtr k) t0 t1 (castPtr b) out'
+                        Block256 <$> BS.packCStringLen (castPtr out', 32)
+
+encrypt256 :: Key256 -> Tweak -> Block256 -> Block256
+encrypt256 (Block256 key) (Tweak t0 t1) (Block256 block) =
+  unsafePerformIO $ unsafeUseAsCString key $ \k ->
+                    unsafeUseAsCString block $ \b -> do
+                      out <- mallocForeignPtrArray 4
+                      withForeignPtr out $ \out' -> do
+                        c_encrypt256 (castPtr k) t0 t1 (castPtr b) out'
+                        Block256 <$> BS.packCStringLen (castPtr out', 32)
diff --git a/Crypto/Threefish/Threefish512.hs b/Crypto/Threefish/Threefish512.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/Threefish512.hs
@@ -0,0 +1,153 @@
+{-# LANGUAGE BangPatterns #-}
+-- | 512 bit Threefish.
+module Crypto.Threefish.Threefish512 where
+import Data.Word
+import Data.Bits
+import Crypto.Threefish.Mix
+import Crypto.Threefish.Common
+import Data.Array.Unboxed
+import Data.Serialize
+import Control.Applicative
+import Crypto.Classes
+import Data.Tagged
+import qualified Data.ByteString as BS
+import Data.List (foldl1')
+
+-- | 512 bit Threefish block cipher.
+data Threefish512 = Threefish512 !Tweak !Key512
+
+data Block512 = Block512 {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                         {-# UNPACK #-} !Word64
+                deriving Eq
+
+type Key512 = Block512
+
+instance Show Block512 where
+  show (Block512 a b c d e f g h) =
+    showBytes a ++ showBytes b ++ showBytes c ++ showBytes d ++
+    showBytes e ++ showBytes f ++ showBytes g ++ showBytes h
+
+instance Serialize Block512 where
+  put (Block512 a b c d e f g h) = do
+    putWord64le a >> putWord64le b >> putWord64le c >> putWord64le d
+    putWord64le e >> putWord64le f >> putWord64le g >> putWord64le h
+  get =
+    Block512 <$> getWord64le <*> getWord64le <*> getWord64le <*> getWord64le
+             <*> getWord64le <*> getWord64le <*> getWord64le <*> getWord64le
+
+instance Serialize Threefish512 where
+  put (Threefish512 tweak key) = put tweak >> put key
+  get = Threefish512 <$> get <*> get
+
+-- | Rotational constants for TF512
+rot :: UArray Word64 Int
+rot = listArray (0,32) [46,36,19,37,33,27,14,42,17,49,36,39,44,9,54,56,
+                        39,30,34,24,13,50,10,17,25,29,39,43,8,35,56,22]
+
+-- | Encrypt a 512 bit Threefish block. Tweak may have any value without
+--   compromising security.
+{-# INLINE encrypt512 #-}
+encrypt512 :: Key512 -> Tweak -> Block512 -> Block512
+encrypt512 (Block512 k0 k1 k2 k3 k4 k5 k6 k7) (Tweak t0 t1) !blockin =
+    case rounds 0 blockin of
+      Block512 a b c d e f g h ->
+        Block512 (a+k0) (b+k1) (c+k2) (d+k3)
+                 (e+k4) (f+k5+t0) (g+k6+t1) (h+k7+18)
+  where
+    k8 = foldl1' xor [k0,k1,k2,k3,k4,k5,k6,k7,keyConst]
+    ks :: UArray Word64 Word64
+    !ks = listArray (0, 8) [k0, k1, k2, k3, k4, k5, k6, k7, k8]
+    ts :: UArray Word64 Word64
+    !ts = listArray (0, 2) [t0, t1, t0 `xor` t1]
+    
+    rounds 18 input   = input
+    rounds !n !input = rounds (n+1) (fourRounds input n (n*16))
+    
+    {-# INLINE fourRounds #-}
+    fourRounds (Block512 a0 b0 c0 d0 e0 f0 g0 h0) keyOff r =
+        Block512 a4 b4 c4 d4 e4 f4 g4 h4
+      where
+        {-# INLINE key #-}
+        key n = ks ! ((keyOff + n) `rem` 9)
+        {-# INLINE t #-}
+        t n = ts ! ((keyOff + n) `rem` 3)
+        (a1, b1) = mixKey a0 b0 (rot ! (r .&. 31)) (key 0) (key 1)
+        (c1, d1) = mixKey c0 d0 (rot ! ((r+1) .&. 31)) (key 2) (key 3)
+        (e1, f1) = mixKey e0 f0 (rot ! ((r+2) .&. 31)) (key 4) (key 5 + t 0)
+        (g1, h1) = mixKey g0 h0 (rot ! ((r+3) .&. 31)) (key 6 + t 1) (key 7+keyOff)
+        (c2, b2) = mix c1 b1 (rot ! ((r+4) .&. 31))
+        (e2, h2) = mix e1 h1 (rot ! ((r+5) .&. 31))
+        (g2, f2) = mix g1 f1 (rot ! ((r+6) .&. 31))
+        (a2, d2) = mix a1 d1 (rot ! ((r+7) .&. 31))
+        (e3, b3) = mix e2 b2 (rot ! ((r+8) .&. 31))
+        (g3, d3) = mix g2 d2 (rot ! ((r+9) .&. 31))
+        (a3, f3) = mix a2 f2 (rot ! ((r+10) .&. 31))
+        (c3, h3) = mix c2 h2 (rot ! ((r+11) .&. 31))
+        (g4, b4) = mix g3 b3 (rot ! ((r+12) .&. 31))
+        (a4, h4) = mix a3 h3 (rot ! ((r+13) .&. 31))
+        (c4, f4) = mix c3 f3 (rot ! ((r+14) .&. 31))
+        (e4, d4) = mix e3 d3 (rot ! ((r+15) .&. 31))
+
+-- | Encrypt a 512 bit Threefish block.
+{-# INLINE decrypt512 #-}
+decrypt512 :: Key512 -> Tweak -> Block512 -> Block512
+decrypt512 (Block512 k0 k1 k2 k3 k4 k5 k6 k7) (Tweak t0 t1) !blockin =
+    case blockin of
+      (Block512 a b c d e f g h) ->
+        rounds 18 $ Block512 (a-k0) (b-k1) (c-k2) (d-k3)
+                            (e-k4) (f-(k5+t0)) (g-(k6+t1)) (h-(k7+18))
+  where
+    ks :: UArray Word64 Word64
+    !ks = listArray (0, 8) [k0, k1, k2, k3, k4, k5, k6, k7, keyConst]
+    ts :: UArray Word64 Word64
+    !ts = listArray (0, 2) [t0, t1, t0 `xor` t1]
+    
+    rounds 0 input   = input
+    rounds !n !input = rounds (n-1) (fourRounds input (n-1) ((n-1)*16))
+    
+    {-# INLINE fourRounds #-}
+    fourRounds (Block512 a0 b0 c0 d0 e0 f0 g0 h0) keyOff r =
+        Block512 a4 b4 c4 d4 e4 f4 g4 h4
+      where
+        {-# INLINE key #-}
+        key n = ks ! ((keyOff + n) `rem` 9)
+        {-# INLINE t #-}
+        t n = ts ! ((keyOff + n) `rem` 3)
+        (g1, b1) = unmix g0 b0 (rot ! ((r+12) .&. 31))
+        (a1, h1) = unmix a0 h0 (rot ! ((r+13) .&. 31))
+        (c1, f1) = unmix c0 f0 (rot ! ((r+14) .&. 31))
+        (e1, d1) = unmix e0 d0 (rot ! ((r+15) .&. 31))
+        (e2, b2) = unmix e1 b1 (rot ! ((r+8) .&. 31))
+        (g2, d2) = unmix g1 d1 (rot ! ((r+9) .&. 31))
+        (a2, f2) = unmix a1 f1 (rot ! ((r+10) .&. 31))
+        (c2, h2) = unmix c1 h1 (rot ! ((r+11) .&. 31))
+        (c3, b3) = unmix c2 b2 (rot ! ((r+4) .&. 31))
+        (e3, h3) = unmix e2 h2 (rot ! ((r+5) .&. 31))
+        (g3, f3) = unmix g2 f2 (rot ! ((r+6) .&. 31))
+        (a3, d3) = unmix a2 d2 (rot ! ((r+7) .&. 31))
+        (a4, b4) = unmixKey a3 b3 (rot ! (r .&. 31)) (key 0) (key 1)
+        (c4, d4) = unmixKey c3 d3 (rot ! ((r+1) .&. 31)) (key 2) (key 3)
+        (e4, f4) = unmixKey e3 f3 (rot ! ((r+2) .&. 31)) (key 4) (key 5 + t 0)
+        (g4, h4) = unmixKey g3 h3 (rot ! ((r+3) .&. 31)) (key 6 + t 1) (key 7+keyOff)
+
+instance BlockCipher Threefish512 where
+  blockSize = Tagged 512
+  keyLength = Tagged 512
+  encryptBlock (Threefish512 tweak key) block =
+    case decode block of
+      Right block' -> encode (encrypt512 key tweak block')
+      Left e       -> error $ "Not a valid Threefish512 block: " ++ show e
+  decryptBlock (Threefish512 tweak key) block =
+    case decode block of
+      Right block' -> encode (decrypt512 key tweak block')
+      Left e       -> error $ "Not a valid Threefish512 block: " ++ show e
+  buildKey bs | BS.length bs /= 64 = Nothing
+              | otherwise          = e2m $ do
+                key <- decode bs
+                return $! Threefish512 defaultTweak key
diff --git a/Crypto/Threefish/UBI.hs b/Crypto/Threefish/UBI.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Threefish/UBI.hs
@@ -0,0 +1,52 @@
+-- | Tweak manipulation for Unique Block Iteration mode.
+module Crypto.Threefish.UBI where
+import Data.Word
+import Data.Bits
+import Crypto.Threefish.Common
+
+data BlockType
+  = Key
+  | Config
+  | Personalization
+  | PublicKey
+  | KeyIdentifier
+  | Nonce
+  | Message
+  | Output
+
+type2w64 :: BlockType -> Word64
+type2w64 Key             = 0
+type2w64 Config          = 4
+type2w64 Personalization = 8
+type2w64 PublicKey       = 12
+type2w64 KeyIdentifier   = 16
+type2w64 Nonce           = 20
+type2w64 Message         = 48
+type2w64 Output          = 63
+
+{-# INLINE newTweak #-}
+newTweak :: BlockType -> Tweak
+newTweak t = setType t $ setFirst True $ Tweak 0 0
+
+{-# INLINE setType #-}
+setType :: BlockType -> Tweak -> Tweak
+setType t (Tweak lo hi) =
+    Tweak lo ((type2w64 t `shiftL` 56) .|. (hi .&. zeroType))
+  where
+    zeroType = complement (63 `shiftL` 56)
+
+{-# INLINE setFirst #-}
+setFirst :: Bool -> Tweak -> Tweak
+setFirst set (Tweak lo hi) = Tweak lo ((if set then setBit else clearBit) hi 62)
+
+{-# INLINE setLast #-}
+setLast :: Bool -> Tweak -> Tweak
+setLast set (Tweak lo hi) = Tweak lo ((if set then setBit else clearBit) hi 63)
+
+{-# INLINE addBytes #-}
+addBytes :: Word64 -> Tweak -> Tweak
+addBytes bs (Tweak lo hi) = Tweak (lo + bs) hi
+
+{-# INLINE configTweak #-}
+configTweak :: Tweak
+configTweak = setFirst True $ setLast True $ newTweak Config
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Anton Ekblad
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Anton Ekblad nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+OWNER 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/cbits/skein256.c b/cbits/skein256.c
new file mode 100644
--- /dev/null
+++ b/cbits/skein256.c
@@ -0,0 +1,105 @@
+#include "threefish.h"
+#include <string.h>
+
+void skein256_init(skein_t* ctx, W64* key, int outlen) {
+  W64 config[4] = {0x0000000133414853, outlen*8, 0, 0};
+  static W64 zeroes[4] = {0,0,0,0};
+
+  /* Set up key if needed */
+  if(key != NULL) {
+    init_tweak(T_KEY, ctx->tweak);
+    set_last(1, ctx->tweak);
+    add_bytes(32, ctx->tweak);
+    encrypt256(zeroes, ctx->tweak[0], ctx->tweak[1], key, ctx->key);
+    ctx->key[0] ^= key[0];
+    ctx->key[1] ^= key[1];
+    ctx->key[2] ^= key[2];
+    ctx->key[3] ^= key[3];
+  } else {
+    ctx->key[0] = 0; ctx->key[1] = 0; ctx->key[2] = 0; ctx->key[3] = 0;
+  }
+
+  /* Process config string */
+  mk_config_tweak(ctx->tweak);
+  add_bytes(32, ctx->tweak);
+  encrypt256(ctx->key, ctx->tweak[0], ctx->tweak[1], config, ctx->key);
+  ctx->key[0] ^= config[0];
+  ctx->key[1] ^= config[1];
+  ctx->key[2] ^= config[2];
+  ctx->key[3] ^= config[3];
+}
+
+void skein256_update(skein_t* ctx, int firstlast, UBIType type, W64 len, W64* data) {
+  W64 buf[4] = {0,0,0,0};
+  W64* k = ctx->key;
+  W64* tweak = ctx->tweak;
+  int lastlen;
+
+  /* Process message */
+  if(firstlast & 1) {
+    init_tweak(type, tweak);
+  }
+  /* If this is not the last update, don't do last block processing  */
+  if(!(firstlast & 2) && len % 32 == 0) {
+    ++len;
+  }
+  while(len > 32) {
+    add_bytes(32, tweak);
+    encrypt256(k, tweak[0], tweak[1], data, k);
+    set_first(0, tweak);
+    k[0] ^= *data; ++data;
+    k[1] ^= *data; ++data;
+    k[2] ^= *data; ++data;
+    k[3] ^= *data; ++data;
+    len -= 32;
+  }
+
+  /* Process last block */
+  if(firstlast & 2) {
+    lastlen = len % 32;
+    if(lastlen == 0 && len > 0) {
+      lastlen = 32;
+    }
+    add_bytes(lastlen, tweak);
+    set_last(1, tweak);
+    memcpy(buf, data, lastlen);
+    encrypt256(k, tweak[0], tweak[1], buf, k);
+    k[0] ^= buf[0];
+    k[1] ^= buf[1];
+    k[2] ^= buf[2];
+    k[3] ^= buf[3];
+  }
+}
+
+void skein256_output(skein_t* ctx, int from, int to, W64* out) {
+  W64 buf[4] = {from,0,0,0};
+  W64 *k = ctx->key;
+  W64 *tweak = ctx->tweak;
+  for(; from <= to; ++from) {
+    init_tweak(T_OUT, tweak);
+    set_last(1, tweak);
+    add_bytes(8, tweak);
+    encrypt256(k, tweak[0], tweak[1], buf, out);
+    out += 4;
+    ++buf[0];
+  }
+}
+
+void hash256(W64* key, W64* nonce, W64 len, W64* data, int outlen, W64* out) {
+  skein_t ctx;
+
+  skein256_init(&ctx, key, outlen);
+
+  /* Process nonce, if available; must be 32 bytes */
+  if(nonce != NULL) {
+    skein256_update(&ctx, 3, T_NONCE, 32, nonce);
+  }
+
+  skein256_update(&ctx, 3, T_MSG, len, data);
+  if(outlen % 32 == 0) {
+    outlen = outlen / 32;
+  } else {
+    outlen = (outlen / 32) + 1;
+  }
+  skein256_output(&ctx, 0, outlen, out);
+}
diff --git a/cbits/threefish256.c b/cbits/threefish256.c
new file mode 100644
--- /dev/null
+++ b/cbits/threefish256.c
@@ -0,0 +1,82 @@
+#include "threefish.h"
+
+W64 key_const = 0x1BD11BDAA9FC1A22;
+
+void encrypt256(W64* key, W64 t0, W64 t1, W64* in, W64* out) {
+  int r;
+  W64 k4 = key[0] ^ key[1] ^ key[2] ^ key[3] ^ key_const;
+  W64 ks[5] = {key[0], key[1], key[2], key[3], k4};
+  W64 ts[3] = {t0, t1, t0 ^ t1};
+  W64 a = in[0] + ks[0];
+  W64 b = in[1] + ks[1] + ts[0];
+  W64 c = in[2] + ks[2] + ts[1];
+  W64 d = in[3] + ks[3];
+
+  for(r = 2; r < 20; r += 2) {
+    a += b; b = rl(b, 14) ^ a;
+    c += d; d = rl(d, 16) ^ c;
+    a += d; d = rl(d, 52) ^ a;
+    c += b; b = rl(b, 57) ^ c;
+    a += b; b = rl(b, 23) ^ a;
+    c += d; d = rl(d, 40) ^ c;
+    a += d; d = rl(d,  5) ^ a;
+    c += b; b = rl(b, 37) ^ c;
+    a += ks[(r-1) % 5];
+    b += ks[r % 5] + ts[(r-1) % 3];
+    c += ks[(r+1) % 5] + ts[r % 3];
+    d += ks[(r+2) % 5] + (r-1);
+    a += b; b = rl(b, 25) ^ a;
+    c += d; d = rl(d, 33) ^ c;
+    a += d; d = rl(d, 46) ^ a;
+    c += b; b = rl(b, 12) ^ c;
+    a += b; b = rl(b, 58) ^ a;
+    c += d; d = rl(d, 22) ^ c;
+    a += d; d = rl(d, 32) ^ a;
+    c += b; b = rl(b, 32) ^ c;
+    a += ks[r % 5];
+    b += ks[(r+1) % 5] + ts[r % 3];
+    c += ks[(r+2) % 5] + ts[(r+1) % 3];
+    d += ks[(r+3) % 5] + r;
+  }
+  out[0] = a; out[1] = b; out[2] = c; out[3] = d;
+}
+
+void decrypt256(W64* key, W64 t0, W64 t1, W64* in, W64* out) {
+  int r;
+  W64 k4 = key[0] ^ key[1] ^ key[2] ^ key[3] ^ key_const;
+  W64 ks[5] = {key[0], key[1], key[2], key[3], k4};
+  W64 ts[3] = {t0, t1, t0 ^ t1};
+  W64 a = in[0] + ks[0];
+  W64 b = in[1] + ks[1] + ts[0];
+  W64 c = in[2] + ks[2] + ts[1];
+  W64 d = in[3] + ks[3];
+
+  for(r = 18; r >= 2; r -= 2) {
+    a -= ks[r % 5];
+    b -= ks[(r+1) % 5] + ts[r % 3];
+    c -= ks[(r+2) % 5] + ts[(r+1) % 3];
+    d -= ks[(r+3) % 5] + r;
+    d = rr(d^a, 32); a -= d;
+    b = rr(b^c, 32); c -= b;
+    b = rr(b^a, 58); a -= b;
+    d = rr(d^c, 22); c -= d;
+    d = rr(d^a, 46); a -= d;
+    b = rr(b^c, 12); c -= b;
+    b = rr(b^a, 25); a -= b;
+    d = rr(d^c, 33); c -= d;
+    a -= ks[(r-1) % 5];
+    b -= ks[r % 5] + ts[(r-1) % 3];
+    c -= ks[(r+1) % 5] + ts[r % 3];
+    d -= ks[(r+2) % 5] + (r-1);
+    d = rr(d^a,  5); a -= d;
+    b = rr(b^c, 37); c -= b;
+    b = rr(b^a, 23); a -= b;
+    d = rr(d^c, 40); c -= d;
+    d = rr(d^a, 52); a -= d;
+    b = rr(b^c, 57); c -= b;
+    b = rr(b^a, 14); a -= b;
+    d = rr(d^c, 16); c -= d;
+  }
+  out[0] = a - ks[0];           out[1] = b - (ks[1] + ts[0]);
+  out[2] = c - (ks[2] + ts[1]); out[3] = d - ks[3];
+}
diff --git a/cbits/ubi.c b/cbits/ubi.c
new file mode 100644
--- /dev/null
+++ b/cbits/ubi.c
@@ -0,0 +1,37 @@
+#include "threefish.h"
+
+inline void init_tweak(UBIType type, W64* t) {
+  t[0] = 0;
+  t[1] = 0;
+  set_first(1, t);
+  set_type(type, t);
+}
+
+inline void mk_config_tweak(W64* t) {
+  init_tweak(T_CONFIG, t);
+  set_last(1, t);
+}
+
+inline void set_type(UBIType type, W64* t) {
+  t[1] = (((W64)type) << 56) | (t[1] & ~(((W64)63) << 56));
+}
+
+inline void set_first(unsigned char first, W64* t) {
+  if(first) {
+    t[1] |= (((W64)1) << 62);
+  } else {
+    t[1] &= ~(((W64)1) << 62);
+  }
+}
+
+inline void set_last(unsigned char last, W64* t) {
+  if(last) {
+    t[1] |= (((W64)1) << 63);
+  } else {
+    t[1] &= ~(((W64)1) << 63);
+  }
+}
+
+inline void add_bytes(W64 bytes, W64* t) {
+  t[0] += bytes;
+}
diff --git a/threefish.cabal b/threefish.cabal
new file mode 100644
--- /dev/null
+++ b/threefish.cabal
@@ -0,0 +1,47 @@
+name:                threefish
+version:             0.1
+synopsis:            The Threefish block cipher and the Skein hash function for Haskell.
+description:         Implements 256 and 512 bit variants of Threefish and Skein. Skein is usable as a "normal" hash function as well as in Skein-MAC mode and as a cryptographically secure PRNG, as specified in the Skein 1.3 paper.
+homepage:            http://github.com/valderman/threefish
+license:             BSD3
+license-file:        LICENSE
+author:              Anton Ekblad
+maintainer:          anton@ekblad.cc
+-- copyright:           
+category:            Codec, Cryptography, Random
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:
+    Crypto.Threefish,
+    Crypto.Threefish.Random,
+    Crypto.Threefish.Skein
+  other-modules:
+    Crypto.Threefish.Common,
+    Crypto.Threefish.Mix,
+    Crypto.Threefish.Threefish256,
+    Crypto.Threefish.Threefish512,
+    Crypto.Threefish.UBI
+  other-extensions:
+    BangPatterns,
+    MultiParamTypeClasses,
+    FunctionalDependencies
+  build-depends:
+    base >=4.6 && <5,
+    bytestring >=0.10,
+    cereal >=0.3,
+    array >=0.4,
+    crypto-api >=0.12,
+    tagged >=0.4,
+    data-default >=0.5,
+    random,
+    entropy >= 0.2.2.2
+  default-language:    Haskell2010
+  ghc-options: -Wall -O2
+  include-dirs: cbits
+  c-sources:
+    cbits/threefish256.c,
+    cbits/skein256.c,
+    cbits/ubi.c
