-- | Skein as a key derivation function.
module Crypto.Threefish.Skein.KDF (deriveKey, deriveKeys) where
import Crypto.Threefish.Skein.Internal
import Crypto.Threefish.Skein
import Crypto.Threefish.UBI
import Crypto.Threefish.Threefish256
import Data.Serialize
import qualified Data.ByteString as BS
import Data.ByteString.Unsafe
import System.IO.Unsafe
import Foreign.Marshal.Alloc
import Foreign.Ptr

-- | Derive up to 2^64 keys from a master key.
--   The key identifiers will be 0, 1, ... 2^64-1.
deriveKeys :: Key256 -> [Key256]
deriveKeys mk =
  [deriveKey mk (Block256 $ runPut $ mapM_ putWord64le [kid,0,0,0]) |
   kid <- [0..]]

-- | Derive a key from a master key using a custom key identifier.
deriveKey :: Key256 -> Block256 -> Key256
deriveKey (Block256 mk) (Block256 kid) =
    unsafePerformIO $ do
      allocaBytes 64 $ \ctx -> do
        allocaBytes 32 $ \outkey -> do
          unsafeUseAsCString mk $ \masterkey -> do
            unsafeUseAsCString kid $ \keyid -> do
              skein256_init ctx (castPtr masterkey) 256
              skein256_update ctx 3 (type2int KeyIdentifier) l (castPtr keyid)
              skein256_output ctx 0 0 outkey
              Block256 `fmap` BS.packCStringLen (castPtr outkey, 32)
  where
    l = fromIntegral $ BS.length kid