Commit 8136e377 authored by Christian Müller's avatar Christian Müller

add causality

parent 67056b7a
......@@ -27,3 +27,4 @@ build
# Resulting ltl formulas
results/*.ltl
results/*.ppltl
results/*.foltl
p cnf 24 29
6 0
-6 7 0
-6 8 0
-7 -1 -2 0
-8 9 0
-8 10 0
-9 -1 -3 0
-10 11 0
-10 12 0
-11 -1 -4 0
-12 13 0
-12 14 0
-13 -1 -5 0
-14 15 0
-14 16 0
-15 -2 -3 0
-16 17 0
-16 18 0
-17 -2 -4 0
-18 19 0
-18 20 0
-19 -2 -5 0
-20 21 0
-20 22 0
-21 -3 -4 0
-22 23 0
-22 24 0
-23 -3 -5 0
-24 -4 -5 0
p cnf 3 2
3 0
-3 1 2 0
......@@ -13,4 +13,4 @@ loop {
Target
(Conf(xa,p) ∧ Read(xa, xb, p, r))
Read(xa, xb, p, r)
......@@ -11,6 +11,10 @@ loop {
forall x,p (A(x,p) ∧ O(x,p,r)) -> Acc += (x,p)
}
Declassify
¬ Conf(xa,p)
Target
(Conf(xa, p) ∧ Acc(xa,xb,p))
Acc(xa,xb,p)
......@@ -11,6 +11,10 @@ loop {
forall x,p,r (A(x,p) ∧ O(x,p,r)) -> Review += (x,p,r)
}
Declassify
¬ Conf(xa,p)
Target
(Conf(xa,p) ∧ Read(xa,xb,p,r))
\ No newline at end of file
Read(xa,xb,p,r)
\ No newline at end of file
......@@ -9,6 +9,10 @@ forall x,p,r
forallmay y,x,p
(Ass(x,p) ∧ Ass(y,p)) -> Comm += (x,y,p)
Declassify
¬ Conf(x,p)
Target
(Conf(x,p) ∧ Comm(x, y, p))
Comm(x, y, p)
......@@ -133,7 +133,7 @@ object Encoding extends LazyLogging {
}
def encodeInitial(sig: Signature, g: Graph[Int, LDiEdge]) = {
val empties = for (f <- sig.preds) yield {
val empties = for (f <- sig.preds toList) yield {
Forall(f.params, Neg(f))
}
And.make(empties)
......
......@@ -23,6 +23,6 @@ object ExampleWorkflows extends LazyLogging {
// drop file ending
val name = f.getName.replace(ENDING, "")
name -> (spec.get._1, spec.get._2)
name -> spec.get
}) toMap
}
......@@ -11,6 +11,7 @@ import de.tum.workflows.foltl.Properties
import java.io.PrintWriter
import java.io.File
import de.tum.workflows.blocks.Workflow
import de.tum.workflows.blocks.Spec
object Main extends App with LazyLogging {
......@@ -18,20 +19,22 @@ object Main extends App with LazyLogging {
def generateExample(name: String) {
logger.info(s"Generating $name")
val (wf, target) = ExampleWorkflows.examples(name)
stubbornnoninter(name, wf, target)
val spec = ExampleWorkflows.examples(name)
generate(name, spec)
}
def write(name: String, prop:Term) {
val file = new File(name)
val writer = new PrintWriter(file)
writer.print(prop)
writer.close()
}
def writeExample(name: String, prop: Term) {
def stubbornnoninter(name: String, w: Workflow, target: Term) {
logger.info(s"Encoding Workflow:\n$w")
logger.info("Using only stubborn agents")
val t1 = "pi1"
val t2 = "pi2"
logger.info(s"Computing noninterference for target $target")
val prop = Properties.noninterStubborn(w, target)
write(s"${name}.foltl", prop.pretty())
val (agents, res) = LTL.eliminateExistentials(prop)
logger.info(s"Using universe $agents")
......@@ -43,17 +46,24 @@ object Main extends App with LazyLogging {
val quantfree = LTL.eliminateUniversals(res, agents)
val ltlprop = LTL.eliminatePredicates(quantfree)
val file = new File(s"results/${name}_stubborn_${agents.size}agents.ltl")
val writer = new PrintWriter(file)
writer.print(ltlprop)
writer.close()
write(s"${name}_${agents.size}agents.ltl", ltlprop)
write(s"${name}_${agents.size}agents.ppltl", ltlprop.pretty())
val ppfile = new File(s"results/${name}_stubborn_${agents.size}agents.ppltl")
val ppwriter = new PrintWriter(ppfile)
ppwriter.print(ltlprop.pretty())
ppwriter.close()
logger.info(s"Written all files for $name")
}
logger.info(s"Saved resulting property at ${file.toPath()}")
def generate(name: String, spec:Spec) {
logger.info(s"Encoding Workflow:\n$spec.w")
val t1 = "pi1"
val t2 = "pi2"
logger.info(s"Computing noninterference for target ${spec.target} using only stubborn agents")
val prop = Properties.noninterStubborn(spec)
writeExample(s"results/${name}_stubborn", prop)
logger.info(s"Computing noninterference for target ${spec.target} using a single causal agent")
val cprop = Properties.noninterCausal(spec, 1)
writeExample(s"results/${name}_causal", cprop)
}
for (k <- ExampleWorkflows.examples.keys) {
......
......@@ -45,7 +45,7 @@ object WorkflowParser extends RegexParsers {
val (oracles, rels) = filtered.partition(_.name.startsWith("O"))
Signature(oracles toList, rels toList)
Signature(oracles toSet, rels toSet)
}
......@@ -78,7 +78,7 @@ object WorkflowParser extends RegexParsers {
Workflow(buildSig(choiceblocks), choiceblocks)
}}
def SPEC = "Workflow" ~> WORKFLOW ~ "Target" ~ TERM ^^ { case w ~ _ ~ t => (w, t) }
def SPEC = "Workflow" ~> WORKFLOW ~ (("Declassify" ~> TERM)?) ~ ("Target" ~> FUN) ^^ { case w ~ decl ~ tar => Spec(w, decl.getOrElse(True), tar) }
def parseWorkflow(s: String) = parseAll(WORKFLOW, s)
def parseSpec(s: String) = parseAll(SPEC, s)
......
......@@ -2,12 +2,14 @@ package de.tum.workflows.blocks
import de.tum.workflows.foltl.FOLTL._
case class Signature(oracles: List[Fun], preds: List[Fun])
case class Signature(oracles: Set[Fun], preds: Set[Fun])
case class Workflow(sig: Signature, steps: List[Block]) {
override def toString() = steps.mkString("\n")
}
case class Spec(w: Workflow, declass: Term, target: Fun)
abstract sealed class Block
abstract sealed class SimpleBlock extends Block {
......
......@@ -22,7 +22,7 @@ object FOLTL {
def pretty(): String = {
val s = this.toString
val broken = s replace ("∧ ", "∧ \n")
val broken = s replace ("∧ ", "∧\n") replace ("∨ ", "∨\n")
// indent
var opens = 0;
......@@ -51,6 +51,7 @@ object FOLTL {
}
case class Fun(name: String, ind: Option[String], params: List[Var]) extends Term {
override def toString() = name + Utils.mkString(ind, "(", "", ")") + params.mkString("(", ",", ")")
def updatedParams(index: Int, p:Var) = Fun(name, ind, params.updated(index, p))
}
trait Quantifier extends Term {
......@@ -97,6 +98,10 @@ object FOLTL {
val make = Until.apply _
val opname = "U"
}
object WUntil {
def apply(t1: Term, t2: Term) = Or(Until(t1, t2), Globally(t1))
}
trait UnOp extends Term {
def make: Term => UnOp
......
......@@ -5,11 +5,12 @@ import de.tum.workflows.Encoding._
import de.tum.workflows.blocks._
import de.tum.workflows.foltl.FOLTL._
import de.tum.workflows.Utils._
import com.typesafe.scalalogging.LazyLogging
object Stubborn {
def apply(agent: Var, choices: Set[Fun], t1: String, t2: String) = {
And.make(for (c <- choices toList) yield {
val f = Fun(c.name, c.params.updated(0, agent))
val f = c.updatedParams(0, agent)
Globally(
Forall(c.params.drop(1), Eq(f.in(t1), f.in(t2)))
)
......@@ -17,45 +18,95 @@ object Stubborn {
}
}
object Noninterference {
def apply(choices: Set[Fun], target: Fun, t1: String, t2:String) = {
object Causal {
def apply(agent: Var, inputs: Set[Fun], outputs: Set[Fun], t1: String, t2: String) = {
val agent = target.params(0)
val equalchoices = for (c <- inputs toList) yield {
val newc = c.updatedParams(0, agent)
Forall(c.params.drop(1), Eq(newc.in(t1), newc.in(t2)))
}
val cechoices = for (f <- choices) yield {
Fun(f.name, f.params.updated(0, agent))
val vexist = outputs flatMap (_.params) toList
val violations = for (o <- outputs toList) yield {
val newo = o.updatedParams(0, agent)
Neg(Eq(newo.in(t1), newo.in(t2)))
}
val samechoices = Stubborn(agent, choices, t1, t2)
val difference = Exists(target.params.drop(1), Neg(Eq(target.in(t1), target.in(t2))))
WUntil(And.make(equalchoices), Exists(vexist, Or.make(violations)))
}
}
object Noninterference extends LazyLogging {
def apply(choices: Set[Fun], spec:Spec, t1: String, t2:String) = {
if (!spec.declass.freeVars().forall(spec.target.params.contains(_))) {
logger.error("Not all variables of the declassification condition bound by the target")
}
Exists(agent, And(samechoices, Finally(difference)))
val agent = spec.target.params(0)
val samechoices = Stubborn(agent, choices, t1, t2)
// TODO: declassification binding?
// val sameoracles = for (o <- oracles) yield {
// Forall(o.params, Eq(o.in(t1), o.in(t2)))
// }
// val decl = Or(Neg(declass.in(t1)), And.make(sameoracles toList))
val difference = Neg(Eq(spec.target.in(t1), spec.target.in(t2)))
Exists(agent, And(samechoices, Finally(Exists(spec.target.params.drop(1), difference))))
}
}
object Properties {
// include optimizations for choices
def noninterStubborn(w: Workflow, target: Term) = {
def noninterStubborn(spec: Spec) = {
val t1 = "t1"
val t2 = "t2"
val graph = toGraph(w)
val graph = toGraph(spec.w)
val cfg = sanecfg(graph)
val sem = exec(w.sig, graph)
val sem = exec(spec.w.sig, graph)
val agents = target freeVars() toList
val agents = spec.target freeVars() toList
// choice and node predicates are equal in both runs (stubborn)
val nodes = nodepredicates(graph) map (_.name)
val choices = allchoices(w) map (_.name)
val choices = allchoices(spec.w) map (_.name)
val equals = nodes ++ choices
val noninter = Exists(agents, Finally(Neg(Eq(target.in(t1), target.in(t2)))))
val noninter = Exists(agents, Finally(Neg(Eq(spec.target.in(t1), spec.target.in(t2)))))
// maybe add exists t1, t2. phi?
And.make(cfg, sem.in(t1, equals), sem.in(t2, equals), noninter)
}
def noninterCausal(spec:Spec, causalNum: Int) = {
val t1 = "t1"
val t2 = "t2"
val causals = (0 until causalNum) map (x => Var("causal" + x)) toList
val graph = toGraph(spec.w)
val cfg = sanecfg(graph)
val sem = exec(spec.w.sig, graph)
val agents = spec.target freeVars() toList
// choice and node predicates are equal in both runs (stubborn)
val nodes = nodepredicates(graph) map (_.name)
val choices = allchoices(spec.w)
val noninter = Noninterference(choices, spec, t1, t2)
val causal = And.make(for (a <- causals) yield {
Exists(a, Causal(a, choices, spec.w.sig.preds, t1, t2))
})
// maybe add exists t1, t2. phi?
And.make(cfg, sem.in(t1, nodes), sem.in(t2, nodes), noninter, causal)
}
}
......@@ -14,10 +14,12 @@ import de.tum.workflows.Utils._
import de.tum.workflows.foltl.Properties
import de.tum.workflows.ltl.LTL
import de.tum.workflows.ExampleWorkflows
import de.tum.workflows.blocks._
class EncodingTest extends FlatSpec {
val w = ExampleWorkflows.examples("simpleChoice")._1
val w = ExampleWorkflows.examples("simpleChoice").w
// shorthands
val n0 = Fun("n0", List())
......@@ -75,11 +77,13 @@ class EncodingTest extends FlatSpec {
}
"Noninterference encoding" should "work" in {
val choices = Set(Fun("choice0", List("x", "y")))
val Rab = Fun("R", List("a", "b"))
val s = Spec(Workflow(Signature(Set(), Set()), List()), True, Rab)
val nonint = Noninterference(choices, Rab, "t1", "t2")
val nonint = Noninterference(choices, s, "t1", "t2")
val aux0ay = Fun("choice0", List("a", "y"))
......@@ -96,14 +100,17 @@ class EncodingTest extends FlatSpec {
choices should be (Set(Fun("choice0", List("x", "s"))))
val ce = Fun("R", List("ax", "as"))
val s = Spec(Workflow(Signature(Set(), Set()), List()), True, ce)
val noninter = Noninterference(choices, ce, t1, t2)
val noninter = Noninterference(choices, s, t1, t2)
noninter should be (
Exists("ax", And(
Stubborn("ax", choices, t1, t2),
Finally(Exists("as", Neg(Eq(ce.in(t1), ce.in(t2))))))))
val prop = Properties.noninterStubborn(w, Fun("R", List("ax", "as")))
val s2 = Spec(w, True, Fun("R", List("ax", "as")))
val prop = Properties.noninterStubborn(s2)
val (agents, res) = LTL.eliminateExistentials(prop)
......
......@@ -11,6 +11,8 @@ import de.tum.workflows.foltl.FOLTL._
import com.typesafe.scalalogging.LazyLogging
class ParserTest extends FlatSpec {
val oxrxssig = Signature(Set(Fun("O", List("x"))), Set(Fun("R", List("x", "s"))))
def testResult(s: String, sig: Signature) {
testResult(s, Workflow(sig, List()))
......@@ -32,7 +34,7 @@ class ParserTest extends FlatSpec {
}
}
def testSpecResult(s: String, wf: Workflow, tar: Term) {
def testSpecResult(s: String, wf: Workflow, declass: Term, tar: Term) {
val parsed = WorkflowParser.parseSpec(s)
if (!parsed.successful) {
......@@ -40,20 +42,21 @@ class ParserTest extends FlatSpec {
}
parsed.successful should be(true)
val pwf = parsed.get._1
val pwf = parsed.get.w
pwf.sig should be(wf.sig)
if (!wf.steps.isEmpty) {
pwf.steps should be(wf.steps)
}
parsed.get._2 should be(tar)
parsed.get.declass should be(declass)
parsed.get.target should be(tar)
}
"Workflow Parser" should "parse a single may block" in {
val s = "forallmay x,s O(x) → R += (x,s)"
val sig = Signature(List(Fun("O", List("x"))), List(Fun("R", List("x", "s"))))
testResult(s, Workflow(sig, List(ForallMayBlock(List("x", "s"), "choice0", List(Add(Fun("O", List("x")), "R", List("x", "s")))))))
testResult(s, Workflow(oxrxssig, List(ForallMayBlock(List("x", "s"), "choice0", List(Add(Fun("O", List("x")), "R", List("x", "s")))))))
}
it should "parse a block with 2 stmts" in {
......@@ -62,7 +65,7 @@ class ParserTest extends FlatSpec {
O(x) → R += (x,s)
(True ∧ O(s)) → R -= (s,x)
"""
testResult(s, Signature(List(Fun("O", List("x"))), List(Fun("R", List("x", "s")))))
testResult(s, oxrxssig)
}
it should "parse a loop block with 2 stmts" in {
......@@ -70,12 +73,12 @@ class ParserTest extends FlatSpec {
loop {
forallmay x,s
O(x) → R += (x,s)
(True ∧ O(s)) → R -= (s,x)
forall y
O(y) → R += (y,y)
(True ∧ O(x)) → R -= (s,x)
forall x
O(x) → R += (x,x)
}
"""
testResult(s, Signature(List(Fun("O", List("s"))), List(Fun("R", List("x", "s")))))
testResult(s, oxrxssig)
}
it should "parse prevexample" in {
......@@ -88,8 +91,8 @@ class ParserTest extends FlatSpec {
"""
val w = Workflow(
Signature(
List(Fun("O", List("s"))),
List(
Set(Fun("O", List("s"))),
Set(
Fun("S", List("x", "y", "s")),
Fun("R", List("y", "s")),
Fun("Q", List("x", "s")))),
......@@ -108,15 +111,13 @@ class ParserTest extends FlatSpec {
it should "parse simplechoice" in {
val s = """
forallmay x,s
O(s) -> R += (x,s)
O(x) -> R += (x,s)
"""
val w = Workflow(
Signature(
List(Fun("O", List("s"))),
List(Fun("R", List("x", "s")))),
oxrxssig,
List(
ForallMayBlock(List("x", "s"), "choice0",
Add(Fun("O", "s"), "R", List("x", "s")))))
Add(Fun("O", "x"), "R", List("x", "s")))))
testResult(s, w)
}
......@@ -124,15 +125,13 @@ class ParserTest extends FlatSpec {
it should "parse simpleNochoice" in {
val s = """
forallmay x,s
O(s) -> R += (x,s)
O(x) -> R += (x,s)
"""
val w = Workflow(
Signature(
List(Fun("O", List("s"))),
List(Fun("R", List("x", "s")))),
oxrxssig,
List(
ForallMayBlock(List("x", "s"), "choice0",
Add(Fun("O", "s"), "R", List("x", "s")))))
Add(Fun("O", "x"), "R", List("x", "s")))))
testResult(s, w)
}
......@@ -146,19 +145,45 @@ class ParserTest extends FlatSpec {
Target
(R(x,s) ∧ O(x))
R(x,s)
"""
val w = Workflow(
Signature(
List(Fun("O", List("s"))),
List(Fun("R", List("x", "s")))),
Set(Fun("O", List("s"))),
Set(Fun("R", List("x", "s")))),
List(
ForallMayBlock(List("x", "s"), "choice0",
Add(Fun("O", "s"), "R", List("x", "s")))))
val t = And(Fun("R", List("x", "s")), Fun("O", List("x")))
val t = Fun("R", List("x", "s"))
testSpecResult(s, w, True, t)
}
it should "parse simpleNochoice with declassification" in {
val s = """
Workflow
forallmay x,s
O(x) -> R += (x,s)
Declassify
O(x)
Target
R(x,s)
"""
val w = Workflow(
oxrxssig,
List(
ForallMayBlock(List("x", "s"), "choice0",
Add(Fun("O", "x"), "R", List("x", "s")))))
val t = Fun("R", List("x", "s"))
testSpecResult(s, w, t)
testSpecResult(s, w, Fun("O", List("x")), t)
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment