scyther-proof 0.4.1.0 → 0.5.0.0
raw patch · 21 files changed
+1234/−969 lines, 21 files
Files
- CHANGES +5/−1
- README +0/−167
- README.md +165/−0
- data/examples/iso9798/isoiec-9798-2-bdkey.spthy +69/−52
- data/examples/iso9798/isoiec-9798-2-udkey.spthy +32/−18
- data/examples/iso9798/isoiec-9798-3.spthy +120/−134
- data/examples/iso9798/isoiec-9798-4-bdkey.spthy +9/−6
- data/examples/iso9798/isoiec-9798-4-udkey.spthy +18/−12
- data/isabelle/README +48/−50
- data/isabelle/src/ESPLogic/InferenceRules.thy +21/−0
- interactive-only-src/Paths_scyther_proof.hs +1/−1
- scyther-proof.cabal +18/−15
- src/Main.hs +56/−55
- src/Scyther/Facts.hs +103/−95
- src/Scyther/Formula.hs +27/−17
- src/Scyther/Proof.hs +52/−37
- src/Scyther/Sequent.hs +125/−60
- src/Scyther/Theory.hs +24/−25
- src/Scyther/Theory/Parser.hs +111/−110
- src/Scyther/Theory/Pretty.hs +196/−101
- src/Text/Isar.hs +34/−13
CHANGES view
@@ -1,3 +1,7 @@+* 0.5.0.0+ - implemented proof generation for injective agreement+ - fixed bug in unification of inverse-key constructors+ * 0.4.1.0 Bugfixes and dropped export of library - widen 'pretty' dependency - update package description@@ -35,6 +39,6 @@ - Changed lexer to also recognize `!' * 0.3.0 First public version- + Aligned with paper submission for CCS'11: Provably repairing the ISO-9798 authentication protocols.
− README
@@ -1,167 +0,0 @@-======================================================================-README for the scyther-proof security protocol verification tool-======================================================================--Author: Simon Meier <simon.meier@inf.ethz.ch>--Creation Date: 13/05/2011-Updated: 02/04/2012---1. Introduction-===============-- Every distribution of the scyther-proof tool contains a copy of the- Isabelle/HOL theories formalizing its verification theory and a copy of the- protocol models that were verified using scyther-proof. In particular, these- examples include models for all of our repaired versions of the protocols- from the ISO/IEC-9798 authentication standard. (In the source distribution- of scyther-proof they can be found at 'data/examples/iso9798'.)-- In the following sections, we give step by step instructions to get the- whole system working and we explain the usage of our tools and provide- further information (e.g., building documentation).---2. Installation instructions-============================--2.1 Installing scyther-proof------------------------------- You need a working Haskell environment that provides a version of GHC from- 7.0.x to 7.4.x and the 'cabal install' tool. The simplest way to get such an- environment is to download and install the Haskell Platform package for your- OS. - - http://hackage.haskell.org/platform/- - Then call-- cabal install-- in the root directory of this source code package. This will use the- Haskell's deployment tool 'cabal-install' to download all missing libraries- from Hackage, the central Haskell library repository and install the- 'scyther-proof' executable in the default installation location of- cabal-install. The installation location is printed at the end of the build- process.-- Call - - scyther-proof-- without any arguments to get an overview of the arguments supported and the- paths to the examples and the Isabelle/HOL theories.---3.2 Installing the Isabelle/HOL theories------------------------------------------- Download and install full Isabelle2009-1 according to the installation- instructions at-- http://isabelle.in.tum.de/website-Isabelle2009-1/download_x86-linux.html-- The first time you call 'scyther-proof' with the '--isabelle' flag it will- build the logic image of the Isabelle/HOL theories formalizing the security- protocol verification theory underlying scyther-proof.-- For enabling interactive processing of theory files based using our- extensions to Isabelle's ISAR proof language use one of the following two- variants.-- Variant a: Brute Force- ------------------------ Overwrite the standard Emacs keywords file- - $ISABELLE_HOME/etc/isar-keywords.el- - with the Emacs keywords file provided with the ESPL distribution.- - $SCYTHER_PROOF_HOME/isabelle/src/isar-keywords.el-- This is needed in order to make the new ISAR keywords "role",- "protocol", "sources", and "prefix_close" available in the Emacs- frontend.--- Variant b: Elegance- --------------------- A more elegant way is to copy the keywords file provided with our- distribution to-- $ISABELLE_HOME/etc/isar-keywords-ESPL.el-- and start Isabelle using the -k switch as follows.-- isabelle emacs -k ESPL <your-theory-file.thy>-- --3. Usage-========---3.1 scyther-proof---------------- Usage Example- --------------- You can now execute the 'scyther-proof' binary without any arguments to get a- list of the available flags. A simple example usage is (assuming- 'scyther-proof' is in the PATH).-- scyther-proof $EXAMPLE_DIR/classic/NS_Public.spthy --shortest-- This will parse the NSL protocol modeled in the 'nsl.spthy' file and output- all specified security properties together with the proof with the fewest- number of chain rule applications to stdout.-- In order to generate an Isabelle proof script two additional options need to- be added-- scyther-proof $EXAMPLE_DIR/classic/NS_Public.spthy --shortest --output=nsl_cert.thy --ASCII-- The first one specifies the output file and the second one instructs- scyther-proof to use Isabelle's ASCII notation as the output format.--- To build the source code documentation you can use the call-- cabal configure; cabal haddock-- which will use cabal-install and Haskell's source code documentation tool- 'haddock' to build the source code documentation. The location of this- documentation is again output on the command line.---3.2 The Isabelle/HOL theories-------------------------------- In the directory output by scyther-proof, load the Tutorial on interactive- proof construction using the following commands-- cd <theory-dir-output-by-scytherproof>/src- isabelle emacs Tutorial.thy-- or if you've chosen the elegant variant-- isabelle emacs -k ESPL Tutorial.thy-- Note that in many case the easiest way to start with constructing- machine-checked proofs interactively is to use a proof script generated by- scyther-proof.---***-* Happy Proving :-)-*-* In case of questions do not hesistate to contact me at-* simon.meier@inf.ethz.ch-***-
+ README.md view
@@ -0,0 +1,165 @@+README: The scyther-proof security protocol verification tool+=============================================================++Author: Simon Meier <simon.meier@inf.ethz.ch>++Creation Date: 13/05/2011 -- Last Updated: 28/06/2012+++1. Introduction+===============++ Every distribution of the scyther-proof tool contains a copy of the+ Isabelle/HOL theories formalizing its verification theory and a copy of the+ protocol models that were verified using scyther-proof. In particular, these+ examples include models for all of our repaired versions of the protocols+ from the ISO/IEC-9798 authentication standard. (In the source distribution+ of scyther-proof they can be found at `data/examples/iso9798`.)++ In the following sections, we give step by step instructions to get the+ whole system working and we explain the usage of our tools and provide+ further information (e.g., building documentation).+++2. Installation instructions+============================++2.1 Installing scyther-proof+----------------------------++ You need a working Haskell environment that provides a version of GHC from+ 7.0.x to 7.4.x and the 'cabal install' tool. The simplest way to get such an+ environment is to download and install the Haskell Platform package for your+ OS.++ http://hackage.haskell.org/platform/++ Then call++ cabal install++ in the root directory of this source code package. This will use the+ Haskell's deployment tool `cabal-install` to download all missing libraries+ from Hackage, the central Haskell library repository and install the+ `scyther-proof` executable in the default installation location of+ cabal-install. The installation location is printed at the end of the build+ process.++ Call++ scyther-proof++ without any arguments to get an overview of the arguments supported and the+ paths to the examples and the Isabelle/HOL theories.+++2.2 Installing the Isabelle/HOL theories+----------------------------------------++Download and install full Isabelle2009-1 according to the installation+instructions at++ http://isabelle.in.tum.de/website-Isabelle2009-1/download_x86-linux.html++The first time you call `scyther-proof` with the `--isabelle` flag it will+build the logic image of the Isabelle/HOL theories formalizing the security+protocol verification theory underlying `scyther-proof`.++For enabling interactive processing of theory files based using our+extensions to Isabelle's ISAR proof language use one of the following two+variants.++Variant a: Brute Force+----------------------++Overwrite the standard Emacs keywords file++ $ISABELLE_HOME/etc/isar-keywords.el++with the Emacs keywords file provided with the ESPL distribution.++ $SCYTHER_PROOF_HOME/isabelle/src/isar-keywords.el++This is needed in order to make the new ISAR keywords `role`,+`protocol`, `sources`, and `prefix_close` available in the Emacs+frontend.+++Variant b: Elegance+-------------------++A more elegant way is to copy the keywords file provided with our+distribution to++ $ISABELLE_HOME/etc/isar-keywords-ESPL.el++and start Isabelle using the -k switch as follows.++ isabelle emacs -k ESPL <your-theory-file.thy>++++3. Usage+========+++3.1 scyther-proof+------------+++ Usage Example+ -------------++ You can now execute the 'scyther-proof' binary without any arguments to get a+ list of the available flags. A simple example usage is (assuming+ 'scyther-proof' is in the PATH).++ scyther-proof $EXAMPLE_DIR/classic/NS_Public.spthy --shortest++ This will parse the NSL protocol modeled in the 'nsl.spthy' file and output+ all specified security properties together with the proof with the fewest+ number of chain rule applications to stdout.++ In order to generate an Isabelle proof script two additional options need to+ be added++ scyther-proof $EXAMPLE_DIR/classic/NS_Public.spthy --shortest --output=nsl_cert.thy --ASCII++ The first one specifies the output file and the second one instructs+ scyther-proof to use Isabelle's ASCII notation as the output format.+++ To build the source code documentation you can use the call++ cabal configure; cabal haddock++ which will use cabal-install and Haskell's source code documentation tool+ 'haddock' to build the source code documentation. The location of this+ documentation is again output on the command line.+++3.2 The Isabelle/HOL theories+-----------------------------++ In the directory output by scyther-proof, load the Tutorial on interactive+ proof construction using the following commands++ cd <theory-dir-output-by-scytherproof>/src+ isabelle emacs Tutorial.thy++ or if you've chosen the elegant variant++ isabelle emacs -k ESPL Tutorial.thy++ Note that in many case the easiest way to start with constructing+ machine-checked proofs interactively is to use a proof script generated by+ scyther-proof.+++***++`Happy Proving :)`++In case of questions do not hesistate to contact me at+iridcode@gmail.com.+
data/examples/iso9798/isoiec-9798-2-bdkey.spthy view
@@ -1,4 +1,4 @@-/* +/* * Modeled from ISO/IEC 9798-2 * Modeler: Cas Cremers, Dec. 2010 * Ported to scyther-proof: Simon Meier, Feb. 2011@@ -33,8 +33,10 @@ 1. A -> B: A, B, Text2, {'isoiec_9798_2_1_enc_1', TNA, B, Text1 }k[A,B] } +// Here we dont get any injective agreement, as there could be two B thread+// receiving the same message from one A. property (of isoiec_9798_2_1_bdkey)- B_non_injective_agreement: + B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A, B}) @@ -60,8 +62,8 @@ } property (of isoiec_9798_2_2_bdkey)- B_non_injective_agreement: - niagree(B_2[A,B,RB,Text2] -> A_2[A,B,RB,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,RB,Text2] -> A_2[A,B,RB,Text2], {A, B}) @@ -89,11 +91,15 @@ 2. B -> A: B, A, Text4, {'isoiec_9798_2_3_enc_2', TNB, A, Text3 }k[B,A] } +// NOTE: We do not get injective agreement here, in neither direction. The+// problem is the same as in isoiec_9798_2_1_bdkey. However, we are missing+// the opportunity to strengthen the property for A, as we could just embed+// TNA in the message sent from B. properties (of isoiec_9798_2_3_bdkey)- A_non_injective_agreement: + A_non_injective_agreement: niagree(A_2[A,B,TNB,Text3] -> B_2[A,B,TNB,Text3], {A, B}) - B_non_injective_agreement: + B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A, B}) @@ -123,11 +129,11 @@ } properties (of isoiec_9798_2_4_bdkey)- A_non_injective_agreement: - niagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A, B})+ A_injective_agreement:+ iagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A, B}) - B_non_injective_agreement: - niagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A, B}) /******************************************************************************@@ -179,15 +185,20 @@ // (a) we also verify agreement on the freshness data TNa, TNb // (b) we also verify agreement with the trusted third party //- A_non_injective_agreement_B: - niagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P})+ A_injective_agreement_B:+ iagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P}) - B_non_injective_agreement_A: + // Injective agreement does not hold. See+ // isoiec_9798_2_5_special_TTP_bdkey for more information.+ B_non_injective_agreement_A: niagree(B_3[A,B,P,Kab,TNa,Text5] -> A_3[A,B,P,Kab,TNa,Text5], {A, B, P}) - A_non_injective_agreement_P: - niagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P})- B_non_injective_agreement_P: + A_injective_agreement_P:+ iagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P})++ // Injective agreement does not hold. See+ // isoiec_9798_2_5_special_TTP_bdkey for more information.+ B_non_injective_agreement_P: niagree(B_3[A,B,P,Kab,TNp,Text2] -> P_2[A,B,P,Kab,TNp,Text2], {A, B, P}) @@ -221,12 +232,12 @@ -> A: P, A, Text5, {'isoiec_9798_2_6_enc_3_1', Ra, Kab, A, B, Text4}k[A,P] , TokenPA_for_B - text_4. -> A: Text6, Text7 + text_4. -> A: Text6, Text7 4. A -> : A, B, Text7, P, TokenPA_for_B , {'isoiec_9798_2_6_enc_4', Rpa, Rb, Text6}Kab -> B: A, B, Text7, P, {'isoiec_9798_2_6_enc_3_2', Rb, Kab, A, B, Text3}k[B,P] , {'isoiec_9798_2_6_enc_4', Rpa, Rb, Text6}Kab- + text_5. -> B: Text8, Text9 5. B -> A: B, A, Text9, {'isoiec_9798_2_6_enc_5', Rb, Rpa, Text8}Kab }@@ -237,20 +248,20 @@ B_secret_Kab: secret(B, 4, Kab, {A, B, P}) // Agreement properties strengthened with respect to Cas Cremers' models:- // (a) we also verify agreement on the freshness data + // (a) we also verify agreement on the freshness data // (b) we also verify agreement with the trusted third party //- A_non_injective_agreement_B: - niagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P})+ A_injective_agreement_B:+ iagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P}) - B_non_injective_agreement_A: - niagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P})+ B_injective_agreement_A:+ iagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P}) - A_non_injective_agreement_P: - niagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P})+ A_injective_agreement_P:+ iagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P}) - B_non_injective_agreement_P: - niagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P})+ B_injective_agreement_P:+ iagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P}) /******************************************************************************@@ -310,22 +321,28 @@ // Agreement properties strengthened with respect to Cas Cremers' models: // (a) we also verify agreement on the freshness data TNa, TNb // (b) we also verify agreement with the trusted third party- // - // Note: If no axiom dependency for a property below, then it can be proven- // without the axiom.- - A_non_injective_agreement_B: - niagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P})+ //+ // Note: If no axiom dependency is noted for a property below, then it can+ // be proven without the axiom. - // depends on 'different_actors_A_P'- B_non_injective_agreement_A: + A_injective_agreement_B:+ iagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P})++ // Depends on 'different_actors_A_P'+ //+ // NO injective agreement can be proven. There may be several B-threads+ // communicating with the same A-thread! Checking the 'TNb' timestamp in+ // later steps could be used to remove this problem, up to the clock+ // resolution.+ B_non_injective_agreement_A: niagree(B_3[A,B,P,Kab,TNa,Text5] -> A_3[A,B,P,Kab,TNa,Text5], {A, B, P}) // depends on 'different_actors_A_P'- A_non_injective_agreement_P: - niagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P})+ A_injective_agreement_P:+ iagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P}) - B_non_injective_agreement_P: + // No injective agreement, as the TTP does not receive any message from 'B'.+ B_non_injective_agreement_P: niagree(B_3[A,B,P,Kab,TNp,Text2] -> P_2[A,B,P,Kab,TNp,Text2], {A, B, P}) @@ -360,12 +377,12 @@ -> A: P, A, Text5, {'isoiec_9798_2_6_special_TTP_enc_3_1', Ra, Kab, B, Text4}k[A,P] , TokenPA_for_B - text_4. -> A: Text6, Text7 + text_4. -> A: Text6, Text7 4. A -> : A, B, Text7, P, TokenPA_for_B , {'isoiec_9798_2_6_special_TTP_enc_4', Rpa, Rb, Text6}Kab -> B: A, B, Text7, P, {'isoiec_9798_2_6_special_TTP_enc_3_2', Rb, Kab, A, Text3}k[B,P] , {'isoiec_9798_2_6_special_TTP_enc_4', Rpa, Rb, Text6}Kab- + text_5. -> B: Text8, Text9 5. B -> A: B, A, Text9, {'isoiec_9798_2_6_special_TTP_enc_5', Rb, Rpa, Text8}Kab }@@ -392,26 +409,26 @@ B_secret_Kab: secret(B, 4, Kab, {A, B, P}) // Agreement properties strengthened with respect to Cas Cremers' models:- // (a) we also verify agreement on the freshness data + // (a) we also verify agreement on the freshness data // (b) we also verify agreement with the trusted third party- // - // Note: If no axiom dependency for a property below, then it can be proven- // without the axiom.+ //+ // Note: If no axiom dependency is noted for a property below, then it can+ // be proven without the axiom. // depends on 'different_actors_B_P' and 'different_actors_A_P'- A_non_injective_agreement_B: - niagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P})+ A_injective_agreement_B:+ iagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P}) // depends on 'different_actors_B_P' and 'different_actors_A_P'- B_non_injective_agreement_A: - niagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P})+ B_injective_agreement_A:+ iagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P}) // depends on 'different_actors_A_P- A_non_injective_agreement_P: - niagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P})+ A_injective_agreement_P:+ iagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P}) // depends on 'different_actors_B_P- B_non_injective_agreement_P: - niagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P})+ B_injective_agreement_P:+ iagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P}) end
data/examples/iso9798/isoiec-9798-2-udkey.spthy view
@@ -31,6 +31,8 @@ 1. A -> B: A, B, Text2, {'isoiec_9798_2_1_enc_1', TNA, Text1 }k(A,B) } +// Here we dont get any injective agreement, as there could be two B thread+// receiving the same message from one A. property (of isoiec_9798_2_1_udkey) B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A, B})@@ -56,8 +58,8 @@ } property (of isoiec_9798_2_2_udkey)- B_non_injective_agreement: - niagree(B_2[A,B,RB,Text2] -> A_2[A,B,RB,Text2], {A, B})+ B_injective_agreement: + iagree(B_2[A,B,RB,Text2] -> A_2[A,B,RB,Text2], {A, B}) @@ -83,6 +85,10 @@ 2. B -> A: B, A, Text4, {'isoiec_9798_2_3_enc_2', TNB, Text3 }k(B,A) } +// NOTE: We do not get injective agreement here, in neither direction. The+// problem is the same as in isoiec_9798_2_1_bdkey. However, we are missing+// the opportunity to strengthen the property for A, as we could just embed+// TNA in the message sent from B. properties (of isoiec_9798_2_3_udkey) A_non_injective_agreement: niagree(A_2[A,B,TNB,Text3] -> B_2[A,B,TNB,Text3], {A, B})@@ -118,11 +124,11 @@ } properties (of isoiec_9798_2_4_udkey)- A_non_injective_agreement: - niagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A, B})+ A_injective_agreement: + iagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A, B}) - B_non_injective_agreement: - niagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A, B})+ B_injective_agreement: + iagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A, B}) /******************************************************************************@@ -173,14 +179,22 @@ // (a) we also verify agreement on the freshness data TNa, TNb // (b) we also verify agreement with the trusted third party //- A_non_injective_agreement_B: - niagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P})+ A_injective_agreement_B: + iagree(A_4[A,B,P,Kab,TNa,Text5,TNb,Text7] -> B_4[A,B,P,Kab,TNa,Text5,TNb,Text7], {A, B, P}) + // Injectivity agreement does not hold. See+ //+ // NO injective agreement can be proven. There may be several B-threads+ // communicating with the same A-thread! Checking the 'TNb' timestamp in+ // later steps could be used to remove this problem, up to the clock+ // resolution. B_non_injective_agreement_A: niagree(B_3[A,B,P,Kab,TNa,Text5] -> A_3[A,B,P,Kab,TNa,Text5], {A, B, P}) - A_non_injective_agreement_P: - niagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P})+ A_injective_agreement_P: + iagree(A_2[A,B,P,Kab,TVPa,Text3] -> P_2[A,B,P,Kab,TVPa,Text3], {A, B, P})++ // No injective agreement, as the TTP does not receive any message from 'B'. B_non_injective_agreement_P: niagree(B_3[A,B,P,Kab,TNp,Text2] -> P_2[A,B,P,Kab,TNp,Text2], {A, B, P}) @@ -233,16 +247,16 @@ // (a) we also verify agreement on the freshness data // (b) we also verify agreement with the trusted third party //- A_non_injective_agreement_B: - niagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P})+ A_injective_agreement_B: + iagree(A_5[A,B,P,Kab,Rpa,Rb,Text6,Text8] -> B_5[A,B,P,Kab,Rpa,Rb,Text6,Text8], {A, B, P}) - B_non_injective_agreement_A: - niagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P})+ B_injective_agreement_A: + iagree(B_4[A,B,P,Kab,Rpa,Rb,Text6] -> A_4[A,B,P,Kab,Rpa,Rb,Text6], {A, B, P}) - A_non_injective_agreement_P: - niagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P})+ A_injective_agreement_P: + iagree(A_3[A,B,P,Ra,Kab,Text4] -> P_3[A,B,P,Ra,Kab,Text4], {A, B, P}) - B_non_injective_agreement_P: - niagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P})+ B_injective_agreement_P: + iagree(B_4[A,B,P,Rb,Kab,Text3] -> P_3[A,B,P,Rb,Kab,Text3], {A, B, P}) end
data/examples/iso9798/isoiec-9798-3.spthy view
@@ -8,7 +8,7 @@ * - time-variant-parameters, time-stamps, and sequence numbers are * modeled by nonces leaked to the adversary at the start of a role. * - Given some pattern 'pkA', then the signature pattern 'sign{m}pkA' is- * translated to the message '(m, {m}inv(pkA))' upon execution. + * translated to the message '(m, {m}inv(pkA))' upon execution. */ theory isoiec_9798_3 begin @@ -30,12 +30,13 @@ leak_A. A -> : TNA text_1. -> A: Text1, Text2- 1. A -> B: A, B, Text2, + 1. A -> B: A, B, Text2, sign{'isoiec_9798_3_1_sig_1', TNA, B, Text1 }pk(A) } +// No injective agreement here. properties (of isoiec_9798_3_1)- B_non_injective_agreement: + B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A}) @@ -56,14 +57,13 @@ 1. B -> A: B, A, Rb, Text1 text_2. -> A: Text2, Text3- 2. A -> B: A, B, Text3, + 2. A -> B: A, B, Text3, sign{'isoiec_9798_3_2_sig_2', Ra, Rb, B, Text2 }pk(A) } properties (of isoiec_9798_3_2)- // Note: for this agreement property the identity B must not be dropped- B_non_injective_agreement: - niagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A})+ B_injective_agreement:+ iagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A}) /****************************************************************************@@ -83,21 +83,24 @@ { leak_A. A -> : TNA leak_B. B -> : TNB- + text_1. -> A: Text1, Text2- 1. A -> B: A, B, Text2, + 1. A -> B: A, B, Text2, sign{'isoiec_9798_3_3_sig_1', TNA, B, Text1 }pk(A) text_2. -> B: Text3, Text4- 2. B -> A: A, B, Text4, + 2. B -> A: A, B, Text4, sign{'isoiec_9798_3_3_sig_2', TNB, A, Text3 }pk(B) } +// No injective agreement. It requires a forth and back message, which does+// not exist for role B. For role 'A', we miss an opportunity for injective+// authentication, as we could include 'TNA' in the second message exchange. properties (of isoiec_9798_3_3)- A_non_injective_agreement: + A_non_injective_agreement: niagree(A_2[A,B,TNB,Text3] -> B_2[A,B,TNB,Text3], {B}) - B_non_injective_agreement: + B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A}) @@ -118,22 +121,22 @@ 1. B -> A: B, A, RB, Text1 text_2. -> A: Text2, Text3- 2. A -> B: A, B, Text3, + 2. A -> B: A, B, Text3, sign{'isoiec_9798_3_4_sig_1', RA, RB, B, Text2 }pk(A) text_3. -> B: Text4, Text5- 3. B -> A: B, A, Text5, + 3. B -> A: B, A, Text5, sign{'isoiec_9798_3_4_sig_2', RB, RA, A, Text4 }pk(B) } properties (of isoiec_9798_3_4) // The identity A in Step 3 is required for this property to hold.- A_non_injective_agreement: - niagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A,B})+ A_injective_agreement:+ iagree(A_3[A,B,RA,RB,Text2,Text4] -> B_3[A,B,RA,RB,Text2,Text4], {A,B}) // The identity B in Step 2 is required for this property to hold.- B_non_injective_agreement: - niagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A})+ B_injective_agreement:+ iagree(B_2[A,B,RA,RB,Text2] -> A_2[A,B,RA,RB,Text2], {A}) /****************************************************************************@@ -158,22 +161,22 @@ 2. B -> A: B, A, RB, Text2 text_3. -> B: Text5, Text6- 3. B -> A: B, A, Text6, + 3. B -> A: B, A, Text6, sign{'isoiec_9798_3_5_sig_1', RB, RA, A, Text5 }pk(B) text_4. -> A: Text3, Text4- 4. A -> B: A, B, Text4, + 4. A -> B: A, B, Text4, sign{'isoiec_9798_3_5_sig_2', RA, RB, B, Text3 }pk(A) } properties (of isoiec_9798_3_5) // The identity A in Step 3 is required for this property to hold.- A_non_injective_agreement: - niagree(A_3[A,B,RA,RB,Text5] -> B_3[A,B,RA,RB,Text5], {B})+ A_injective_agreement:+ iagree(A_3[A,B,RA,RB,Text5] -> B_3[A,B,RA,RB,Text5], {B}) // The identity B in Step 4 is required for this property to hold.- B_non_injective_agreement: - niagree(B_4[A,B,RA,RB,Text3,Text5] -> A_4[A,B,RA,RB,Text3,Text5], {A,B})+ B_injective_agreement:+ iagree(B_4[A,B,RA,RB,Text3,Text5] -> A_4[A,B,RA,RB,Text3,Text5], {A,B}) /****************************************************************************@@ -195,13 +198,13 @@ protocol isoiec_9798_3_6_1 { text_1. -> A: Text1- 1. A -> B: A, B, Ra, Text1 - + 1. A -> B: A, B, Ra, Text1+ text_2. -> B: Text2, Text3 2. B -> : A, B, Ra, Rb, Text3, sign{'isoiec_9798_3_6_opt_1_sig_2', B, Ra, Rb, A, Text2}pk(B) - -> A: A, B, Ra, Rb, Text3, + -> A: A, B, Ra, Rb, Text3, TokenBA text_3. -> A: Text4@@ -209,57 +212,52 @@ text_4. -> T: Text5, Text6, Text7 4. T -> : T, A, Text7, A, pk(A), B, pk(B),- sign{'isoiec_9798_3_6_opt_1_sig_4_1', Rpa, B, pk(B), Text6}pk(T), + sign{'isoiec_9798_3_6_opt_1_sig_4_1', Rpa, B, pk(B), Text6}pk(T), sign{'isoiec_9798_3_6_opt_1_sig_4_2', Rb, A, pk(A), Text5}pk(T) -> A: T, A, Text7, A, pk(A), B, pkB,- sign{'isoiec_9798_3_6_opt_1_sig_4_1', Rpa, B, pkB, Text6}pk(T), + sign{'isoiec_9798_3_6_opt_1_sig_4_1', Rpa, B, pkB, Text6}pk(T), TokenTA_for_B // Here, A checks TokenBA against the public key pkB received from T. // As we lack support for additionally checked equalities, A sends a message // to itself encrypted with a fresh nonce to emulate the equality check.- check_4_out. A -> : {'check_4', + check_4_out. A -> : {'check_4', TokenBA }check_nonce_4- check_4_in. -> A: {'check_4', - sign{'isoiec_9798_3_6_opt_1_sig_2', B, Ra, Rb, A, Text2}pkB + check_4_in. -> A: {'check_4',+ sign{'isoiec_9798_3_6_opt_1_sig_2', B, Ra, Rb, A, Text2}pkB }check_nonce_4 // Note: we additionally send T such that B knows which key to use for // checking TokenTA_for_B text_5. -> A: Text8, Text9 5. A -> : A, B, Text9, T,- TokenTA_for_B, + TokenTA_for_B, sign{'isoiec_9798_3_6_opt_1_sig_5', Rb, Ra, B, A, Text8}pk(A) -> B: A, B, Text9, T, sign{'isoiec_9798_3_6_opt_1_sig_4_2', Rb, A, pkA, Text5}pk(T),- // Note: the standard does not require to check that 'Ra5 = Ra'!- sign{'isoiec_9798_3_6_opt_1_sig_5', Rb, Ra5, B, A, Text8}pkA+ sign{'isoiec_9798_3_6_opt_1_sig_5', Rb, Ra, B, A, Text8}pkA } +// All properties except hold injectively. NOTE: Investigate this+// authentication "flaw" between Ra5 and Ra in role 'B'. properties (of isoiec_9798_3_6_1)- A_non_injective_agreement: - niagree(A_5[A,B,Ra,Rb,Text2] -> B_2[A,B,Ra,Rb,Text2], {B, T})+ A_injective_agreement:+ iagree(A_5[A,B,Ra,Rb,Text2] -> B_2[A,B,Ra,Rb,Text2], {B, T}) - // Possible authentication FLAW:- // We do NOT get agreement on Ra because the standard does not require B to- // check its occurence in the signature from Step 5 to be equal with the- // one in Step 2.- //- // FIXME: What are the consequences?- B_non_injective_agreement: - niagree(B_5[A,B,Ra5,Rb,Text8] -> A_5[A,B,Ra,Rb,Text8], {A, T})+ B_injective_agreement:+ iagree(B_5[A,B,Ra,Rb,Text8] -> A_5[A,B,Ra,Rb,Text8], {A, T}) // We additionally verify agreement of A and B with the TTP T.- A_non_injective_agreement_T: - niagree(A_5[B,T,Rpa,pkB ,Text6] -> - T_4[B,T,Rpa,pk(B),Text6], {T})+ A_injective_agreement_T:+ iagree(A_5[B,T,Rpa,pkB ,Text6] ->+ T_4[B,T,Rpa,pk(B),Text6], {T}) - B_non_injective_agreement_T: - niagree(B_5[A,T,Rb,pkA ,Text5] -> - T_4[A,T,Rb,pk(A),Text5], {T})+ B_injective_agreement_T:+ iagree(B_5[A,T,Rb,pkA ,Text5] ->+ T_4[A,T,Rb,pk(A),Text5], {T}) @@ -278,33 +276,30 @@ * - Option 1 and Option 2 must not share tags! * - We model the case where T sends the public keys instead of just verifying * the certificates; i.e., 'ResA = (A, pk(A))' and 'ResB = (B, pk(B))'.-- FIXME: Compare equality checks done here to what is prescribed in the- standard. We probably do a few more => there may be attacks on the- standard.-- We probably also shouldn't check the public keys delivered by T.-+ * - Our model might perform a few equality checks more than what is strictly+ * required by the standard. All of them are practically feasible and we+ * advise to implement them. We do not verify whether we the security+ * properties also hold with fewer equality checks. */ protocol isoiec_9798_3_6_2 { text_1. -> A: Text1- 1. A -> B: A, B, Ra, Text1 - + 1. A -> B: A, B, Ra, Text1+ text_2. -> B: Text2, Text3 2. B -> : A, B, Ra, Rb, Text3, sign{'isoiec_9798_3_6_opt_2_sig_2', B, Ra, Rb, A, Text2}pk(B) - -> A: A, B, Ra, Rb, Text3, + -> A: A, B, Ra, Rb, Text3, TokenBA text_3. -> A: Text4 3. A -> T: A, T, Rpa, Rb, B, Text4 // Note: Text6 is not used in Option 2- text_4. -> T: Text5, Text7 + text_4. -> T: Text5, Text7 4. T -> : T, A, Text7, A, pk(A), B, pk(B),- sign{ 'isoiec_9798_3_6_opt_2_sig_4', + sign{ 'isoiec_9798_3_6_opt_2_sig_4', Rpa, Rb, A, pk(A), B, pk(B), Text5 }pk(T) -> A: T, A, Text7, A, pk(A), B, pkB, TokenTA@@ -313,12 +308,12 @@ // key received from T. As we lack support for additionally checked // equalities, A sends a message to itself encrypted with a fresh nonce to // emulate the equality check.- check_4_out. A -> : {'check_4', + check_4_out. A -> : {'check_4', TokenTA, TokenBA }check_nonce_4- check_4_in. -> A: {'check_4', - sign{'isoiec_9798_3_6_opt_2_sig_4', + check_4_in. -> A: {'check_4',+ sign{'isoiec_9798_3_6_opt_2_sig_4', Rpa, Rb, A, pkA, B, pkB, Text5 }pk(T), sign{'isoiec_9798_3_6_opt_2_sig_2', B, Ra, Rb, A, Text2}pkB }check_nonce_4@@ -326,39 +321,32 @@ // Note: we additionally send T such that B knows which key to use for // checking TokenTA text_5. -> A: Text8, Text9- 5. A -> : A, B, Rpa, Text9, T, + 5. A -> : A, B, Rpa, Text9, T, TokenTA, sign{'isoiec_9798_3_6_opt_2_sig_5', Rb, Ra, B, A, Text8}pk(A) -> B: A, B, Rpa, Text9, T,- sign{'isoiec_9798_3_6_opt_2_sig_4', - Rpa, Rb, A, pkA, B, pkB, Text5 }pk(T), - // Note: the standard does not require to check that 'Ra5 = Ra'!- sign{'isoiec_9798_3_6_opt_2_sig_5', Rb, Ra5, B, A, Text8}pkA+ sign{'isoiec_9798_3_6_opt_2_sig_4',+ Rpa, Rb, A, pkA, B, pkB, Text5 }pk(T),+ sign{'isoiec_9798_3_6_opt_2_sig_5', Rb, Ra, B, A, Text8}pkA } properties (of isoiec_9798_3_6_2) // Note that we do not get agreement on T!- A_non_injective_agreement: - niagree(A_5[A,B,Ra,Rb,Text2] -> B_2[A,B,Ra,Rb,Text2], {B, T})+ A_injective_agreement:+ iagree(A_5[A,B,Ra,Rb,Text2] -> B_2[A,B,Ra,Rb,Text2], {B, T}) - // Possible authentication FLAW:- // We do NOT get agreement on Ra because the standard does not require B to- // check its occurence in the signature from Step 5 to be equal with the- // one in Step 2.- //- // FIXME: What are the consequences?- B_non_injective_agreement: - niagree(B_5[A,B,Ra5,Rb,Text8] -> A_5[A,B,Ra,Rb,Text8], {A, T})+ B_injective_agreement:+ iagree(B_5[A,B,Ra,Rb,Text8] -> A_5[A,B,Ra,Rb,Text8], {A, T}) // We additionally verify agreement of A and B with the TTP T- A_non_injective_agreement_T: - niagree(A_5[A,B,T,Rpa,Rb,pkA ,pkB ,Text5] -> - T_4[A,B,T,Rpa,Rb,pk(A),pk(B),Text5], {T})+ A_injective_agreement_T:+ iagree(A_5[A,B,T,Rpa,Rb,pkA ,pkB ,Text5] ->+ T_4[A,B,T,Rpa,Rb,pk(A),pk(B),Text5], {T}) - B_non_injective_agreement_T: - niagree(B_5[A,B,T,Rpa,Rb,pkA ,pkB ,Text5] -> - T_4[A,B,T,Rpa,Rb,pk(A),pk(B),Text5], {T})+ B_injective_agreement_T:+ iagree(B_5[A,B,T,Rpa,Rb,pkA ,pkB ,Text5] ->+ T_4[A,B,T,Rpa,Rb,pk(A),pk(B),Text5], {T}) @@ -377,21 +365,20 @@ * - Option 1 and Option 2 must not share tags! * - We model the case where T sends the public keys instead of just verifying * the certificates; i.e., 'ResA = (A, pk(A))' and 'ResB = (B, pk(B))'.-- FIXME: Compare equality checks done here to what is prescribed in the- standard. We probably do a few more => there may be attacks on the- standard.-+ * - Our model might perform a few equality checks more than what is strictly+ * required by the standard. All of them are practically feasible and we+ * advise to implement them. We do not verify whether we the security+ * properties also hold with fewer equality checks. */ protocol isoiec_9798_3_7_1 { text_1. -> B: Text1- 1. B -> A: B, A, Rb, Text1 - + 1. B -> A: B, A, Rb, Text1+ text_2. -> A: Text2- 2. A -> T: A, T, Rpa, Rb, A, B, Text2 + 2. A -> T: A, T, Rpa, Rb, A, B, Text2 - text_3. -> T: Text3, Text4, Text5 + text_3. -> T: Text3, Text4, Text5 3. T -> : T, A, Text5, A, pk(A), B, pk(B), sign{ 'isoiec_9798_3_7_opt_1_sig_3_1', Rpa, B, pk(B), Text4 }pk(T), sign{ 'isoiec_9798_3_7_opt_1_sig_3_2', Rb, A, pk(A), Text3 }pk(T)@@ -402,36 +389,36 @@ // Note: we additionally send T such that B knows which key to use for // checking TokenTA text_4. -> A: Text6, Text7- 4. A -> : A, B, Rpa, Text7, T, + 4. A -> : A, B, Rpa, Text7, T, TokenTA_for_B, sign{'isoiec_9798_3_7_opt_1_sig_4', Rb, Ra, B, A, Text6}pk(A) -> B: A, B, Rpa, Text9, T,- sign{'isoiec_9798_3_7_opt_1_sig_3_2', Rb, A, pkA, Text3}pk(T), + sign{'isoiec_9798_3_7_opt_1_sig_3_2', Rb, A, pkA, Text3}pk(T), sign{'isoiec_9798_3_7_opt_1_sig_4', Rb, Ra, B, A, Text6}pkA text_5. -> B: Text8, Text9 5. B -> : A, B, Ra, Rb, Text9, sign{'isoiec_9798_3_7_opt_1_sig_5', Ra, Rb, A, B, Text8}pk(B) - -> A: A, B, Ra, Rb, Text9, + -> A: A, B, Ra, Rb, Text9, sign{'isoiec_9798_3_7_opt_1_sig_5', Ra, Rb, A, B, Text8}pkB } properties (of isoiec_9798_3_7_1) // Note that we do not get agreement on T!- A_non_injective_agreement: - niagree(A_5[A,B,Ra,Rb,Text8] -> B_5[A,B,Ra,Rb,Text8], {B, T})+ A_injective_agreement:+ iagree(A_5[A,B,Ra,Rb,Text8] -> B_5[A,B,Ra,Rb,Text8], {B, T}) - B_non_injective_agreement: - niagree(B_4[A,B,Ra,Rb,Text6] -> A_4[A,B,Ra,Rb,Text6], {A, T})+ B_injective_agreement:+ iagree(B_4[A,B,Ra,Rb,Text6] -> A_4[A,B,Ra,Rb,Text6], {A, T}) // We additionally verify agreement of A and B with the TTP T- A_non_injective_agreement_T: - niagree(A_3[B,T,Rpa,pkB,Text4] -> T_3[B,T,Rpa,pk(B),Text4], {T})- B_non_injective_agreement_T: - niagree(B_4[A,T,Rb,pkA,Text3] -> T_3[A,T,Rb,pk(A),Text3], {T})+ A_injective_agreement_T:+ iagree(A_3[B,T,Rpa,pkB,Text4] -> T_3[B,T,Rpa,pk(B),Text4], {T})+ B_injective_agreement_T:+ iagree(B_4[A,T,Rb,pkA,Text3] -> T_3[A,T,Rb,pk(A),Text3], {T}) @@ -450,24 +437,23 @@ * - Option 1 and Option 2 must not share tags! * - We model the case where T sends the public keys instead of just verifying * the certificates; i.e., 'ResA = (A, pk(A))' and 'ResB = (B, pk(B))'.-- FIXME: Compare equality checks done here to what is prescribed in the- standard. We probably do a few more => there may be attacks on the- standard.-+ * - Our model might perform a few equality checks more than what is strictly+ * required by the standard. All of them are practically feasible and we+ * advise to implement them. We do not verify whether we the security+ * properties also hold with fewer equality checks. */ protocol isoiec_9798_3_7_2 { text_1. -> B: Text1- 1. B -> A: B, A, Rb, Text1 - + 1. B -> A: B, A, Rb, Text1+ text_2. -> A: Text2- 2. A -> T: A, T, Rpa, Rb, A, B, Text2 + 2. A -> T: A, T, Rpa, Rb, A, B, Text2 // Note: Text4 is not used in Option 2- text_3. -> T: Text3, Text5 + text_3. -> T: Text3, Text5 3. T -> : T, A, Text5, A, pk(A), B, pk(B),- sign{ 'isoiec_9798_3_7_opt_2_sig_3', + sign{ 'isoiec_9798_3_7_opt_2_sig_3', Rpa, Rb, A, pk(A), B, pk(B), Text3 }pk(T) -> A: T, A, Text5, A, pk(A), B, pkB, TokenTA@@ -475,50 +461,50 @@ // Here, A checks TokenTA. As we lack support for additionally checked // equalities, A sends a message to itself encrypted with a fresh nonce to // emulate the equality check.- check_3_out. A -> : {'check_4', + check_3_out. A -> : {'check_4', TokenTA }check_nonce_4- check_3_in. -> A: {'check_4', - sign{'isoiec_9798_3_7_opt_2_sig_3', + check_3_in. -> A: {'check_4',+ sign{'isoiec_9798_3_7_opt_2_sig_3', Rpa, Rb, A, pkA, B, pkB, Text3 }pk(T) }check_nonce_4 // Note: we additionally send T such that B knows which key to use for // checking TokenTA text_4. -> A: Text6, Text7- 4. A -> : A, B, Rpa, Text7, T, + 4. A -> : A, B, Rpa, Text7, T, TokenTA, sign{'isoiec_9798_3_7_opt_2_sig_4', Rb, Ra, B, A, Text6}pk(A) -> B: A, B, Rpa, Text9, T,- sign{'isoiec_9798_3_7_opt_2_sig_3', - Rpa, Rb, A, pkA, B, pkB, Text3 }pk(T), + sign{'isoiec_9798_3_7_opt_2_sig_3',+ Rpa, Rb, A, pkA, B, pkB, Text3 }pk(T), sign{'isoiec_9798_3_7_opt_2_sig_4', Rb, Ra, B, A, Text6}pkA text_5. -> B: Text8, Text9 5. B -> : A, B, Ra, Rb, Text9, sign{'isoiec_9798_3_7_opt_2_sig_5', Ra, Rb, A, B, Text8}pk(B) - -> A: A, B, Ra, Rb, Text9, + -> A: A, B, Ra, Rb, Text9, sign{'isoiec_9798_3_7_opt_2_sig_5', Ra, Rb, A, B, Text8}pkB } properties (of isoiec_9798_3_7_2)- A_non_injective_agreement: - niagree(A_5[A,B,Ra,Rb,Text8] -> B_5[A,B,Ra,Rb,Text8], {B, T})+ A_injective_agreement:+ iagree(A_5[A,B,Ra,Rb,Text8] -> B_5[A,B,Ra,Rb,Text8], {B, T}) - B_non_injective_agreement: - niagree(B_4[A,B,Ra,Rb,Text6] -> A_4[A,B,Ra,Rb,Text6], {A, T})+ B_injective_agreement:+ iagree(B_4[A,B,Ra,Rb,Text6] -> A_4[A,B,Ra,Rb,Text6], {A, T}) // We additionally verify agreement of A and B with the TTP T- A_non_injective_agreement_T: - niagree(A_4[A,B,T,Rpa,Rb,pkA ,pkB ,Text3] -> - T_3[A,B,T,Rpa,Rb,pk(A),pk(B),Text3], {T})+ A_injective_agreement_T:+ iagree(A_4[A,B,T,Rpa,Rb,pkA ,pkB ,Text3] ->+ T_3[A,B,T,Rpa,Rb,pk(A),pk(B),Text3], {T}) - B_non_injective_agreement_T: - niagree(B_4[A,B,T,Rpa,Rb,pkA ,pkB ,Text3] -> - T_3[A,B,T,Rpa,Rb,pk(A),pk(B),Text3], {T})+ B_injective_agreement_T:+ iagree(B_4[A,B,T,Rpa,Rb,pkA ,pkB ,Text3] ->+ T_3[A,B,T,Rpa,Rb,pk(A),pk(B),Text3], {T})
data/examples/iso9798/isoiec-9798-4-bdkey.spthy view
@@ -31,6 +31,7 @@ h(('CCF', k[A,B]), ('isoiec_9798_4_1_ccf_1', TNA, B, Text1)) } +// Does not hold injectively. properties (of isoiec_9798_4_1_bdkey) B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A, B})@@ -55,8 +56,8 @@ } properties (of isoiec_9798_4_2_bdkey)- B_non_injective_agreement:- niagree(B_2[A,B,Rb,Text2] -> A_2[A,B,Rb,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,Rb,Text2] -> A_2[A,B,Rb,Text2], {A, B}) /******************************************************************************@@ -81,6 +82,8 @@ h(('CCF', k[A,B]), ('isoiec_9798_4_3_ccf_2', TNb, A, Text3)) } +// Does not hold injectively. Missing an opportunity to get injective+// agreement for role A by adding TNa in the second message. properties (of isoiec_9798_4_3_bdkey) A_non_injective_agreement: niagree(A_2[A,B,TNb,Text3] -> B_2[A,B,TNb,Text3], {A, B})@@ -112,11 +115,11 @@ } properties (of isoiec_9798_4_4_bdkey)- A_non_injective_agreement:- niagree(A_3[A,B,Ra,Rb,Text2,Text4] -> B_3[A,B,Ra,Rb,Text2,Text4], {A, B})+ A_injective_agreement:+ iagree(A_3[A,B,Ra,Rb,Text2,Text4] -> B_3[A,B,Ra,Rb,Text2,Text4], {A, B}) - B_non_injective_agreement:- niagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A, B}) end
data/examples/iso9798/isoiec-9798-4-udkey.spthy view
@@ -4,7 +4,7 @@ * Ported to scyther-proof: Simon Meier, Feb. 2011. * * Modeling notes:- * - Variant of ISO/IEC 9798-4 with unidirectional keys k(A,B) and + * - Variant of ISO/IEC 9798-4 with unidirectional keys k(A,B) and * identities dropped if possible. * - time-variant-parameters, time-stamps, and sequence numbers are * modeled by nonces leaked to the adversary at the start of a role.@@ -27,15 +27,18 @@ leak_A. A -> : TNA text_1. -> A: Text1, Text2- 1. A -> B: A, B, TNA, Text2, Text1, + 1. A -> B: A, B, TNA, Text2, Text1, h(('CCF', k(A,B)), ('isoiec_9798_4_1_ccf_1', TNA, Text1)) } +// No injective agreement here, as the message from A can be received by+// multiple 'B's. properties (of isoiec_9798_4_1_udkey) B_non_injective_agreement: niagree(B_1[A,B,TNA,Text1] -> A_1[A,B,TNA,Text1], {A, B}) + /****************************************************************************** * Protocol 2 ******************************************************************************@@ -50,13 +53,13 @@ 1. B -> A: B, A, Rb, Text1 text_2. -> A: Text2, Text3- 2. A -> B: A, B, Text3, Rb, Text2, + 2. A -> B: A, B, Text3, Rb, Text2, h(('CCF', k(A,B)), ('isoiec_9798_4_2_ccf_2', Rb, Text2)) } properties (of isoiec_9798_4_2_udkey)- B_non_injective_agreement:- niagree(B_2[A,B,Rb,Text2] -> A_2[A,B,Rb,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,Rb,Text2] -> A_2[A,B,Rb,Text2], {A, B}) /******************************************************************************@@ -73,14 +76,17 @@ leak_B. B -> : TNb text_1. -> A: Text1, Text2- 1. A -> B: A, B, TNa, Text2, Text1, + 1. A -> B: A, B, TNa, Text2, Text1, h(('CCF', k(A,B)), ('isoiec_9798_4_3_ccf_1', TNa, Text1)) text_2. -> B: Text3, Text4- 2. B -> A: B, A, TNb, Text4, Text3, + 2. B -> A: B, A, TNb, Text4, Text3, h(('CCF', k(A,B)), ('isoiec_9798_4_3_ccf_2', TNb, Text3)) } +// The following properties do not hold injectively. This protocols misses a+// simple opportunity to get injective agreement for role A by adding TNa in+// the second message. properties (of isoiec_9798_4_3_udkey) A_non_injective_agreement: niagree(A_2[A,B,TNb,Text3] -> B_2[A,B,TNb,Text3], {A, B})@@ -103,7 +109,7 @@ 1. B -> A: B, A, Rb, Text1 text_2. -> A: Text2, Text3- 2. A -> B: A, B, Ra, Text3, Text2, + 2. A -> B: A, B, Ra, Text3, Text2, h(('CCF', k(A,B)), ('isoiec_9798_4_4_ccf_2', Ra, Rb, Text2)) text_3. -> B: Text4, Text5@@ -112,10 +118,10 @@ } properties (of isoiec_9798_4_4_udkey)- A_non_injective_agreement:- niagree(A_3[A,B,Ra,Rb,Text2,Text4] -> B_3[A,B,Ra,Rb,Text2,Text4], {A, B})+ A_injective_agreement:+ iagree(A_3[A,B,Ra,Rb,Text2,Text4] -> B_3[A,B,Ra,Rb,Text2,Text4], {A, B}) - B_non_injective_agreement:- niagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A, B})+ B_injective_agreement:+ iagree(B_2[A,B,Ra,Rb,Text2] -> A_2[A,B,Ra,Rb,Text2], {A, B}) end
data/isabelle/README view
@@ -1,11 +1,9 @@-======================================================================-README for the scyther-proof security protocol verification tool-======================================================================+README: The scyther-proof security protocol verification tool+============================================================= Author: Simon Meier <simon.meier@inf.ethz.ch> -Creation Date: 13/05/2011-Updated: 02/04/2012+Creation Date: 13/05/2011 -- Last Updated: 28/06/2012 1. Introduction@@ -16,7 +14,7 @@ protocol models that were verified using scyther-proof. In particular, these examples include models for all of our repaired versions of the protocols from the ISO/IEC-9798 authentication standard. (In the source distribution- of scyther-proof they can be found at 'data/examples/iso9798'.)+ of scyther-proof they can be found at `data/examples/iso9798`.) In the following sections, we give step by step instructions to get the whole system working and we explain the usage of our tools and provide@@ -32,75 +30,75 @@ You need a working Haskell environment that provides a version of GHC from 7.0.x to 7.4.x and the 'cabal install' tool. The simplest way to get such an environment is to download and install the Haskell Platform package for your- OS. - + OS.+ http://hackage.haskell.org/platform/- + Then call cabal install in the root directory of this source code package. This will use the- Haskell's deployment tool 'cabal-install' to download all missing libraries+ Haskell's deployment tool `cabal-install` to download all missing libraries from Hackage, the central Haskell library repository and install the- 'scyther-proof' executable in the default installation location of+ `scyther-proof` executable in the default installation location of cabal-install. The installation location is printed at the end of the build process. - Call - + Call+ scyther-proof without any arguments to get an overview of the arguments supported and the paths to the examples and the Isabelle/HOL theories. -3.2 Installing the Isabelle/HOL theories+2.2 Installing the Isabelle/HOL theories ---------------------------------------- - Download and install full Isabelle2009-1 according to the installation- instructions at+Download and install full Isabelle2009-1 according to the installation+instructions at - http://isabelle.in.tum.de/website-Isabelle2009-1/download_x86-linux.html+ http://isabelle.in.tum.de/website-Isabelle2009-1/download_x86-linux.html - The first time you call 'scyther-proof' with the '--isabelle' flag it will- build the logic image of the Isabelle/HOL theories formalizing the security- protocol verification theory underlying scyther-proof.+The first time you call `scyther-proof` with the `--isabelle` flag it will+build the logic image of the Isabelle/HOL theories formalizing the security+protocol verification theory underlying `scyther-proof`. - For enabling interactive processing of theory files based using our- extensions to Isabelle's ISAR proof language use one of the following two- variants.+For enabling interactive processing of theory files based using our+extensions to Isabelle's ISAR proof language use one of the following two+variants. - Variant a: Brute Force- ----------------------+Variant a: Brute Force+---------------------- - Overwrite the standard Emacs keywords file- - $ISABELLE_HOME/etc/isar-keywords.el- - with the Emacs keywords file provided with the ESPL distribution.- - $SCYTHER_PROOF_HOME/isabelle/src/isar-keywords.el+Overwrite the standard Emacs keywords file - This is needed in order to make the new ISAR keywords "role",- "protocol", "sources", and "prefix_close" available in the Emacs- frontend.+ $ISABELLE_HOME/etc/isar-keywords.el +with the Emacs keywords file provided with the ESPL distribution. - Variant b: Elegance- -------------------+ $SCYTHER_PROOF_HOME/isabelle/src/isar-keywords.el - A more elegant way is to copy the keywords file provided with our- distribution to+This is needed in order to make the new ISAR keywords `role`,+`protocol`, `sources`, and `prefix_close` available in the Emacs+frontend. - $ISABELLE_HOME/etc/isar-keywords-ESPL.el - and start Isabelle using the -k switch as follows.+Variant b: Elegance+------------------- - isabelle emacs -k ESPL <your-theory-file.thy>+A more elegant way is to copy the keywords file provided with our+distribution to - + $ISABELLE_HOME/etc/isar-keywords-ESPL.el +and start Isabelle using the -k switch as follows.++ isabelle emacs -k ESPL <your-theory-file.thy>+++ 3. Usage ======== @@ -147,11 +145,11 @@ proof construction using the following commands cd <theory-dir-output-by-scytherproof>/src- isabelle emacs Tutorial.thy+ isabelle emacs Tutorial.thy or if you've chosen the elegant variant - isabelle emacs -k ESPL Tutorial.thy+ isabelle emacs -k ESPL Tutorial.thy Note that in many case the easiest way to start with constructing machine-checked proofs interactively is to use a proof script generated by@@ -159,9 +157,9 @@ ***-* Happy Proving :-)-*-* In case of questions do not hesistate to contact me at-* simon.meier@inf.ethz.ch-***++`Happy Proving :)`++In case of questions do not hesistate to contact me at+iridcode@gmail.com.
data/isabelle/src/ESPLogic/InferenceRules.thy view
@@ -478,4 +478,25 @@ end +subsubsection{* Reducing injective to non-injective agreement proofs *}++lemma iagree_to_niagree:+ assumes niagree: "\<And> i. prem i \<Longrightarrow> (\<exists> j. conc i j)"+ and conc_inj: "\<And> i1 i2 j. \<lbrakk> conc i1 j \<and> conc i2 j \<rbrakk> \<Longrightarrow> i1 = i2"+ shows "let prems = prem; + concs = conc+ in \<exists>f. inj_on f prems \<and> (\<forall>i. prems i \<longrightarrow> concs i (f i))"+proof -+ let ?f = "\<lambda> i. SOME j. conc i j"+ have "inj_on ?f prem"+ apply(auto simp: inj_on_def mem_def dest!: niagree)+ apply(rule_tac j="(SOME j. conc x j)" in conc_inj[OF conjI])+ apply(rule someI, simp+)++ done+ moreover have "\<forall> i. prem i \<longrightarrow> conc i (?f i)"+ by(auto simp: inj_on_def dest!: niagree intro: someI)+ ultimately show ?thesis+ by auto+qed+ end
interactive-only-src/Paths_scyther_proof.hs view
@@ -12,7 +12,7 @@ version :: Version-version = Version {versionBranch = [0,4,1,0], versionTags = []}+version = Version {versionBranch = [0,5,0,0], versionTags = []} bindir, libdir, datadir, libexecdir :: FilePath bindir = "."
scyther-proof.cabal view
@@ -1,6 +1,6 @@ build-type: Simple name: scyther-proof-version: 0.4.1.0+version: 0.5.0.0 license: GPL license-file: LICENSE category: Security, Theorem Provers@@ -79,12 +79,12 @@ examples/iso9798/isabelle-parallel-proofs/*.thy examples/experimental/ExponentialBlowup.spthy - isabelle/AUTHORS - isabelle/document/root.tex - isabelle/IsaMakefile - isabelle/Makefile - isabelle/README - isabelle/ROOT.ML + isabelle/AUTHORS+ isabelle/document/root.tex+ isabelle/IsaMakefile+ isabelle/Makefile+ isabelle/README+ isabelle/ROOT.ML isabelle/src/isar-keywords.el isabelle/src/*.thy isabelle/src/ESPLogic/*.thy@@ -96,7 +96,7 @@ .ghci interactive-only-src/Paths_scyther_proof.hs src/Scyther/Theory/Lexer.x_src_only- README+ README.md CHANGES @@ -106,19 +106,19 @@ executable scyther-proof build-depends:- base == 4.* , - array >= 0.3 && < 0.5 , + base == 4.* ,+ array >= 0.3 && < 0.5 , containers >= 0.4.2 && < 0.5 , safe >= 0.2 && < 0.4 ,- + mtl == 2.0.* ,- + cmdargs >= 0.6.8 && < 0.7 , filepath >= 1.2 && < 1.4 , directory >= 1.0 && < 1.3 ,- process == 1.1.* , + process == 1.1.* , time >= 1.1 && < 1.3 ,- + parsec == 3.1.* , pretty >= 1.0 && < 1.2 , tagsoup == 0.12.* ,@@ -134,7 +134,7 @@ other-modules: Extension.Prelude- Data.Color + Data.Color Data.Table Data.DAG.Simple Data.UnionFind@@ -166,4 +166,7 @@ Scyther.GoalFlow +source-repository head+ type: git+ location: https://github.com/meiersi/scyther-proof.git
src/Main.hs view
@@ -30,7 +30,7 @@ import System.Process import System.Console.CmdArgs.Text-import System.Console.CmdArgs.Explicit +import System.Console.CmdArgs.Explicit import Extension.Prelude @@ -77,7 +77,7 @@ , ", ETH Zurich, 2009-2012" ] where- link href name = + link href name = "<a href=\"" ++ href ++ "\" target=\"new\">" ++ name ++ "</a>" -- | Line width to use.@@ -122,11 +122,11 @@ | argExists "version" as = putStrLn versionStr | otherwise = io as - + ------------------------------------------------------------------------------ -- Argument Parsing ------------------------------------------------------------------------------- + data PrintMode = SPTheory -- ^ Output as a security protocol theory. | IsarXSymbol -- ^ Output as an Isabelle theory using XSymbol symbols.@@ -139,7 +139,7 @@ examplePath <- getDataFileName "examples" readmePath <- getDataFileName "isabelle/README" isabellePath <- esplTheoryDir- return $ + return $ ( defaultMode programName "Automatic generation of machine-checked proofs for security protocols." [ "Additional information:"@@ -157,7 +157,7 @@ ) { modeCheck = upd "mode" "translate" , modeArgs = Just $ flagArg (upd "inFile") "FILES"- , modeGroupFlags = Group + , modeGroupFlags = Group { groupUnnamed = [ flagNone ["first", "f"] (addArg "strategy" "first") "Return the first found proof."@@ -234,14 +234,14 @@ ] } }- where + where upd a v = Right . addArg a v addEmpty a = addArg a "" defaultMode name help helpSuffix = Mode { modeGroupModes = toGroup []- , modeNames = [name] - , modeValue = [] + , modeNames = [name]+ , modeValue = [] , modeCheck = upd "mode" name , modeReform = const Nothing-- no reform possibility , modeHelp = help@@ -250,7 +250,7 @@ , modeGroupFlags = toGroup [] -- no flags } - outputFlags = + outputFlags = [ flagOpt "" ["output","o"] (upd "outFile") "FILE" "Output file" , flagOpt "" ["Output","O"] (upd "outDir") "DIR" "Output directory" ]@@ -262,7 +262,7 @@ mainMode <- setupMainMode putStrLn $ "error: " ++ msg putStrLn $ ""- putStrLn $ showText (Wrap 100) + putStrLn $ showText (Wrap 100) $ helpText HelpFormatDefault mainMode exitFailure @@ -292,7 +292,7 @@ -- | Execute a translation.-translateWorker :: Arguments +translateWorker :: Arguments -> FilePath -- ^ Path to HTML template file. -> MVar (T.Table String) -- ^ Empty MVar for building the report. -> IO ()@@ -301,9 +301,9 @@ | otherwise = do -- check required tools when (not dryRun && html) (ensureGraphVizDot dotTool)- when (rebuildLogic || (not dryRun && isabelle)) + when (rebuildLogic || (not dryRun && isabelle)) (ensureIsabelleESPL rebuildLogic isabelleTool)- + -- translate all input files and ensure report is written with a special -- interrupted marker when an exception like Ctrl-C happened unless (n <= 1) (putInfoLn $ "processing "++show n++" files:")@@ -331,10 +331,10 @@ --------------- dryRun = outFile == "" && outDir == "" reportFile = fromMaybe "" $ findArg "report" as- outFile = fromMaybe "" $ findArg "outFile" as - outDir = fromMaybe "" $ findArg "outDir" as - + outFile = fromMaybe "" $ findArg "outFile" as+ outDir = fromMaybe "" $ findArg "outDir" as + -- automatically generate the filename for output mkAutoPath :: FilePath -> String -> FilePath mkAutoPath dir name@@ -343,7 +343,7 @@ mkOutPath :: FilePath -- ^ Input file name. -> FilePath -- ^ Output file name.- mkOutPath inFile = + mkOutPath inFile = case outFile of "" -> case outDir of "" -> error "outPath: undefined in dry-run mode"@@ -366,7 +366,7 @@ -- protocol composition ----------------------- composeProtos :: Theory -> Theory- composeProtos + composeProtos | argExists "composeParallel" as = composeParallel | otherwise = id @@ -380,7 +380,7 @@ Nothing -> io Just t -> do res <- timeout (t * 1000000) io- case res of + case res of Nothing -> do updateReport (T.appendCell $ "timeout: "++show maxTime++"s") throw CustomTimeout@@ -403,15 +403,15 @@ reportNewRow = updateReport T.newRow >> putInfoLn "" reportNumber :: Show a => String -> a -> IO ()- reportNumber header x = + reportNumber header x = updateReport (T.headerLastCell header . T.alignLastCell T.AlignRight. T.appendCell (show x)) reportString :: String -> String -> IO ()- reportString header msg = + reportString header msg = updateReport (T.headerLastCell header . T.appendCell msg)- + updateReport :: (T.Table String -> T.Table String) -> IO ()- updateReport + updateReport | null reportFile = const $ return () -- no report will be produced => don't update it | otherwise = modifyMVar_ reportVar . (return .) @@ -420,7 +420,7 @@ reportNumber "#chain rules" nChains reportNumber "#forward resolutions" nForward reportNumber "#missing proofs" nMissing- where + where (nChains, nForward, nMissing) = getProofSize s reportProperties :: Theory -> IO ()@@ -447,7 +447,7 @@ ] return . concat $ [ "\n\n"- , header + , header , "\n" , T.toLaTeX (filter (/='\t')) table ]@@ -457,7 +457,7 @@ "" -> return () "-" -> mkReport >>= putStrLn _ -> mkReport >>= appendFile reportFile- + -- HTML generation ------------------ generateHtml :: FilePath -- ^ Input file@@ -475,14 +475,14 @@ , giTemplate = templateFile , giOutDir = mkOutPath inFile , giMarkup = thyToDoc printMode thy- , giProofScript = - \outPath -> renderDoc . runIdentity . + , giProofScript =+ \outPath -> renderDoc . runIdentity . thyToDoc (ensureIsabellePrintMode printMode) $ adaptTheoryName outPath thy , giCmdLine = cmdLine , giDotTool = dotTool- , giIsabelle = - if isabelle + , giIsabelle =+ if isabelle then Just $ checkTheoryFile isabelleTool isabelleThreads 0 "ESPL" else Nothing }@@ -495,7 +495,7 @@ makeOverview | overview = protocolsOnly -- theoryOverview | otherwise = id where- protocolsOnly (Theory name items) = + protocolsOnly (Theory name items) = Theory name [x | x@(ThyProtocol _) <- items ] thyToOverviewDoc :: Theory -> Isar.Doc@@ -508,9 +508,9 @@ reportString "Revision" =<< getSvnRevision inFile thy <- (makeOverview . ensureUniqueRoleNames) `liftM` parseTheory inFile reportProperties thy- return . + return . removeHiddenItems .- proveThy . + proveThy . composeProtos . addMissingTypingInvariants . -- if no reuse is done, then remove the hidden items before proving@@ -532,18 +532,19 @@ reuseSelector = case findArg "reuse" as of Just "none" -> mkReuse $ const False Just "secrecy" -> mkReuse $ \th ->- (complete $ thmProof th) && (FAtom AFalse == seConcl (thmSequent th))+ (complete $ thmProof th) &&+ (FAtom (ABool False) == seConcl (thmSequent th)) _ -> mkReuse $ (complete . thmProof) where mkReuse thmSel se th- | isTypingFormula $ seConcl se = + | isTypingFormula $ seConcl se = -- we do not support reusing in typing proofs -- because the ISAR pretty printer cannot handle it yet correctly. False- | otherwise = + | otherwise = -- type invariants and axioms are always reused isAxiom th || (isTypingFormula . seConcl $ thmSequent th) || thmSel th- + minimizer :: Proof -> Proof minimizer | noMinimize = id | otherwise = minimizeProof@@ -554,22 +555,22 @@ Just "shortest" -> allClaims $ shortestProof bound oldestOpenMessages _ -> id where- allClaims prover0 = + allClaims prover0 = proveSequents reuseSelector prover where prover toReuse se = fmap minimizer $- (do guard (not noAttackSearch && bound == Nothing) + (do guard (not noAttackSearch && bound == Nothing) -- TODO: Make attack search respect bound. existsPossibleAttack oldestOpenMessages toReuse se- `mplus` + `mplus` prover0 toReuse se)- + -- Theory output ---------------- noGeneration = argExists "noGeneration" as html = argExists "html" as- isabelle = argExists "isabelle" as + isabelle = argExists "isabelle" as width = maybe lineWidth read $ findArg "charsPerLine" as isabelleThreads :: Maybe Int@@ -587,7 +588,7 @@ thyToDoc :: MarkupMonad m => PrintMode -> Theory -> m Isar.Doc thyToDoc pmode thy0 = case pmode of- SPTheory -> runTaggedIdentityT SlimOutput thyDoc + SPTheory -> runTaggedIdentityT SlimOutput thyDoc IsarXSymbol -> runReaderT thyDoc (Isar.defaultIsarConf { Isar.isarStyle = Isar.XSymbol }) IsarASCII -> runReaderT thyDoc Isar.defaultIsarConf where@@ -603,7 +604,7 @@ renderDoc = Isar.renderStyle (Isar.style { Isar.lineLength = width }) thyToString :: Theory -> String- thyToString + thyToString | html = renderDoc . evalHtmlMarkup . thyToDoc printMode -- | fol = render . tptpProblem . toTPTP | overview = renderDoc . thyToOverviewDoc@@ -612,7 +613,7 @@ writeThyFile :: FilePath -> Theory -> IO () writeThyFile outPath thy = do- if not noGeneration + if not noGeneration then do tGen <- execWithTimeout . timed_ . writeFile outPath . thyToString $ adaptTheoryName outPath thy@@ -640,7 +641,7 @@ mapM_ (putInfo . ('\t':)) ["CHECK FAILED", show tCheck, show msg] reportString "Isabelle Status" "check failed" reportString "Isabelle Message" (show msg)- else + else return () -- Translate one theory file@@ -650,7 +651,7 @@ -- writeThyFile. I didn't do this once, which cost me quite some -- debugging time. processThy :: FilePath -> Theory -> IO ()- processThy inFile thy + processThy inFile thy | dryRun = putStrLn (thyToString thy) | html = do generateHtml inFile thy@@ -691,7 +692,7 @@ -- | Read the SVN revision of the given file using the SVN command. getSvnRevision :: FilePath -> IO String-getSvnRevision file = +getSvnRevision file = handle handler $ do (_, info, _) <- readProcessWithExitCode "svn" ["info",file] [] return $ maybe "unversioned"@@ -703,7 +704,7 @@ -- | Read the cpu info using a call to cat /proc/cpuinfo getCpuModel :: IO String-getCpuModel = +getCpuModel = handle handler $ do (_, info, _) <- readProcessWithExitCode "cat" ["/proc/cpuinfo"] [] return $ maybe errMsg@@ -717,7 +718,7 @@ -- | Test if a process is executable and check its response. This is used to -- determine the versions and capabilities of tools that we depend on.-testProcess :: (String -> String -> Either String String) +testProcess :: (String -> String -> Either String String) -- ^ Analysis of stdout, stderr. Use 'Left' to report error. -> String -- ^ Test description to display. -> FilePath -- ^ Process to start@@ -739,7 +740,7 @@ case exitCode of ExitFailure code -> errMsg $ "failed with exit code " ++ show code- ExitSuccess -> + ExitSuccess -> case check out err of Left msg -> errMsg msg Right msg -> do putStrLn msg@@ -805,16 +806,16 @@ putStrLn "---" putStrLn "Attempting to build ESPL logic (this may take several minutes):" theoryDir <- esplTheoryDir- let isamake args = + let isamake args = runProcess isabelle ("make" :args) (Just theoryDir) Nothing Nothing Nothing Nothing >>= waitForProcess exitCode <- isamake ["clean"] >> isamake [] case exitCode of ExitSuccess -> putStrLn "Sucess! :-)\n---"- ExitFailure code -> putStrLn $ unlines + ExitFailure code -> putStrLn $ unlines [ " Logic building failed with code: " ++ show code , " Proof checking is likely not to work." , " To investigate the problem try manually loading/building the theories in" , " '" ++ theoryDir ++ "'" ]- +
src/Scyther/Facts.hs view
@@ -11,7 +11,7 @@ -- * Facts , Facts , protocol- + -- ** Construction , empty , freshTID@@ -29,6 +29,7 @@ , proveAtom , proveFormula , toAtoms+ , toFormula , nextTID , nextArbMsgId , quantifiedTIDs@@ -45,7 +46,7 @@ , saturateFacts , exploitTypingFacts , exploitLongTermKeySecrecy- + , splittableEqs , splitEqFacts @@ -103,7 +104,7 @@ ------------------------------------------------------------------------------ -- | A conjunction of logical facts.--- +-- -- Invariants that hold for a value @facts = Facts _ evs evord co uc eqs@: -- -- 1. All facts are invariant under their corresponding substitution. This@@ -132,7 +133,7 @@ -- NOTE: The Maybe is used for facts that don't have a typing. , typeAnns :: S.Set TypeAnn -- ^ Type annotations on messages. - , covered :: S.Set Message -- ^ The messages that have already been used in a + , covered :: S.Set Message -- ^ The messages that have already been used in a -- case distinction. , protocol :: Protocol -- ^ The protocol that the current state -- is a reachable state of.@@ -147,7 +148,7 @@ -- well-typedness claim are present. Note that there may be quantifiers and -- covered goals. nullFacts :: Facts -> Bool-nullFacts facts = +nullFacts facts = S.null (events facts) && S.null (eventOrd facts) && S.null (compromised facts) &&@@ -170,19 +171,19 @@ -- -- Uses 'fail' for error reporting. setProtocol :: Monad m => Protocol -> Facts -> m Facts-setProtocol proto facts +setProtocol proto facts | proto == protocol facts = return facts- | otherwise = fail $ "setProtocol: '" ++ show proto ++ "' /= '" - ++ show (protocol facts) ++ "'" + | otherwise = fail $ "setProtocol: '" ++ show proto ++ "' /= '"+ ++ show (protocol facts) ++ "'" --- | Set the typing. +-- | Set the typing. -- -- PRE: There mustn't be a different existing typing. -- -- Uses 'fail' for error reporting. setTyping :: Monad m => Typing -> Facts -> m Facts setTyping typ facts = case optTyping facts of- Just typ' | typ /= typ' -> fail $ "setTyping: '" ++ show typ ++ "' /= '" + Just typ' | typ /= typ' -> fail $ "setTyping: '" ++ show typ ++ "' /= '" ++ show typ' ++ "'" _ -> return $ facts { optTyping = Just typ } @@ -198,23 +199,23 @@ -- `fail` is called in the given monad. quantifyTID :: Monad m => TID -> Facts -> m Facts quantifyTID tid facts- | null (tidQuantified facts tid) = + | null (tidQuantified facts tid) = fail $ "quantifyTID: " ++ show tid ++ " already quantified."- | otherwise = + | otherwise = return $ facts { tidQuantifiers = S.insert tid $ tidQuantifiers facts } -- | Tries to quantify the given agent identifier. If it is already quantified -- `fail` is called in the given monad. quantifyArbMsgId :: Monad m => ArbMsgId -> Facts -> m Facts quantifyArbMsgId aid facts- | null (arbMsgIdQuantified facts aid) = + | null (arbMsgIdQuantified facts aid) = fail $ "quantifyArbMsgId: " ++ show aid ++ " already quantified."- | otherwise = + | otherwise = return $ facts { amQuantifiers = S.insert aid $ amQuantifiers facts } -- | Get a fresh TID and the updated set of facts. freshTID :: Facts -> (TID, Facts)-freshTID facts = +freshTID facts = (tid, facts { tidQuantifiers = S.insert tid $ tidQuantifiers facts }) where tid = nextTID facts @@ -260,7 +261,7 @@ -- | Check if a TID is quantified in these facts tidQuantified :: Facts -> TID -> CertResult-tidQuantified facts tid = +tidQuantified facts tid = certErrorIf (tid `S.notMember` tidQuantifiers facts) $ "unquantified tid: " ++ show tid @@ -299,7 +300,7 @@ -- | Check if all logical variables in a type annotation are quantified. typeAnnQuantified :: Facts -> TypeAnn -> CertResult-typeAnnQuantified facts (m, _, tid) = +typeAnnQuantified facts (m, _, tid) = msgQuantified facts m >< tidQuantified facts tid -- | Check if an equality contains only quantified logical variables.@@ -312,11 +313,11 @@ E.AVarEq (av1, av2) -> avarQuantified facts av1 >< avarQuantified facts av2 E.MVarEq (mv, m) -> mvarQuantified facts mv >< msgQuantified facts m E.MsgEq (m1, m2) -> msgQuantified facts m1 >< msgQuantified facts m2- + -- | Check if an atom contains only quantified logical variables. atomQuantified :: Facts -> Atom -> CertResult atomQuantified facts atom = case atom of- AFalse -> certSuccess+ ABool _ -> certSuccess AEq eq -> anyEqQuantified facts eq AEv ev -> evQuantified facts ev AEvOrd ord -> evOrdQuantified facts ord@@ -329,9 +330,9 @@ -- | Certification of a value with respect to a check and a morphism required -- to establish the required invariants in the context of a set of facts.-certify :: Show a => (Facts -> a -> CertResult) -> (Facts -> a -> b) +certify :: Show a => (Facts -> a -> CertResult) -> (Facts -> a -> b) -> Facts -> a -> Cert b-certify check conv facts x = +certify check conv facts x = case check facts x of [] -> x' -- FIXME: Somehow bidirectional shared keys lead in some cases to an@@ -339,8 +340,8 @@ -- dealt with proofs that failed (i.e., attackable security -- properties). Therfore, we have not yet debugged this to its full -- extent.- errs -> trace - (unlines $ + errs -> trace+ (unlines $ ("warning: internal check failed for '" ++ show x ++ "' because of") : (map (" "++) $ errs) )@@ -438,8 +439,8 @@ -- This operation is logically safe iff there are no references to the mapped -- logical variables outside the facts. trimQuantifiers :: Facts -> Facts-trimQuantifiers facts = facts { - equalities = eqs' +trimQuantifiers facts = facts {+ equalities = eqs' , tidQuantifiers = S.filter (`notElem` tids) $ tidQuantifiers facts , amQuantifiers = S.filter (`notElem` aids) $ amQuantifiers facts }@@ -463,7 +464,7 @@ , covered = S.map (E.substMsg eqs) (covered facts) } - + -- Compromised agents --------------------- @@ -497,7 +498,7 @@ -- | Inserts an event order fact. insertEvOrd :: Cert (Event, Event) -> Facts -> Facts-insertEvOrd ord prems = +insertEvOrd ord prems = prems { eventOrd = S.insert (certified ord) (eventOrd prems) } -- | Delete an event order fact.@@ -526,24 +527,24 @@ -- -- PRE: The atom must pass certification under the given facts. ----- PRE: The atom must not be a 'AHasTyp' or a 'ATyping' atom, as they are not--- representable by 'Facts' conjoinAtoms :: Monad m => [Atom] -> Facts -> m (Maybe Facts)-conjoinAtoms atoms facts0 = foldM conjoinAtom (Just facts0) atoms+conjoinAtoms atoms facts0 =+ foldM conjoinAtom (Just facts0) atoms where- conjoinAtom Nothing _ = return Nothing- conjoinAtom (Just facts) atom = case certified $ certAtom facts atom of- AFalse -> return Nothing- -- FIXME: repeated calls to solve may be a bit expensive due to duplicated- -- work of 'removeTrivialFacts'.- AEq eq -> return $ solve [Cert eq] facts- AEv ev -> return . Just $ insertEvNonTrivial (Cert ev) facts- AEvOrd ord -> return . Just $ insertEvOrdNonTrivial (Cert ord) facts- ACompr m -> return . Just $ compromise m facts- AUncompr m -> return . Just $ uncompromise m facts- AReachable p -> Just `liftM` setProtocol p facts- ATyping typ -> Just `liftM` setTyping typ facts- AHasType tya -> return . Just $ insertTypeAnn (Cert tya) facts+ conjoinAtom Nothing _ = return Nothing+ conjoinAtom (Just facts) atom = case certified $ certAtom facts atom of+ ABool False -> return Nothing+ ABool True -> return (Just facts)+ -- FIXME: repeated calls to solve may be a bit expensive due to duplicated+ -- work of 'removeTrivialFacts'.+ AEq eq -> return $ solve [Cert eq] facts+ AEv ev -> return . Just $ insertEvNonTrivial (Cert ev) facts+ AEvOrd ord -> return . Just $ insertEvOrdNonTrivial (Cert ord) facts+ ACompr m -> return . Just $ compromise m facts+ AUncompr m -> return . Just $ uncompromise m facts+ AReachable p -> Just `liftM` setProtocol p facts+ ATyping typ -> Just `liftM` setTyping typ facts+ AHasType tya -> return . Just $ insertTypeAnn (Cert tya) facts -- Combined construction and application of inference rules@@ -552,8 +553,8 @@ -- | Inserts an event order fact and both event facts implied by the presence -- of this event oder fact. insertEvOrdAndEvs :: Cert (Event, Event) -> Facts -> Facts-insertEvOrdAndEvs ord = - insertEv (mapCertified fst ord) . +insertEvOrdAndEvs ord =+ insertEv (mapCertified fst ord) . insertEv (mapCertified snd ord) . insertEvOrd ord -- | Insert all non-trivial events implied by the given event.@@ -570,7 +571,7 @@ _ -> insertEvOrdAndEvs ord prems where insertLearnBefore to p m = insertEvOrdAndEvs (Cert (Learn m, to)) p- + -- | Insert an executed role step and all non-trivial facts implied by the -- Input rule. insertStepInputClosed :: Cert (TID, RoleStep) -> Facts -> Facts@@ -600,7 +601,7 @@ (Step tid step) -> insertStepPrefixClosed (Cert (tid, step)) -- | Derive all non-trivial facts implied by the splitting rules and remove--- remove trivial facts.+-- trivial facts. removeTrivialFacts :: Facts -> Facts removeTrivialFacts = execState $ do evs <- S.toList <$> gets events@@ -633,8 +634,8 @@ -- -- Is equal to 'mzero' in case the facts don't contain a typing. exploitTypingFacts :: MonadPlus m => Facts -> m Facts-exploitTypingFacts facts0 = return facts0 -- TODO: implement this check. - {- cf. the one for weak atomicity below: +exploitTypingFacts facts0 = return facts0 -- TODO: implement this check.+ {- cf. the one for weak atomicity below: do -- WeaklyAtomic -> foldl' weaklyAtomic facts0 . E.getMVarEqs . equalities $ facts0 where@@ -644,11 +645,11 @@ weaklyAtomic facts (MVar (LocalId (i, tid)), m ) = case threadRole tid facts of Nothing -> error $ "exploitTypingFacts: no role assigned to '"++show tid++"'"- Just role -> + Just role -> case find (S.member i . stepFMV) (roleSteps role) of- Nothing -> + Nothing -> error $ "exploitTypingFacts: variable '"++show i++"' does not occur in role."- Just step -> + Just step -> insertEvOrdNonTrivial (Cert (Learn m, Step tid step)) facts -} @@ -673,7 +674,7 @@ -- NOTE: This is not the same as trying to prove the atom AFalse under these -- premises. The checks are separated due to efficiency reasons. proveFalse :: Facts -> Bool-proveFalse prems = +proveFalse prems = not (S.null (compromised prems `S.intersection` uncompromised prems)) || any noAgent (S.toList (compromised prems)) || cyclic (eventOrd prems)@@ -694,7 +695,7 @@ where -- PRE: atom is fully substituted checkAtom atom = case atom of- AFalse -> False+ ABool b -> b AEq eq -> E.reflexive eq AEv (Learn m) -> all checkLearn (splitNonTrivial m) AEv ev -> ev `S.member` events facts@@ -709,12 +710,12 @@ checkLearn m = Learn m `S.member` events facts checkLearnBefore to m = before (eventOrd facts) (Learn m) to - hasType (m0, ty0, tid0) = + hasType (m0, ty0, tid0) = go m0 ty0 where go (MAVar _) (AgentT) = True go (MConst i) (ConstT i') = i == i'- go (MFresh (Fresh (LocalId (i, tid)))) (NonceT role i') = + go (MFresh (Fresh (LocalId (i, tid)))) (NonceT role i') = i == i' && threadRole tid facts == Just role go (MHash m) (HashT ty) = go m ty go (MEnc m1 m2) (EncT ty1 ty2) = go m1 ty1 && go m2 ty2@@ -728,7 +729,7 @@ (m', ty', tid') <- S.toList $ typeAnns facts guard (m == m') return $ (ty', tid') `subType` (ty, tid0)- + -- ty `subType` ty' holds iff every instance of ty is also an instance of ty'. subType (ty0, tid) (ty'0, tid') = go ty0 ty'0@@ -792,6 +793,13 @@ , AHasType <$> S.toList (typeAnns facts) ] +-- | Represent the facts as a formula.+toFormula :: Facts -> Formula+toFormula facts = case toAtoms facts of+ [] -> FAtom (ABool True)+ atoms -> foldr1 FConj (map FAtom atoms)++ ------------------------------------------------------------------------------ -- Chain Rule Application ------------------------------------------------------------------------------@@ -802,9 +810,9 @@ -- set of events. openMessages :: Facts -> [Message] openMessages prems = nub $ filter okGoal $ catMaybes- [ case e of Learn m -> Just m; _ -> Nothing + [ case e of Learn m -> Just m; _ -> Nothing | e <- S.toList $ events prems `S.difference` S.map snd (eventOrd prems) ]- where + where okGoal (MMVar _) = False okGoal (MArbMsg _) = False okGoal (MAsymPK _) = False@@ -813,12 +821,12 @@ -- | Sort open messages ascending with respect to the maximal thread id. oldestOpenMessages :: Facts -> [Message]-oldestOpenMessages prems = +oldestOpenMessages prems = map fst . sortOn snd . mapMaybe score $ ms where ms = openMessages prems co = compromised prems- ltkLocalIds (MAsymSK m@(MAVar a)) + ltkLocalIds (MAsymSK m@(MAVar a)) | m `S.member` co = [] | otherwise = [getAVar a] ltkLocalIds (MSymK ma@(MAVar a) mb@(MAVar b))@@ -843,7 +851,7 @@ type ChainRuleM = StateT ChainRuleState [] --- | Add a fragment of the case name. +-- | Add a fragment of the case name. addCaseFragment :: String -> ChainRuleM () addCaseFragment name = modify $ \crs -> crs { crsCaseName = crsCaseName crs ++ [name] } @@ -866,13 +874,13 @@ -- try to unify the two messages in the context of the current facts; -- this function will always succeed, but the facts may be Nothing unify :: Message -> Message -> ChainRuleM ()-unify m m' +unify m m' | m == m' = return () -- performance optimization | otherwise = do crs <- get- maybe mzero (modifyFacts . const) $ + maybe mzero (modifyFacts . const) $ solve [certAnyEq (crsFacts crs) $ E.MsgEq (m', m)] (crsFacts crs)- + -- | Get a fresh thread identifier and update the facts accordingly getFreshTID :: ChainRuleM TID getFreshTID = do@@ -880,7 +888,7 @@ modifyFacts (const facts) addNewVar (Left tid) return tid- + -- | Get a fresh thread arbitrary-message id and update the facts accordingly getFreshAMID :: ChainRuleM ArbMsgId getFreshAMID = do@@ -891,13 +899,13 @@ -- | Add a type annotation. addTypeAnn :: TypeAnn -> ChainRuleM ()-addTypeAnn tya = modifyFacts $ +addTypeAnn tya = modifyFacts $ \facts -> insertTypeAnn (certTypeAnn facts tya) facts -- | Delete the given type annotation. Logically, this means forgetting -- the corresponding assumption. deleteTypeAnn :: TypeAnn -> ChainRuleM ()-deleteTypeAnn tya = modifyFacts $ +deleteTypeAnn tya = modifyFacts $ \facts -> facts { typeAnns = S.delete tya (typeAnns facts) } -- | Add a type annotation in expanded form. Expansion stops at SumT types@@ -912,13 +920,13 @@ expand :: ChainRuleM Message -> Type -> ChainRuleM Message expand mkV AgentT = do v <- mkV- addTypeAnn (v, AgentT, tid) + addTypeAnn (v, AgentT, tid) return v expand _ (NonceT role n) = do nTid <- getFreshTID modifyFacts $ insertRole nTid role return $ MFresh (Fresh (LocalId (n, nTid)))- + expand mkV ty@(SumT _ _) = do v <- mkV addTypeAnn (v, ty, tid)@@ -939,7 +947,7 @@ arb :: ChainRuleM Message arb = MArbMsg <$> getFreshAMID- + -- | Get the type annotation of a message. -- -- PRE: The message must be normalized with respect to the current facts.@@ -965,10 +973,10 @@ extractCase :: [E.AnyEq] -> ChainRuleState -> Maybe ((String, [Either TID ArbMsgId]), Facts) extractCase delayedEqs0 crs = do let facts0 = crsFacts crs- unmappedTID tid + unmappedTID tid | substTID facts0 tid == tid = return (Left tid) | otherwise = mzero- unmappedAMID aid + unmappedAMID aid | substAMID facts0 aid == MArbMsg aid = return (Right aid) | otherwise = mzero @@ -1025,12 +1033,12 @@ chainRuleFacts (MInvKey _) _ = error $ "chainRuleFacts: application to symbolically inverted keys not supported." chainRuleFacts (MAsymPK _) _ = error $ "chainRuleFacts: no support for public keys." chainRuleFacts (MTup _ _) _ = error $ "chainRuleFacts: no support for tuples."-chainRuleFacts m facts0 +chainRuleFacts m facts0 | proveAtom facts0 (F.AEv (Learn m)) = assembleCases `liftM` getTyping facts0 | otherwise = error $ "chainRuleFacts: could not prove that '" ++ show m ++ "' is known to the intruder." where- assembleCases typ = - mapMaybe (extractCase delayedEqs) . numberCases . + assembleCases typ =+ mapMaybe (extractCase delayedEqs) . numberCases . flip execStateT (ChainRuleState [] [] facts1 Nothing) $ ( initialIntruderKnowledge m `mplus`@@ -1055,10 +1063,10 @@ \facts -> foldl' insertSingle facts prev where insertSingle p prevEv = insertEvOrdNonTrivial (certEvOrd p (prevEv, ev)) p- + -- enumerate chainRuleFacts starting from the given role roleChains :: Typing -> TID -> Role -> ChainRuleM ()- roleChains typ tid role = do + roleChains typ tid role = do modifyFacts $ insertRole tid role addCaseFragment $ roleName role msum . map stepChains $ roleSteps role@@ -1072,7 +1080,7 @@ mapM_ annotateMVarType $ S.toList $ patFMV pt msgChains [(Step tid step)] (inst tid pt) where- -- annotating message variables with their type + -- annotating message variables with their type annotateMVarType mv = case M.lookup (mv, role) typ of Nothing -> error $ "stepChains: no type provided for '"++show v++"'"@@ -1105,8 +1113,8 @@ msgChains prev v@(MMVar _) = do addCaseFragment (msgName v) typChains prev v- - msgChains prev m'@(MEnc m1 m2) = ++ msgChains prev m'@(MEnc m1 m2) = do insertPrevious prev (Learn m') ( do -- trace ("msgChains: unify " ++ show m' ++ " =?= " ++ show m) (return ()) addCaseFragment $ msgName m'@@ -1120,7 +1128,7 @@ insertPrevious prev (Learn m') addCaseFragment $ msgName m' -- here we have to unify, as Isabelle is also doing it early- unify m' m + unify m' m -- the m' is an atomic message: just unify and be done with it. msgChains prev m' = do@@ -1136,7 +1144,7 @@ then msgChains prev v' else do tya <- getTypeAnn v- case tya of + case tya of (_,tyaTy,tyaTid) -> case tyaTy of KnownT _ -> mzero -- protocol wellformedness checks that KnownT implies cyclicity AgentT -> mzero -- we already know the agent names@@ -1158,7 +1166,7 @@ -- | Equalities that can be splitted. splittableEqs :: Facts -> [MsgEq]-splittableEqs facts = +splittableEqs facts = do eq@(MShrK _ _, MShrK _ _) <- getPostEqs $ equalities facts guard (not $ uncurry (==) eq) return eq@@ -1172,7 +1180,7 @@ where addEqs eqs = solve (map (certAnyEq facts) eqs) facts -splitEqFacts eq _ = +splitEqFacts eq _ = error $ "splitEqFacts: cannot split equality '" ++ show eq ++ "'" @@ -1215,7 +1223,7 @@ -- | Apply the mapping of agent and thread equalities to the facts. ----- TODO: Improve error handling. Currently, 'error' is called if the facts +-- TODO: Improve error handling. Currently, 'error' is called if the facts -- are contradictory after the substitution. applyMapping :: Mapping -> Facts -> Facts applyMapping mapping facts0 = case newFacts of@@ -1232,11 +1240,11 @@ quantifyTIDs facts = foldM qTID facts $ S.toList $ tidQuantifiers facts0 quantifyAIDs facts = foldM qAID facts $ S.toList $ amQuantifiers facts0 - addAtoms = conjoinAtoms atoms + addAtoms = conjoinAtoms atoms atoms = map (F.substAtom (getMappingEqs mapping)) . toAtoms $ facts0 extractArbMsgId (MArbMsg aid) = aid- extractArbMsgId m = error $ + extractArbMsgId m = error $ "applyMapping: arbitrary-message id mapped to '" ++ show m ++ "'" @@ -1260,7 +1268,7 @@ ---------- -- | Pretty print the facts in Isar format.-isaFacts :: IsarConf -> Facts +isaFacts :: IsarConf -> Facts -> ([Doc],[Doc],[Doc]) -- ^ Quantified variables, representable facts, and non-representable facts isaFacts conf facts = ( ppSet (isar conf) (tidQuantifiers facts) ++@@ -1271,7 +1279,7 @@ ppSet (isaEventOrd conf (Mapping eqs)) (eventOrd facts) ++ ppSet (isaEvent conf (Mapping eqs)) (events facts) , map (isar conf) nonReprEqs- ) + ) where eqs = equalities facts (reprEqs, nonReprEqs) = partition isReprEq $ E.toAnyEqs eqs@@ -1295,21 +1303,21 @@ , (map (sptTypeAnn (`threadRole` facts)) $ S.toList $ typeAnns facts) ++ map sptAnyEq nonReprEqs ++ (map ppCovered $ S.toList $ covered facts)- ) + ) where eqs = equalities facts (reprEqs, nonReprEqs) = partition isReprEq $ E.toAnyEqs eqs ppCovered m = text "covered" <> parens (sptMessage m)- ppComprInfo setName set + ppComprInfo setName set | S.null set = mzero | otherwise = return . fsep $- (text setName <> lparen : (map (nest 2) . punctuate comma) + (text setName <> lparen : (map (nest 2) . punctuate comma) (ppSet sptMessage set)) ++ [rparen] {- sptSimpleFacts :: Facts -> Doc-sptSimpleFacts facts = case sptFacts facts of +sptSimpleFacts facts = case sptFacts facts of (ds1, ds2, ds3) -> vcat $ map vcat [ds1, [text ""], ds2, [text ""], ds3] -} @@ -1321,7 +1329,7 @@ findChain sel x cs = case break ((x ==) . sel) cs of (_,[]) -> (cs, [x]) (cs1, c:cs2) -> (cs1 ++ cs2, c)- insertEdge chains0 (from,to) = + insertEdge chains0 (from,to) = let (chains1, prefix) = findChain last from chains0 (chains2, suffix) = findChain head to chains1 in (prefix ++ suffix) : chains2
src/Scyther/Formula.hs view
@@ -15,6 +15,7 @@ , isTypingFormula , destTypingFormula , atomTIDs+ , formulaTIDs , findRole -- * Pretty Printing@@ -48,7 +49,7 @@ -- | A representable logical atom. data Atom =- AFalse -- ^ 'False' in Isabelle.+ ABool Bool -- ^ 'False' and 'True' in Isabelle. | AEq AnyEq -- ^ An equality | AEv Event -- ^ An event must have happened. | AEvOrd (Event, Event) -- ^ An event order.@@ -61,7 +62,7 @@ deriving( Eq, Show, Ord, Data, Typeable ) -- | A representable logical formula. Currently these are monotonic formula.-data Formula = +data Formula = FAtom Atom | FConj Formula Formula | FExists (Either TID ArbMsgId) Formula@@ -81,7 +82,7 @@ -- | Relabel quantified TIDs according to the given list of labels. relabelTIDs :: [TID] -> Formula -> Formula-relabelTIDs tids0 formula = +relabelTIDs tids0 formula = runReader (evalStateT (go formula) tids0) (Mapping E.empty) where go (FAtom atom) = FAtom <$> ((substAtom . getMappingEqs) <$> ask <*> pure atom)@@ -90,7 +91,7 @@ tids <- get case tids of [] -> error "relabelTIDs: out of labels"- tid':tids' -> do + tid':tids' -> do put tids' FExists (Left tid') <$> local (addTIDMapping tid tid') (go inner) go (FExists q@(Right _) inner) = FExists q <$> go inner@@ -98,7 +99,7 @@ -- | Compute the threads associated to the given atom. atomTIDs :: Atom -> [TID]-atomTIDs AFalse = mzero+atomTIDs (ABool _) = mzero atomTIDs (ATyping _) = mzero atomTIDs (AReachable _) = mzero atomTIDs (AEv e) = evTIDs e@@ -107,18 +108,25 @@ atomTIDs (AUncompr m) = msgTIDs m atomTIDs (AHasType tya) = typeAnnTIDs tya atomTIDs (AEq eq) = anyEqTIDs eq- +-- | Compute the free thread variables of the given formula.+formulaTIDs :: Formula -> [TID]+formulaTIDs (FAtom atom) = atomTIDs atom+formulaTIDs (FConj f1 f2) = formulaTIDs f1 ++ formulaTIDs f2+formulaTIDs (FExists (Left tid) f) = filter (tid /=) $ formulaTIDs f+formulaTIDs (FExists (Right _) f) = formulaTIDs f++ -- Substitution --------------- -- | Substitute all variables in an atom.--- +-- -- NOTE: A 'HasType' atom will only have its thread identifier substituted, but -- not the whole message variable. substAtom :: Equalities -> Atom -> Atom substAtom eqs atom = case atom of- AFalse -> atom+ ABool _ -> atom AEq eq -> AEq $ substAnyEq eqs eq AEv ev -> AEv $ substEv eqs ev AEvOrd ord -> AEvOrd $ substEvOrd eqs ord@@ -140,9 +148,9 @@ -- 'fail' for error reporting. conjunctionToAtoms :: MonadPlus m => Formula -> m [Atom] conjunctionToAtoms (FAtom a) = return [a]-conjunctionToAtoms (FConj f1 f2) = +conjunctionToAtoms (FConj f1 f2) = (++) `liftM` conjunctionToAtoms f1 `ap` conjunctionToAtoms f2-conjunctionToAtoms _ = +conjunctionToAtoms _ = fail "conjunctionToAtoms: existential quantifier encountered." -- | Split all toplevel conjunctions.@@ -188,17 +196,18 @@ -- | Pretty print an atom in Isar format. isaAtom :: IsarConf -> Mapping -> Atom -> Doc isaAtom conf mapping atom = case atom of- AFalse -> text "False"+ ABool False -> text "False"+ ABool True -> text "True" AEq eq -> ppIsar eq AEv ev -> isaEvent conf mapping ev AEvOrd ord -> isaEventOrd conf mapping ord ACompr av -> isaCompr conf av AUncompr av -> isaUncompr conf av- AHasType (m,ty,i) -> ppIsar m <-> isaIn conf <-> - isar conf ty <-> ppIsar i <-> + AHasType (m,ty,i) -> ppIsar m <-> isaIn conf <->+ isar conf ty <-> ppIsar i <-> isaExecutionSystemState conf ATyping _ -> text "well-typed"- AReachable p -> + AReachable p -> text "(t,r,s)" <-> isaIn conf <-> text "reachable" <-> text (protoName p) where ppIsar :: Isar a => a -> Doc@@ -208,13 +217,14 @@ -- | Pretty print an atom in security protocol theory format. sptAtom :: Mapping -> Atom -> Doc sptAtom mapping atom = case atom of- AFalse -> text "False"+ ABool False -> text "False"+ ABool True -> text "True" AEq eq -> sptAnyEq eq AEv ev -> sptEvent mapping ev AEvOrd (e1,e2) -> sptEventOrd mapping [e1,e2] ACompr av -> sptCompr av AUncompr av -> sptUncompr av- AHasType tya -> sptTypeAnn (const Nothing) tya + AHasType tya -> sptTypeAnn (const Nothing) tya ATyping typ -> sptTyping typ AReachable p -> text "reachable" <-> text (protoName p) @@ -225,7 +235,7 @@ where ppIsar :: Isar a => a -> Doc ppIsar = isar conf- + pp m (FAtom atom) = isaAtom conf m atom pp m (FConj f1 f2) = sep [pp m f1 <-> isaAnd conf, pp m f2] pp m (FExists v f) = parens $
src/Scyther/Proof.hs view
@@ -49,8 +49,8 @@ import Extension.Prelude -import Scyther.Facts -import qualified Scyther.Equalities as E +import Scyther.Facts+import qualified Scyther.Equalities as E import Scyther.Sequent ------------------------------------------------------------------------------@@ -79,6 +79,7 @@ -- | Supported rules. data Rule = Saturate+ | ReduceInjectivity | ForwardResolution (Named Sequent) E.Mapping | ChainRule Message [(String, [Either TID ArbMsgId])] | SplitEq E.MsgEq [Bool] -- True, if sub-proof for this case is present.@@ -121,38 +122,45 @@ -- | Try proving a sequent by assumption; i.e. by showing that the premises -- directly imply the conclusion. byAssumption :: Monad m => Sequent -> m Proof-byAssumption se@(Sequent prem concl)+byAssumption se@(Sequent prem concl Standard) | proveFalse prem = return $ Trivial se TrivContradictoryPremises | proveFormula prem concl = return $ Trivial se TrivPremisesImplyConclusion | otherwise = case exploitLongTermKeySecrecy prem of Just key -> return $ Trivial se (TrivLongTermKeySecrecy key) Nothing -> fail $ "byAssumption: cannot prove conclusion from premises"+byAssumption _ = fail $ "byAssumption: only work for standard sequents" -- | An order preferring typing conclusions and then false conclusions. reuseOrder :: Formula -> Formula -> Ordering-reuseOrder (FAtom (ATyping typ1)) (FAtom (ATyping typ2)) = compare typ1 typ2-reuseOrder (FAtom (ATyping _)) _ = LT-reuseOrder _ (FAtom (ATyping _)) = GT-reuseOrder (FAtom AFalse) (FAtom AFalse) = EQ-reuseOrder (FAtom AFalse) _ = LT-reuseOrder _ (FAtom AFalse) = GT-reuseOrder f1 f2 = compare f1 f2+reuseOrder (FAtom (ATyping typ1)) (FAtom (ATyping typ2)) = compare typ1 typ2+reuseOrder (FAtom (ATyping _)) _ = LT+reuseOrder _ (FAtom (ATyping _)) = GT+reuseOrder (FAtom (ABool False)) (FAtom (ABool False)) = EQ+reuseOrder (FAtom (ABool False)) _ = LT+reuseOrder _ (FAtom (ABool False)) = GT+reuseOrder f1 f2 = compare f1 f2 -- | A generic proof strategy for proving sequents. Eagerly reuses lemmas in -- the order they were given using forward resolution.-genericProof :: MonadPlus m +genericProof :: MonadPlus m => m () -- ^ A marker for usages of the chain rule. -> (Facts -> [Message]) -- ^ A heuristic for the order of -- messages to be tried using the chain rule. -> [Named Sequent] -- ^ Rules that should be reused in this proof. -> Sequent -- ^ The sequent to be proven -> m Proof-genericProof chainRuleMarker goals rawRules se0 = - case saturate se0 of- Just se1 -> (RuleApp se0 Saturate . return) `liftM` prove se1- Nothing -> prove se0+genericProof chainRuleMarker goals rawRules se0 =+ case reduceInjectivity se0 of+ Just (Left err) ->+ return $ Missing se0 ("failed to reduce injectivity: " ++ err) True+ Just (Right se1) ->+ (RuleApp se0 ReduceInjectivity . return) `liftM`+ (genericProof chainRuleMarker goals rawRules se1)+ Nothing -> case saturate se0 of+ Just se1 -> (RuleApp se0 Saturate . return) `liftM` prove se1+ Nothing -> prove se0 where -- the theorems for reuse: prefer false conclusions over arbitrary conclusions reusableRules :: [Named Sequent]@@ -162,7 +170,7 @@ reuseRule se rule = do (mapping, optSequent) <- frule (snd rule) se let mkProof = RuleApp se (ForwardResolution rule mapping)- case optSequent of + case optSequent of Nothing -> return $ return $ mkProof [] Just sequent -> return $ (mkProof . return) `liftM` prove sequent @@ -176,7 +184,7 @@ Nothing -> return $ Missing se "prover stuck => no type invariant available" True -- prove a sequent- prove se = + prove se = case byAssumption se of Just prf -> return prf -- proven by assumption Nothing ->@@ -194,7 +202,7 @@ let sequents = splitEq eq se RuleApp se (SplitEq eq (map isJust sequents)) `liftM` mapM prove (catMaybes sequents)- + -- there is none => chain rule is our last resort [] -> case goals (sePrem se) of@@ -206,23 +214,23 @@ -- | Use a (possibly bounded) depth-first search for finding the proof. -- TODO: Make error handling explicit. dfsProof :: Maybe Int -> (Facts -> [Message]) -> [Named Sequent] -> Sequent -> Maybe Proof-dfsProof Nothing heuristic rules se = +dfsProof Nothing heuristic rules se = runUnboundedDFS $ genericProof (return ()) heuristic rules se-dfsProof (Just bound) heuristic rules se = +dfsProof (Just bound) heuristic rules se = evalBoundedDFS (genericProof (updateCost succ) heuristic rules se) (<= bound) 0- + -- | Use branch-and-bound search to find the shortest proof. -- TODO: Make error handling explicit. shortestProof :: Maybe Int -> (Facts -> [Message]) -> [Named Sequent] -> Sequent -> Maybe Proof-shortestProof optBound heuristic rules se = +shortestProof optBound heuristic rules se = evalBranchAndBound (genericProof (updateCost (fmap succ)) heuristic rules se)- optBound + optBound -- | Check if there exists a case where the prover gets stuck using unbounded DFS -- and the given heuristic and theorems to reuse. existsPossibleAttack :: (Facts -> [Message]) -> [Named Sequent] -> Sequent -> Maybe Proof-existsPossibleAttack heuristic rules se = +existsPossibleAttack heuristic rules se = findAttack =<< (runUnboundedDFS $ genericProof (return ()) heuristic rules se) ------------------------------------------------------------------------------@@ -236,7 +244,7 @@ mkSubproof se prf = put se >> go prf go :: Proof -> StateT Sequent Maybe Proof- go (Missing _ reason showSequent) = + go (Missing _ reason showSequent) = Missing <$> get <*> pure reason <*> pure showSequent go prf@(Axiom seAxiom) = do se <- get@@ -260,6 +268,13 @@ Just key' -> do guard (key == key') Trivial <$> get <*> pure reason Nothing -> mzero+ go (RuleApp _ ReduceInjectivity [prf]) = do+ se <- get+ case reduceInjectivity se of+ Just (Right se') ->+ (RuleApp se ReduceInjectivity . return) <$> (put se' >> go prf)+ Just _ -> mzero+ Nothing -> go prf go (RuleApp _ Saturate [prf]) = do se <- get case saturate se of@@ -270,7 +285,7 @@ let statePrem = sePrem se guard (proveFacts statePrem (sePrem $ snd thm) mapping) optSequent <- fruleInst (snd thm) mapping se- case optSequent of + case optSequent of Nothing -> do guard (null prfs) return $ RuleApp se rule prfs Just sequent -> case prfs of@@ -309,19 +324,19 @@ findAttack = fmap head . go . return where unvisited prf = Missing (prfSequent prf) "not yet investigated" False- + go :: [Proof] -> Maybe [Proof] go [] = Nothing go (prf : prfs) = case prf of- RuleApp se rule subprfs -> + RuleApp se rule subprfs -> -- look for an attack in the subproofs of the given rule (do subprfs' <- go subprfs return $ RuleApp se rule subprfs' : map unvisited prfs ) `mplus` -- look for an attack in the remaining proofs- ((unvisited prf :) `liftM` go prfs) + ((unvisited prf :) `liftM` go prfs) -- attack found: return it together with the unvisited siblings- PossibleAttack _ _ -> return $ prf : map unvisited prfs + PossibleAttack _ _ -> return $ prf : map unvisited prfs -- look for an attack in the siblings and prepend ourselves unvisited _ -> (unvisited prf :) `liftM` go prfs @@ -339,10 +354,10 @@ -- | Minimize a proof by removing all unnecesary forward resolutions. minimizeProof :: Proof -> Proof minimizeProof prf0 = fromMaybe prf0 $ do- prf <- go prf0 + prf <- go prf0 checkProof (prfSequent prf) prf where- go (RuleApp se rule@(ForwardResolution _ _) [prf]) = do + go (RuleApp se rule@(ForwardResolution _ _) [prf]) = do prf' <- go prf (checkProof se prf' <|> pure (RuleApp se rule [prf'])) go (RuleApp se rule prfs) =@@ -358,7 +373,7 @@ -- | Extracts the first type invariant occurring in a forward resolution. This -- is required because in Isabelle type invariants are handled using locales,--- while we are handling them using forward resolution. +-- while we are handling them using forward resolution. -- -- Note that our system here is more general, but for the current setup, we do -- not see this generality.@@ -381,14 +396,14 @@ deriving( Eq, Ord, Monoid ) instance Show ProofSize where- show s = + show s = concat ["C:", show nChain, " F:", show nForward, " M:", show nMissing] where (nChain, nForward, nMissing) = getProofSize s -- | Extract the raw proof size information. getProofSize :: ProofSize -> (Int, Int, Int)-getProofSize (ProofSize (nChain, nForward, nMissing)) = +getProofSize (ProofSize (nChain, nForward, nMissing)) = (getSum nChain, getSum nForward, getSum nMissing) -- | The size of a missing proof.@@ -425,10 +440,10 @@ displayChainRule :: Protocol -> Maybe Typing -> Proof displayChainRule _ _ = error "displayChainRule: not yet upgraded" {--displayChainRule proto optTyp = +displayChainRule proto optTyp = RuleApp se (ChainRule optTyp m (map fst cases)) (map mkPrf cases) where- tid = 0 + tid = 0 m = (MMVar (LocalId (Id "m", tid))) prem = insertEv (Learn m) $ insertThread tid $ emptyFacts se = Sequent proto (FFacts prem) FFalse
src/Scyther/Sequent.hs view
@@ -3,10 +3,12 @@ -- * Datatype Sequent(..)+ , SequentQualifier(..) , seProto -- ** Logically safe construction , wellTypedCases+ , reduceInjectivity , saturate , frule , fruleInst@@ -23,7 +25,10 @@ import Control.Arrow import Control.Monad+import Control.Applicative +import Extension.Prelude (sortednub)+ import qualified Scyther.Equalities as E import Scyther.Facts import Scyther.Formula@@ -33,12 +38,25 @@ -- Sequents ------------------------------------------------------------------------------ +-- | A qualifier changing the interpretation of a sequent.+data SequentQualifier =+ Standard+ -- ^ The standard interpretation of a sequent as given in our CSF'10+ -- paper on strong invariants for the efficient construction of+ -- machine-checked security proofs.+ | Injective+ -- ^ An injective interpretation of a sequent. Only valid for sequents+ -- that have exactly one free TID-variable in the premise and exactly+ -- one further TID-variable in the conclusion.+ deriving( Eq, Ord, Show, Data, Typeable )+ -- | A sequent with a conjunction of a set of facts as the premise and a single -- formula as the conclusion denoting a statement about a reachable state of -- a protocol. data Sequent = Sequent {- sePrem :: Facts- , seConcl :: Formula+ sePrem :: Facts+ , seConcl :: Formula+ , seQualifier :: SequentQualifier } deriving( Eq, Show, Ord, Data, Typeable ) @@ -46,15 +64,19 @@ seProto :: Sequent -> Protocol seProto = protocol . sePrem +-- | 'True' iff the sequent is viewed with the 'Standard' interpretation.+isStandard :: Sequent -> Bool+isStandard = (Standard ==) . seQualifier + -- Construction --------------- -- | Make all thread identifiers occurring in the sequent unique by -- consistently relabeling the thread identifiers in the conclusion. uniqueTIDQuantifiers :: Sequent -> Sequent-uniqueTIDQuantifiers (Sequent prem concl) = - Sequent prem (relabelTIDs [nextTID prem..] concl)+uniqueTIDQuantifiers (Sequent prem concl quali) =+ Sequent prem (relabelTIDs [nextTID prem..] concl) quali -- | Apply a function to the premise, but return only the updated sequent if -- the premise was changed.@@ -73,42 +95,40 @@ -- -- Uses 'fail' for error reporting. wellTypedCases :: MonadPlus m => Sequent -> m [(String, Sequent)]-wellTypedCases se = case seConcl se of- FAtom (ATyping typ) -> - return $ protoRoles (seProto se) >>= roleProofs typ- _ -> mzero+wellTypedCases se@(Sequent _ (FAtom (ATyping typ)) Standard) =+ return $ protoRoles (seProto se) >>= roleProofs where- roleProofs typ role = proveRecvs S.empty (roleSteps role)- where- proveRecvs _ [] = []- proveRecvs recv ( Send _ _ : steps) = proveRecvs recv steps- proveRecvs recv ((Recv _ (PMVar lid)) : steps) = - -- don't prove single reiceves as they are handled directly by the tactic- -- on the Isabelle side.- proveRecvs (S.insert lid recv) steps- proveRecvs recv (step@(Recv _ pt) : steps) = - let mvars = patFMV pt- in (S.toList mvars >>= proveVar) `mplus` - (proveRecvs (recv `S.union` mvars) steps)+ roleProofs role =+ proveRecvs S.empty (roleSteps role) where- proveVar v- | v `S.member` recv = fail "proveVar: not first receive"- | otherwise = do- return (name, Sequent prem concl)- where- name = roleName role ++ "_" ++ stepLabel step ++ "_" ++ getId v- (tid, prem0) = freshTID (sePrem se)- mv = MVar (LocalId (v, tid))- premErr = error $ "wellTypedCases: could not add thread " ++ show tid ++ ". This should not happen."- prem1 = maybe premErr saturateFacts . join $ - conjoinAtoms [AEv (Step tid step), AEq (E.TIDRoleEq (tid, role))] prem0- prem = fromMaybe (error "failed to set typing") $ setTyping typ prem1- concl = FAtom $ - case M.lookup (v, role) typ of- Just ty -> AHasType (MMVar mv, ty, tid)- Nothing -> error $ - "wellTypedCases: no type given for '"++show v++"' in role '"++roleName role++"'"+ proveRecvs _ [] = []+ proveRecvs recv ( Send _ _ : steps) = proveRecvs recv steps+ proveRecvs recv ((Recv _ (PMVar lid)) : steps) =+ -- don't prove single reiceves as they are handled directly by the tactic+ -- on the Isabelle side.+ proveRecvs (S.insert lid recv) steps+ proveRecvs recv (step@(Recv _ pt) : steps) =+ let mvars = patFMV pt+ in (S.toList mvars >>= proveVar) `mplus`+ (proveRecvs (recv `S.union` mvars) steps)+ where+ proveVar v+ | v `S.member` recv = fail "proveVar: not first receive"+ | otherwise = return (name, Sequent prem concl Standard)+ where+ name = roleName role ++ "_" ++ stepLabel step ++ "_" ++ getId v+ (tid, prem0) = freshTID (sePrem se)+ mv = MVar (LocalId (v, tid))+ premErr = error $ "wellTypedCases: could not add thread " ++ show tid ++ ". This should not happen."+ prem1 = maybe premErr saturateFacts . join $+ conjoinAtoms [AEv (Step tid step), AEq (E.TIDRoleEq (tid, role))] prem0+ prem = fromMaybe (error "failed to set typing") $ setTyping typ prem1+ concl = FAtom $ case M.lookup (v, role) typ of+ Just ty -> AHasType (MMVar mv, ty, tid)+ Nothing -> error $+ "wellTypedCases: no type given for '"++show v++"' in role '"++roleName role++"'" +wellTypedCases _ = mzero -- | Emulate a variant Isabelle's 'frule' tactic. It works only if the given -- maping of free variables of the rule makes the premise of the rule provable@@ -117,10 +137,10 @@ -- last step works currently only for conclusions being false of pure -- conclusions. ----- NOTE that 'frule' works only for rules that contain no existential--- quantifiers in the conclusion.+-- NOTE that 'frule' works only for rules that are standard sequents and that+-- contain no existential quantifiers in the conclusion. fruleInst :: MonadPlus m- => Sequent -- ^ rule + => Sequent -- ^ rule -> E.Mapping -- ^ mapping of free variables of rule to proof state -> Sequent -- ^ proof state -> m (Maybe Sequent) -- ^ some result if resolution worked. Nothing@@ -128,21 +148,23 @@ -- premises of proof state were extended. -- -- mzero if rule could not be applied-fruleInst rule mapping state = do- atoms <- conjunctionToAtoms $ seConcl rule- let statePrem = sePrem state- guard (proveFacts statePrem (sePrem rule) mapping)- optStatePrem' <- conjoinAtoms (map (substAtom (E.getMappingEqs mapping)) atoms) statePrem- case optStatePrem' of- Nothing -> do return Nothing- Just statePrem' -> do guard (statePrem /= statePrem')- return . Just $ Sequent statePrem' (seConcl state)+fruleInst rule mapping state+ | isStandard rule && isStandard state = do+ atoms <- conjunctionToAtoms $ seConcl rule+ let statePrem = sePrem state+ guard (proveFacts statePrem (sePrem rule) mapping)+ optStatePrem' <- conjoinAtoms (map (substAtom (E.getMappingEqs mapping)) atoms) statePrem+ case optStatePrem' of+ Nothing -> do return Nothing+ Just statePrem' -> do guard (statePrem /= statePrem')+ return . Just $ Sequent statePrem' (seConcl state) Standard+ | otherwise = mzero -- | Like 'fruleInst' but tries all mappings. frule :: MonadPlus m- => Sequent -- ^ rule + => Sequent -- ^ rule -> Sequent -- ^ proof state- -> m (E.Mapping, Maybe Sequent) + -> m (E.Mapping, Maybe Sequent) -- ^ some result if resolution worked. Nothing denotes that False was -- derived. Just means that premises of proof state were extended. --@@ -180,21 +202,64 @@ return (mapping, Just $ Sequent prem1 (seConcl state)) -} +-- | Try to prove an 'Injective' sequent by reducing it to its non-injective+-- counterpart.+reduceInjectivity :: Sequent -> Maybe (Either String Sequent)+reduceInjectivity se+ | seQualifier se /= Injective = Nothing+ -- We have an injective sequent => we must reduce it or report a failure to+ -- reduce it.+ | otherwise = Just $+ case sortednub $ formulaTIDs $ toFormula $ sePrem se of+ [premTID] -> case decomposeConcl $ seConcl se of+ Just (concTID, atoms) ->+ if check premTID concTID atoms+ then Right (se { seQualifier = Standard })+ else Left "conclusion does not immediatly entail injectivity"+ Nothing -> Left $+ "only works for conclusions of the form '? tid. ato1 & ... & atoN'"+ premTIDs -> Left $+ "too few/many thread identifiers in premises " ++ show premTIDs+ where+ decomposeConcl (FExists (Left tid) fm) = (,) tid <$> conjunctionToAtoms fm+ decomposeConcl _ = Nothing++ -- check that conclusion entails equality of premise TIDs+ check premTID0 concTID atoms =+ case E.solve (rawEqs0 ++ rawEqs1) E.empty of+ Just eqs -> E.substTID eqs premTID0 == E.substTID eqs premTID1+ _ -> False+ where+ premTID1 = 1 + max premTID0 concTID++ rawEqs0 = [ eq | AEq eq <- atoms ]+ rawEqs1 = map rename rawEqs0++ rename = E.substAnyEq $ E.getMappingEqs $+ E.addTIDMapping premTID0 premTID1 E.emptyMapping+++++ -- | Try to saturate a sequent, if possible and leading to new facts. saturate :: MonadPlus m => Sequent -> m Sequent-saturate = changePrem (return . saturateFacts)+saturate se = do+ guard (isStandard se)+ changePrem (return . saturateFacts) se --- | Try to use the chain rule. +-- | Try to use the chain rule. -- -- MonadPlus is used to report a failure to apply the rule. ---chainRule :: MonadPlus m - => Sequent -> Message +chainRule :: MonadPlus m+ => Sequent -> Message -> m [((String, [Either TID ArbMsgId]), Sequent)]-chainRule se m = +chainRule se m = do+ guard (isStandard se) map (second mkSequent) `liftM` chainRuleFacts m (sePrem se)- where - mkSequent prem = Sequent prem (seConcl se)+ where+ mkSequent prem = Sequent prem (seConcl se) Standard -- | Try to exploit the typing. Fails if no new facts could be derived. exploitTyping :: MonadPlus m => Sequent -> m Sequent@@ -203,14 +268,14 @@ -- | Split a splittable equality. -- splitting can be done. splitEq :: E.MsgEq -> Sequent -> [Maybe Sequent]-splitEq eq se +splitEq eq se | eq `elem` splittableEqs prems = map (fmap updPrem) $ splitEqFacts eq prems | otherwise = error $ "splitEq: equality not present" where prems = sePrem se updPrem prem' = se {sePrem = prem'} - +
src/Scyther/Theory.hs view
@@ -31,7 +31,7 @@ , theoryProofSize , theoryOverview , classifyProperties- -- , wfTheory -- TODO: Implement type wellformedness check. + -- , wfTheory -- TODO: Implement type wellformedness check. -- Currently, we rely on Isabelle for the last soundness check. ) where@@ -141,11 +141,11 @@ where mkTypingSequent p = case mscTyping p of Just typ -> return $ ThySequent ( protoName p ++ "_msc_typing"- , Sequent (empty p) (FAtom (ATyping typ)))+ , Sequent (empty p) (FAtom (ATyping typ)) Standard) Nothing -> mzero add item = case item of- ThyProtocol p + ThyProtocol p | noTypingInvariant p -> [item] ++ mkTypingSequent p _ -> [item] @@ -160,10 +160,10 @@ -- | Prove all claims with the given heuristic optionally using the given -- bound.-proveSequents :: (Sequent -> Theorem -> Bool) +proveSequents :: (Sequent -> Theorem -> Bool) -- ^ Predicate determining theorems for reuse for proving the -- given sequent.- -> ([Named Sequent] -> Sequent -> Maybe Proof) + -> ([Named Sequent] -> Sequent -> Maybe Proof) -- ^ Proof construction function. -> Theory -> Theory proveSequents reuse prover thy =@@ -171,7 +171,7 @@ where prove (item@(ThySequent (name, se))) = do prevItems <- get- let reusableThms = [ (thmName th, thmSequent th) + let reusableThms = [ (thmName th, thmSequent th) | ThyTheorem th <- prevItems, reuse se th, thmProto th == seProto se] case prover reusableThms se of@@ -189,7 +189,7 @@ thmInfo thm = (thmName thm, thmProto thm) kept = [ thmInfo thm | ThyTheorem thm <- items, mustKeep $ thmName thm ] dependencies =- [ (thmInfo thm, dep) + [ (thmInfo thm, dep) | ThyTheorem thm <- items, dep <- S.toList . depends $ thmProof thm ] toKeep = reachableSet kept dependencies keep :: ThyItem -> Bool@@ -224,35 +224,34 @@ -- | Compose all protocol in the theory in parallel. Assumes that both protocol -- names as well as role names are /globally/ unique. composeParallel :: Theory -> Theory-composeParallel thy +composeParallel thy | length ps <= 1 = thy | otherwise = thy { thyItems = [ThyProtocol pc, ThySequent sec] ++ items3 } where (ps, items1) = partitionEithers $ do item <- thyItems thy- case item of + case item of ThyProtocol p -> return $ Left p _ -> return $ Right item (typs, items2) = partitionEithers $ do- item <- items1 + item <- items1 case item of- ThySequent (_, Sequent _ (FAtom (ATyping typ)))+ ThySequent (_, Sequent _ (FAtom (ATyping typ)) Standard) -> return $ Left typ ThySequent x -> return $ Right $ ThySequent $ prefixProtoName (seProto $ snd x) x ThyTheorem x -> return $ Right $ ThyTheorem $ prefixProtoName (thmProto x) x _ -> return $ Right item - prefixProtoName :: Protocol -> (String, a) -> (String, a) prefixProtoName p x@(n, v) | protoName p `isPrefixOf` n = x | otherwise = (protoName p ++ "_" ++ n, v)- + namec = thyName thy pc = Protocol namec (concatMap protoRoles ps) sec = ( namec ++ "_composed_typing"- , Sequent (empty pc) (FAtom (ATyping (M.unions typs)))+ , Sequent (empty pc) (FAtom (ATyping (M.unions typs))) Standard ) replaceProtocol :: Protocol -> Protocol@@ -266,16 +265,16 @@ -- | Find a protocol in the theory according to its name. lookupProtocol :: String -> Theory -> Maybe Protocol-lookupProtocol name thy = +lookupProtocol name thy = headMay [proto | ThyProtocol proto <- thyItems thy, protoName proto == name] -- | Find all unsound theorems of the theory. unsoundTheorems :: Theory -> [(Protocol, String)]-unsoundTheorems (Theory _ items) = - [ (prfProto prf, name) - | ThyTheorem (name, prf) <- items, not (sound prf) +unsoundTheorems (Theory _ items) =+ [ (prfProto prf, name)+ | ThyTheorem (name, prf) <- items, not (sound prf) ] ++- [ (seProto se, name) + [ (seProto se, name) | ThySequent (name, se) <- items ] @@ -291,7 +290,7 @@ classifyProperties toClassify = foldl classify (0,0,0) . thyItems where getSequent (ThyTheorem thm) = do- guard (toClassify $ thmName thm) + guard (toClassify $ thmName thm) return $ thmSequent thm getSequent (ThySequent se) = do guard (toClassify (fst se))@@ -306,7 +305,7 @@ -- | True, if the sequent implies 'FFalse'. isContradictionProp :: Sequent -> Bool- isContradictionProp = (FAtom AFalse ==) . seConcl+ isContradictionProp = (FAtom (ABool False) ==) . seConcl -- | True, if the sequents conclusion contains an existentially quantified -- thread.@@ -337,11 +336,11 @@ -- together with the corresponding chain rule. If no typing is given, then it -- tries to infer it from the message sequence chart. theoryOverview :: Theory -> Theory-theoryOverview thy@(Theory name items) = +theoryOverview thy@(Theory name items) = Theory name $ concat [ protoOverview proto | ThyProtocol proto <- items] where- protoOverview proto = - (ThyText "section" $ "Overview of protocol '"++protoName proto++"'") : + protoOverview proto =+ (ThyText "section" $ "Overview of protocol '"++protoName proto++"'") : ThyProtocol proto : typInvs where typInvs = case lookupTypeInvariants proto thy of@@ -355,7 +354,7 @@ \(maybe some message patterns are not unifiable)" xs -> concatMap addChainRule xs - addChainRule (item, typ) = + addChainRule (item, typ) = [ item , ThyText "text" $ unlines [ "Note that the chain rule below is only an informal representation of the"
src/Scyther/Theory/Parser.hs view
@@ -34,7 +34,7 @@ , genFunApp , funOpen , funApp- + -- ** Security protocol theorys for the free-algebra , protocol , claims@@ -90,7 +90,7 @@ -- -- NOTE: Lexical errors are thrown using 'error'. scanString :: FilePath -> String -> [Token]-scanString filename s = +scanString filename s = case runAlex s gatherUntilEOF of Left err -> error err Right kws -> kws@@ -99,7 +99,7 @@ AlexPn _ line col <- alexGetPos let pos = newPos filename line col k <- alexMonadScan- case k of + case k of EOF -> return [(pos,EOF)] _ -> do kws <- gatherUntilEOF return $ (pos,k) : kws@@ -125,7 +125,7 @@ -- | Parse a term. kw :: Keyword -> Parser s () kw t = token check- where + where check t' | t == t' = Just () | otherwise = Nothing -- | Parse content between keywords.@@ -210,7 +210,7 @@ Left err -> error $ show err -- | Parse a security protocol theory given as a string using the given--- filename for the error messages +-- filename for the error messages parseTheory :: FilePath -> IO Theory parseTheory = parseFile theory () @@ -339,7 +339,7 @@ -- | Resolve all identifiers in the pattern. resolveIds :: S.Set Id -> S.Set Id -> Pattern -> Pattern resolveIds avars mvars = go- where + where resolve = resolveId avars mvars go pt@(PConst _) = pt go pt@(PFresh _) = pt@@ -360,7 +360,7 @@ -- PRE: The role must use disjoint identifiers for fresh messages, agent -- variables, and message variables. resolveIdsLocalToRole :: Role -> Pattern -> Pattern-resolveIdsLocalToRole role = resolveIds (roleFAV role) (roleFMV role) +resolveIdsLocalToRole role = resolveIds (roleFAV role) (roleFMV role) -- Messages@@ -377,13 +377,13 @@ -- | Resolve a thread identifier resolveTID :: MonadPlus m => E.Mapping -> TID -> m Role resolveTID mapping tid =- liftMaybe' ("resolveTID: no role assigned to thread "++show tid) + liftMaybe' ("resolveTID: no role assigned to thread "++show tid) (E.threadRole tid (E.getMappingEqs mapping)) -- | Resolve a local identifier. -- PRE: Thread id must be in the role equalities. resolveLocalId :: MonadPlus m => E.Mapping -> LocalId -> m Message-resolveLocalId mapping (LocalId (i, tid)) = +resolveLocalId mapping (LocalId (i, tid)) = do role <- resolveTID mapping tid return $ inst tid (resolveId (roleFAV role) (roleFMV role) i) `mplus`@@ -395,16 +395,16 @@ role <- resolveTID mapping tid let roleId = roleName role if isPrefixOf roleId stepId- then liftMaybe' ("resolveStep: step '"++stepId++"' not part of role '"++roleName role++"'") + then liftMaybe' ("resolveStep: step '"++stepId++"' not part of role '"++roleName role++"'") (lookupRoleStep (drop (length roleId + 1) stepId) role) else fail ("resolveStep: role of step '"++stepId++"' does not agree to role '"- ++roleName role++"' of thread "++show tid) + ++roleName role++"' of thread "++show tid) -- | Parse an instantiation of a pattern. instantiation :: MonadPlus m => Parser s (E.Mapping -> m Message)-instantiation = do +instantiation = do tid <- TID <$> (funOpen "inst" *> integer <* kw COMMA)- m <- (do i <- identifier + m <- (do i <- identifier let (stepId, iTyp) = splitAt (length i - 3) i when (iTyp /= "_pt") (fail $ "inst: could not resolve pattern '"++i++"'") return $ \mapping -> do@@ -449,7 +449,7 @@ TODO: Update to new syntax Protocol := "protocol" Identifier "{" Transfer+ "}"-Transfer := Identifier "->" Identifier ":" Term +Transfer := Identifier "->" Identifier ":" Term | Identifier "->" ":" Term "<-" Identifier ":" Term | Identifier "<-" ":" Term "->" Identifier ":" Term Term := "{" Term "}" Key@@ -504,36 +504,36 @@ return [(left, Recv lbl pt), (right, Send lbl pt)] ) )- ) + ) -- | Parse a protocol. protocol :: Parser s Protocol protocol = do- name <- string "protocol" *> identifier + name <- string "protocol" *> identifier transfers <- concat <$> braced (many1 transfer) -- convert parsed transfers into role scripts let roleIds = S.fromList $ map fst transfers roles = do actor <- S.toList roleIds let steps = [ step | (i, step) <- transfers, i == actor ]- return $ Role (getId actor) + return $ Role (getId actor) (ensureFreshSteps actor (S.map addSpacePrefix roleIds) steps) return $ Protocol name roles where addSpacePrefix = Id . (' ':) . getId dropSpacePrefixes = S.map dropSpacePrefix -- | Ensure message variables are received before they are sent.- ensureFreshSteps actor possibleAvars = + ensureFreshSteps actor possibleAvars = go (S.singleton (addSpacePrefix actor)) S.empty S.empty where go _ _ _ [] = []- go avars mvars fresh (Send l pt : rs) = - let avars' = avars `S.union` (((patFMV pt `S.intersection` possibleAvars) + go avars mvars fresh (Send l pt : rs) =+ let avars' = avars `S.union` (((patFMV pt `S.intersection` possibleAvars) `S.difference` mvars) `S.difference` fresh) fresh' = fresh `S.union` ((patFMV pt `S.difference` avars') `S.difference` mvars) pt' = resolveIds (dropSpacePrefixes avars') (dropSpacePrefixes mvars) pt in Send l pt' : go avars' mvars fresh' rs- go avars mvars fresh (Recv l pt : rs) = + go avars mvars fresh (Recv l pt : rs) = let mvars' = mvars `S.union` ((patFMV pt `S.difference` avars) `S.difference` fresh) pt' = resolveIds (dropSpacePrefixes avars) (dropSpacePrefixes mvars') pt in Recv l pt' : go avars mvars' fresh rs@@ -542,23 +542,23 @@ -- Parse Claims ------------------------------------------------------------------------------ --- | Parse a claim. +-- | Parse a claim. claims :: (String -> Maybe Protocol) -> Parser s [ThyItem] claims protoMap = do let mkAxiom (name, se) = ThyTheorem (name, Axiom se)- (multiplicity, mkThyItem) <- - (string "property" *> pure (id, ThySequent)) <|> + (multiplicity, mkThyItem) <-+ (string "property" *> pure (id, ThySequent)) <|> (string "properties" *> pure ((concat <$>) . many1, ThySequent)) <|> (string "axiom" *> pure (id, mkAxiom )) protoId <- kw LPAREN *> string "of" *> identifier- proto <- liftMaybe' ("unknown protocol '"++protoId++"'") $ protoMap protoId + proto <- liftMaybe' ("unknown protocol '"++protoId++"'") $ protoMap protoId kw RPAREN multiplicity $ do claimId <- try $ identifier <* kw COLON when ('-' `elem` claimId) (fail $ "hyphen '-' not allowed in property name '"++claimId++"'") let mkThySequent (mkClaimId, se) = mkThyItem (mkClaimId claimId, se) singleSequents = map (liftM (return . (,) id) . ($ proto))- [ niagreeSequent, secrecySequent, implicationSequent, parseTypingSequent ]+ [ agreeSequent, secrecySequent, implicationSequent, parseTypingSequent ] multiSequents = map ($ proto) [ parseNonceSecrecy, parseFirstSends , parseTransferTyping, parseAutoProps ]@@ -569,74 +569,74 @@ -- msc-typing parseAutoProps :: Protocol -> Parser s [(String -> String,Sequent)] parseAutoProps proto =- string "auto" *> kw MINUS *> string "properties" *> + string "auto" *> kw MINUS *> string "properties" *> pure (concat [ transferTyping proto, firstSendSequents proto, nonceSecrecySequents proto ]) -- | Create secrecy claims for nonces and variables. parseNonceSecrecy :: Protocol -> Parser s [(String -> String,Sequent)]-parseNonceSecrecy proto = +parseNonceSecrecy proto = string "nonce" *> kw MINUS *> string "secrecy" *> pure (nonceSecrecySequents proto) -- | Create secrecy claims for nonces and variables. nonceSecrecySequents :: Protocol -> [(String -> String,Sequent)]-nonceSecrecySequents proto = - concatMap mkSequent . toposort $ protoOrd proto+nonceSecrecySequents proto =+ concatMap mkSequent . toposort $ protoOrd proto where- mkSequent (step, role) = case step of- Send _ pt -> do- n@(PFresh i) <- S.toList $ subpatterns pt- guard (not (plainUse pt n) && firstUse n)- return $ secrecySe (MFresh . Fresh) i- Recv _ pt -> do- v@(PMVar i) <- S.toList $ subpatterns pt- guard (not (plainUse pt v) && firstUse v)- return $ secrecySe (MMVar . MVar) i- where- (tid, prem0) = freshTID (empty proto)- (prefix, _) = break (step ==) $ roleSteps role- firstUse = (`S.notMember` (S.unions $ map (subpatterns . stepPat) prefix))- plainUse pt = (`S.member` splitpatterns pt)- avars = [ MAVar (AVar (LocalId (v,tid)))- | (PAVar v) <- S.toList . S.unions . map (subpatterns.stepPat) $ roleSteps role ]- secrecySe constr i =- ( (++("_"++roleName role++"_sec_"++getId i))- , Sequent prem (FAtom AFalse)- )- -- flip (Sequent proto) FFalse $ FFacts $- -- insertEv (Learn (constr (LocalId (i,tid)))) $ - -- insertEv (Step tid step) $ - -- foldr uncompromise (insertRole tid role emptyFacts) avars+ mkSequent (step, role) = case step of+ Send _ pt -> do+ n@(PFresh i) <- S.toList $ subpatterns pt+ guard (not (plainUse pt n) && firstUse n)+ return $ secrecySe (MFresh . Fresh) i+ Recv _ pt -> do+ v@(PMVar i) <- S.toList $ subpatterns pt+ guard (not (plainUse pt v) && firstUse v)+ return $ secrecySe (MMVar . MVar) i where- -- here we know that conjunction will work, as we build it by ourselves- Just (Just prem) = conjoinAtoms atoms prem0- atoms = [ AEq (E.TIDRoleEq (tid, role))- , AEv (Step tid step)- , AEv (Learn (constr (LocalId (i, tid))))- ] ++ map AUncompr avars+ (tid, prem0) = freshTID (empty proto)+ (prefix, _) = break (step ==) $ roleSteps role+ firstUse = (`S.notMember` (S.unions $ map (subpatterns . stepPat) prefix))+ plainUse pt = (`S.member` splitpatterns pt)+ avars = [ MAVar (AVar (LocalId (v,tid)))+ | (PAVar v) <- S.toList . S.unions . map (subpatterns.stepPat) $ roleSteps role ]+ secrecySe constr i =+ ( (++("_"++roleName role++"_sec_"++getId i))+ , Sequent prem (FAtom (ABool False)) Standard+ )+ -- flip (Sequent proto) FFalse $ FFacts $+ -- insertEv (Learn (constr (LocalId (i,tid)))) $+ -- insertEv (Step tid step) $+ -- foldr uncompromise (insertRole tid role emptyFacts) avars+ where+ -- here we know that conjunction will work, as we build it by ourselves+ Just (Just prem) = conjoinAtoms atoms prem0+ atoms = [ AEq (E.TIDRoleEq (tid, role))+ , AEv (Step tid step)+ , AEv (Learn (constr (LocalId (i, tid))))+ ] ++ map AUncompr avars parseFirstSends :: Protocol -> Parser s [(String -> String,Sequent)] parseFirstSends proto = string "first" *> kw MINUS *> string "sends" *> pure (firstSendSequents proto) firstSendSequents :: Protocol -> [(String -> String,Sequent)]-firstSendSequents proto = - concatMap mkRoleSequents $ protoRoles proto+firstSendSequents proto =+ concatMap mkRoleSequents $ protoRoles proto where- mkRoleSequents role = - concatMap mkStepSequents $ zip (inits steps) steps- where- steps = roleSteps role- (tid, prem0) = freshTID (empty proto)- mkStepSequents (_, Recv _ _ ) = []- mkStepSequents (prefix, step@(Send _ pt)) = do- n@(PFresh i) <- S.toList $ splitpatterns pt- guard (n `S.notMember` S.unions (map (subpatterns.stepPat) prefix))- let learn = (Learn (MFresh (Fresh (LocalId (i, tid)))))- atoms = [ AEq (E.TIDRoleEq (tid, role)), AEv learn ]- Just (Just prem) = conjoinAtoms atoms prem0- concl = FAtom (AEvOrd (Step tid step, learn))- return ( (++("_" ++ getId i)), Sequent prem concl)+ mkRoleSequents role =+ concatMap mkStepSequents $ zip (inits steps) steps+ where+ steps = roleSteps role+ (tid, prem0) = freshTID (empty proto)+ mkStepSequents (_, Recv _ _ ) = []+ mkStepSequents (prefix, step@(Send _ pt)) = do+ n@(PFresh i) <- S.toList $ splitpatterns pt+ guard (n `S.notMember` S.unions (map (subpatterns.stepPat) prefix))+ let learn = (Learn (MFresh (Fresh (LocalId (i, tid)))))+ atoms = [ AEq (E.TIDRoleEq (tid, role)), AEv learn ]+ Just (Just prem) = conjoinAtoms atoms prem0+ concl = FAtom (AEvOrd (Step tid step, learn))+ return ( (++("_" ++ getId i)), Sequent prem concl Standard) parseTransferTyping :: Protocol -> Parser s [(String -> String, Sequent)] parseTransferTyping proto =@@ -645,7 +645,7 @@ transferTyping :: Protocol -> [(String -> String, Sequent)] transferTyping proto = case mscTyping proto of Just typing -> pure- ((++"_msc_typing"), Sequent (empty proto) (FAtom (ATyping typing)))+ ((++"_msc_typing"), Sequent (empty proto) (FAtom (ATyping typing)) Standard) Nothing -> fail "transferTyping: failed to construct typing" -- | Parse secrecy formula.@@ -655,7 +655,7 @@ roleId <- funOpen "secret" *> identifier role <- liftMaybe' ("could not find role '"++roleId++"'") $ lookupRole roleId proto lbl <- kw COMMA *> (identifier <|> kw MINUS *> pure "-")- stepAtoms <- + stepAtoms <- (do step <- liftMaybe $ lookupRoleStep lbl role return $ [AEv (Step tid step)] `mplus` return [])@@ -667,22 +667,23 @@ kw COMMA uncompromised <- map (inst tid . resolveIdsLocalToRole role) <$> msgSet kw RPAREN- -- construct secrecy claim - let atoms = [ AEq (E.TIDRoleEq (tid, role)), AEv (Learn m) ] ++ stepAtoms ++ + -- construct secrecy claim+ let atoms = [ AEq (E.TIDRoleEq (tid, role)), AEv (Learn m) ] ++ stepAtoms ++ map AUncompr uncompromised prem = case conjoinAtoms atoms prem0 of Just (Just prem1) -> prem1 Just Nothing -> error "secrecySequent: secrecy claim is trivially true" Nothing -> error "secrecySequent: failed to construct secrecy claim"- - return $ Sequent prem (FAtom AFalse)++ return $ Sequent prem (FAtom (ABool False)) Standard where msgSet = braced $ sepBy pattern (kw COMMA) --- | Parse non-injective agreement claim.-niagreeSequent :: Protocol -> Parser s Sequent-niagreeSequent proto = do- funOpen "niagree"+-- | Parse (non-)injective agreement claim.+agreeSequent :: Protocol -> Parser s Sequent+agreeSequent proto = do+ qualifier <- (funOpen "niagree" *> pure Standard) <|>+ (funOpen "iagree" *> pure Injective) (roleCom, stepCom, patCom) <- roleStepPattern kw RIGHTARROW (roleRun, stepRun, patRun) <- roleStepPattern@@ -690,12 +691,12 @@ uncompromised <- map (AUncompr . instPat tidCom roleCom) <$> patSet kw RPAREN let premAtoms = [ AEq (E.TIDRoleEq (tidCom, roleCom))- , AEv (Step tidCom stepCom) - ] ++ + , AEv (Step tidCom stepCom)+ ] ++ uncompromised conclAtoms = [ AEq (E.TIDRoleEq (tidRun, roleRun))- , AEv (Step tidRun stepRun) + , AEv (Step tidRun stepRun) , AEq (E.MsgEq ( instPat tidRun roleRun patRun , instPat tidCom roleCom patCom )) ]@@ -705,11 +706,11 @@ Just Nothing -> error "niagreeSequent: claim is trivially true" Nothing -> error "niagreeSequent: failed to construct claim" - return (Sequent prem concl)+ return (Sequent prem concl qualifier) where (tidCom, prem0) = freshTID (empty proto) tidRun = succ tidCom- + patSet = braced $ sepBy pattern (kw COMMA) instPat tid role = inst tid . resolveIdsLocalToRole role @@ -723,11 +724,11 @@ parseTypingSequent :: Protocol -> Parser s Sequent parseTypingSequent proto = do typ <- M.fromList <$> doubleQuoted (many1 typeAssertion)- return $ Sequent (empty proto) (FAtom (ATyping typ))+ return $ Sequent (empty proto) (FAtom (ATyping typ)) Standard where variable = (,) <$> (Id <$> identifier) <*> (kw AT *> roleById proto) - typeAssertion = (,) <$> (variable <* kw COLON <* kw COLON) + typeAssertion = (,) <$> (variable <* kw COLON <* kw COLON) <*> (normType <$> msgTypeDisj) msgTypeTup = foldr1 TupT <$> sepBy1 msgTypeDisj (kw COMMA)@@ -735,7 +736,7 @@ msgType = asum [ pure AgentT <* string "Agent"- , (ConstT . Id) <$> singleQuoted identifier + , (ConstT . Id) <$> singleQuoted identifier , HashT <$> funApp "h" msgTypeTup , EncT <$> braced msgTypeTup <*> msgType , SymKT <$> (funOpen "k" *> msgTypeDisj) <*> (kw COMMA *> msgTypeDisj <* kw RPAREN)@@ -760,11 +761,11 @@ roleStepById proto = do stepId <- identifier let (lbl, roleId) = (reverse *** (init . reverse)) (break ('_' ==) $ reverse stepId)- role <- liftMaybe' ("could not find role '" ++ roleId ++ + role <- liftMaybe' ("could not find role '" ++ roleId ++ "' in protocol '" ++ protoName proto ++ "'") (lookupRole roleId proto) - step <- liftMaybe' ("unknown label '" ++ lbl ++ "' in role '" ++ roleId ++ "'") + step <- liftMaybe' ("unknown label '" ++ lbl ++ "' in role '" ++ roleId ++ "'") (lookupRoleStep lbl role) return (role, step) @@ -849,13 +850,13 @@ -- | Parse a raw fact. rawFacts :: MonadPlus m => Parser s [RawFact m]-rawFacts = foldl1 (<|>) - ( [try orderFacts, honestFact] ++ +rawFacts = foldl1 (<|>)+ ( [try orderFacts, honestFact] ++ map (liftM return) [roleEqFact, knowsFact, stepFact, eqFact] ) -- | Parse the conclusion "False". falseConcl :: Parser s Formula-falseConcl = doubleQuoted (string "False" *> pure (FAtom AFalse))+falseConcl = doubleQuoted (string "False" *> pure (FAtom (ABool False))) -- | Parse a conclusion stating existence of some threads of a specific -- structure.@@ -870,9 +871,9 @@ return $ foldr FExists (foldr1 FConj . map FAtom $ atoms) tids where singleThread = pure <$> (strings ["a", "thread"] *> integer)- multiThreads = strings ["threads"] *> sepBy1 integer (kw COMMA) - threads = (singleThread <|> multiThreads) <* strings ["such","that"] - + multiThreads = strings ["threads"] *> sepBy1 integer (kw COMMA)+ threads = (singleThread <|> multiThreads) <* strings ["such","that"]+ -- | Parse a conclusion. -- currently only contradiction supported conclusion :: Protocol -> E.Mapping -> Parser s Formula@@ -881,7 +882,7 @@ -- | Parse a general implication claim. implicationSequent :: Protocol -> Parser s Sequent-implicationSequent proto = do +implicationSequent proto = do facts <- concat <$> (string "premises" *> many (doubleQuoted rawFacts)) roleeqs <- extractRoleEqs proto facts let mapping = foldr (uncurry E.addTIDRoleMapping) (E.Mapping E.empty) roleeqs@@ -893,7 +894,7 @@ prems <- maybe (fail "contradictory premises") return optPrems string "imply" concl <- conclusion proto mapping- return $ Sequent prems concl+ return $ Sequent prems concl Standard {- -- | Construct the premise modifier. The given set of role equalities is used@@ -901,14 +902,14 @@ mkPremiseMod :: MonadPlus m => E.Mapping -> [RawFact m] -> Facts -> m Facts mkPremiseMod mapping rawfacts facts = do atoms <- sequence [ mkAtom mapping | RawAtom mkAtom <- rawfacts ]- optFacts <- conjoinAtoms atoms facts + optFacts <- conjoinAtoms atoms facts maybe (error "mkPremiseMod: contradictory facts") return $ optFacts -} -- | A formal comment; i.e., (header, body) formalComment :: Parser s (String, String) formalComment =- (,) <$> text begin + (,) <$> text begin <*> (concat <$> many (text content) <* text end) where text f = token (\t -> case t of TEXT ty -> f ty; _ -> mzero)@@ -920,11 +921,11 @@ end _ = mzero - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ -- Parse a theory- ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ - + -- | Parse a theory. theory :: Parser s Theory theory = do@@ -933,7 +934,7 @@ string "begin" *> addItems (Theory thyId []) <* string "end" where addItems :: Theory -> Parser s (Theory)- addItems thy = + addItems thy = do p <- protocol case lookupProtocol (protoName p) thy of Just _ -> fail $ "duplicate protocol '" ++ protoName p ++ "'"
src/Scyther/Theory/Pretty.hs view
@@ -21,11 +21,13 @@ import Control.Monad.State import Control.Monad.Reader +import Extension.Prelude (sortednub)+ import Text.Isar import Text.Dot (Dot) import qualified Scyther.Equalities as E-import Scyther.Facts +import Scyther.Facts import Scyther.Sequent import Scyther.Proof import Scyther.Theory@@ -55,8 +57,8 @@ nest = liftA2 nest . pure caseEmptyDoc = liftA3 caseEmptyDoc - + ------------------------------------------------------------------------------ -- A type-tagging identity monad transformer ------------------------------------------------------------------------------@@ -65,7 +67,7 @@ deriving( Functor, Applicative, Monad ) instance MonadTrans (TaggedIdentityT t) where- lift = TaggedIdentityT + lift = TaggedIdentityT runTaggedIdentityT :: t -> TaggedIdentityT t m a -> m a runTaggedIdentityT _ = unTaggedIdentityT@@ -82,7 +84,7 @@ theoremRef :: Document d => Protocol -> String -> m d theoremDef :: Document d => Theorem -> m d -> m d keyword :: Document d => String -> m d -> m d- noteCases :: Document d => Dot a + noteCases :: Document d => Dot a -> [(String, Dot b)] -- ^ named non-trivial cases -> [(String, Dot b)] -- ^ named trivial cases -> m d -> m d@@ -110,6 +112,7 @@ prettyTrivial :: TrivReason -> m Doc prettyMissing :: Sequent -> String -> m Doc prettySaturate :: Sequent -> m Doc+ prettyReduceInjectivity :: m Doc -> m Doc prettyForwardContradiction :: m Doc -> m Doc prettyForwardResolution :: m Doc -> Sequent -> E.Mapping -> m Doc prettyNextCase :: m Doc@@ -140,7 +143,7 @@ --------------- explainSequent :: Sequent -> String-explainSequent = +explainSequent = render . runIdentity . runTaggedIdentityT SlimOutput . prettySequent withExplainedSequent :: (Document d, MarkupMonad m) => Sequent -> m d -> m d@@ -167,29 +170,36 @@ extractTrivial (Trivial se reason) = case reason of TrivContradictoryPremises -> Just reason TrivLongTermKeySecrecy _ -> Nothing- TrivPremisesImplyConclusion -> - case seConcl se of + TrivPremisesImplyConclusion ->+ case seConcl se of FAtom (AHasType _) -> Just reason _ -> Nothing extractTrivial _ = Nothing -- | Pretty print a sequent with quantifiers, logical, and meta-logical facts for -- the premise and a single document representing the conclusion.-prettySequentParts :: PrettyMonad m => Sequent -> m (([Doc], [Doc], [Doc]), Doc)-prettySequentParts (Sequent prem concl) = do- ppPrem <- prettyFacts prem- ppConcl <- prettyFormula (eqsToMapping prem) concl- return (ppPrem, ppConcl)+prettySequentParts :: PrettyMonad m+ => Sequent+ -> m (([Doc], [Doc], [Doc]), SequentQualifier, Doc)+prettySequentParts (Sequent prem concl qualifier) = do+ ppPrem <- prettyFacts prem+ ppConcl <- prettyFormula (eqsToMapping prem) concl+ return (ppPrem, qualifier, ppConcl) +-- | Pretty-print a sequent qualifier.+prettySequentQualifier :: PrettyMonad m => SequentQualifier -> m Doc+prettySequentQualifier Standard = emptyDoc+prettySequentQualifier Injective = text "injectively"+ -- | Pretty print a proof.-prettyProof :: PrettyMonad m => +prettyProof :: PrettyMonad m => String -- ^ Name of the theorem that is proven -> (Bool, Bool) -- ^ (facts are loaded, thesis is being proven)- -> Proof + -> Proof -> m Doc prettyProof _ _ (Axiom _) = emptyDoc -prettyProof _ prfConf (Trivial _ reason) = +prettyProof _ prfConf (Trivial _ reason) = ensureProofMode prfConf <-> prettyTrivial reason prettyProof _ _ (Missing se reason showSequent)@@ -204,15 +214,20 @@ withProofSequent prf (prettySaturate se) $-$ prettyProof thName (True, False) prf +prettyProof thName prfConf (RuleApp _ ReduceInjectivity [prf]) =+ withFactsMode prfConf $+ withProofSequent prf $+ prettyReduceInjectivity $ prettyProof thName (True, False) prf+ -- A forward resolution that lead to no further proofs prettyProof _ prfConf (RuleApp se (ForwardResolution (thName, _) _) []) =- ensureProofMode prfConf <-> + ensureProofMode prfConf <-> prettyForwardContradiction (theoremRef (seProto se) thName) -- a forward resolution with a single successor. prettyProof outerThName prfConf (RuleApp se (ForwardResolution (thName, thSe) tideqs) [prf]) = wrapper $- withProofSequent prf - (prettyForwardResolution (theoremRef (seProto se) thName) thSe tideqs) $-$ + withProofSequent prf+ (prettyForwardResolution (theoremRef (seProto se) thName) thSe tideqs) $-$ prettyProof outerThName newMode prf where isTyping = isTypingFormula . seConcl $ thSe@@ -234,7 +249,7 @@ $-$ vcat (intersperse prettyNextCase (map (pure . nest 2) ppCases)) $-$ prettyChainRuleQED m trivialCases- where + where selectCase (info, prf) = withProofSequent prf (prettyChainRuleCase info) $-$ prettyProof thName (True, False) prf @@ -243,14 +258,14 @@ let mkDot = dotSequentMarked S.empty . prfSequent nonTrivialCases = zip names $ map mkDot prfs noteCases (dotSequentMarked S.empty se) nonTrivialCases []- (withExplanation (explainSequent se) pre) + (withExplanation (explainSequent se) pre) $-$ modifier (vcat . intersperse prettyNextCase $ map (nest 2 . pure) ppCases) $-$ post where (pre, modifier, post) = prettyTypeCheckInduction thName ppCase (name, prf) =- let caseDoc = prettyTypingCase thName name + let caseDoc = prettyTypingCase thName name in withProofSequent prf caseDoc $-$ prettyProof thName (True, True) prf prettyProof thName _ (RuleApp se (SplitEq eq@(MShrK _ _, MShrK _ _) [True,True]) prfs) = do@@ -260,20 +275,20 @@ prettySplitEqQed where ppCase (name, prf) =- withProofSequent prf (prettySplitEqCase name) $-$ + withProofSequent prf (prettySplitEqCase name) $-$ prettyProof thName (True, False) prf prettyProof _ _ (RuleApp _ rule _) = error $ "prettyProof: unmatched rule\n" ++ show rule- + -- | Pretty print a theory item. -- -- Note that this also ensures that quantifiers are made unique. prettyThyItem :: PrettyMonad m => [ThyItem] -> ThyItem -> m Doc-prettyThyItem items (ThyProtocol proto) = prettyProtoDef proto +prettyThyItem items (ThyProtocol proto) = prettyProtoDef proto [th | ThyTheorem th <- items, isAxiom th, thmProto th == proto]-prettyThyItem _ (ThySequent (name, se)) = +prettyThyItem _ (ThySequent (name, se)) = prettyTheorem (name, Missing (uniqueTIDQuantifiers se) ("proof to be done") False)-prettyThyItem _ (ThyTheorem (name, prf)) = +prettyThyItem _ (ThyTheorem (name, prf)) = prettyTheorem (name, prf) prettyThyItem _ (ThyText header txt) = prettyFormalComment header txt @@ -287,8 +302,8 @@ prettySoundness :: Applicative f => Theory -> f Doc prettySoundness thy = case unsoundTheorems thy of [] -> emptyDoc- ths -> vcat $ - map text ["", "(* NOTE that the proofs of the following theorems are UNSOUND:", ""] ++ + ths -> vcat $+ map text ["", "(* NOTE that the proofs of the following theorems are UNSOUND:", ""] ++ map ppThm ths ++ map text ["*)"] where@@ -317,7 +332,7 @@ withExplanation e = TaggedIdentityT . withExplanation e . unTaggedIdentityT theoremRef proto = lift . theoremRef proto theoremDef thm = TaggedIdentityT . theoremDef thm . unTaggedIdentityT- keyword tag = TaggedIdentityT . keyword tag . unTaggedIdentityT + keyword tag = TaggedIdentityT . keyword tag . unTaggedIdentityT noteCases se ntc tc = TaggedIdentityT . noteCases se ntc tc . unTaggedIdentityT @@ -329,7 +344,7 @@ isaTactic :: TrivReason -> String isaTactic TrivContradictoryPremises = "((clarsimp, order?) | order)" isaTactic (TrivLongTermKeySecrecy _) = "(fastsimp dest!: ltk_secrecy)"-isaTactic TrivPremisesImplyConclusion = "(fastsimp intro: event_predOrdI split: if_splits)" +isaTactic TrivPremisesImplyConclusion = "(fastsimp intro: event_predOrdI split: if_splits)" -- | Isabelle proof of long-term key secrecy. isaLongTermKeySecrecyProof :: Protocol -> Doc@@ -346,8 +361,8 @@ isaSequentProp :: MarkupMonad m => Sequent -> ReaderT IsarConf m Doc isaSequentProp se = do conf <- ask- ( (premTids, ppPremFacts, _), ppConcl ) <- prettySequentParts se- let quantify q vs = case vs of + ( (premTids, ppPremFacts, _), _qualifier, ppConcl ) <- prettySequentParts se+ let quantify q vs = case vs of [] -> id _ -> ((q conf <> fsep vs <> char '.') $$) . nest 2 doc | nullFacts (sePrem se) = ppConcl@@ -389,7 +404,7 @@ -- where -- singleFact f = return ([], [f], []) prettySequent se = do- ( (_, ppPremFacts, _), ppConcl) <- prettySequentParts se+ ( (_, ppPremFacts, _), _qualifier, ppConcl) <- prettySequentParts se let doc | nullFacts (sePrem se) = doubleQuotes ppConcl | otherwise = text "assumes facts:" $-$@@ -398,7 +413,7 @@ pure doc -- proof output ensureProofMode (factsLoaded, proofMode) =- (if proofMode then emptyDoc else text "thus ?thesis") <-> + (if proofMode then emptyDoc else text "thus ?thesis") <-> (if factsLoaded then emptyDoc else text "using facts") withFactsMode (_, proofMode) doc@@ -411,6 +426,39 @@ prettySaturate _ = text "note_prefix_closed facts = facts" + -- NOTE: Proof script works only for two-party authentication.+ prettyReduceInjectivity inner =+ ask >>= pp+ where+ pp conf = vcat+ [ lbrace+ , nest 2 $ vcat+ [ text "fix tid0 tid1"+ , text "assume facts: \"?prems tid0\""+ , text "have \"" <-> isaExists conf <> text "tid1. ?concs tid0 tid1\""+ , text "proof -"+ , nest 2 $ vcat+ [ text "note_unified facts = facts"+ , inner+ ]+ , text "qed"+ ]+ , rbrace+ , text "note niagree = this"+ , lbrace+ , nest 2 $ vcat+ [ text "fix i1 i2 j"+ , text "assume \"?concs i1 j" <-> isaAnd conf <-> text "?concs i2 j\""+ , text "note_unified facts = this"+ , text "have \"i1 = i2\" using facts by simp"+ ]+ , rbrace+ , text "note conc_inj = this"+ , text "show ?thesis"+ , text " by (fast intro!: iagree_to_niagree elim!: niagree conc_inj)"+ ]++ prettyForwardContradiction thRef = kwBy <-> text "(fastsimp dest:" <-> thRef <-> text "intro: event_predOrdI)" prettyForwardResolution thRef thSe mapping@@ -418,20 +466,20 @@ | otherwise = do conf <- ask let mappedPrems = applyMapping mapping $ sePrem thSe- ppPrems = zip [1..] . map (isaAtom conf (eqsToMapping mappedPrems)) $ + ppPrems = zip [1..] . map (isaAtom conf (eqsToMapping mappedPrems)) $ toAtoms mappedPrems- ppPremProve (i, premFact) = + ppPremProve (i, premFact) = text "have f" <> int i <> colon <-> doubleQuotes premFact <-> text "using facts by (auto intro: event_predOrdI)" ppProven = pure . vcat $ map ppPremProve ppPrems- ppResolve = - thRef <> text "[OF" <> (hcat $ map ((text " f" <>) . int . fst) ppPrems) <> + ppResolve =+ thRef <> text "[OF" <> (hcat $ map ((text " f" <>) . int . fst) ppPrems) <> text ", simplified]" ppProven $-$ kwNote <-> text ("facts = facts") <-> ppResolve prettyNextCase = kwNext- prettyChainRuleSplitCases = return . fst . genericChainRuleSplitCases snd + prettyChainRuleSplitCases = return . fst . genericChainRuleSplitCases snd prettyChainRuleApplication m = sep [ kwProof <> text "(sources! \"", nest 4 m <-> text "\")"] prettyChainRuleCase (name, newVars) =@@ -444,25 +492,25 @@ | otherwise = parens $ text name <-> hsep (map ppNewVar newVars) prettyChainRuleQED _ trivCases | null tactics = kwQED <-> text "(insert facts, fastsimp+)?" -- be conservative- | otherwise = + | otherwise = kwQED <-> text "(insert facts, (" <> hsep (intersperse (text "|") tactics) <> text ")+)?" where tactics = map text . nub . map isaTactic . snd $ genericChainRuleSplitCases snd trivCases - prettyTypeCheckInduction typName = + prettyTypeCheckInduction typName = ( kwProof <-> text "-" $$ ( nest 2 $ vcat- [ text "have" <-> doubleQuotes + [ text "have" <-> doubleQuotes (text "(t,r,s)" <-> (isaIn <$> ask) <-> text wellTypedStates) , kwProof <> text "(cases rule: reachable_in_approxI_ext"- , text " [OF" <-> text monoTyp <> text ", completeness_cases_rule])" + , text " [OF" <-> text monoTyp <> text ", completeness_cases_rule])" ] ) , nest 2 , (nest 2 $ vcat [ kwQED , text "thus" <-> doubleQuotes (text (typingLocale typName) <-> text "t r s") <->- text "by unfold_locales auto" + text "by unfold_locales auto" ] ) $$ kwQED )@@ -474,10 +522,10 @@ kwCase <-> text ("("++ name ++" t r s") <-> prettyTID 0 <> text") note facts = this" $-$ text ("then interpret state: "++ typingLocale typName ++" t r s") $-$ nest 2 (text "by unfold_locales auto") $-$- text "show ?case using facts" + text "show ?case using facts" -- equality splitting- prettySplitEqCase name = + prettySplitEqCase name = text "case" <-> text name <-> text "note_unified facts = this facts" prettySplitEqApplication eq =@@ -488,59 +536,63 @@ (text "thus ?thesis proof(cases rule: Kbd_cases)") prettySplitEqQed = text "qed (fastsimp+)?"- + -- theory output prettyComment comment = text "(*" <-> text comment <-> text "*)"- prettyFormalComment header comment = + prettyFormalComment header comment = text "(*" <-> text header <> colon <-> text comment <-> text "*)"- prettyProtoDef proto axioms = + prettyProtoDef proto axioms = withGraph (dotProtocol proto) $ (isar <$> ask <*> pure proto) $-$ text "" $-$ (text restrictedLocaleDecl) $-$ (nest 2 . vcat . map ppAxiom $ axioms) where- restrictedLocaleDecl = concat + restrictedLocaleDecl = concat [ "locale ", restrictedStateLocale proto, " = "++stateLocale proto , if null axioms then "" else " +" ]- ppAxiom axiom = - text "assumes" <-> text (thmName axiom) <> colon $-$ + ppAxiom axiom =+ text "assumes" <-> text (thmName axiom) <> colon $-$ (nest 2 . doubleQuotes . isaSequentProp $ thmSequent axiom) - prettyTheorem th@(name, prf) + prettyTheorem th@(name, prf) | isAxiom th = emptyDoc- | otherwise = case destTypingFormula (seConcl se) of + | otherwise = case destTypingFormula (seConcl se) of Just typ -> ppTypingLocale typ- Nothing -> ppLemma+ Nothing -> ask >>= ppLemma where- p = prfProto prf- se = prfSequent prf- ppPrf = prettyProof name (False, True) prf- - -- pretty print a lemma- ppLemma = ppProp $-$ ppPrf- where- locale = "(in " ++ restrictedStateLocale (seProto $ prfSequent prf) ++ ") "- ppName = keyword "property" (text "lemma") <-> text (locale ++ name ++ ":")- ppProp = withProofSequent prf $ - theoremDef th ppName $-$ nest 2 (prettySequent $ prfSequent prf)- -- pretty print a typing locale definition - ppTypingLocale typ = do- conf <- ask- vcat - [ keyword "property" (text "type_invariant") <-> - text name <->- text "for" <-> text (protoName p)- , text "where \"" <> text name <-> text "= mk_typing" $$- nest 2 (pure $ isar conf typ) <> char '"'- , text ""- , keyword "property" (text "sublocale") <-> - text (stateLocale p) <-> isaSublocale conf <-> text (typingLocale name)- , ppPrf- , text ""- , pure $ isaLongTermKeySecrecyProof p- ]+ p = prfProto prf+ se = prfSequent prf+ ppPrf = prettyProof name (False, True) prf + -- pretty print a lemma+ ppLemma conf =+ ppProp $-$ ppPrf+ where+ locale = "(in " ++ restrictedStateLocale (seProto $ prfSequent prf) ++ ") "+ ppName = keyword "property" (text "lemma") <-> text (locale ++ name ++ ":")+ ppProp = withProofSequent prf $ theoremDef th ppName $-$+ case seQualifier se of+ Standard -> nest 2 (prettySequent se)+ Injective -> nest 2 (pure $ isaInjectivitySequent conf se)++ -- pretty print a typing locale definition+ ppTypingLocale typ = do+ conf <- ask+ vcat+ [ keyword "property" (text "type_invariant") <->+ text name <->+ text "for" <-> text (protoName p)+ , text "where \"" <> text name <-> text "= mk_typing" $$+ nest 2 (pure $ isar conf typ) <> char '"'+ , text ""+ , keyword "property" (text "sublocale") <->+ text (stateLocale p) <-> isaSublocale conf <-> text (typingLocale name)+ , ppPrf+ , text ""+ , pure $ isaLongTermKeySecrecyProof p+ ]+ prettyTheoryDef name body = text "theory" <-> doubleQuotes (text name) $-$ text "imports" $-$@@ -552,6 +604,45 @@ imports = ["../ESPLogic"] +-- | Pretty-print an injectivity sequent. Works only for two-party+-- authentication; i.e., sequents that have one TID in the premise and one+-- additional, existentially quantified TID in the conclusion.+isaInjectivitySequent :: IsarConf -> Sequent -> Doc+isaInjectivitySequent conf se =+ doubleQuotes (+ vcat [ text "let"+ , nest 2 (sep [text "prems =", nest 2 $ ppPrems <> semi])+ , nest 2 (sep [text "concs =", nest 2 $ ppConcs])+ , text "in" <->+ isaExists conf <> text "f. inj_on f prems" <-> isaAnd conf <->+ parens ( isaForall conf <> text "i. prems i" <->+ isaImplies conf <-> text "concs i (f i)" )+ ]+ ) $-$+ text "(is \"let prems = ?prems; concs = ?concs in ?P prems concs\")"+ where+ premFormula = toFormula $ sePrem se+ premTIDs = sortednub $ formulaTIDs premFormula+ premMapping = extendMapping premTIDs premFormula E.emptyMapping+ ppPrems = abstract premMapping premTIDs premFormula++ (concTIDs0, concFormula) = dropTIDs [] $ seConcl se+ concTIDs = premTIDs ++ filter (not . (`elem` premTIDs)) concTIDs0+ concMapping = extendMapping concTIDs0 concFormula premMapping+ ppConcs = abstract concMapping concTIDs concFormula++ abstract mapping tids fm = parens $ sep+ [ isaLambda conf <> hsep (map (isar conf) tids) <> text "."+ , nest 2 (isaFormula conf mapping fm)+ ]++ extendMapping tids fm mapping = foldr ($) mapping $+ [ maybe id (E.addTIDRoleMapping tid) (findRole tid fm) | tid <- tids ]++ dropTIDs tids (FExists (Left tid) fm) = dropTIDs (tid : tids) fm+ dropTIDs tids fm = (reverse tids, fm)++ -- Slim Pretty Printing ----------------------- @@ -564,20 +655,21 @@ prettyTID = pure . sptTID prettyArbMsgId = pure . sptArbMsgId prettyMessage = pure . sptMessage- prettyFacts = pure . sptFacts + prettyFacts = pure . sptFacts prettyFormula mapping form = pure $ sptFormula mapping form -- case form of -- FFalse -> singleFact $ text "False" -- FFacts facts -> pure $ sptFacts moreRoleEqs facts -- FHasType lid Nothing -> singleFact $ text "weakly-atomic" <> parens (sptLocalId lid)- -- FHasType lid (Just ty) -> + -- FHasType lid (Just ty) -> -- singleFact $ text "hasType" <> parens (sptLocalId lid <> comma <-> sptType sptRoleStep ty) -- FTyping Nothing -> singleFact $ text "weakly-atomic" -- FTyping (Just typing) -> singleFact $ sptTyping typing -- where -- singleFact f = return ([], [f], []) prettySequent se = do- ( (premQuantified, ppPremRepr, ppPremNonRepr), ppConclRaw) <- prettySequentParts se+ ((premQuantified, ppPremRepr, ppPremNonRepr), qualifier, ppConclRaw)+ <- prettySequentParts se let ppPremFacts = ppPremRepr ++ ppPremNonRepr premQuantifier = pure $ case premQuantified of [] -> text "premises"@@ -587,31 +679,34 @@ | otherwise = premQuantifier $-$ (nest 2 . vcat $ map (kwFact . pure . doubleQuotes) ppPremFacts) $-$- (sep [text "imply", nest 2 ppConcl])+ (sep [ text "imply" <-> prettySequentQualifier qualifier+ , nest 2 ppConcl]) doc -- proof output ensureProofMode _ = emptyDoc withFactsMode _ = id prettyTrivial reason = case reason of TrivPremisesImplyConclusion -> text "tautology"- TrivLongTermKeySecrecy key -> + TrivLongTermKeySecrecy key -> text "contradicts secrecy of" <-> pure (sptMessage key) _ -> emptyDoc- prettyMissing se reason = + prettyMissing se reason = nestShort' "(*" "*)" (text reason $-$ prettySequent se) prettySaturate _ = keyword "proof" $ text "saturate"+ prettyReduceInjectivity inner =+ keyword "proof" (text "reduce_injectivity") $-$ inner prettyForwardContradiction thRef = text "contradictory due to '" <> thRef <> text "'"- prettyForwardResolution thRef _ mapping = + prettyForwardResolution thRef _ mapping = keyword "proof" (text "resolve") <-> text "'" <> thRef <> ppMapping where ppMapping | null (E.toAnyEqs (E.getMappingEqs mapping)) = emptyDoc | otherwise = text "' mapping" <-> ppTidEqs- ppTidEqs = pure . hsep . punctuate comma . map E.sptAnyEq . E.toAnyEqs $ + ppTidEqs = pure . hsep . punctuate comma . map E.sptAnyEq . E.toAnyEqs $ E.getMappingEqs mapping prettyNextCase = kwNext- prettyChainRuleSplitCases = return . fst . genericChainRuleSplitCases snd - prettyChainRuleApplication m = - sep [ keyword "proof" (text "sources") <> lparen, + prettyChainRuleSplitCases = return . fst . genericChainRuleSplitCases snd+ prettyChainRuleApplication m =+ sep [ keyword "proof" (text "sources") <> lparen, nest 4 (m <-> rparen)] prettyChainRuleCase (name, newVars) = kwCase <-> selector@@ -632,14 +727,14 @@ prettySplitEqCase name = text "case" <-> text name prettySplitEqApplication eq =- sep [ keyword "proof" (text "split") <> lparen, + sep [ keyword "proof" (text "split") <> lparen, nest 4 (pure $ E.sptAnyEq (E.MsgEq eq) <-> rparen)] prettySplitEqQed = text "qed"- + -- theory output prettyComment comment = text "/*" <-> text comment <-> text "*/"- prettyFormalComment header comment = + prettyFormalComment header comment = text (header ++ "{*") <> text comment <> text "*}" prettyProtoDef proto _ = withGraph (dotProtocol proto) (pure $ sptProtocol proto) @@ -648,19 +743,19 @@ withProofSequent prf ppProp $-$ ppPrf where p = prfProto prf- thmType | isAxiom th = "axiom" + thmType | isAxiom th = "axiom" | otherwise = "property"- ppName = keyword "property" (text thmType) <-> + ppName = keyword "property" (text thmType) <-> text ("(of "++protoName p++") "++name++":") ppProp = theoremDef th ppName $-$ nest 2 (prettySequent $ prfSequent prf) ppPrf = prettyProof name (False, True) prf prettyTheoryDef name body = vcat [- keyword "theory" (text "theory") <-> text name <-> + keyword "theory" (text "theory") <-> text name <-> keyword "theory" (text "begin") , text "" , pure body- , text "" + , text "" , keyword "theory" (text "end") ] kwFact :: (MarkupMonad m, Document d) => m d -> m d@@ -693,7 +788,7 @@ isar conf se = runReader (prettySequent se) conf instance Isar Proof where isar conf prf = runReader (prettyProof "" (False, True) prf) conf- + -- instance Isar Theorem where -- isar conf thm = runReader (prettyTheorem thm Nothing) conf
src/Text/Isar.hs view
@@ -22,7 +22,10 @@ , isaRBrack , isaMetaAll , isaExists+ , isaForall+ , isaLambda , isaAnd+ , isaImplies , isaNotIn , isaIn , isaSubsetEq@@ -50,14 +53,14 @@ import Text.PrettyPrint.Class -- | The ISAR style to be used for output.-data IsarStyle = - PlainText +data IsarStyle =+ PlainText | XSymbol deriving( Eq, Show ) -- | The configuration to be used for output. Apart from the ISAR style, the -- configuration also stores the representation of the reachable state of the--- protocol which we are reasoning about. +-- protocol which we are reasoning about. data IsarConf = IsarConf { isarStyle :: IsarStyle , isarTrace :: Doc -- ^ The ISAR code of the trace@@ -68,14 +71,14 @@ -- | Default configuration: plaintext ISAR style and reachable state @(t,r,s)@. defaultIsarConf :: IsarConf-defaultIsarConf = IsarConf PlainText (char 't') (char 'r') (char 's') +defaultIsarConf = IsarConf PlainText (char 't') (char 'r') (char 's') -- | Check if the plaintext style was chosen. isPlainStyle :: IsarConf -> Bool isPlainStyle = (PlainText ==) . isarStyle --- | Values that can be output as ISAR code. +-- | Values that can be output as ISAR code. -- -- Minimal definition: 'isar' class Isar a where@@ -117,7 +120,7 @@ -- | Nest document between two strings and indent body by @length lead + 1@. nestShort' :: Document d => String -> String -> d -> d-nestShort' lead finish = +nestShort' lead finish = nestShort (length lead + 1) (text lead) (text finish) -- | Like 'nestShort' but doesn't print the lead and finish, if the document is@@ -129,7 +132,7 @@ -- | Like 'nestShort'' but doesn't print the lead and finish, if the document is -- empty. nestShortNonEmpty' :: Document d => String -> String -> d -> d-nestShortNonEmpty' lead finish = +nestShortNonEmpty' lead finish = nestShortNonEmpty (length lead + 1) (text lead) (text finish) -- | Output text with a fixed width: if it is smaller then nothing happens,@@ -138,7 +141,7 @@ fixedWidthText n cs | length cs <= n = text cs | otherwise = text as <> zeroWidthText bs- where + where (as,bs) = splitAt n cs -- | Print string as symbol having width 1.@@ -149,13 +152,13 @@ -- separator. numbered :: Document d => d -> [d] -> d numbered _ [] = emptyDoc-numbered vsep ds = +numbered vsep ds = foldr1 ($-$) $ intersperse vsep $ map pp $ zip [(1::Int)..] ds where n = length ds nWidth = length (show n) pp (i, d) = text (flushRight nWidth (show i)) <> d- + -- | Number a list of documents with numbers terminated by "." and vertically -- separate using an empty line. numbered' :: Document d => [d] -> d@@ -169,8 +172,8 @@ -- | Isabelle representation of the exeuction system state of our operational -- semantics. isaExecutionSystemState :: IsarConf -> Doc-isaExecutionSystemState conf = - parens . hcat . punctuate comma $ +isaExecutionSystemState conf =+ parens . hcat . punctuate comma $ [isarTrace conf, isarPool conf, isarSubst conf] @@ -245,12 +248,30 @@ | isPlainStyle conf = text "? " | otherwise = symbol "\\<exists>" +-- | The forall symbol: @!@+isaForall :: Document d => IsarConf -> d+isaForall conf+ | isPlainStyle conf = text "! "+ | otherwise = symbol "\\<forall>"++-- | The lambda symbol: @%@+isaLambda :: Document d => IsarConf -> d+isaLambda conf+ | isPlainStyle conf = text "% "+ | otherwise = symbol "\\<lambda>"+ -- | The logical and symbol: @&@ isaAnd :: Document d => IsarConf -> d isaAnd conf | isPlainStyle conf = text "&" | otherwise = symbol "\\<and>" +-- | The logical and symbol: @-->@+isaImplies :: Document d => IsarConf -> d+isaImplies conf+ | isPlainStyle conf = text "-->"+ | otherwise = symbol "\\<longrightarrow>"+ -- | The non-strict subset symbol. isaSubsetEq :: Document d => IsarConf -> d isaSubsetEq conf@@ -291,7 +312,7 @@ -- | A generic text command. genTextCmd :: String -> String -> Doc-genTextCmd name content = +genTextCmd name content = sep [text name <> text "{*", nest 2 (fsep . map text $ words content), text "*}"] chapter = genTextCmd "chapter"