packages feed

afis (empty) → 0.1.0

raw patch · 6 files changed

+307/−0 lines, 6 filesdep +HUnitdep +QuickCheckdep +afissetup-changed

Dependencies added: HUnit, QuickCheck, afis, base, byteable, bytedump, bytestring, crypto-random-api, cryptohash, mtl, packer, test-framework, test-framework-hunit, test-framework-quickcheck2

Files

+ Crypto/Data/AFIS.hs view
@@ -0,0 +1,141 @@+-- |+-- Module      : Crypto.Data.AFIS+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+--+-- haskell implementation of the Anti-forensic information splitter+-- available in LUKS. <http://clemens.endorphin.org/AFsplitter>+--+-- The algorithm bloats an arbitrary secret with many bits that are necessary for+-- the recovery of the key (merge), and allow greater way to permanently+-- destroy a key stored on disk.+--+module Crypto.Data.AFIS+    ( split+    , merge+    ) where++import Crypto.Hash+import Crypto.Random.API+import Control.Monad (forM_, foldM)+import Data.ByteString (ByteString)+import Data.Byteable+import Data.Packer+import Data.Tuple+import Data.Word+import Data.Bits+import Foreign.Storable+import Foreign.Ptr+import Foreign.ForeignPtr (withForeignPtr, newForeignPtr_)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B++import System.IO.Unsafe (unsafePerformIO)++-- | Split data to diffused data, using a random generator and+-- an hash algorithm.+--+-- the diffused data will consist of random data for (expandTimes-1)+-- then the last block will be xor of the accumulated random data diffused by+-- the hash algorithm.+--+-- ----------+-- -  orig  -+-- ----------+--+-- ---------- ---------- --------------+-- - rand1  - - rand2  - - orig ^ acc -+-- ---------- ---------- --------------+--+-- where acc is :+--   acc(n+1) = hash (n ++ rand(n)) ^ acc(n)+--+split :: (HashAlgorithm a, CPRG rng)+      => HashFunctionBS a  -- ^ Hash function to use as diffuser+      -> rng               -- ^ Random generator to use+      -> Int               -- ^ Number of times to diffuse the data.+      -> ByteString        -- ^ original data to diffuse.+      -> (ByteString, rng) -- ^ The diffused data+{-# NOINLINE split #-}+split hashF rng expandTimes src+    | expandTimes <= 1 = error "invalid expandTimes value"+    | otherwise        = unsafePerformIO $ do+        fptr <- B.mallocByteString diffusedLen+        rng' <- withForeignPtr fptr runOp+        return (B.fromForeignPtr fptr 0 diffusedLen, rng')+  where diffusedLen = blockSize * expandTimes+        blockSize   = B.length src+        runOp dstPtr = do+            let lastBlock = dstPtr `plusPtr` (blockSize * (expandTimes-1))+            B.memset lastBlock 0 (fromIntegral blockSize)+            let randomBlockPtrs = map (plusPtr dstPtr . (*) blockSize) [0..(expandTimes-2)]+            rng' <- foldM fillRandomBlock rng randomBlockPtrs+            mapM_ (addRandomBlock lastBlock) randomBlockPtrs+            withBytePtr src $ \srcPtr -> xorMem srcPtr lastBlock blockSize+            return rng'+        addRandomBlock lastBlock blockPtr = do+            xorMem blockPtr lastBlock blockSize+            diffuse hashF lastBlock blockSize+        fillRandomBlock g blockPtr = do+            let (rand, g') = genRandomBytes blockSize g+            withBytePtr rand $ \randPtr -> B.memcpy blockPtr randPtr blockSize+            return g'++-- | Merge previously diffused data back to the original data.+merge :: HashAlgorithm a+      => HashFunctionBS a -- ^ Hash function used as diffuser+      -> Int              -- ^ Number of times to un-diffuse the data+      -> ByteString       -- ^ Diffused data+      -> ByteString       -- ^ Original data+{-# NOINLINE merge #-}+merge hashF expandTimes bs+    | r /= 0            = error "diffused data not a multiple of expandTimes"+    | originalSize <= 0 = error "diffused data null"+    | otherwise         = unsafePerformIO $ B.create originalSize $ \dstPtr ->+        withBytePtr bs $ \srcPtr -> do+        B.memset dstPtr 0 (fromIntegral originalSize)+        forM_ [0..(expandTimes-2)] $ \i -> do+            xorMem (srcPtr `plusPtr` (i * originalSize)) dstPtr originalSize+            diffuse hashF dstPtr originalSize+        xorMem (srcPtr `plusPtr` ((expandTimes-1) * originalSize)) dstPtr originalSize+        return ()+  where (originalSize,r) = len `quotRem` expandTimes+        len              = B.length bs++-- | inplace Xor with an input+-- dst = src `xor` dst+xorMem :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()+xorMem src dst sz+    | sz `mod` 64 == 0 = loop 8 (castPtr src :: Ptr Word64) (castPtr dst) sz+    | sz `mod` 32 == 0 = loop 4 (castPtr src :: Ptr Word32) (castPtr dst) sz+    | otherwise        = loop 1 (src :: Ptr Word8) dst sz+  where loop _    _ _ 0 = return ()+        loop incr s d n = do a <- peek s+                             b <- peek d+                             poke d (a `xor` b)+                             loop incr (s `plusPtr` incr) (d `plusPtr` incr) (n-incr)++diffuse :: HashAlgorithm a+        => HashFunctionBS a -- ^ Hash function to use as diffuser+        -> Ptr Word8+        -> Int+        -> IO ()+diffuse hashF src sz = loop src 0+  where (full,pad) = sz `quotRem` digestSize +        loop s i | i < full = do h <- hashBlock i `fmap` byteStringOfPtr s digestSize+                                 withBytePtr h $ \hPtr -> B.memcpy s hPtr digestSize+                                 loop (s `plusPtr` digestSize) (i+1)+                 | pad /= 0 = do h <- hashBlock i `fmap` byteStringOfPtr s pad+                                 withBytePtr h $ \hPtr -> B.memcpy s hPtr pad+                                 return ()+                 | otherwise = return ()++        digestSize = B.length $ digestToByteString $ hashF B.empty++        byteStringOfPtr :: Ptr Word8 -> Int -> IO ByteString+        byteStringOfPtr ptr sz = newForeignPtr_ ptr >>= \fptr -> return $ B.fromForeignPtr fptr 0 sz++        hashBlock n src =+            digestToByteString $ hashF $ runPacking (B.length src+4) (putWord32BE (fromIntegral n) >> putBytes src)
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2013 Vincent hanquez <vincent@snarc.org>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+afis+=======++Documentation: [afis on hackage](http://hackage.haskell.org/package/afis)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ afis.cabal view
@@ -0,0 +1,53 @@+Name:                afis+Version:             0.1.0+Synopsis:            Anti-forensic Information Splitter+Description:         Anti-forensic Information Splitter as defined for LUKS+License:             BSD3+License-file:        LICENSE+Copyright:           Vincent Hanquez <vincent@snarc.org>+Author:              Vincent Hanquez <vincent@snarc.org>+Maintainer:          vincent@snarc.org+Category:            Cryptography,Data+Stability:           experimental+Build-Type:          Simple+Homepage:            http://github.com/vincenthz/hs-afis+Cabal-Version:       >=1.8+data-files:          README.md++Library+  Build-Depends:     base >= 3 && < 5+                   , bytestring+                   , cryptohash+                   , crypto-random-api+                   , packer+                   , byteable+                   , bytedump+  Exposed-modules:   Crypto.Data.AFIS++--Executable           afis+--  Main-Is:           afis.hs+--  ghc-options:       -Wall -fno-warn-missing-signatures+--  Hs-Source-Dirs:    .+--  Build-depends:     base >= 4 && < 5++Test-Suite test-afis+  type:              exitcode-stdio-1.0+  hs-source-dirs:    tests+  Main-is:           Tests.hs+  Build-Depends:     base >= 3 && < 5+                   , mtl+                   , QuickCheck >= 2+                   , HUnit+                   , test-framework+                   , test-framework-quickcheck2+                   , test-framework-hunit+                   , afis+                   , crypto-random-api+                   , cryptohash+                   , bytestring+                   , bytedump+  ghc-options:       -Wall -fno-warn-orphans -fno-warn-missing-signatures++source-repository head+  type: git+  location: git://github.com/vincenthz/hs-afis
+ tests/Tests.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ExistentialQuantification #-}+module Main where++import Control.Applicative+import Control.Monad++import Test.Framework (Test, defaultMain, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit++import Test.QuickCheck+import Test.QuickCheck.Test+import Test.Framework.Providers.QuickCheck2 (testProperty)++import qualified Crypto.Data.AFIS as AFIS+import Crypto.Hash+import Crypto.Random.API+import qualified Data.ByteString as B++import Text.Bytedump++mergeVec =+    [ (3+      , hash :: HashFunctionBS SHA1+      , "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02"+      , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\xd4\x76\xc8\x58\xbd\xf0\x15\xbe\x9f\x40\xe3\x65\x20\x1c\x9c\xb8\xd8\x1c\x16\x64"+      )+    , (3+      , hash :: HashFunctionBS SHA1+      , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17"+      , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\xd6\x75\xc8\x59\xbb\xf7\x11\xbb\x95\x4b\xeb\x6c\x2e\x13\x90\xb5\xca\x0f\x06\x75\x17\x70\x39\x28"+      )+    ]++mergeKATs = map toProp $ zip mergeVec [0..]+  where toProp ((nbExpands, hashF, expected, dat), i) =+            testCase ("merge " ++ show i) (expected @=? AFIS.merge hashF nbExpands dat)++data AFISParams = forall a . HashAlgorithm a => AFISParams B.ByteString Int (HashFunctionBS a) FakeRNG++instance Show AFISParams where+    show (AFISParams dat expand _ _) = "data: " ++ show dat ++ " expanded: " ++ show expand++data FakeRNG = FakeRNG Int B.ByteString++instance CPRG FakeRNG where+    cprgNeedReseed _ = NeverReseed+    cprgSupplyEntropy b2 (FakeRNG o b) = FakeRNG o (B.append b b2)+    cprgGenBytes n (FakeRNG o b)+        | n > B.length b - o = (B.take n $ B.drop o (B.concat $ replicate 10 b), FakeRNG 0 b)+        | otherwise          = (B.take n $ B.drop o b, FakeRNG (o+n) b)++instance Arbitrary AFISParams where+    arbitrary = AFISParams <$> arbitraryBS <*> choose (2,2) <*> elements [hash :: HashFunctionBS SHA1] <*> arbitraryRandom+      where arbitraryBS = choose (3,46) >>= \sz -> B.pack <$> replicateM sz arbitrary +            arbitraryRandom = choose (1024,4096) >>= \sz -> FakeRNG 0 . B.pack <$> replicateM sz arbitrary++tests =+    [ testGroup "KAT merge" mergeKATs+    , testProperty "merge.split == id" $ \(AFISParams bs e hf rng) -> (AFIS.merge hf e $ fst (AFIS.split hf rng e bs)) `assertEq` bs+    ]++assertEq :: B.ByteString -> B.ByteString -> Bool+assertEq b1 b2 | b1 /= b2  = error ("b1: " ++ show b1 ++ " b2: " ++ show b2)+               | otherwise = True++main = defaultMain tests+{-+    let hf    = hash :: HashFunctionBS SHA1+        e     = 4+        bs    = B.pack [1,2,3,4,5,1,2]+        (z,_) = AFIS.split hf (FakeRNG 0 $ B.replicate 4000 3) e bs+        bs2   = AFIS.merge hf e z+    when (bs /= bs2) $ do+        putStrLn ("bs : " ++ dumpRawBS bs)+        putStrLn ("z  : " ++ dumpRawBS z)+        putStrLn ("bs2: " ++ dumpRawBS bs2)+-}