scyther-proof-0.3.0: src/Scyther/Event.hs
{-# LANGUAGE DeriveDataTypeable #-}
-- | Symbolic events that we are reasoning about
module Scyther.Event (
-- * Events
Event(..)
, substEv
, substEvOrd
, evTIDs
, evOrdTIDs
-- ** Event Order
, EventOrder
, cyclic
, before
-- * Output
, isaEvent
, isaEventOrd
, sptEvent
, sptRawEvent
, sptEventOrd
) where
import qualified Data.Set as S
import Data.Data
import Control.Arrow ( (***) )
import Control.Monad
import Text.Isar
import Scyther.Protocol
import Scyther.Message
import Scyther.Equalities
------------------------------------------------------------------------------
-- Events
------------------------------------------------------------------------------
-- | The logical denotation of an event of the execution of a security
-- protocol.
data Event =
Step TID RoleStep -- ^ The thread executed the role step.
| Learn Message -- ^ The message was learnt by the intruder.
deriving( Eq, Ord, Show, Data, Typeable )
-- | The event order induced by the trace of the reachable state we are
-- reasoning about.
type EventOrder = S.Set (Event,Event)
-- | Is the event order cyclic?
cyclic :: EventOrder -> Bool
cyclic evord =
maybe True (const False) $ foldM visitForest S.empty (map fst evPairs)
where
evPairs = S.toList evord
visitForest visited x
| x `S.member` visited = return visited
| otherwise = findLoop S.empty visited x
-- findLoop :: Ord n => S.Set n -> S.Set n -> n -> Maybe (S.Set n)
findLoop parents visited x
| x `S.member` parents = mzero
| x `S.member` visited = return visited
| otherwise =
S.insert x `liftM` foldM (findLoop parents') visited next
where
next = [ e' | (e,e') <- evPairs, e == x ]
parents' = S.insert x parents
-- | Must an event have happened before another one with respect to the given
-- event order.
--
-- /before evord from to/ holds iff the event order /evord/ implies that the
-- event /from/ must have happened before the event /to/ in the reachable state
-- we are reasoning about.
before :: EventOrder -> Event -> Event -> Bool
before evord from to = existsPath S.empty [from]
where
evPairs = S.toList evord
existsPath _ [] = False
existsPath visited (x:xs)
| x == to = True
| x `S.member` visited = existsPath visited xs
| otherwise =
existsPath (S.insert x visited) (next ++ xs)
where
next = [ e' | (e,e') <- evPairs, e == x ]
-- | Substitute an event according to the equalities.
substEv :: Equalities -> Event -> Event
substEv eqs (Learn m) = Learn (substMsg eqs m)
substEv eqs (Step tid step) = Step (substTID eqs tid) step
-- | Substitute an event order according to the equalities.
substEvOrd :: Equalities -> (Event, Event) -> (Event, Event)
substEvOrd eqs = substEv eqs *** substEv eqs
-- | The threads associated to an event.
evTIDs :: Event -> [TID]
evTIDs (Step tid _) = return tid
evTIDs (Learn m) = msgTIDs m
-- | The threads associated to an event order.
evOrdTIDs :: (Event, Event) -> [TID]
evOrdTIDs (e1, e2) = evTIDs e1 `mplus` evTIDs e2
------------------------------------------------------------------------------
-- Pretty Printing
------------------------------------------------------------------------------
-- Helper function
------------------
eventTag :: (Event -> Doc) -- ^ Raw pretty printing function
-> (Doc -> Doc) -- ^ Modifier for learn events
-> (Doc -> Doc) -- ^ Modifier for step events
-> Event -> Doc
eventTag pp ln _ ev@(Learn _) = ln (pp ev)
eventTag pp _ st ev@(Step _ _) = st (pp ev)
-- Isar Output
--------------
-- | Pretty print the before order predicate.
esplBefore :: IsarConf -> Doc -> Doc -> Doc
esplBefore conf e1 e2
| isPlainStyle conf = sep [text "predOrd t", nest 2 (parens e1), nest 2 (parens e2)]
| otherwise = sep [e1 <-> symbol "\\<prec>", e2]
-- | Render an event in the Isar format.
isaRawEvent :: Bool -> IsarConf -> Mapping -> Event -> Doc
isaRawEvent addParens conf _ (Learn m) =
(if addParens then parens else id) $ isar conf m
isaRawEvent _ conf mapping (Step tid step) =
nestShort 2 lparen rparen (fsep [isar conf tid <> comma, ppStep])
where
ppStep = isaRoleStep conf (threadRole tid (getMappingEqs mapping)) step
-- | Render an event in the Isar format.
isaEvent :: IsarConf
-> Mapping -- ^ A mapping of the logical variables. The thread id to
-- role assigment is used for resolving steps to their
-- abbreviated notation consisting of role name plus step
-- label.
-> Event -> Doc
isaEvent conf m =
eventTag (isaRawEvent False conf m) (inSet "knows t") (inSet "steps t")
where
inSet set d = d <-> isaIn conf <-> text set
-- | Render an event order in the Isar format. See 'isaEvent' for an
-- explanation of the mapping.
isaEventOrd :: IsarConf -> Mapping -> (Event, Event) -> Doc
isaEventOrd conf m (e1, e2) =
esplBefore conf (ppEv e1) (ppEv e2)
where
ppEv = eventTag (isaRawEvent True conf m) (text "Ln" <>) (text "St" <>)
instance Isar Event where
isar conf = isaEvent conf (Mapping empty)
-- SP Theory Output
-------------------
-- | Render an event in security protocol theory format without any added
-- tagging; i.e. learned messages are rendered as is, and executed steps are
-- rendered as a tuple of thread identifier and role step (abbreviated if
-- possible).
sptRawEvent :: Mapping -> Event -> Doc
sptRawEvent _ (Learn m) = sptMessage m
sptRawEvent mapping (Step tid step) =
parens $ fsep [sptTID tid <> comma, ppStep]
where
ppStep = sptRoleStep (threadRole tid (getMappingEqs mapping)) step
-- | Render a fact that an event happened in security protocol theory format.
sptEvent :: Mapping -> Event -> Doc
sptEvent m = eventTag (sptRawEvent m) ((text "knows" <>) . parens) (text "step" <>)
-- | Render an event order in security protocol theory format.
sptEventOrd :: Mapping -> [Event] -> Doc
sptEventOrd m =
fcat . punctuate (text " < ") . map ppEv
where
ppEv = eventTag (sptRawEvent m) ((text "Ln" <>) . parens) (text "St" <>)