packages feed

threefish 0.1 → 0.2

raw patch · 9 files changed

+339/−71 lines, 9 filesdep ~bytestring

Dependency ranges changed: bytestring

Files

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,