-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MWE for proving SIF #386
Conversation
verification/scratch/mwe1/mwe1.gobra
Outdated
type Collect domain { | ||
func Post(ClassificationSpec, Trace) ObservationTrace | ||
func Pre(ClassificationSpec, Trace) ObservationTrace | ||
func pre_(Specification) Observation | ||
func post_(Specification) Observation | ||
|
||
// NOTE: these are the low projections mentioned in the paper | ||
axiom { | ||
(forall p,q Observation :: {pre_(Spec{p,q})} pre_(Spec{p,q}) == p) && | ||
(forall p,q Observation :: {post_(Spec{p,q})} post_(Spec{p,q}) == q) | ||
} | ||
|
||
axiom { | ||
(forall sig ClassificationSpec :: {Post(sig,Empty{})} Post(sig,Empty{}) == EmptyObs{}) && | ||
(forall sig ClassificationSpec :: {Pre(sig,Empty{})} Pre(sig,Empty{}) == EmptyObs{}) && | ||
(forall sig ClassificationSpec, t Trace, e Action :: {Post(sig,Snoc{t,e})} Post(sig,Snoc{t,e}) == SnocObs{Post(sig,t), post_(sig.Classification(e))}) && | ||
(forall sig ClassificationSpec, t Trace, e Action :: {Pre(sig,Snoc{t,e})} Pre(sig,Snoc{t,e}) == SnocObs{Pre(sig,t), pre_(sig.Classification(e))}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the type Collect
introduced here does not seem to be used anywhere. As such, I think we should define these operations not through a domain, but with pure functions instead. Please check if this applies to other domains you introduced as well
verification/scratch/mwe1/mwe1.gobra
Outdated
func Reaches(IODSpec, Trace, state) bool | ||
|
||
axiom { | ||
forall r IODSpec, t Trace, s state, a Action :: { Snoc{t, a}, Reaches(r, t, s) } Reaches(r, t, s) && r.Guard(s, a) ==> Reaches(r, Snoc{t, a}, r.Update(s, a)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this produce a verification error? I would have expected that the call r.Guard(s, a)
can only succeed if r
is not nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not produce a verification error as is. Only when moving the call r.Guard(s, a)
to a pure function (while trying to extract the Restriction
domain / Reaches
), Gobra complains
verification/scratch/mwe1/mwe1.gobra
Outdated
/* ghost */ Declassify(t) | ||
/* ghost */ LowPost(DefaultClassification{}) | ||
Send(t) | ||
/* ghost */ LowPost(DefaultClassification{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have a comment here about what would happen if we, for example, forgot to call LowPost(DefaultClassification{})
or Declassify
in any of the three places where we currently do. The interesting point is that we would get a verification error in the postcondition instead of in the the call to Send
verification/scratch/mwe2/mwe2.gobra
Outdated
ensures token(old(Recv_T(p))) | ||
ensures m == old(Recv_R(p)) | ||
ensures low(m) | ||
func Recv(ghost p Place) (m int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would return the new place here as a ghost return value
verification/scratch/mwe2/mwe2.gobra
Outdated
requires token(p) && SendPerm(p, t) | ||
requires low(t) | ||
ensures token(old(Send_T(p, t))) | ||
func Send(ghost p Place, t int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here and in declassify
verification/scratch/mwe2/mwe2.gobra
Outdated
// 1. Receive a message. | ||
RecvPerm(p) && | ||
// 2. Compute MAC tag and declassify it. | ||
DeclassifyPerm(Recv_T(p), Recv_R(p), MAC(key, Recv_R(p))) && | ||
// 3. Send MAC tag over network. | ||
SendPerm(Declassify_T(Recv_T(p), Recv_R(p), MAC(key, Recv_R(p))), MAC(key, Recv_R(p))) && | ||
// 4. Restart. | ||
Protocol1(Send_T(Declassify_T(Recv_T(p), Recv_R(p), MAC(key, Recv_R(p))), MAC(key, Recv_R(p))), key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you might want to simplify this with let
bindings to introduce intermediate variables for the places and for the mac
verification/scratch/mwe2/mwe2.gobra
Outdated
lastMsg2 int // 2nd | ||
lastMsg3 int // 3rd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why did we introduce lastMsg2
and lastMsg3
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment below. Protocol2
allows sending the MAC tag of either the most or second most recently received message, and to ensure this, we need to keep track of these in our state. lastMsg3
is actually not needed, however, and I removed it. I put it in initially to make it more explicit which messages' tags can be sent and which can't.
verification/scratch/mwe2/mwe2.gobra
Outdated
pred Protocol2(ghost p Place, s state) { | ||
// Receive a message at any time. | ||
RecvPerm(p) && Protocol2(Recv_T(p), Recv_S(s, Recv_R(p))) && | ||
// NOTE: at the moment we can declassify things before receiving anything | ||
// Declassify and send either the most or the 2nd most recently received message. | ||
DeclassifyPerm(p, s.lastMsg1, MAC(s.key, s.lastMsg1)) && Protocol2(Declassify_T(p, s.lastMsg1, MAC(s.key, s.lastMsg1)), s) && | ||
DeclassifyPerm(p, s.lastMsg2, MAC(s.key, s.lastMsg2)) && Protocol2(Declassify_T(p, s.lastMsg2, MAC(s.key, s.lastMsg2)), s) && | ||
SendPerm(p, MAC(s.key, s.lastMsg1)) && Protocol2(Send_T(p, MAC(s.key, s.lastMsg1)), s) && | ||
SendPerm(p, MAC(s.key, s.lastMsg2)) && Protocol2(Send_T(p, MAC(s.key, s.lastMsg2)), s) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this protocol show sth that the other did not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I constructed Protocol2
to show a protocol ...
- ... which allows declassifying different values at any point, and thus needs the additional parameter
p
for declassification (to make the IOD spec deterministic and the low data, which includesp
). - ... where one is allowed by the IOD spec to send something before declassifying, which is only "caught" (in case it was high data) by the classification spec / pre- and postconditions of the I/O functions (in the other protocol, one had to declassify before sending just to conform to the IOD spec). This is supposed to exemplify that SIF goes beyond simply following the IOD spec.
- ... which contains more complex, mutable state, and how to work with it.
It may be worth to move these examples to the test cases of viperproject/gobra#802 |
Not supposed to be merged. (Very) minimal working example of how to verify secure information flow for the router