|
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
|