(* Message Algebra Copyright (c) 2021 The MITRE Corporation This program is free software: you can redistribute it and/or modify it under the terms of the BSD License as published by the University of California. *) (** * The Message Algebra *) Require Import FunInd Nat Bool DecBool Monad Proc. (** printing <- #←# *) Notation var := nat (only parsing). (** Symmetric keys *) Inductive skey: Set := | Sv: var -> skey (* Variable as key *) | Lt: var -> var -> skey. (* Long term key of two names *) (* An uninformative comment added to make coqdoc happy *) Definition skey_dec: forall x y: skey, {x = y} + {x <> y}. Proof. intros. decide equality; decide equality. Defined. #[global] Hint Resolve skey_dec : core. (** Asymmetric keys *) Inductive akey: Set := | Av: var -> akey (* Variable as key *) | Pb: var -> akey (* Public key of name *) | Pb2: string -> var -> akey. (* Tagged public key of name *) (* An uninformative comment added to make coqdoc happy *) Definition akey_dec: forall x y: akey, {x = y} + {x <> y}. Proof. intros. decide equality; try apply string_dec; decide equality. Defined. #[global] Hint Resolve akey_dec : core. Definition akey_eqb x y: bool := if akey_dec x y then true else false. (** Message algebra *) Inductive alg: Set := | Tx: var -> alg (* Text *) | Dt: var -> alg (* Data *) | Nm: var -> alg (* Name *) | Sk: skey -> alg (* Symmetric key *) | Ak: akey -> alg (* Asymmetric key *) | Ik: akey -> alg (* Inverse asymmetric key *) | Ch: var -> alg (* Channel *) | Mg: var -> alg (* Message *) | Tg: string -> alg (* Tag *) | Pr: alg -> alg -> alg (* Pair *) | En: alg -> alg -> alg (* Encryption *) | Hs: alg -> alg. (* Hash *) (* An uninformative comment added to make coqdoc happy *) Definition alg_dec: forall x y: alg, {x = y} + {x <> y}. Proof. intros. decide equality; try apply string_dec; decide equality. Defined. #[global] Hint Resolve alg_dec : core. Definition alg_eqb x y: bool := if alg_dec x y then true else false. Lemma alg_eq_correct: forall x y, alg_eqb x y = true <-> x = y. Proof. intros. unfold alg_eqb. destruct (alg_dec x y); subst; intuition. inversion H. Qed. Lemma alg_eq_complete: forall x y, alg_eqb x y = false <-> x <> y. Proof. intros. unfold alg_eqb. destruct (alg_dec x y); subst; intuition auto with *. Qed. (** Event *) Inductive evt: Set := | Sd: var -> alg -> evt (* Send *) | Rv: var -> alg -> evt. (* Recv *) (* An uninformative comment added to make coqdoc happy *) Definition evt_dec: forall x y: evt, {x = y} + {x <> y}. Proof. intros. decide equality; decide equality. Defined. #[global] Hint Resolve evt_dec : core. (** The message communicated by an event *) Definition evt_msg (e: evt): alg := match e with | Sd _ t => t | Rv _ t => t end. (** ** Kinds of Messages *) (** Is [x] a basic value? *) Definition is_basic (x: alg): bool := match x with | Tx _ => true | Dt _ => true | Nm _ => true | Sk _ => true | Ak _ => true | Ik _ => true | _ => false end. (** Is [x] a channel variable? *) Definition is_chan (x: alg): bool := match x with | Ch _ => true | _ => false end. (** Is [x] a message variable? *) Definition is_mesg_var (x: alg): bool := match x with | Mg _ => true | _ => false end. (** Is [x] a simple message, one that is not a pair, encryption, or a hash? *) Definition is_simple (x: alg): bool := match x with | Pr _ _ => false | En _ _ => false | Hs _ => false | _ => true end. (** Is [x] an elementary message, one that is not a pair, encryption, a hash, or a tag? *) Definition is_elem (x: alg): Prop := match x with | Pr _ _ => False | En _ _ => False | Hs _ => False | Tg _ => False | _ => True end. (** Is [x] not an elementary message? *) Definition is_not_elem (x: alg): Prop := match x with | Pr _ _ => True | En _ _ => True | Hs _ => True | Tg _ => True | _ => False end. Lemma is_elem_dec: forall x: alg, {is_elem x} + {is_not_elem x}. Proof. intros. unfold is_elem. unfold is_not_elem. destruct x; auto. Qed. Lemma alg_elem_ind: forall P: alg -> Prop, (forall x:alg, is_elem x -> P x) -> (forall s: string, P (Tg s)) -> (forall y: alg, P y -> forall z: alg, P z -> P (Pr y z)) -> (forall y: alg, P y -> forall z: alg, P (En y z)) -> (forall y: alg, P y -> P (Hs y)) -> forall x: alg, P x. Proof. intros. induction x; simpl; auto; apply H; simpl; auto. Qed. (** ** Inverse of a Message *) Definition inv (x: alg): alg := match x with | Ak k => Ik k | Ik k => Ak k | k => k end. Lemma inv_inv: forall (x: alg), inv (inv x) = x. Proof. intros. destruct x; simpl; auto. Qed. Lemma inv_swap: forall (x y: alg), x = inv y <-> y = inv x. Proof. split; intros; subst; rewrite inv_inv; auto. Qed. (** ** Type of an Algebra Term *) Definition type_of (x: alg): type := match x with | Tx _ => Text | Dt _ => Data | Nm _ => Name | Sk _ => Skey | Ak _ => Akey | Ik _ => Ikey | Ch _ => Chan | _ => Mesg end. (** ** Is a Term Well Formed? When a term is well formed, variables must have a consistent type. An example of a term that is not well formed is << Pr (Tx 0) (Nm 0) >> The type of variable 0 can't be both [Text] and [Name]. *) (** Lookup [v] in association list [e]. *) Fixpoint lookup {A} (v: var) (e: list (var * A)): option A := match e with | nil => None | (u, a) :: e => if u =? v then Some a else lookup v e end. (** Extend the decls if the extension is consistent with previous declarations. *) Definition extend decls (v: var) (s: type): option (list decl) := match lookup v decls with | None => Some ((v, s) :: decls) | Some s' => if type_dec s' s then Some decls else (* Type clash! *) None end. (** Is skey well formed? *) Definition well_formed_skey decls (k: skey): option (list decl) := match k with | Sv v => extend decls v Skey | Lt v v' => ds <- extend decls v Name;; extend ds v' Name end. (** Is akey well formed? *) Definition well_formed_akey decls (k: akey): option (list decl) := match k with | Av v => extend decls v Akey | Pb v => extend decls v Name | Pb2 c v => extend decls v Name end. (** Is algebra term well formed? For term [x], [well_formed [] x] returns the declarations for the variables that occur in x or [None] when the term is not well formed. Note that channels are considered not well formed in this function. *) Fixpoint well_formed decls (x: alg): option (list decl) := match x with | Tx v => extend decls v Text | Dt v => extend decls v Data | Nm v => extend decls v Name | Sk k => well_formed_skey decls k | Ak k => well_formed_akey decls k | Ik k => well_formed_akey decls k | Ch v => None (* Channels are forbidden *) | Mg v => extend decls v Mesg | Tg z => Some decls | Pr y z => ds <- well_formed decls y;; well_formed ds z | En y z => ds <- well_formed decls y;; well_formed ds z | Hs y => well_formed decls y end. Definition well_formed_event decls (x: evt): option (list decl) := match x with | Sd ch y => ds <- extend decls ch Chan;; well_formed ds y | Rv ch y => ds <- extend decls ch Chan;; well_formed ds y end. (** ** Is a Term Well Typed? Like well formed except it does not extend its declarations. *) Definition well_typed_var decls (v: var) (s: type): bool := match lookup v decls with | None => false | Some s' => ifdec (type_dec s' s) true false end. (** Is skey well typed? *) Definition well_typed_skey decls (k: skey): bool := match k with | Sv v => well_typed_var decls v Skey | Lt v v' => well_typed_var decls v Name && well_typed_var decls v' Name end. (** Is akey well typed? *) Definition well_typed_akey decls (k: akey): bool := match k with | Av v => well_typed_var decls v Akey | Pb v => well_typed_var decls v Name | Pb2 c v => well_typed_var decls v Name end. (** Is algebra term well typed? For term [x], [well_typed decl x] is true when [x] is true when it is well typed with respect to the declarations [decls]. Note that channels are considered not well typed in this function. *) Fixpoint well_typed decls (x: alg): bool := match x with | Tx v => well_typed_var decls v Text | Dt v => well_typed_var decls v Data | Nm v => well_typed_var decls v Name | Sk k => well_typed_skey decls k | Ak k => well_typed_akey decls k | Ik k => well_typed_akey decls k | Ch v => false (* Channels are forbidden *) | Mg v => well_typed_var decls v Mesg | Tg z => true | Pr y z => well_typed decls y && well_typed decls z | En y z => well_typed decls y && well_typed decls z | Hs y => well_typed decls y end. (** Is algebra term or channel well typed? *) Definition well_typed_with_chan decls (x: alg): bool := match x with | Ch v => well_typed_var decls v Chan | _ => well_typed decls x end. (** ** Measure of a term *) Fixpoint size (x: alg): nat := match x with | Pr y z => S (size y + size z) | En y z => S (size y + size z) | Hs y => S (size y) | _ => 1 end. Lemma inv_size: forall x, size (inv x) = size x. Proof. intros. destruct x; simpl; auto. Qed. (** ** Origination *) (** Carried by *) Fixpoint cb (x y: alg): bool := alg_eqb x y || match y with | Pr a b => cb x a || cb x b | En a _ => cb x a | _ => false end. Functional Scheme cb_ind := Induction for cb Sort Prop. Lemma cb_refl: forall x, cb x x = true. Proof. intros. cut (alg_eqb x x = true); intros. - destruct x; simpl; rewrite H; auto. - apply alg_eq_correct; auto. Qed. Inductive carried_by: alg -> alg -> Prop := | Carried_by_refl: forall x, carried_by x x | Carried_by_frst: forall x y z, carried_by z x -> carried_by z (Pr x y) | Carried_by_scnd: forall x y z, carried_by z y -> carried_by z (Pr x y) | Carried_by_decr: forall x y z, carried_by z x -> carried_by z (En x y). #[global] Hint Constructors carried_by : core. Lemma carried_by_trans: forall x y z, carried_by x y -> carried_by y z -> carried_by x z. Proof. intros. induction H0; simpl; auto. Qed. Lemma carried_by_reflect: forall x y, cb x y = true <-> carried_by x y. Proof. split; intros. - functional induction (cb x y); try inversion H; auto; apply orb_true_iff in H; destruct H; try inversion H; try (apply alg_eq_correct in H; subst; auto). + apply orb_true_iff in H. destruct H. * apply IHb in H; auto. * apply IHb0 in H; auto. + apply IHb in H; auto. - induction H. + apply cb_refl. + destruct (alg_dec z (Pr x y)) as [G|G]; subst. * apply cb_refl. * rewrite <- alg_eq_complete in G. destruct x; unfold cb; rewrite G; fold cb; simpl in IHcarried_by; apply orb_true_iff in IHcarried_by; destruct IHcarried_by; try inversion IHcarried_by; try rewrite H0; simpl; auto; repeat rewrite orb_true_iff; auto. + destruct (alg_dec z (Pr x y)) as [G|G]; subst. * apply cb_refl. * rewrite <- alg_eq_complete in G. destruct x; unfold cb; rewrite G; fold cb; rewrite IHcarried_by; repeat rewrite orb_true_iff; auto. + destruct (alg_dec z (En x y)) as [G|G]; subst. * apply cb_refl. * rewrite <- alg_eq_complete in G. destruct x; unfold cb; rewrite G; fold cb; simpl in IHcarried_by; apply orb_true_iff in IHcarried_by; destruct IHcarried_by; try inversion IHcarried_by; try rewrite H0; simpl; auto; repeat rewrite orb_true_iff; auto. Qed. (** Does [x] originate in [tr]? *) Fixpoint orig (x: alg) (tr: list evt): bool := match tr with | nil => false | Sd _ y :: tr => if cb x y then true else orig x tr | Rv _ y :: tr => if cb x y then false else orig x tr end. (** ** Receivable messages A message [t] is _receivable_ iff - [t] contains no occurrence of an encryption in the key of an encryption, and - [t] contains no occurrence of an encryption within a hash. *) (** Does an encryption occur in [x]? *) Fixpoint has_enc (x: alg): bool := match x with | Hs y => has_enc y | Pr y z => has_enc y || has_enc z | En _ _ => true | _ => false end. Lemma inv_has_enc: forall x, has_enc (inv x) = has_enc x. Proof. destruct x; simpl; auto. Qed. (** Is [x] receivable? *) Fixpoint receivable (x: alg): bool := match x with | Hs y => negb (has_enc y) | Pr y z => receivable y && receivable z | En y z => receivable y && negb (has_enc z) | _ => true end.