module Crypto.Random.HmacDrbg
(
HmacDrbg
, initialize
) where
import Crypto.Hash (HashAlgorithm, digestFromByteString,
hashDigestSize)
import Crypto.MAC.HMAC (HMAC (..), hmac)
import Crypto.Random (DRG (..))
import Data.ByteArray (ByteArray, convert, singleton)
import qualified Data.ByteArray as BA (null, take)
import qualified Data.ByteString as B (replicate)
import Data.Maybe (fromJust)
import Data.Monoid ((<>))
import Data.Word (Word8)
newtype HmacDrbg a = HmacDrbg (HMAC a, HMAC a)
deriving Eq
instance HashAlgorithm a => DRG (HmacDrbg a) where
randomBytesGenerate = generate
update :: (ByteArray bin, HashAlgorithm a)
=> bin
-> HmacDrbg a
-> HmacDrbg a
update input | BA.null input = go 0x00
| otherwise = go 0x01 . go 0x00
where
go :: HashAlgorithm a => Word8 -> HmacDrbg a -> HmacDrbg a
go c (HmacDrbg (k, v)) = let k' = hmac k (convert v <> singleton c <> input)
v' = hmac k' v
in HmacDrbg (k', v')
initialize :: (ByteArray seed, HashAlgorithm a)
=> seed
-> HmacDrbg a
initialize = flip update $ HmacDrbg (hmac0 undefined 0x00, hmac0 undefined 0x01)
where
hmac0 :: HashAlgorithm a => a -> Word8 -> HMAC a
hmac0 a = HMAC . fromJust . digestFromByteString . B.replicate (hashDigestSize a)
generate :: (HashAlgorithm a, ByteArray output) => Int -> HmacDrbg a -> (output, HmacDrbg a)
generate reqBytes (HmacDrbg (k, v)) = (output, HmacDrbg (k, vFinal))
where
getV (u, rest) = let v' = hmac k u in (v', rest <> convert v')
(vFinal, output) = BA.take reqBytes <$> iterate getV (v, mempty) !! reqBytes