{- |
Module: Crypto.Stego.UUID
See README.md for an example and security considerations.
-}

module Crypto.Stego.UUID (
  mark ,
  isMarked,
  StegoKeyHi (..),
  StegoKeyLo (..)
  ) where


import           Data.Maybe
import           Data.Word

import           Data.UUID
import           Crypto.Hash

import qualified Data.ByteArray             as BA
import qualified Data.ByteString.Builder    as BSB
import qualified Data.ByteString            as BS
import qualified Data.ByteString.Lazy       as BSL

-- | Secret key, high 64 bits.
newtype StegoKeyHi = KeyHi64 Word64
-- | Secret key, low 64 bits.
newtype StegoKeyLo = KeyLo64 Word64

-- | Creates a secretly "marked" UUID given a two-part secret key and random input
mark :: StegoKeyHi -> StegoKeyLo -> Word64 -> UUID
mark (KeyHi64 selfKeyHi) (KeyLo64 selfKeyLo) rand =
  let selfKeyHiAsLazyBS = BSB.toLazyByteString (BSB.word64BE selfKeyHi)
      selfKeyLoAsLazyBS = BSB.toLazyByteString (BSB.word64BE selfKeyLo)
      randAsLazyBS      = BSB.toLazyByteString (BSB.word64BE rand)
      hashInput         = BSL.concat [randAsLazyBS, selfKeyHiAsLazyBS, selfKeyLoAsLazyBS]
      digest            = hash (BSL.toStrict hashInput) :: Digest SHA256
      hashBitsStrict    = BS.take 8  (BA.convert digest)
      halfAndHalf       = BSL.concat [randAsLazyBS, BSL.fromStrict hashBitsStrict]
   in fromJust (fromByteString halfAndHalf)

-- | Detects UUIDs previously marked with the given key
isMarked :: StegoKeyHi -> StegoKeyLo -> UUID -> Bool
isMarked selfKeyHi selfKeyLo uuid =
  let (rHi, rLo, _, _) = toWords uuid
      r = fromIntegral rHi * 2^(32 :: Int) + fromIntegral rLo
  in mark selfKeyHi selfKeyLo r == uuid