Blame Crypto/Data/AFIS.hs

Packit 141393
-- |
Packit 141393
-- Module      : Crypto.Data.AFIS
Packit 141393
-- License     : BSD-style
Packit 141393
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit 141393
-- Stability   : experimental
Packit 141393
-- Portability : unknown
Packit 141393
--
Packit 141393
-- haskell implementation of the Anti-forensic information splitter
Packit 141393
-- available in LUKS. <http://clemens.endorphin.org/AFsplitter>
Packit 141393
--
Packit 141393
-- The algorithm bloats an arbitrary secret with many bits that are necessary for
Packit 141393
-- the recovery of the key (merge), and allow greater way to permanently
Packit 141393
-- destroy a key stored on disk.
Packit 141393
--
Packit 141393
{-# LANGUAGE ScopedTypeVariables #-}
Packit 141393
module Crypto.Data.AFIS
Packit 141393
    ( split
Packit 141393
    , merge
Packit 141393
    ) where
Packit 141393
Packit 141393
import           Crypto.Hash
Packit 141393
import           Crypto.Random.Types
Packit 141393
import           Crypto.Internal.Compat
Packit 141393
import           Control.Monad (forM_, foldM)
Packit 141393
import           Data.Word
Packit 141393
import           Data.Bits
Packit 141393
import           Foreign.Storable
Packit 141393
import           Foreign.Ptr
Packit 141393
Packit 141393
import           Crypto.Internal.ByteArray (ByteArray, Bytes, MemView(..))
Packit 141393
import qualified Crypto.Internal.ByteArray as B
Packit 141393
Packit 141393
import           Data.Memory.PtrMethods (memSet, memCopy)
Packit 141393
Packit 141393
-- | Split data to diffused data, using a random generator and
Packit 141393
-- an hash algorithm.
Packit 141393
--
Packit 141393
-- the diffused data will consist of random data for (expandTimes-1)
Packit 141393
-- then the last block will be xor of the accumulated random data diffused by
Packit 141393
-- the hash algorithm.
Packit 141393
--
Packit 141393
-- ----------
Packit 141393
-- -  orig  -
Packit 141393
-- ----------
Packit 141393
--
Packit 141393
-- ---------- ---------- --------------
Packit 141393
-- - rand1  - - rand2  - - orig ^ acc -
Packit 141393
-- ---------- ---------- --------------
Packit 141393
--
Packit 141393
-- where acc is :
Packit 141393
--   acc(n+1) = hash (n ++ rand(n)) ^ acc(n)
Packit 141393
--
Packit 141393
split :: (ByteArray ba, HashAlgorithm hash, DRG rng)
Packit 141393
      => hash  -- ^ Hash algorithm to use as diffuser
Packit 141393
      -> rng   -- ^ Random generator to use
Packit 141393
      -> Int   -- ^ Number of times to diffuse the data.
Packit 141393
      -> ba    -- ^ original data to diffuse.
Packit 141393
      -> (ba, rng)         -- ^ The diffused data
Packit 141393
{-# NOINLINE split #-}
Packit 141393
split hashAlg rng expandTimes src
Packit 141393
    | expandTimes <= 1 = error "invalid expandTimes value"
Packit 141393
    | otherwise        = unsafeDoIO $ do
Packit 141393
        (rng', bs) <- B.allocRet diffusedLen runOp
Packit 141393
        return (bs, rng')
Packit 141393
  where diffusedLen = blockSize * expandTimes
Packit 141393
        blockSize   = B.length src
Packit 141393
        runOp dstPtr = do
Packit 141393
            let lastBlock = dstPtr `plusPtr` (blockSize * (expandTimes-1))
Packit 141393
            memSet lastBlock 0 blockSize
Packit 141393
            let randomBlockPtrs = map (plusPtr dstPtr . (*) blockSize) [0..(expandTimes-2)]
Packit 141393
            rng' <- foldM fillRandomBlock rng randomBlockPtrs
Packit 141393
            mapM_ (addRandomBlock lastBlock) randomBlockPtrs
Packit 141393
            B.withByteArray src $ \srcPtr -> xorMem srcPtr lastBlock blockSize
Packit 141393
            return rng'
Packit 141393
        addRandomBlock lastBlock blockPtr = do
Packit 141393
            xorMem blockPtr lastBlock blockSize
Packit 141393
            diffuse hashAlg lastBlock blockSize
Packit 141393
        fillRandomBlock g blockPtr = do
Packit 141393
            let (rand :: Bytes, g') = randomBytesGenerate blockSize g
Packit 141393
            B.withByteArray rand $ \randPtr -> memCopy blockPtr randPtr (fromIntegral blockSize)
Packit 141393
            return g'
Packit 141393
Packit 141393
-- | Merge previously diffused data back to the original data.
Packit 141393
merge :: (ByteArray ba, HashAlgorithm hash)
Packit 141393
      => hash  -- ^ Hash algorithm used as diffuser
Packit 141393
      -> Int   -- ^ Number of times to un-diffuse the data
Packit 141393
      -> ba    -- ^ Diffused data
Packit 141393
      -> ba    -- ^ Original data
Packit 141393
{-# NOINLINE merge #-}
Packit 141393
merge hashAlg expandTimes bs
Packit 141393
    | r /= 0            = error "diffused data not a multiple of expandTimes"
Packit 141393
    | originalSize <= 0 = error "diffused data null"
Packit 141393
    | otherwise         = B.allocAndFreeze originalSize $ \dstPtr ->
Packit 141393
        B.withByteArray bs $ \srcPtr -> do
Packit 141393
            memSet dstPtr 0 originalSize
Packit 141393
            forM_ [0..(expandTimes-2)] $ \i -> do
Packit 141393
                xorMem (srcPtr `plusPtr` (i * originalSize)) dstPtr originalSize
Packit 141393
                diffuse hashAlg dstPtr originalSize
Packit 141393
            xorMem (srcPtr `plusPtr` ((expandTimes-1) * originalSize)) dstPtr originalSize
Packit 141393
            return ()
Packit 141393
  where (originalSize,r) = len `quotRem` expandTimes
Packit 141393
        len              = B.length bs
Packit 141393
Packit 141393
-- | inplace Xor with an input
Packit 141393
-- dst = src `xor` dst
Packit 141393
xorMem :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
Packit 141393
xorMem src dst sz
Packit 141393
    | sz `mod` 64 == 0 = loop 8 (castPtr src :: Ptr Word64) (castPtr dst) sz
Packit 141393
    | sz `mod` 32 == 0 = loop 4 (castPtr src :: Ptr Word32) (castPtr dst) sz
Packit 141393
    | otherwise        = loop 1 (src :: Ptr Word8) dst sz
Packit 141393
  where loop _    _ _ 0 = return ()
Packit 141393
        loop incr s d n = do a <- peek s
Packit 141393
                             b <- peek d
Packit 141393
                             poke d (a `xor` b)
Packit 141393
                             loop incr (s `plusPtr` incr) (d `plusPtr` incr) (n-incr)
Packit 141393
Packit 141393
diffuse :: HashAlgorithm hash
Packit 141393
        => hash      -- ^ Hash function to use as diffuser
Packit 141393
        -> Ptr Word8 -- ^ buffer to diffuse, modify in place
Packit 141393
        -> Int       -- ^ length of buffer to diffuse
Packit 141393
        -> IO ()
Packit 141393
diffuse hashAlg src sz = loop src 0
Packit 141393
  where (full,pad) = sz `quotRem` digestSize 
Packit 141393
        loop s i
Packit 141393
            | i < full = do h <- hashBlock i s digestSize
Packit 141393
                            B.withByteArray h $ \hPtr -> memCopy s hPtr digestSize
Packit 141393
                            loop (s `plusPtr` digestSize) (i+1)
Packit 141393
            | pad /= 0 = do h <- hashBlock i s pad
Packit 141393
                            B.withByteArray h $ \hPtr -> memCopy s hPtr pad
Packit 141393
                            return ()
Packit 141393
            | otherwise = return ()
Packit 141393
Packit 141393
        digestSize = hashDigestSize hashAlg
Packit 141393
Packit 141393
        -- Hash [ BE32(n), (p .. p+hashSz) ]
Packit 141393
        hashBlock n p hashSz = do
Packit 141393
            let ctx = hashInitWith hashAlg
Packit 141393
            return $! hashFinalize $ hashUpdate (hashUpdate ctx (be32 n)) (MemView p hashSz)
Packit 141393
Packit 141393
        be32 :: Int -> Bytes
Packit 141393
        be32 n = B.allocAndFreeze 4 $ \ptr -> do
Packit 141393
            poke ptr               (f8 (n `shiftR` 24))
Packit 141393
            poke (ptr `plusPtr` 1) (f8 (n `shiftR` 16))
Packit 141393
            poke (ptr `plusPtr` 2) (f8 (n `shiftR` 8))
Packit 141393
            poke (ptr `plusPtr` 3) (f8 n)
Packit 141393
          where
Packit 141393
                f8 :: Int -> Word8
Packit 141393
                f8 = fromIntegral