(* 

Certified email protocol by Abadi, Glew, Horne, and Pinkas
WWW2002

*)

set ignoreTypes = true.

type pkey.
type skey.
type spkey.
type sskey.
type key.
type tag.
type authtag.
type passwd.
type host.
type tquery.
type reply.
type messageid.
type tcertif.

fun channel_for_host(host): channel [data, typeConverter].
fun channel_with_host(host, channel): channel [data].

(* Public key encryption *)

fun pk(skey):pkey.
fun A(pkey, bitstring): bitstring.
reduc forall y: skey, x: bitstring; decA(y, A(pk(y),x)) = x.

(* Signatures *)

fun S(sskey, bitstring): tcertif.
fun Spk(sskey): spkey.
reduc forall y: sskey, x: bitstring; checkS(Spk(y), S(y, x)) = x.
reduc forall y: sskey, x: bitstring; getS(S(y, x)) = x.

(* Shared key encryption *)

fun E(key, bitstring): bitstring.
reduc forall y: key, x: bitstring; decE(y, E(y,x)) = x.

(* Hash function *)

fun H(bitstring): key.

(* Constants to identify messages *)

const Give,      (* Message 1 *)
      Wants,     (* Message 2 *)
      Try,       (* Message 3 *)
      Released,  (* Message 4 *)
      Received: tag [data].  (* For the judge *)

(* Constant authentication modes 
   We encode the authentication modes as pairs:
     BothAuth in the paper is coded (Auth,Auth) 
     SAuth in the paper is coded (Auth,NoAuth)
     TTPAuth in the paper is coded (NoAuth,Auth)
     NoAuth in the paper is coded (NoAuth,NoAuth)
   This makes testing whether S / TTP must authenticate R easier *)

const Auth, NoAuth: authtag [data].

(* Function from R's password to R's name *)

fun PasswdTable(passwd): host.

ifdef(`PROP4',`

(* It is assumed that an attacker cannot relate q and r = Reply(h,q) 
   except for the hosts h it creates itself *)

fun Reply(host, tquery): reply [private]. 
reduc forall x: passwd, q: tquery; ReplyOwnHost(x, q) = Reply(PasswdTable(x), q).

',`
ifdef(`SECRECY',`

(* It is assumed that an attacker cannot relate q and r = Reply(h,q) 
   except for the hosts h it creates itself *)

fun Reply(host, tquery): reply [private]. 
reduc forall x: passwd, q: tquery; ReplyOwnHost(x, q) = Reply(PasswdTable(x), q).

',

(* R or S are included in the adversary, so the adversary can compute Reply *)

fun Reply(host, tquery): reply.

)')

const NullQ: tquery [data].

reduc forall q: tquery; getAuthQ(q,NoAuth) = NullQ;
      forall q: tquery; getAuthQ(q,Auth) = q.

const NullR: reply [data].

reduc forall r: reply; getAuthR(r,NoAuth) = NullR;
      forall r: reply; getAuthR(r,Auth) = r.

const NullP: passwd [data].

reduc forall p: passwd; getAuthP(p,NoAuth) = NullP;
      forall p: passwd; getAuthP(p,Auth) = p.

(* Build a message *)

fun Message(host, messageid, bitstring): bitstring [private].

(* TTPname, Sname are public constants, representing the names
   of the hosts. We allow Sname to send to anybody, not just Rname.
   We also allow the TTP to talk to recipients other than Rname
   (represented by the adversary) on an insecure channel.
   cleartext is a public constant, header of the message from S to R *)	

free c: channel.
free cleartext: bitstring.
free Sname,Judgename,TTPname: host.
free TTPSigKey: sskey [private].
free TTPDecKey: skey [private].
free Rpwd: passwd [private].

(* Secrecy assumptions *)

not attacker(TTPDecKey).
not attacker(TTPSigKey).

(* Queries *)

event Rreceived(bitstring).
event JudgeSays(tag, host, bitstring).
event SThinksRhas(bitstring).
event Shas(host, key, bitstring, tquery, reply, bitstring).
event Rhas(channel, bitstring, key).
event TTPSendS(host, tcertif).
event TTPSendS2(host, tcertif).
event TTPSendR(channel, bitstring).
event TTPSendR2(channel, bitstring).

ifdef(`PROP1',`
  (* For proposition 1, we want to know under which
     conditions the adversary can have the message sent by S,
     when TTP authenticates R *)
  query x: host, y: messageid, z: authtag; 
        putbegin event:Shas, TTPSendS;
	event(Rreceived(Message(x,y,(z,Auth)))).
')
ifdef(`PROP2',`
  query x: bitstring;
  	putbegin event:Rhas, TTPSendR;
	event(JudgeSays(Received,PasswdTable(Rpwd),x)).
')
ifdef(`PROP3',`
  (* For proposition 3, we want to know under which
     conditions the adversary can have the message sent by S *)
  query x: host, y: messageid, z: bitstring;
  	putbegin event:Shas, TTPSendS, TTPSendS2;
	event(Rreceived(Message(x,y,z))).
')
ifdef(`PROP4',`
  query y: messageid, z: authtag;
  	putbegin event:Rhas, TTPSendR, TTPSendR2;
	event(SThinksRhas(Message(PasswdTable(Rpwd),y,(Auth,z)))).
')
ifdef(`SECRECY',`
  (* For secrecy, we also want to know under which
     conditions the adversary can have the message sent by S
     to R, that is Message(PasswdTable(Rpwd),x,z).
   *)
  query x: messageid, z: authtag; 
  	attacker(Message(PasswdTable(Rpwd),x,(Auth,z))).
  query x: messageid, z: authtag; 
  	attacker(Message(PasswdTable(Rpwd),x,(z,Auth))).
')

let processJudge(TTPEncKey: pkey, TTPVerKey: spkey) =
ifdef(`PROP4',
  0.
,ifdef(`PROP3',
  0.
,ifdef(`SECRECY',
  0.
,
  (* S must send TTP's certificate plus other information *)
  in(channel_for_host(Judgename), (certif: tcertif, Sname5: host, k5: key, cleartext5: bitstring, q5: tquery, r5: reply, em5: bitstring));
  let (=Released, S2TTP5: bitstring, Rname5: host) = checkS(TTPVerKey, certif) in
  let m5 = decE(k5,em5) in
  let hs5 = H((cleartext5, q5, r5, em5)) in
  if S2TTP5 = A(TTPEncKey, (Sname5, (Auth, Auth), (Give, k5, Rname5, hs5))) then
     (* The judge says that Rname5 has received m5 *)
     event JudgeSays(Received, Rname5, m5)
  else if S2TTP5 = A(TTPEncKey, (Sname5, (NoAuth, Auth), (Give, k5, Rname5, hs5))) then
     (* The judge says that Rname5 has received m5 *)
     event JudgeSays(Received, Rname5, m5).
)))

let processS(TTPEncKey: pkey, TTPVerKey: spkey) = 
ifdef(`PROP2',
  (* S is included in the adversary *)
  0.
,
  (* The attacker chooses possible recipients of the message *)
  in(c, recipient: host);
  (* The attacker chooses the authentication mode *)
  in(c, (sauth: authtag,ttpauth: authtag));
  (* Build the message to send *)
  new msgid: messageid;
  let m = Message(recipient,msgid,(sauth,ttpauth)) in
  (* Step 1 *)
  new k: key;
  new qtmp: tquery;
  let q = getAuthQ(qtmp,sauth) in
  let r = getAuthR(Reply(recipient,qtmp),sauth) in
  let em = E(k,m) in
  let hs = H((cleartext, q, r, em)) in
  let S2TTP = A(TTPEncKey, (Sname, (sauth,ttpauth), (Give, k, recipient, hs))) in
  event Shas(Sname, k, cleartext, q, r, m);
  out(channel_for_host(recipient), (TTPname, em, (sauth,ttpauth), cleartext, q, S2TTP));

  (* Step 4 *)
  !
  in(channel_for_host(Sname), inmess4: tcertif);
  if ttpauth = Auth then
  (
     let (=Released, =S2TTP, =recipient) = checkS(TTPVerKey, inmess4) in
     (* S knows that R has read the message *)

     event SThinksRhas(m);
     ifdef(`SECRECY',0,
       (* S sends to the judge the necessary information so that the
          judge says that R has read the message *)
       out(channel_for_host(Judgename), (inmess4, Sname, k, cleartext, q, r, em))
     )
     else out(channel_for_host(Sname), inmess4)
  ) else (
     let (=Released, =S2TTP) = checkS(TTPVerKey, inmess4) in 
     (* S knows that R has read the message, but cannot go to the judge,
        because in case S is dishonest, he could have queried TTP himself *)
     event SThinksRhas(m)
     else out(channel_for_host(Sname), inmess4)
  ).
)

let processR(Rname: host, ChannelToTTP: channel) = 
ifdef(`PROP3',
  (* R is included in the adversary *)
  out(c, ChannelToTTP);
  out(c, Rpwd)
  |
  in(c,m: bitstring);
  event Rreceived(m).
,
ifdef(`PROP1',
  (* R is included in the adversary *)
  out(c, ChannelToTTP);
  out(c, Rpwd)
  |
  in(c,m: bitstring);
  event Rreceived(m).
,
  (* Step 2 *)
  in(channel_for_host(Rname), (=TTPname, em2: bitstring, (sauth: authtag,ttpauth: authtag), cleartext2: bitstring, q2: tquery, S2TTP2: bitstring));
  let r2 = getAuthR(Reply(Rname,q2),sauth) in
  let hr = H((cleartext2, q2, r2, em2)) in

  (* Establish the secure channel R-TTP *)
  new secchannel: channel;
  out(ChannelToTTP, Rname);
  out(ChannelToTTP, secchannel);
  let outchannel = channel_with_host(TTPname, secchannel) in
  let inchannel = channel_with_host(Rname, secchannel) in

  event Rhas(secchannel,em2,hr);
  out(outchannel, (S2TTP2, (Wants, getAuthP(Rpwd,ttpauth), hr)));

  (* Step 3 *)
  !
  in(inchannel, (=Try, k3: key, =hr));
  let m3 = decE(k3, em2) in
  (* R has obtained the message m3 = m *)
  event Rreceived(m3).
))

let processTTP(ChannelToTTP: channel) = 
  (* Establish the secure channel R-TTP *)
  in(ChannelToTTP, receivername: host);
  in(ChannelToTTP, secchannel: channel);
  let inchannel = channel_with_host(TTPname, secchannel) in
  let outchannel = channel_with_host(receivername, secchannel) in

  (* Step 3 *)
  in(inchannel, (S2TTP3: bitstring, (=Wants, RPwd3: passwd, hr3: key)));
  let (Sname3: host, (sauth3: authtag,ttpauth3: authtag), (=Give, k3: key, R3: host, =hr3)) = 
     decA(TTPDecKey, S2TTP3) in
  if R3 = receivername then
  (
     if (ttpauth3, R3) = (Auth, PasswdTable(RPwd3)) then

        (* Records the event *)
        event TTPSendS(Sname3,S(TTPSigKey, (Released, S2TTP3, R3)));
        event TTPSendR(outchannel, (Try, k3, hr3));

        (* Step 3 *)
        out(outchannel, (Try, k3, hr3));

        (* Step 4 *)
        out(channel_for_host(Sname3), S(TTPSigKey, (Released, S2TTP3, R3)))

     else if ttpauth3 = NoAuth then

        (* Records the event *)
        event TTPSendS2(Sname3,S(TTPSigKey, (Released, S2TTP3)));
        event TTPSendR2(outchannel, (Try, k3, hr3));

        (* Step 3 *)
        out(outchannel, (Try, k3, hr3));

        (* Step 4 *)
        out(channel_for_host(Sname3), S(TTPSigKey, (Released, S2TTP3)))
  ).


process 
        let TTPEncKey = pk(TTPDecKey) in
        out(c, TTPEncKey);
        let TTPVerKey = Spk(TTPSigKey) in
	out(c, TTPVerKey);
	let Rname = PasswdTable(Rpwd) in
	out(c, Rname);
	new ChannelToTTP: channel;
        ((!processS(TTPEncKey, TTPVerKey)) | (!processJudge(TTPEncKey, TTPVerKey)) | 
	 (!processR(Rname, ChannelToTTP)) | (!processTTP(ChannelToTTP)) |
	 (!in(c, m: bitstring); out(ChannelToTTP, m)))

ifdef(`SECRECY',`
(* EXPECTPV FILENAME: examples/pitype/certified-mail-AbadiGlewHornePinkas/journalsas/protocol.m4.pv TAG: 1
RESULT not attacker(Message(PasswdTable(Rpwd[]),x,(Auth,z))) is true.
RESULT not attacker(Message(PasswdTable(Rpwd[]),x,(z,Auth))) is true.
0.288s (user 0.264s + system 0.024s), max rss 12816K
END *)
')
ifdef(`PROP1',`
(* EXPECTPV FILENAME: examples/pitype/certified-mail-AbadiGlewHornePinkas/journalsas/protocol.m4.pv TAG: 2
RESULT not event(Rreceived(Message(x,y,(z,Auth)))) is false.
0.180s (user 0.152s + system 0.028s), max rss 12308K
END *)
')
ifdef(`PROP2',`
(* EXPECTPV FILENAME: examples/pitype/certified-mail-AbadiGlewHornePinkas/journalsas/protocol.m4.pv TAG: 3
RESULT not event(JudgeSays(Received,PasswdTable(Rpwd[]),x)) cannot be proved.
0.120s (user 0.092s + system 0.028s), max rss 10884K
END *)
')
ifdef(`PROP3',`
(* EXPECTPV FILENAME: examples/pitype/certified-mail-AbadiGlewHornePinkas/journalsas/protocol.m4.pv TAG: 4
RESULT not event(Rreceived(Message(x,y,z))) is false.
0.220s (user 0.208s + system 0.012s), max rss 12928K
END *)
')
ifdef(`PROP4',`
(* EXPECTPV FILENAME: examples/pitype/certified-mail-AbadiGlewHornePinkas/journalsas/protocol.m4.pv TAG: 5
RESULT not event(SThinksRhas(Message(PasswdTable(Rpwd[]),y,(Auth,z)))) cannot be proved.
0.388s (user 0.364s + system 0.024s), max rss 18976K
END *)
')
