summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonEkblad <>2013-10-01 18:43:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2013-10-01 18:43:00 (GMT)
commitce5fad77a4dd0a6ec73ea566274347003d0c8bf9 (patch)
treee0af0b4019a85959939d9b2474ae0db8c03fa01a
version 0.2.10.2.1
-rw-r--r--Crypto/Threefish.hs43
-rw-r--r--Crypto/Threefish/Authenticated.hs122
-rw-r--r--Crypto/Threefish/Common.hs73
-rw-r--r--Crypto/Threefish/Mix.hs32
-rw-r--r--Crypto/Threefish/Random.hs104
-rw-r--r--Crypto/Threefish/Skein.hs159
-rw-r--r--Crypto/Threefish/Skein/KDF.hs34
-rw-r--r--Crypto/Threefish/Skein/StreamCipher.hs67
-rw-r--r--Crypto/Threefish/Threefish256.hs91
-rw-r--r--Crypto/Threefish/Threefish512.hs153
-rw-r--r--Crypto/Threefish/UBI.hs62
-rw-r--r--LICENSE30
-rw-r--r--Setup.hs2
-rw-r--r--cbits/skein256.c86
-rw-r--r--cbits/threefish256.c82
-rw-r--r--cbits/ubi.c37
-rw-r--r--threefish.cabal54
17 files changed, 1231 insertions, 0 deletions
diff --git a/Crypto/Threefish.hs b/Crypto/Threefish.hs
new file mode 100644
index 0000000..ee7670a
--- /dev/null
+++ b/Crypto/Threefish.hs
@@ -0,0 +1,43 @@
+{-# 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
+import qualified Data.ByteString as BS
+
+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
+ -- | Create an appropriately sized block.
+ toBlock :: BS.ByteString -> Maybe a
+ -- | Extract the contents of a block.
+ fromBlock :: a -> BS.ByteString
+
+instance Threefish Block256 Threefish256 where
+ threefishKey = Threefish256
+ threefishEncrypt = encrypt256
+ threefishDecrypt = decrypt256
+ toBlock bs = if BS.length bs /= 32 then Nothing else Just (Block256 bs)
+ fromBlock (Block256 bs) = bs
+
+instance Threefish Block512 Threefish512 where
+ threefishKey = Threefish512
+ threefishEncrypt = encrypt512
+ threefishDecrypt = decrypt512
+ toBlock bs =
+ case decode bs of
+ Right block -> Just block
+ _ -> Nothing
+ fromBlock = encode
diff --git a/Crypto/Threefish/Authenticated.hs b/Crypto/Threefish/Authenticated.hs
new file mode 100644
index 0000000..d0e0e3a
--- /dev/null
+++ b/Crypto/Threefish/Authenticated.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE OverloadedStrings #-}
+-- | Authenticated encryption using Skein for PRNG, KDF, stream cipher and MAC.
+module Crypto.Threefish.Authenticated (
+ DecryptFailure (..), Encrypted, Plaintext, Block256, Nonce256, Key256,
+ encrypt, decrypt, encrypt', decrypt', encryptBytes, decryptBytes,
+ generateNonce, toBlock, fromBlock
+ ) where
+import Crypto.Threefish
+import Crypto.Threefish.Threefish256 (Block256(..))
+import Crypto.Threefish.Skein
+import Crypto.Threefish.Skein.KDF hiding (deriveKeys)
+import qualified Crypto.Threefish.Skein.StreamCipher as SC
+import Crypto.Threefish.Random
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import System.IO.Unsafe
+import Data.IORef
+import Data.Serialize
+import Control.Monad
+
+{-# NOINLINE prng #-}
+prng :: IORef SkeinGen
+prng = unsafePerformIO $ newSkeinGen >>= newIORef
+
+-- | Generate a 256 bit nonce using the Skein PRNG.
+generateNonce :: IO Nonce256
+generateNonce =
+ Block256 `fmap` atomicModifyIORef' prng (pflip . randomBytes 32)
+ where
+ pflip (a, b) = (b, a)
+
+type MAC256 = Block256
+type Plaintext = BSL.ByteString
+
+data DecryptFailure = BadMAC | NoDecode String deriving Show
+
+-- | An encrypt-then-MACed value. The binary format is as follows:
+-- Bytes What
+-- 0-31 256 bit nonce
+-- 32-63 256 bit Skein-MAC
+-- 64-71 Length of cryptotext as a 64 bit little endian word.
+-- 71- Cryptotext
+data Encrypted a = Encrypted {
+ encNonce :: Nonce256,
+ encMAC :: MAC256,
+ encData :: BSL.ByteString
+ } deriving Show
+
+instance Serialize (Encrypted a) where
+ put (Encrypted (Block256 nonce) (Block256 mac) cryptotext) = do
+ putByteString nonce
+ putByteString mac
+ putWord64le (fromIntegral $ BSL.length cryptotext)
+ putLazyByteString cryptotext
+ get = do
+ nonce <- getByteString 32
+ mac <- getByteString 32
+ len <- getWord64le
+ cryptotext <- getLazyByteString (fromIntegral len)
+ return $! Encrypted (Block256 nonce) (Block256 mac) cryptotext
+
+deriveKeys :: Key256 -> (Key256, Key256)
+deriveKeys k = (deriveKey k (Block256 $ BS.append "crypt" (BS.replicate 27 0)),
+ deriveKey k (Block256 $ BS.append "mac" (BS.replicate 29 0)))
+
+-- | Encrypt-then-MAC a message given a key and a nonce. Note that using the
+-- same nonce more than once for a given key will completely destroy
+-- security.
+encrypt' :: Key256 -> Nonce256 -> Plaintext -> Encrypted a
+encrypt' k n plaintext =
+ Encrypted n mac cryptotext
+ where
+ (cryptKey, macKey) = deriveKeys k
+ cryptotext = SC.encrypt cryptKey n plaintext
+ mac = skeinMAC macKey cryptotext
+
+-- | Verify and decrypt a message.
+decrypt' :: Key256 -> Encrypted a -> Either DecryptFailure Plaintext
+decrypt' k (Encrypted n mac cryptotext) = do
+ when (mac' /= mac) $ Left BadMAC
+ return $! SC.decrypt cryptKey n cryptotext
+ where
+ (cryptKey, macKey) = deriveKeys k
+ mac' = skeinMAC macKey cryptotext
+
+-- | Encrypt-then-MAC any serializable value.
+-- The 256 bit nonce is generated using a Skein-based PRNG seeded from the
+-- system's entropy pool. This means that two successive calls to encrypt
+-- will not yield the exact same output; however, the identity
+-- forall k, x. Right x == decrypt k (encrypt k x)
+-- will always hold.
+--
+-- The subkeys for encryption and MAC are generated by applying Skein-KDF
+-- to the master key, with the key identifiers "crypt" and "mac"
+-- respectively, zero padded at the end until 32 bytes.
+encrypt :: Serialize a => Key256 -> a -> Encrypted a
+encrypt k x = unsafePerformIO $ do
+ nonce <- generateNonce
+ return $! encrypt' k nonce (runPutLazy (put x))
+
+-- | Encrypt-then-MAC a lazy ByteString.
+encryptBytes :: Key256 -> BSL.ByteString -> BSL.ByteString
+encryptBytes k bs = unsafePerformIO $ do
+ nonce <- generateNonce
+ return $! runPutLazy $! put (encrypt' k nonce bs)
+
+-- | Decrypt and decode a message. Will fail if there is a MAC mismatch or if
+-- the message can't be decoded into the given data type.
+decrypt :: Serialize a => Key256 -> Encrypted a -> Either DecryptFailure a
+decrypt k enc = do
+ plaintext <- decrypt' k enc
+ case runGetLazy get plaintext of
+ Right x -> return x
+ Left err -> Left (NoDecode err)
+
+-- | Verify and decrypt a lazy ByteString.
+decryptBytes :: Key256 -> BSL.ByteString -> Either DecryptFailure Plaintext
+decryptBytes k bs = do
+ enc <- case runGetLazy get bs of
+ Right x -> return x
+ Left err -> Left (NoDecode err)
+ decrypt' k enc
diff --git a/Crypto/Threefish/Common.hs b/Crypto/Threefish/Common.hs
new file mode 100644
index 0000000..bd495b0
--- /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
index 0000000..3798d58
--- /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
index 0000000..dea58cc
--- /dev/null
+++ b/Crypto/Threefish/Random.hs
@@ -0,0 +1,104 @@
+-- | Skein 256 as a PRNG.
+module Crypto.Threefish.Random (
+ SkeinGen, Block256, Random (..), RandomGen (..),
+ newSkeinGen, mkSkeinGen, mkSkeinGenEx, randomBytes, reseedSkeinGen,
+ toBlock, fromBlock
+ ) where
+import Crypto.Threefish.Skein
+import Crypto.Threefish.Threefish256
+import System.Random
+import System.Entropy
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Unsafe
+import System.IO.Unsafe
+import Foreign.Storable (sizeOf, peek)
+import Foreign.Ptr (castPtr)
+import Data.Serialize
+import Crypto.Random
+import Data.Tagged
+
+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 $ BSL.fromStrict (BS.replicate 32 0 `BS.append` seed),
+ sgPool = BS.empty,
+ sgPoolSize = poolsize
+ }
+
+-- | Reseed a Skein PRNG.
+reseedSkeinGen :: Block256 -> SkeinGen -> SkeinGen
+reseedSkeinGen (Block256 seed) (SkeinGen (Block256 state) _ poolsize) =
+ SkeinGen {
+ sgState = skein $ BSL.fromStrict (state `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' = fromIntegral $ 32 + max (nbytes + (32-(nbytes`rem`32))) poolsize
+ bytes = hash256 nbytes' emptyKey (BSL.fromStrict state)
+ (state', buffer) = BS.splitAt 32 bytes
+ (out, pool') = BS.splitAt (nbytes - BS.length pool) buffer
+
+instance CryptoRandomGen SkeinGen where
+ newGen seed =
+ case BS.length seed of
+ n | n >= 32 ->
+ Right $ mkSkeinGenEx ps (Block256 $ BS.take 32 seed)
+ | otherwise ->
+ Left NotEnoughEntropy
+ where ps = defaultSkeinGenPoolSize
+ genSeedLength = Tagged 32
+ genBytes n g = Right $ randomBytes n g
+ reseedInfo = const Never
+ reseedPeriod = const Never
+ reseed seed g =
+ case BS.length seed of
+ n | n >= 32 ->
+ Right $ reseedSkeinGen (Block256 $ BS.take 32 seed) g
+ | otherwise ->
+ Left NotEnoughEntropy
diff --git a/Crypto/Threefish/Skein.hs b/Crypto/Threefish/Skein.hs
new file mode 100644
index 0000000..aaac98b
--- /dev/null
+++ b/Crypto/Threefish/Skein.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE BangPatterns, OverloadedStrings, MultiParamTypeClasses #-}
+-- | 256 and 512 bit Skein. Supports "normal" hashing and Skein-MAC.
+module Crypto.Threefish.Skein (
+ Skein (..), Threefish (..), Block256, Block512, Key256, Key512, Nonce256,
+ hash256, hash512
+ ) where
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import Crypto.Threefish.Threefish256
+import Crypto.Threefish.Threefish512
+import Crypto.Threefish.UBI
+import Crypto.Threefish
+import Crypto.Threefish.Skein.Internal
+import Data.Bits
+import Data.Serialize
+import Data.Word
+import Data.ByteString.Unsafe
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Foreign.Marshal.Alloc
+import System.IO.Unsafe
+
+class Skein a where
+ -- | Calculate the Skein-MAC of a message.
+ skeinMAC :: a -> BSL.ByteString -> a
+ -- | Calculate the Skein checksum of a message.
+ skein :: BSL.ByteString -> a
+
+type Nonce256 = Block256
+
+init256 :: Key256 -> Word64 -> Skein256Ctx
+init256 (Block256 k) outlen =
+ unsafePerformIO $ do
+ c <- mallocForeignPtrBytes 64
+ withForeignPtr c $ \ctx -> do
+ withKey $ \key -> do
+ skein256_init ctx (castPtr key) (outlen*8)
+ return (Skein256Ctx c)
+ where
+ withKey f | BS.length k == 32 = unsafeUseAsCString k (f . castPtr)
+ | otherwise = f nullPtr
+
+update256 :: Skein256Ctx -> Int -> BSL.ByteString -> BS.ByteString
+update256 (Skein256Ctx c) outlen bytes =
+ unsafePerformIO $ withForeignPtr c $ go 1 bytes
+ where
+ outblocks =
+ case outlen `quotRem` 32 of
+ (blocks, 0) -> blocks
+ (blocks, _) -> blocks+1
+ !msgtype = type2int Message
+ go !first !msg !ctx = do
+ case BSL.splitAt 16384 msg of
+ (chunk, rest)
+ | BSL.null chunk ->
+ allocaBytes (outblocks*32) $ \ptr -> do
+ skein256_output ctx 0 (outblocks-1) ptr
+ BS.packCStringLen (castPtr ptr, outlen)
+ | otherwise -> do
+ let !chunk' =
+ BSL.toStrict chunk
+ (!lst, !len) =
+ if BSL.null rest
+ then (2, fromIntegral $ BS.length chunk')
+ else (0, 16384)
+ unsafeUseAsCString chunk' $ \ptr -> do
+ skein256_update ctx (first .|. lst) msgtype len (castPtr ptr)
+ go 0 rest ctx
+
+hash256 :: Word64 -> Key256 -> BSL.ByteString -> BS.ByteString
+hash256 outlen k bs =
+ case init256 k outlen of
+ ctx -> update256 ctx (fromIntegral outlen) bs
+
+{-# INLINE skein256 #-}
+-- | Hash a message using 256 bit Skein.
+skein256 :: BSL.ByteString -> Block256
+skein256 = Block256 . hash256 32 (Block256 "")
+
+{-# INLINE skeinMAC256 #-}
+-- | Create a 256 bit Skein-MAC.
+skeinMAC256 :: Key256 -> BSL.ByteString -> Block256
+skeinMAC256 key = Block256 . hash256 32 key
+
+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 -> BSL.ByteString -> Block512
+hash512 !firstkey !bs =
+ case flip runGetLazy bs' $ go len (init512 firstkey) (newTweak Message) of
+ Right x -> x
+ Left _ -> error "hash512 failed to get output bytes - impossible!"
+ where
+ !len = BSL.length bs
+ !lastLen = case len `rem` 64 of 0 -> 64 ; n -> n
+ !lastLenW64 = fromIntegral lastLen
+ !bs' = BSL.append bs (BSL.replicate (64-fromIntegral 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 :: BSL.ByteString -> Block512
+skein512 = hash512 zero512
+
+{-# INLINE skeinMAC512 #-}
+-- | Create a 512 bit Skein-MAC.
+skeinMAC512 :: Key512 -> BSL.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/Skein/KDF.hs b/Crypto/Threefish/Skein/KDF.hs
new file mode 100644
index 0000000..f07eebd
--- /dev/null
+++ b/Crypto/Threefish/Skein/KDF.hs
@@ -0,0 +1,34 @@
+-- | Skein as a key derivation function.
+module Crypto.Threefish.Skein.KDF (deriveKey, deriveKeys) where
+import Crypto.Threefish.Skein.Internal
+import Crypto.Threefish.Skein
+import Crypto.Threefish.UBI
+import Crypto.Threefish.Threefish256
+import Data.Serialize
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+
+-- | Derive up to 2^64 keys from a master key.
+-- The key identifiers will be 0, 1, ... 2^64-1.
+deriveKeys :: Key256 -> [Key256]
+deriveKeys mk =
+ [deriveKey mk (Block256 $ runPut $ mapM_ putWord64le [kid,0,0,0]) |
+ kid <- [0..]]
+
+-- | Derive a key from a master key using a custom key identifier.
+deriveKey :: Key256 -> Block256 -> Key256
+deriveKey (Block256 mk) (Block256 kid) =
+ unsafePerformIO $ do
+ allocaBytes 64 $ \ctx -> do
+ allocaBytes 32 $ \outkey -> do
+ unsafeUseAsCString mk $ \masterkey -> do
+ unsafeUseAsCString kid $ \keyid -> do
+ skein256_init ctx (castPtr masterkey) 256
+ skein256_update ctx 3 (type2int KeyIdentifier) l (castPtr keyid)
+ skein256_output ctx 0 0 outkey
+ Block256 `fmap` BS.packCStringLen (castPtr outkey, 32)
+ where
+ l = fromIntegral $ BS.length kid
diff --git a/Crypto/Threefish/Skein/StreamCipher.hs b/Crypto/Threefish/Skein/StreamCipher.hs
new file mode 100644
index 0000000..dba3067
--- /dev/null
+++ b/Crypto/Threefish/Skein/StreamCipher.hs
@@ -0,0 +1,67 @@
+-- | 256 bit Skein as a stream cipher, as specified in the Skein 1.3 paper.
+module Crypto.Threefish.Skein.StreamCipher (
+ Key256, Nonce256, Block256,
+ encrypt, decrypt, toBlock, fromBlock
+ ) where
+import Crypto.Threefish.Skein (Nonce256)
+import Crypto.Threefish.UBI
+import Crypto.Threefish.Threefish256
+import Crypto.Threefish
+import Crypto.Threefish.Skein.Internal
+import Data.ByteString.Unsafe
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import Foreign.ForeignPtr
+import Foreign.Ptr
+import Foreign.Marshal.Alloc
+import System.IO.Unsafe
+import Data.Bits (xor)
+
+init256 :: Key256 -> Nonce256 -> Skein256Ctx
+init256 (Block256 k) (Block256 n) =
+ unsafePerformIO $ do
+ c <- mallocForeignPtrBytes 64
+ withForeignPtr c $ \ctx -> do
+ unsafeUseAsCString k $ \key -> do
+ unsafeUseAsCString n $ \nonce -> do
+ skein256_init ctx (castPtr key) 0xffffffffffffffff
+ skein256_update ctx 3 (type2int Nonce) len (castPtr nonce)
+ return (Skein256Ctx c)
+ where
+ len = fromIntegral $ BS.length n
+
+stream256 :: Skein256Ctx -> [BS.ByteString]
+stream256 (Skein256Ctx c) =
+ unsafePerformIO $ go 0
+ where
+ go n = unsafeInterleaveIO $ do
+ bs <- withForeignPtr c $ \ctx -> do
+ allocaBytes 1024 $ \ptr -> do
+ skein256_output ctx n (n+32) ptr
+ BS.packCStringLen (castPtr ptr, 1024)
+ bss <- go (n+32)
+ return $ bs : bss
+
+keystream256 :: Key256 -> Nonce256 -> [BS.ByteString]
+keystream256 k n = stream256 (init256 k n)
+
+-- | Encrypt a lazy ByteString using 256 bit Skein as a stream cipher.
+encrypt :: Key256 -> Nonce256 -> BSL.ByteString -> BSL.ByteString
+encrypt k n plaintext =
+ BSL.fromChunks $ go (keystream256 k n) plaintext
+ where
+ go (ks:kss) msg = unsafePerformIO . unsafeInterleaveIO $ do
+ case BSL.splitAt 1024 msg of
+ (chunk, rest)
+ | BSL.null chunk ->
+ return []
+ | otherwise ->
+ let chunk' = BSL.toStrict chunk
+ in return $ (BS.pack $ BS.zipWith xor ks chunk') : go kss rest
+ go _ _ =
+ error "The key stream is infinite, so this will never happen."
+
+-- | Encryption and decryption are the same operation for a stream cipher, but
+-- we may want to have a function called encrypt for clarity.
+decrypt :: Key256 -> Nonce256 -> BSL.ByteString -> BSL.ByteString
+decrypt = encrypt
diff --git a/Crypto/Threefish/Threefish256.hs b/Crypto/Threefish/Threefish256.hs
new file mode 100644
index 0000000..fb1219b
--- /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 unsafe "encrypt256" c_encrypt256 :: Ptr Word64
+ -> Word64
+ -> Word64
+ -> Ptr Word64
+ -> Ptr Word64
+ -> IO ()
+
+foreign import ccall unsafe "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
index 0000000..b1d4c1f
--- /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
index 0000000..7e2be56
--- /dev/null
+++ b/Crypto/Threefish/UBI.hs
@@ -0,0 +1,62 @@
+-- | 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
+
+type2int :: BlockType -> Int
+type2int Key = 0
+type2int Config = 4
+type2int Personalization = 8
+type2int PublicKey = 12
+type2int KeyIdentifier = 16
+type2int Nonce = 20
+type2int Message = 48
+type2int 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
index 0000000..c898f7f
--- /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
index 0000000..9a994af
--- /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
index 0000000..7a20b6c
--- /dev/null
+++ b/cbits/skein256.c
@@ -0,0 +1,86 @@
+#include "threefish.h"
+#include <string.h>
+
+void skein256_init(skein_t* ctx, W64* key, W64 outlen) {
+ W64 config[4] = {0x0000000133414853, outlen, 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];
+ }
+}
diff --git a/cbits/threefish256.c b/cbits/threefish256.c
new file mode 100644
index 0000000..b88a298
--- /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
index 0000000..06d634e
--- /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
index 0000000..5919e49
--- /dev/null
+++ b/threefish.cabal
@@ -0,0 +1,54 @@
+name: threefish
+version: 0.2.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, as a cryptographically secure PRNG, as a stream cipher and as a key derivation function, all implemented according to the specifications of 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
+
+source-repository head
+ type: git
+ location: https://github.com/valderman/threefish.git
+
+library
+ exposed-modules:
+ Crypto.Threefish,
+ Crypto.Threefish.Authenticated,
+ Crypto.Threefish.Random,
+ Crypto.Threefish.Skein,
+ Crypto.Threefish.Skein.KDF,
+ Crypto.Threefish.Skein.StreamCipher
+ 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