feat(allocations): switch to csprng

This commit is contained in:
Gregor Kleen 2020-04-27 09:12:32 +02:00
parent 11c86bb5fa
commit 3ea7371465
8 changed files with 61 additions and 11 deletions

View File

@ -21,6 +21,7 @@ Allocation -- attributes with prefix staff- affect lecturers only, but are invis
registerByCourse UTCTime Maybe -- course registration dates are ignored until this day has passed or always prohibited registerByCourse UTCTime Maybe -- course registration dates are ignored until this day has passed or always prohibited
overrideDeregister UTCTime Maybe -- course deregistration enforced to be this date, i.e. students may disenrol from course after or never overrideDeregister UTCTime Maybe -- course deregistration enforced to be this date, i.e. students may disenrol from course after or never
-- overrideVisible not needed, since courses are always visible -- overrideVisible not needed, since courses are always visible
matchingSeed ByteString default=''
TermSchoolAllocationShort term school shorthand -- shorthand must be unique within school and semester TermSchoolAllocationShort term school shorthand -- shorthand must be unique within school and semester
TermSchoolAllocationName term school name -- name must be unique within school and semester TermSchoolAllocationName term school name -- name must be unique within school and semester
deriving Show Eq Ord Generic deriving Show Eq Ord Generic

View File

@ -0,0 +1,19 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Crypto.Random.Instances
(
) where
import ClassyPrelude
import Crypto.Random
import System.Random (RandomGen(..))
import qualified Data.ByteArray as BA
import Data.Bits
instance RandomGen ChaChaDRG where
next g = withRandomBytes g (finiteBitSize (maxBound :: Int) `div` 8) (foldr (\x acc -> acc `shiftL` 8 .|. fromIntegral x) zeroBits . BA.unpack @BA.Bytes)
split g = withDRG g drgNew

View File

@ -115,7 +115,7 @@ postAComputeR tid ssh ash = do
formResult computeFormRes $ \AllocationComputeForm{..} -> do formResult computeFormRes $ \AllocationComputeForm{..} -> do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
(allocFp, allocMatching, allocLog) <- computeAllocation aId acfRestrictCourses (allocFp, allocMatching, allocLog) <- computeAllocation aEnt acfRestrictCourses
tellSessionJson SessionAllocationResults . SessionDataAllocationResults $ tellSessionJson SessionAllocationResults . SessionDataAllocationResults $
Map.singleton (tid, ssh, ash) (now, allocFp, allocMatching, allocLog) Map.singleton (tid, ssh, ash) (now, allocFp, allocMatching, allocLog)
addMessageI Success MsgAllocationComputed addMessageI Success MsgAllocationComputed

View File

