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
skeinMAC :: a -> BSL.ByteString -> a
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 (outblocks1) 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
skein256 :: BSL.ByteString -> Block256
skein256 = Block256 . hash256 32 (Block256 "")
skeinMAC256 :: Key256 -> BSL.ByteString -> Block256
skeinMAC256 key = Block256 . hash256 32 key
instance Skein Block256 where
skeinMAC = skeinMAC256
skein = skein256
config512 :: Block512
config512 = Block512 0x0000000133414853 512 0 0 0 0 0 0
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)
init512 :: Key512 -> Block512
init512 key = fst $ processBlock512 32 key configTweak config512
zero512 :: Block512
zero512 = Block512 0 0 0 0 0 0 0 0
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
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 (64fromIntegral lastLen) 0)
go !n !key !tweak
| n > 64 = do
block <- get
let (block', tweak') = processBlock512 64 key tweak block
go (n64) 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
skein512 :: BSL.ByteString -> Block512
skein512 = hash512 zero512
skeinMAC512 :: Key512 -> BSL.ByteString -> Block512
skeinMAC512 =
hash512 . fst . processBlock512 64 zero512 (setLast True $ newTweak Key)
instance Skein Block512 where
skeinMAC = skeinMAC512
skein = skein512