packages feed

scyther-proof 0.4.1.0 → 0.5.0.0

raw patch · 21 files changed

+1234/−969 lines, 21 files

Files

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"