@ -24,7 +24,10 @@ import qualified Data.Vector as Vector
import Data.Vector.Lens (vector) import Data.Vector.Lens (vector)
import qualified Data.Set as Set import qualified Data.Set as Set
import System.Random (mkStdGen) import qualified Data.Binary as Binary
import Crypto.Hash.Algorithms (SHAKE256)
import Crypto.Random (drgNewSeed, seedFromBinary)
import Crypto.Error (onCryptoFailure)
import Utils.Allocation import Utils.Allocation
@ -33,7 +36,8 @@ import qualified Data.Conduit.List as C
import Data.Generics.Product.Param import Data.Generics.Product.Param
import qualified Crypto.Hash as Crypto import qualified Crypto.Hash as Crypto
import qualified Data.Binary as Binary
import Language.Haskell.TH (nameBase)
data MatchingExcludedReason data MatchingExcludedReason
@ -81,13 +85,13 @@ sinkAllocationPriorities allocId = fmap getSum . C.foldMapM . ifoldMapM $ \matr
E.&&. user E.^. UserMatrikelnummer E.==. E.val (Just matr) E.&&. user E.^. UserMatrikelnummer E.==. E.val (Just matr)
computeAllocation :: AllocationId computeAllocation :: Entity Allocation
-> Maybe (Set CourseId) -- ^ Optionally restrict allocation to only consider the given courses -> Maybe (Set CourseId) -- ^ Optionally restrict allocation to only consider the given courses
-> DB ( AllocationFingerprint -> DB ( AllocationFingerprint
, Set (UserId, CourseId) , Set (UserId, CourseId)
, Seq MatchingLogRun , Seq MatchingLogRun
) )
computeAllocation allocId cRestr = do computeAllocation (Entity allocId Allocation{allocationMatchingSeed}) cRestr = do
allocations <- selectList [ CourseParticipantAllocated ==. Just allocId ] [] allocations <- selectList [ CourseParticipantAllocated ==. Just allocId ] []
let allocations' = allocations let allocations' = allocations
& map ((, Sum 1) . courseParticipantUser . entityVal) & map ((, Sum 1) . courseParticipantUser . entityVal)
@ -193,10 +197,12 @@ computeAllocation allocId cRestr = do
= id = id
let let
fingerprint :: AllocationFingerprint inputs = Binary.encode (users'', capacities, preferences, gradeScale, gradeOrdinalPlaces)
fingerprint = Crypto.hash . toStrict $ Binary.encode (users'', capacities, preferences, gradeScale, gradeOrdinalPlaces)
g = mkStdGen $ hash fingerprint fingerprint :: AllocationFingerprint
fingerprint = Crypto.hashlazy inputs
g = onCryptoFailure (\_ -> error "Could not create DRG") id . fmap drgNewSeed . seedFromBinary $ kmaclazy @(SHAKE256 320) (encodeUtf8 . (pack :: String -> Text) $ nameBase 'computeAllocation) allocationMatchingSeed inputs
let let
doAllocationWithout :: Set CourseId -> Writer (Seq (MatchingLog UserId CourseId Natural)) (Set (UserId, CourseId)) doAllocationWithout :: Set CourseId -> Writer (Seq (MatchingLog UserId CourseId Natural)) (Set (UserId, CourseId))

View File

@ -26,10 +26,8 @@ import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime)
import qualified Data.Binary as Binary import qualified Data.Binary as Binary
import qualified Crypto.MAC.KMAC as KMAC
import Crypto.Hash.Algorithms (SHAKE256) import Crypto.Hash.Algorithms (SHAKE256)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteArray as BA import qualified Data.ByteArray as BA
import Language.Haskell.TH import Language.Haskell.TH
@ -94,7 +92,7 @@ memcachedKey :: ( Typeable a
) )
=> AEAD.Key -> Proxy a -> k -> ByteString => AEAD.Key -> Proxy a -> k -> ByteString
memcachedKey (Saltine.encode -> kmacKey) p k = Binary.encode k memcachedKey (Saltine.encode -> kmacKey) p k = Binary.encode k
& KMAC.finalize . KMAC.updates (KMAC.initialize @(SHAKE256 256) (encodeUtf8 . tshow $ typeRep p) kmacKey) . LBS.toChunks & kmaclazy @(SHAKE256 256) (encodeUtf8 . tshow $ typeRep p) kmacKey
& BA.convert & BA.convert
memcachedByGet :: forall a k m. memcachedByGet :: forall a k m.

View File

