hmac-drbg

Pure Haskell HMAC-DRBG CSPRNG per NIST-SP 800-90A.
git clone git://git.ppad.tech/hmac-drbg.git
Log | Files | Refs | LICENSE

commit c972e4e4738d596cc20d40838cf9897f24fc0416
parent feb28359549ea350b4d550dba9470f0ec5ecc4b3
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed,  2 Oct 2024 13:26:13 +0400

lib: basic monadic interface

Diffstat:
Mlib/Crypto/DRBG/HMAC.hs | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 64 insertions(+), 19 deletions(-)

diff --git a/lib/Crypto/DRBG/HMAC.hs b/lib/Crypto/DRBG/HMAC.hs @@ -4,8 +4,8 @@ module Crypto.DRBG.HMAC ( DRBG - , read_v - , read_k + , _read_v + , _read_k , new , gen @@ -16,6 +16,9 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as BSB import Data.Word (Word64) +import Control.Monad.Primitive (PrimMonad, PrimState) +import qualified Data.Primitive.MutVar as P + -- keystroke savers and utilities --------------------------------------------- fi :: (Integral a, Num b) => a -> b @@ -44,20 +47,23 @@ data DRBG = DRBG {-# UNPACK #-} !BS.ByteString -- key -- | Read the 'V' value from the DRBG state. -read_v :: DRBG -> BS.ByteString -read_v (DRBG _ v _) = v +_read_v :: DRBG -> BS.ByteString +_read_v (DRBG _ v _) = v -- | Read the 'Key' value from the DRBG state. -read_k :: DRBG -> BS.ByteString -read_k (DRBG _ _ key) = key +_read_k :: DRBG -> BS.ByteString +_read_k (DRBG _ _ key) = key + +-- | Primitive formulation. +newtype Gen s = Gen (P.MutVar s DRBG) -- drbg interaction ----------------------------------------------------------- -update +update_pure :: BS.ByteString -> DRBG -> DRBG -update provided_data (DRBG h@(HMAC hmac _) v0 k0) = +update_pure provided_data (DRBG h@(HMAC hmac _) v0 k0) = let !k1 = hmac k0 (cat v0 0x00 provided_data) !v1 = hmac k1 v0 in if BS.null provided_data @@ -69,45 +75,72 @@ update provided_data (DRBG h@(HMAC hmac _) v0 k0) = cat bs byte suf = toStrict $ BSB.byteString bs <> BSB.word8 byte <> BSB.byteString suf +update_prim + :: PrimMonad m + => BS.ByteString + -> Gen (PrimState m) + -> m () +update_prim pd (Gen gen) = P.modifyMutVar' gen (update_pure pd) + -- | Create a DRBG from the provided HMAC function, entropy, nonce, and -- personalization string. -new +new_pure :: (BS.ByteString -> BS.ByteString -> BS.ByteString) -- HMAC function -> BS.ByteString -- entropy -> BS.ByteString -- nonce -> BS.ByteString -- personalization string -> DRBG -new hmac entropy nonce ps = +new_pure hmac entropy nonce ps = let !drbg = DRBG (HMAC hmac outlen) v0 k0 - in update seed_material drbg + in update_pure seed_material drbg where seed_material = entropy <> nonce <> ps outlen = fi (BS.length (hmac mempty mempty)) k0 = BS.replicate (fi outlen) 0x00 v0 = BS.replicate (fi outlen) 0x01 +new_prim + :: PrimMonad m + => (BS.ByteString -> BS.ByteString -> BS.ByteString) -- HMAC function + -> BS.ByteString -- entropy + -> BS.ByteString -- nonce + -> BS.ByteString -- personalization string + -> m (Gen (PrimState m)) +new_prim hmac entropy nonce ps = do + let !drbg = new_pure hmac entropy nonce ps + mut <- P.newMutVar drbg + pure (Gen mut) + -- | Inject entropy and additional bytes into a DRBG. -- -- Note that we don't support "proper" reseeding (i.e., we don't track -- a reseed counter), but this can be used for injecting entropy per -- spec. -reseed :: BS.ByteString -> BS.ByteString -> DRBG -> DRBG -reseed entropy addl drbg = update (entropy <> addl) drbg - -gen +reseed_pure :: BS.ByteString -> BS.ByteString -> DRBG -> DRBG +reseed_pure entropy addl drbg = update_pure (entropy <> addl) drbg + +reseed_prim + :: PrimMonad m + => BS.ByteString + -> BS.ByteString + -> Gen (PrimState m) + -> m () +reseed_prim ent add (Gen gen) = P.modifyMutVar' gen (reseed_pure ent add) + +gen_pure :: BS.ByteString -> Word64 -> DRBG -> (BS.ByteString, DRBG) -gen addl bytes drbg0@(DRBG h@(HMAC hmac outlen) _ _) = +gen_pure addl bytes drbg0@(DRBG h@(HMAC hmac outlen) _ _) = let !(Pair temp drbg1) = loop mempty 0 v1 !returned_bits = BS.take (fi bytes) temp - !drbg = update addl drbg1 - in (returned_bits, drbg) + !drbg = update_pure addl drbg1 + in (returned_bits, drbg) -- XX this could use a strict pair where !(DRBG _ v1 k1) | BS.null addl = drbg0 - | otherwise = update addl drbg0 + | otherwise = update_pure addl drbg0 loop !acc !len !vl | len < bytes = @@ -120,3 +153,15 @@ gen addl bytes drbg0@(DRBG h@(HMAC hmac outlen) _ _) = let facc = toStrict acc in Pair facc (DRBG h vl k1) +gen_prim + :: PrimMonad m + => BS.ByteString + -> Word64 + -> Gen (PrimState m) + -> m BS.ByteString +gen_prim addl bytes (Gen mut) = do + drbg0 <- P.readMutVar mut + let !(bs, !drbg1) = gen_pure addl bytes drbg0 + P.writeMutVar mut drbg1 + pure $! bs +