threefish 0.1 → 0.2
raw patch · 9 files changed
+339/−71 lines, 9 filesdep ~bytestring
Dependency ranges changed: bytestring
Files
- Crypto/Threefish.hs +14/−2
- Crypto/Threefish/Authenticated.hs +107/−0
- Crypto/Threefish/Random.hs +38/−5
- Crypto/Threefish/Skein.hs +56/−39
- Crypto/Threefish/Skein/KDF.hs +34/−0
- Crypto/Threefish/Skein/StreamCipher.hs +67/−0
- Crypto/Threefish/UBI.hs +10/−0
- cbits/skein256.c +2/−21
- threefish.cabal +11/−4
Crypto/Threefish.hs view
@@ -2,8 +2,8 @@ -- | 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,+ Block256, Threefish256, Key256,+ Block512, Threefish512, Key512, Tweak (..), parseHex, readHex, defaultTweak, Threefish (..) ) where@@ -11,6 +11,7 @@ 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.@@ -19,13 +20,24 @@ 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
+ Crypto/Threefish/Authenticated.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Authenticated encryption using Skein for PRNG, KDF, stream cipher and MAC.+module Crypto.Threefish.Authenticated (+ DecryptFailure (..), Encrypted, Plaintext, Block256, Nonce256,+ encrypt, decrypt, encrypt', decrypt', 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))++-- | 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)
Crypto/Threefish/Random.hs view
@@ -1,17 +1,22 @@ -- | Skein 256 as a PRNG. module Crypto.Threefish.Random (- SkeinGen, Block256 (..), Random (..), RandomGen (..),- newSkeinGen, mkSkeinGen, mkSkeinGenEx, randomBytes+ 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@@ -50,11 +55,20 @@ -- across splits. mkSkeinGenEx :: Int -> Block256 -> SkeinGen mkSkeinGenEx poolsize (Block256 seed) = SkeinGen {- sgState = skein (BS.replicate 32 0 `BS.append` seed),+ 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)@@ -65,7 +79,26 @@ (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+ 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
Crypto/Threefish/Skein.hs view
@@ -1,69 +1,86 @@-{-# LANGUAGE BangPatterns, OverloadedStrings, ForeignFunctionInterface #-}+{-# LANGUAGE BangPatterns, OverloadedStrings, MultiParamTypeClasses #-} -- | 256 and 512 bit Skein. Supports "normal" hashing and Skein-MAC. module Crypto.Threefish.Skein (- Skein (..), Block256 (..), Block512 (..), Key256, Key512, Nonce256,+ 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 -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+ skeinMAC :: a -> BSL.ByteString -> a -- | Calculate the Skein checksum of a message.- skein :: BS.ByteString -> a+ skein :: BSL.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)+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- !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+ !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 :: BS.ByteString -> Block256-skein256 = Block256 . hash256 32 (Block256 "") (Block256 "")+skein256 :: BSL.ByteString -> Block256+skein256 = Block256 . hash256 32 (Block256 "") {-# INLINE skeinMAC256 #-} -- | Create a 256 bit Skein-MAC.-skeinMAC256 :: Key256 -> BS.ByteString -> Block256-skeinMAC256 key = Block256 . hash256 32 key (Block256 "")+skeinMAC256 :: Key256 -> BSL.ByteString -> Block256+skeinMAC256 key = Block256 . hash256 32 key instance Skein Block256 where skeinMAC = skeinMAC256@@ -103,16 +120,16 @@ -- | 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 :: Key512 -> BSL.ByteString -> Block512 hash512 !firstkey !bs =- case flip runGet bs' $ go len (init512 firstkey) (newTweak Message) of+ 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 = BS.length bs+ !len = BSL.length bs !lastLen = case len `rem` 64 of 0 -> 64 ; n -> n !lastLenW64 = fromIntegral lastLen- !bs' = BS.append bs (BS.replicate (64-lastLen) 0)+ !bs' = BSL.append bs (BSL.replicate (64-fromIntegral lastLen) 0) go !n !key !tweak | n > 64 = do block <- get@@ -128,12 +145,12 @@ {-# INLINE skein512 #-} -- | Hash a message using 512 bit Skein.-skein512 :: BS.ByteString -> Block512+skein512 :: BSL.ByteString -> Block512 skein512 = hash512 zero512 {-# INLINE skeinMAC512 #-} -- | Create a 512 bit Skein-MAC.-skeinMAC512 :: Key512 -> BS.ByteString -> Block512+skeinMAC512 :: Key512 -> BSL.ByteString -> Block512 skeinMAC512 = hash512 . fst . processBlock512 64 zero512 (setLast True $ newTweak Key)
+ Crypto/Threefish/Skein/KDF.hs view
@@ -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
+ Crypto/Threefish/Skein/StreamCipher.hs view
@@ -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 256 $ \ptr -> do+ skein256_output ctx n (n+8) ptr+ BS.packCStringLen (castPtr ptr, 256)+ bss <- go (n+8)+ 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 256 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
Crypto/Threefish/UBI.hs view
@@ -24,6 +24,16 @@ 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
cbits/skein256.c view
@@ -1,8 +1,8 @@ #include "threefish.h" #include <string.h> -void skein256_init(skein_t* ctx, W64* key, int outlen) {- W64 config[4] = {0x0000000133414853, outlen*8, 0, 0};+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 */@@ -83,23 +83,4 @@ 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); }
threefish.cabal view
@@ -1,7 +1,7 @@ name: threefish-version: 0.1+version: 0.2 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.+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@@ -13,11 +13,18 @@ -- 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,+ Crypto.Threefish.Skein.KDF,+ Crypto.Threefish.Skein.StreamCipher other-modules: Crypto.Threefish.Common, Crypto.Threefish.Mix,@@ -30,7 +37,7 @@ FunctionalDependencies build-depends: base >=4.6 && <5,- bytestring >=0.10,+ bytestring >=0.10.2.0, cereal >=0.3, array >=0.4, crypto-api >=0.12,