@ -156,8 +156,10 @@ import Yesod.Form.Fields.Instances as Import ()
import Data.MonoTraversable.Instances as Import () import Data.MonoTraversable.Instances as Import ()
import Web.Cookie.Instances as Import () import Web.Cookie.Instances as Import ()
import Network.HTTP.Types.Method.Instances as Import () import Network.HTTP.Types.Method.Instances as Import ()
import Crypto.Random.Instances as Import ()
import Crypto.Hash as Import (Digest, SHA3_256) import Crypto.Hash as Import (Digest, SHA3_256)
import Crypto.Random as Import (ChaChaDRG, Seed)
import Control.Lens as Import import Control.Lens as Import
hiding ( (<.>) hiding ( (<.>)

View File

@ -78,6 +78,10 @@ import qualified Data.ByteString.Base64.URL as Base64
import qualified Crypto.Saltine.Core.SecretBox as SecretBox import qualified Crypto.Saltine.Core.SecretBox as SecretBox
import qualified Crypto.Saltine.Class as Saltine import qualified Crypto.Saltine.Class as Saltine
import qualified Crypto.Data.PKCS7 as PKCS7 import qualified Crypto.Data.PKCS7 as PKCS7
import Crypto.MAC.KMAC (KMAC, HashSHAKE)
import qualified Crypto.MAC.KMAC as KMAC
import Data.ByteArray (ByteArrayAccess)
import Data.Fixed import Data.Fixed
-- import Data.Ratio ((%)) -- import Data.Ratio ((%))
@ -963,6 +967,20 @@ encodedSecretBoxOpen ciphertext = do
sKey <- secretBoxKey sKey <- secretBoxKey
encodedSecretBoxOpen' sKey ciphertext encodedSecretBoxOpen' sKey ciphertext
kmaclazy :: forall a string key ba chunk.
( HashSHAKE a
, ByteArrayAccess string
, ByteArrayAccess key
, ByteArrayAccess chunk
, LazySequence ba chunk
)
=> string
-> key
-> ba
-> KMAC a
kmaclazy str k = KMAC.finalize . KMAC.updates (KMAC.initialize @a str k) . toChunks
------------- -------------
-- Caching -- -- Caching --
------------- -------------

View File

@ -24,6 +24,8 @@ import qualified Data.CaseInsensitive as CI
import qualified Data.Csv as Csv import qualified Data.Csv as Csv
import Crypto.Random (getRandomBytes)
testdataDir :: FilePath testdataDir :: FilePath
testdataDir = "testdata" testdataDir = "testdata"
@ -969,6 +971,7 @@ fillDb = do
} }
aSeedFunc <- liftIO $ getRandomBytes 40
funAlloc <- insert' Allocation funAlloc <- insert' Allocation
{ allocationName = "Funktionale Zentralanmeldung" { allocationName = "Funktionale Zentralanmeldung"
, allocationShorthand = "fun" , allocationShorthand = "fun"
@ -986,6 +989,7 @@ fillDb = do
, allocationRegisterByStaffTo = Nothing , allocationRegisterByStaffTo = Nothing
, allocationRegisterByCourse = Nothing , allocationRegisterByCourse = Nothing
, allocationOverrideDeregister = Just $ termTime True Summer 1 False Monday toMidnight , allocationOverrideDeregister = Just $ termTime True Summer 1 False Monday toMidnight
, allocationMatchingSeed = aSeedFunc
} }
insert_ $ AllocationCourse funAlloc pmo 100 insert_ $ AllocationCourse funAlloc pmo 100
insert_ $ AllocationCourse funAlloc ffp 2 insert_ $ AllocationCourse funAlloc ffp 2
@ -1088,6 +1092,7 @@ fillDb = do
forM_ (take participants manyUsers') $ \uid -> forM_ (take participants manyUsers') $ \uid ->
void . insert $ CourseParticipant cid uid now Nothing Nothing void . insert $ CourseParticipant cid uid now Nothing Nothing
aSeedBig <- liftIO $ getRandomBytes 40
bigAlloc <- insert' Allocation bigAlloc <- insert' Allocation
{ allocationName = "Große Zentralanmeldung" { allocationName = "Große Zentralanmeldung"
, allocationShorthand = "big" , allocationShorthand = "big"
@ -1105,6 +1110,7 @@ fillDb = do
, allocationRegisterByStaffTo = Nothing , allocationRegisterByStaffTo = Nothing
, allocationRegisterByCourse = Nothing , allocationRegisterByCourse = Nothing
, allocationOverrideDeregister = Just $ termTime True Summer 1 False Monday toMidnight , allocationOverrideDeregister = Just $ termTime True Summer 1 False Monday toMidnight
, allocationMatchingSeed = aSeedBig
} }
bigAllocCourses <- forM ([1..40] :: [Int]) $ \n -> do bigAllocCourses <- forM ([1..40] :: [Int]) $ \n -> do
csh <- pack . take 3 <$> getRandomRs ('A', 'Z') csh <- pack . take 3 <$> getRandomRs ('A', 'Z')