


(*---------------------------- ProVerif internal flags ----------------------------*)

set attacker = active.
set reconstructTrace = false.
set simplifyDerivation = true.
set unifyDerivation = true.
set predicatesImplementable=nocheck.
(* set redundantHypElim=true. *)



(*---------------------------- Flags determining attacker power ----------------------------*)

letfun NetworkSetup() = true.
letfun NetworkAttackers() = false.
letfun MaliciousUsers() = false.
letfun UntrustedJavaScript() = false.
letfun HttpRedirectors() = false.
letfun MaliciousServers() = false. (* Use with care: This option may cause ProVerif to use up all your memory. *)
letfun UntrustedWebsites() = false. (* try this with redirector => *)
letfun DynamicCompromise() = false. 



(*---------------------------- Auxiliary functions ----------------------------*)

letfun AND(b1:bool,b2:bool) =  if b1 = true && b2 = true then true else false.



(*---------------------------- Standard Crypto primitives ----------------------------*)

(* Types *)
type Secret.

(* Authenticated Encryption *)
type symkey.
fun aenc(bitstring,symkey): bitstring.
reduc forall b:bitstring,k:symkey; adec(aenc(b,k),k) = b.

(* Nonce *)
fun mkCorrelator(symkey):bitstring [data].

(* Key Derivation *)
fun pbkdf2(Secret,bitstring): symkey.
fun keygen(bitstring): symkey.

(* MAC *)
type Mac.
fun mac(bitstring, symkey):Mac.

(* Public-key Encryption and Signatures *)
type privkey.
type pubkey.
fun pk(privkey):pubkey.
fun wrap(symkey,pubkey): bitstring.
reduc forall k:symkey,dk:privkey; unwrap(wrap(k,pk(dk)),dk) = k.
fun sign(bitstring,privkey): bitstring.
reduc forall b:bitstring,sk:privkey; verify(sign(b,sk),pk(sk)) = b.



(*---------------------------- HTTP model ----------------------------*)

(* Types *)

type Uri. (* Uri modeled as http[s]://Host/Path?Params *)
type Protocol.
type Domain.
type SubDomain.
type Host.
type Path.
type Params.
type Origin.
type Data.
type Cookie.
type CookiePair.
type Session.
type HttpRequest.
type HttpResponse.
type Ajax.
type XDR.
type Headers. (* Headers = referer, cookie, XmlHttpRequest *)
type Page.

(* Constructors *)

fun noneUri():Uri.
fun https():Protocol [data].
fun http():Protocol [data].
fun empty():SubDomain [data].
fun domainHost(SubDomain,Domain):Host [data].
letfun topLevelHost(d:Domain) = domainHost(empty(), d).
fun orig(Protocol,Host):Origin  [data].
fun uri(Protocol,Host,Path,Params):Uri  [data].
fun nullParams():Params.
fun nullData():Data.
fun nullBits():bitstring.
fun slash():Path.
fun httpGet():HttpRequest [data].
fun httpPost(Params):HttpRequest [data].
fun httpOk(bitstring):HttpResponse [data].
fun httpRedirect(Uri):HttpResponse [data].
fun httperror():HttpResponse [data].
fun notajax():Ajax [data].
fun ajax():Ajax [data].
fun notxdr():XDR [data].
fun xdr():XDR [data].
fun headers(Uri, CookiePair, Ajax): Headers [data].
fun httpReq(Uri,Headers,HttpRequest) : bitstring [data].
fun httpResp(HttpResponse,CookiePair,XDR) : bitstring [data].
fun aboutBlank():Page.
fun mkPage(Uri):Page [private].

(* Destructors *)

reduc forall r:Protocol,h:Host,p:Path,q:Params; path(uri(r,h,p,q)) = p.
reduc forall r:Protocol,h:Host,p:Path,q:Params; host(uri(r,h,p,q)) = h.
reduc forall r:Protocol,h:Host,p:Path,q:Params; protocol(uri(r,h,p,q)) = r.
reduc forall r:Protocol,h:Host,p:Path,q:Params; params(uri(r,h,p,q)) = q.
reduc forall r:Protocol,h:Host,p:Path,q:Params; origin(uri(r,h,p,q)) = orig(r,h).
reduc forall r:Protocol,h:Host; originhost(orig(r,h)) = h.
reduc forall c:CookiePair, u:Uri, a:Ajax; getCookie(headers(u, c, a)) = c.
reduc forall c:CookiePair, u:Uri, a:Ajax; gerReferer(headers(u, c, a)) = u.
reduc forall c:CookiePair, u:Uri, a:Ajax; getAjax(headers(u, c, a)) = a.

(* Cookie constructors *)

fun cookie(bitstring,bitstring,bitstring,bitstring):Cookie [data].
fun pathCookie(Cookie,Path):Cookie [data].
fun cookiePair(Cookie,Cookie):CookiePair [data].
fun nullCookieFragment():bitstring [data].
letfun nullCookie() = cookie(nullCookieFragment(),nullCookieFragment(),nullCookieFragment(),nullCookieFragment()).
letfun nullCookiePair() = cookiePair(nullCookie(), nullCookie()).

(* Cookie destructors *)

reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; 
         domcookie(cookie(sh,ih,sj,ij)) = cookie(nullCookieFragment(),nullCookieFragment(),sj,ij).

reduc forall bs:bitstring;
         mkHttpOnlySecureCookie(bs) = cookie(nullCookieFragment(),bs,nullCookieFragment(),nullCookieFragment()).

reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring,nsj:bitstring,nij:bitstring;
         updatedomcookie(cookie(sh,ih,sj,ij),nsj,nij) = cookie(sh,ih,nsj,nij).

reduc forall sj:bitstring; sjcookie(sj) = cookie(nullCookieFragment(),nullCookieFragment(),sj,nullCookieFragment()).
reduc forall sh:bitstring; shcookie(sh) = cookie(sh,nullCookieFragment(),nullCookieFragment(),nullCookieFragment()).
reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; securehttp(cookie(sh,ih,sj,ij)) = sh.
reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; securejs(cookie(sh,ih,sj,ij)) = sj.
reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; insecurehttp(cookie(sh,ih,sj,ij)) = ih.
reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; insecurejs(cookie(sh,ih,sj,ij)) = ij.
reduc forall sh:bitstring,ih:bitstring,sj:bitstring,ij:bitstring; secure(cookie(sh,ih,sj,ij)) = cookie(sh,nullCookieFragment(),sj,ij).

(* Cookie auxiliary functions *)
letfun extractPathCookie(c:Cookie, h:Path) =
    let pathCookie(nc,nh) = c in
        (nc, nh)
     else
        (c, h).
letfun protocolCookie(c:Cookie, o:Origin) =
    let orig(r,h) = o in
    (if r = https() 
     then c
     else cookie(nullCookieFragment(),insecurehttp(c),nullCookieFragment(),insecurejs(c))).



(* ---------------------------- HTML --------------------- *) 

type HTMLtag.
fun formGen(HTMLtag,Uri,Cookie): bitstring [data].
reduc forall c: Cookie, tag: HTMLtag, action:Uri; formId(formGen(tag,action,c))=c .
reduc forall c: Cookie, tag: HTMLtag, action:Uri; formAction(formGen(tag,action,c))=action .
reduc forall c: Cookie, tag: HTMLtag, action:Uri; formTag(formGen(tag,action,c))=tag .



(*---------------------------- Browser and server ----------------------------*)

type Id.
type Principal.
type Browser.

(* Initialization *)

fun privateKey(Principal, Host):privkey [private].
fun browser(Principal,Id): Browser [private].

reduc forall p:Principal,id:Id; principal(browser(p,id)) = p.

letfun makeServer(p:Principal, h:Host) =
    let sk = privateKey(p, h) in
    sk.

letfun makeUser(p:Principal, h:Host) =
    new user: Id;
    new pwd: Secret;
    (user,pwd).

(* Tables *)

table cookies(Browser,Host,Path,Cookie).
table storage(Browser,Origin,Data).
table credentials(Host, Principal, Id, bitstring).
table serverSessions(Host,Cookie,Session).
table pageOrigin(Page,Origin,Path,Uri).
table pageParent(bitstring,Page,Page).
table serverIdentities(Host,Principal,pubkey,privkey,XDR).

(* Attacker channels *)

free admin : channel [private].
free config : channel [private].
free pub:channel. (* channel that represents public knowledge *)

(* Server channels *)

free httpServerRequest : channel [private].
free httpServerResponse : channel [private].

(* Browser Channels *)

fun rawRequest():channel [private].
fun internalRequest(Browser):channel [private].
fun browserRequest(Browser):channel [private].
fun ajaxRequest(Browser):channel [private].
fun ajaxResponse(Browser):channel [private].
fun getCookieStorage(Browser):channel [private].
fun setCookieStorage(Browser):channel [private].
fun newPage(Browser):channel [private].
fun pageClick(Browser):channel [private].
fun upgradeDomain(Browser):channel [private].

(* The network channel *)

free net:channel.

(* Derived notion of host-based encryption *)

letfun hostenc(x:bitstring, host:Host, p:pubkey) =
  new k:symkey;
  (k,(wrap(k,p),aenc((host,x),k))).
letfun hostdec(m:bitstring, host:Host, s:privkey) =
  let (ek:bitstring,ex:bitstring) = m in
  let k = unwrap(ek,s) in
  let (=host,x:bitstring) = adec(ex,k) in
  (k,x).

(* Derived notion of HTTP/HTTPS encryption *)

free nullkey:symkey.
letfun reqenc(o:Origin,x:bitstring,p:pubkey) =
  let orig(=https(),h) = o in
      hostenc(x,h,p)
  else (nullkey,x).
letfun reqdec(o:Origin,m:bitstring,s:privkey) =
  let orig(=https(),h) = o in
      hostdec(m,h,s)
  else (nullkey,m).

letfun respenc(o:Origin,x:bitstring,k:symkey) =
  let orig(=https(),h) = o in
      aenc(x,k) else x.
letfun respdec(o:Origin,m:bitstring,k:symkey) =
  let orig(=https(),h) = o in
  adec(m,k) else m.



(*---------------------------- Attacker capabilities ----------------------------*)

type Command.
type AppCommand.

fun createServer(Principal, Host, XDR): Command [data].
fun createUser(Principal, Host, Id, bitstring): Command [data].
fun surf(Browser,Uri): Command [data].
fun appCommand(AppCommand): Command [data].
fun openBrowser(Principal): Command [data].
fun initRequest(Browser,Uri,HttpRequest) : Command [data].
fun initPageClick(Browser,Page,Uri,HttpRequest) : Command [data].
fun initAjaxRequest(Browser,Page,Uri,HttpRequest) : Command [data].
fun fetchCookieStorage(Browser,Page) : Command [data].
fun injectCookieStorage(Browser,Page,CookiePair,Data) : Command [data].
fun forceUpgradeDomain(Browser,Page) : Command [data].
fun createMaliciousUser(Principal, Host, Id, bitstring): Command [data].
fun createMaliciousServer(Principal, XDR): Command [data].
fun compromiseServer(Host): Command [data].
fun compromiseUser(Id, Host): Command [data]. 
fun compromiseConnection(Uri,Uri): Command [data].
fun injectMessage(Uri,Uri,bitstring): Command [data].
fun interceptMessage(Uri,Uri): Command [data].
fun startUntrustedApp(Uri): Command [data].
fun getServerRequest(Uri): Command [data].
fun sendServerResponse(Uri,bitstring): Command [data].
fun getClientResponse(Browser,Uri): Command [data].
fun sendClientRequest(Browser,Uri,Uri,HttpRequest,Uri,Page,Ajax): Command [data].
fun startRedirector(Uri,Uri): Command[data].
fun httpRequestResponse(Headers,Uri,HttpRequest): Command[data].

letfun commandEnabled(c:Command) =
   let createServer(p,h,x) = c in NetworkSetup() else
   let createUser(p,h,i,ck) = c in NetworkSetup() else
   let openBrowser(p) = c in NetworkSetup() else
   let surf(b,r) = c in NetworkSetup() else
   let appCommand(ac) = c in NetworkSetup() else
   let initRequest(b,u,r) = c in UntrustedJavaScript() else
   let initPageClick(b,p,u,r) = c in UntrustedJavaScript() else
   let initAjaxRequest(b,p,u,r) = c in UntrustedJavaScript() else
   let fetchCookieStorage(b,p) = c in UntrustedJavaScript() else
   let injectCookieStorage(b,p,ck,s) = c in UntrustedJavaScript() else
   let forceUpgradeDomain(b,p) = c in UntrustedJavaScript() else
   let createMaliciousServer(p,x) = c in MaliciousServers() else
   let createMaliciousUser(p,h,a,x) = c in MaliciousUsers() else
   let compromiseServer(h) = c in DynamicCompromise() else
   let compromiseUser(i,h) = c in DynamicCompromise() else
   let compromiseConnection(e1,e2) = c in NetworkAttackers() else
   let injectMessage(e1,e2,x) = c in NetworkAttackers() else
   let interceptMessage(e1,e2) = c in NetworkAttackers() else
   let startUntrustedApp(a) = c in UntrustedWebsites() else
   let getServerRequest(a) = c in UntrustedWebsites() else
   let sendServerResponse(a,x) = c in UntrustedWebsites() else
   let httpRequestResponse(hdr,u,r) = c in UntrustedWebsites() else
   let getClientResponse(b,a) = c in AND(UntrustedWebsites(),UntrustedJavaScript()) else
   let sendClientRequest(b,u1,u2,req,ref,pId,aj) = c in AND(UntrustedWebsites(),UntrustedJavaScript()) else
   let startRedirector(a,b) = c in HttpRedirectors() else 
   false.



(*---------------------------- Distributed security policies ----------------------------*)

type Fact.

pred assume(Fact) [block].
pred fact(Fact).
event Expect(Fact).

fun Compromised(Principal) : Fact.
fun Says(Principal,Fact) : Fact.

clauses forall e:Fact; assume(e) -> fact(e);
        forall p:Principal,e:Fact; fact(e) -> fact(Says(p,e));
        forall p:Principal,e:Fact; fact(Compromised(p)) -> fact(Says(p,e)).
(* query e:Fact; event(Expect(e)) ==> fact(e). 
query e:Fact; event(Expect(e)).
*)


(* ---------------------------- Events --------------------- *)

fun User(Principal,Id,Host): Fact.
fun Server(Principal,Host): Fact.











(* ------------------------------------------------------------ *)
(* ---------------------------- Processes --------------------- *)
(* ------------------------------------------------------------ *)



(* ---------------------------- Http Server --------------------- *)

let HttpServer() =
  in(net,(b:Browser,o:Origin,m:bitstring));
  get serverIdentities(=originhost(o),pr,pk_P,sk_P,xdrp) in
  let (k:symkey,httpReq(u,hs,req)) = reqdec(o,m,sk_P) in
  if origin(u) = o then
  let corr = mkCorrelator(k) in
  out(httpServerRequest,(u,hs,req,corr));
  in(httpServerResponse,(=u,resp:HttpResponse,cookieOut:CookiePair,=corr));
  out(net,(o,b,respenc(o,httpResp(resp,cookieOut,xdrp),k))).



(*---------------------------- Http Client ----------------------------*)

let HttpClient(b:Browser) =
    (in (browserRequest(b),(u:Uri,req:HttpRequest));
     out (internalRequest(b),(u,req,noneUri,aboutBlank(),notajax())))
  | (in (pageClick(b),(p:Page,u:Uri,req:HttpRequest));
     get pageOrigin(=p,oldorig,h,ref) in
     out (internalRequest(b),(u,req,ref,aboutBlank(),notajax())))
  | (in (ajaxRequest(b),(p:Page,u:Uri,req:HttpRequest));
     get pageOrigin(=p,oldorig,h,ref) in
     out (internalRequest(b),(u,req,ref,p,ajax())))
  | (in (internalRequest(b),(u:Uri,req:HttpRequest,ref:Uri,p:Page,aj:Ajax));
     let o = origin(u) in
     let hst = host(u) in
     let h = path(u) in
     get cookies(=b,=hst,=slash(),cs) in
     get cookies(=b,=hst,=h,ch) in
     let header = headers(ref, cookiePair(protocolCookie(cs, o),protocolCookie(ch, o)), aj) in
     get serverIdentities(=hst,pr,pk_host,xxxx,xdrp) in
     let m = httpReq(u,header,req) in
     let (k:symkey,e:bitstring) = reqenc(o,m,pk_host) in
     out(net,(b, o, e));
     in(net,(=o,=b,x:bitstring));
     let httpResp(resp,cookiePair(csn,chn),foo) = respdec(o,x,k) in
     insert cookies(b,hst,slash(),csn);
     let (nc:Cookie,nh:Path) = extractPathCookie(chn, h) in
     insert cookies(b,hst,nh,nc);
     ((let httpOk(dataIn) = resp in
      if p = aboutBlank() then
        (let p1 = mkPage(u) in
         insert pageOrigin(p1,o,h,u);
         out (newPage(b),(p1,u,dataIn)))
      else
        (if aj = ajax() then
           (get pageOrigin(=p,oldorig,oldh,olduri) in
            if (foo = xdr() || oldorig = o) then
            out (ajaxResponse(b),(p,u,dataIn)))))
     |(let httpRedirect(redir) = resp in
       out (internalRequest(b),(redir,httpGet(),ref,p,notajax())))))
  | (get pageOrigin(p,o,h,ref) in
     get cookies(=b,=originhost(o),=slash(),cs) in
     get cookies(=b,=originhost(o),=h,ch) in
     get storage(=b,=o,s) in
     out (getCookieStorage(b),(p,cookiePair(protocolCookie(domcookie(cs),o),protocolCookie(domcookie(ch),o)),s)))
  | (in (setCookieStorage(b),(p:Page,c:CookiePair,ns:Data));
     get pageOrigin(=p,o,h,ref) in
     let cookiePair(cs,ch) = c in
     get cookies(=b,=originhost(o),=h,ckh) in
     insert cookies(b,originhost(o),h,updatedomcookie(ckh,securejs(ch),insecurejs(ch)));
     get cookies(=b,=originhost(o),=slash(),cks) in
     insert cookies(b,originhost(o),h,updatedomcookie(cks,securejs(cs),insecurejs(cs)));
     insert storage(b,o,ns))
  | (in(pub,(o:Origin,h:Path));
     insert cookies(b,originhost(o),h,nullCookie());
     insert storage(b,o,nullData()))
  | (in (upgradeDomain(b),(p:Page));
     get pageOrigin(=p,o,h,ref) in
     let orig(r,domainHost(s,d)) = o in 
     insert pageOrigin(p,orig(r,topLevelHost(d)),h,ref)).



(* ---------------------------- HttpProxy --------------------- *)

let HttpProxy() =
    in (rawRequest(),(b:Browser,u:Uri,req:HttpRequest,hdr:Headers,corr:bitstring));
    let o = origin(u) in
    let hst = host(u) in
    let h = path(u) in
    get serverIdentities(=hst,pr,pk_host,xxxx,xdrp) in
    let m = httpReq(u,hdr,req) in
    let (k:symkey,e:bitstring) = reqenc(o,m,pk_host) in
    out(net,(b, o, e));
    in(net,(=o,=b,x:bitstring));
    let httpResp(resp,c,foo) = respdec(o,x,k) in
    out(rawRequest(),(b,resp,c,foo,corr)).



(* ---------------------------- HTTP Redirector --------------- *)

let HttpRedirector() =
   in (admin,startRedirector(uri(pr,h,p,q), uri(pr1,h1,p1,q1)));
 (!(in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   let uri(=https(),=h,=p,q2) = u in
   let c = getCookie(hs) in
   let redirectUri = uri(pr1,h1,p1,params(u)) in
   out(httpServerResponse,(u,httpRedirect(redirectUri),c,corr)))).

fun pageform(Uri,Params):bitstring [data].



(* ---------------------------- User surfing the web --------------- *)

let WebSurfer() =
   (in (admin,openBrowser(p));
    new bId:Id;
    let b = browser(p,bId) in
    out(pub,b);
    ((out (config,(openBrowser(p),b))) | (!HttpClient(b)) |
    !((in(admin,surf(=b,u:Uri));
       out(browserRequest(b),(u,httpGet())))
|
     (in (newPage(b),(p1:Page,u:Uri,pageform(formUri,form)));
      out(pageClick(b),(p1,formUri,httpPost(form))))))).



(* ---------------------------- Create users and attackers --------------- *)

let CredentialFactory() =
(
    (in(admin,createUser(p:Principal,h:Host,uId:Id,c:bitstring));
(*    if assume(User(p,uId,h)) then   *)
    insert credentials(h, p, uId, c);
    out(config,createUser(p,h,uId,c)))

|  (in(admin,createMaliciousUser(p:Principal,h:Host,uId:Id,c:bitstring));
(*    if assume(User(p,uId,h)) then  *)
    insert credentials(h,p,uId,c);
    out(config,createMaliciousUser(p,h,uId,c)))

|   (in(admin,createServer(p, h, x));
    let skey:privkey = makeServer(p, h) in
    if assume(Server(p,h)) then   
    insert serverIdentities(h,p,pk(skey),skey,x);
    out(pub,(orig(https(),h),slash()));
    out(config,(createServer(p, h, x), pk(skey))))

|  (in(admin,createMaliciousServer(p, x));
    new d:Domain;
    let h = topLevelHost(d) in
    let skey:privkey = makeServer(p, h) in
    out(pub, (d, h, pk(skey)));
    insert serverIdentities(h,p,pk(skey),skey,x);
    out(config,(createMaliciousServer(p, x),(h,pk(skey),skey))))

|  (in(admin,compromiseUser(uId,h));
    get credentials(=h,p,=uId,c) in
    out (config,(compromiseUser(uId,h),c)))

|  (in(admin,compromiseServer(h));
    get serverIdentities(=h,p,pubk,sk,x) in
    out (config,(compromiseServer(h),sk)))
).



(* ---------------------------- Attacker proxy --------------- *)

let AttackerProxy() =
   (in (pub,x:Command);
    if commandEnabled(x) = true then
    out(admin,x);
    in (config,(=x,y:bitstring));
    out(pub,y)).



(*---------------------------- Script Attacker ----------------------------*)

let ScriptAttacker() =
    (in (admin,initRequest(b,u,req));
     out (browserRequest(b),(u,req)))
  | (in (admin,initPageClick(b,p,u,req));
     out (pageClick(b),(p,u,req)))
  | (in (admin,initAjaxRequest(b,p,u,req));
     out (ajaxRequest(b),(p,u,req));
     in (ajaxResponse(b),(=p,=u,dataIn:bitstring));
     out (pub,dataIn))
  | (in (admin,fetchCookieStorage(b:Browser,p:Page));
     in (getCookieStorage(b),(=p,c:CookiePair,s:Data));
     out (pub, (c,s)))
  | (in (admin,injectCookieStorage(b,p,c,s));
     out (setCookieStorage(b),(p,c,s)))
  | (in (admin,forceUpgradeDomain(b,p));
     out (upgradeDomain(b),p)).



(* ---------------------------- Network Attacker --------------- *)

let NetworkAttacker() =
    in (admin,compromiseConnection(e1,e2));
((  in (admin,injectMessage(=e1,=e2,x));
    out (net,(e1,e2,x)))
|(  in (admin,injectMessage(=e2,=e1,x));
    out (net,(e2,e1,x)))
|(  in (admin,interceptMessage(=e1,=e2));
    in (net,(=e1,=e2,x:bitstring));
    out (config,(interceptMessage(e1,e2),x)))
|(  in (admin,interceptMessage(=e2,=e1));
    in (net,(=e2,=e1,x:bitstring));
    out (config,(interceptMessage(e2,e1),x)))).



(* ---------------------------- Untrusted server application --------------- *)

let UntrustedApp() =
   in(admin,startUntrustedApp(uri(p,h,a,q)));
   out(pub,(orig(p,h),a));
   out(pub,(orig(p,h),slash()));
  ((in(admin,getServerRequest(uri(=p,=h,=a,q1)));
    in(httpServerRequest,(u:Uri,x:HttpRequest,hs:Headers,corr:bitstring));
    if h = host(u) && a = path(u) && protocol(u) = p then
    out(config,(getServerRequest(uri(p,h,a,q)),(u,x,hs,corr))))
|  (in(admin,sendServerResponse(uri(=p,=h,=a,q2),(u:Uri,x:HttpResponse,ck:Cookie,corr:bitstring)));
    if h = host(u) && a = path(u) && protocol(u) = p then
    out(httpServerResponse,(u,x,ck,corr)))
|  (in(admin,getClientResponse(b,=uri(p,h,a,q)));
    in(newPage(b),(pId:Page,u:Uri,d:bitstring));
    if h = host(u) && a = path(u) && protocol(u) = p then
    out(config,(getClientResponse(b,uri(p,h,a,q)),(pId,u,d))))
|  (in(admin,sendClientRequest(b,=uri(p,h,a,q),u,req,ref,pId,aj));
    if h = host(u) && a = path(u) && protocol(u) = p then
    out(internalRequest(b),(u,req,ref,pId,aj)))
|  (in(admin,httpRequestResponse(hdr,u,req));
    new corr:bitstring;
    new b:Browser;
    out(rawRequest(),(b,u,req,hdr,corr));
    in(rawRequest(),(=b, resp:HttpResponse, c:CookiePair, foo:XDR, =corr));
    out(config,(httpRequestResponse(hdr,u,req),(c,resp))))).



(* ---------------------------- The Network --------------------- *)

let Network()=
    (!ScriptAttacker()) | (!AttackerProxy()) | (!NetworkAttacker()) | (!WebSurfer())
   | (!HttpRedirector()) | (!UntrustedApp()) | (!HttpServer()) | (!CredentialFactory())
   | (!HttpProxy()).




fun localLogin(Id):Session [data].
fun socialLogin(Uri,Id,bitstring):Session [data].

letfun loginCSRFCheck() = true. (* true enables login CSRF checking *)
letfun dataCSRFCheck() = true. (* true enables data form CSRF checking *)
letfun autoSocialLoginCSRFCheck() = true. (* true enables automatic social login CSRF checking *)
letfun stateSocialLoginCSRFCheck() = true. (* true enables social login CSRF checking *)
letfun socialDataCSRFCheck() = true. (* true enables social data form CSRF checking *)
letfun secureCookies() = true. (* true enables HTTPS-only cookies for login/data *)
letfun passwordHash() = false. (* true enables Login JavaScript that hashes password *)
letfun oauthImplicitFlow() = true. (* true enables OAuth User-Agent flow *)
letfun oauthExplicitFlow() = false. (* true enables OAuth Web Server flow *)

fun Authorize(Principal,Browser,Cookie,Uri,Uri):Fact.
fun SocialLogin(Principal,Browser,Cookie,Uri,Uri):Fact.
fun SocialLoginAccepted(Principal,bitstring,Uri,Uri,Cookie):Fact.
fun SocialLoginDone(Principal,Browser,Cookie,Uri,Uri):Fact.

fun DataWrite(Principal,Browser,Cookie,Uri,bitstring): Fact.
fun DataStoreAuthorized(Principal,Uri,Id,Cookie,bitstring): Fact.
fun DataStore(Principal,Host,Path,Id,bitstring): Fact.
fun DataRead(Principal,Browser,Cookie,Uri,bitstring): Fact.
fun DataReleaseAuthorized(Principal,Uri,Id,Cookie): Fact.

letfun checkSecureCookie(u:Uri,c:Cookie) =
   if secureCookies() = false &&
      secure(c) <> nullCookie() then true   (* WHY? *)
   else
    if protocol(u) = https() &&
       secureCookies() = true &&
       secure(c) <> nullCookie() then true
    else false.

fun newCorrCookie(Uri,bitstring):bitstring [private].
letfun makeCorrCookie(u:Uri,c:bitstring) =
   let sC = newCorrCookie(u,c) in
   shcookie(sC).

letfun makeClient(p:Principal,ep:Uri,as:Uri) =
    new appId: Id;
    new appSecret: Secret;
    (appId,appSecret).

(* Login App *)

fun startLogin(Host,Path,Browser): AppCommand [data].
fun startOAuthPermissionApp(Host,Path,Browser): AppCommand [data].
fun loginPath():Path [data].
fun permissionPath():Path.
fun loginSuccess(): bitstring [data].
fun loginFailure(): bitstring [data].
free loginForm : HTMLtag .
fun loginFormReply(bitstring,Id,Secret): Params [data].

fun Login(Principal,Browser,Uri,Id):Fact.
fun LoginAuthorized(Principal,Id,Uri,Cookie):Fact.
fun ValidSession(Principal,Browser,Cookie,Uri): Fact.


table authorizedClients(Host,Id,Id).
table oauthClients(Host,Principal,Id,Secret,Uri).
table userData(Principal,Id,Host,bitstring).

fun oauthLogin() : Params [data].
fun oauthGetData() : Params [data].
fun oauthSetData() : Params [data].

fun oauthRequest(Id,Cookie,Uri): Params [data].
fun oauthCode(Id,bitstring,Uri,bitstring): Params [data].
fun oauthToken(Cookie,bitstring): Params [data].
fun oauthCodeToken(bitstring): Params [data].

fun oauthTokenRequest(Id,Secret,bitstring): Params [data].
fun oauthDataRequest(bitstring): Params [data].
fun oauthDataSubmit(bitstring,bitstring): Params [data].
fun socialDataFormReply(bitstring,bitstring): Params [data].
fun oauthFormReply(bitstring): Params [data].

fun oauthClientPath(Path):Path [data].
fun oauthServerPath(Path):Path [data].

fun createOAuthClient(Principal,Uri,Uri):AppCommand [data].
fun startOAuthServerApp(Host,Path):AppCommand [data].
fun startOAuthClientApp(Host,Path,Uri):AppCommand [data].


fun dataGen(HTMLtag,bitstring): bitstring [data].
reduc forall tag: HTMLtag, d:bitstring; dataTag(dataGen(tag,d))=tag .
reduc forall tag: HTMLtag, d:bitstring; dataPayload(dataGen(tag,d))=d .
fun dataFormReply(bitstring,bitstring): Params [data]. (* or UDATA? *)
fun genData(Principal,Cookie):bitstring [private].

free socialDataForm: HTMLtag .
free socialDataDisplay: HTMLtag .

fun authCode(Uri,Id,Cookie,Id,Cookie): bitstring [private].
fun authToken(bitstring): bitstring [private].


reduc forall as:Uri,appId:Id,state:Cookie,uid:Id,c:Cookie;
      codeAppId(authCode(as,appId,state,uid,c),as) = appId.
reduc forall as:Uri,appId:Id,state:Cookie,uid:Id,c:Cookie;
      tokenUId(authToken(authCode(as,appId,state,uid,c)),as) = uid.

reduc forall as:Uri,appId:Id,state:Cookie,uid:Id,c:Cookie;
      verifyCode(authCode(as,appId,state,uid,c),as,appId,uid,c) = true.
reduc forall as:Uri,appId:Id,state:Cookie,uid:Id,c:Cookie;
      verifyToken(authToken(authCode(as,appId,state,uid,c)),as,appId,uid) = true.
reduc forall as:Uri,appId:Id,state:Cookie,uid:Id,c:Cookie;
      verifyTokenUid(authToken(authCode(as,appId,state,uid,c)),as,uid) = true.


(* Forms *)
fun oauthForm(Uri) : HTMLtag [data].
fun socialLoginForm(Uri) : HTMLtag [data].
fun socialLoginFormReply(bitstring): Params [data].
fun socialLoginSuccess(Uri): bitstring [data].

let OAuthCommonClientApp(h:Host,app:Path,as:Uri) =
(
  (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   let uri(=https(),=h,=oauthClientPath(app),q) = u in
   if oauthLogin() = params(u) then
   let c = makeCorrCookie(u,corr) in
   out(httpServerResponse,(u,httpOk(formGen(socialLoginForm(as),u,c)),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   let uri(=https(),=h,=oauthClientPath(app),q) = u in
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   let httpPost(socialLoginFormReply(d)) = req in
   if (formTag(d) = socialLoginForm(as)) then
   if (autoSocialLoginCSRFCheck() = false || formId(d) = cs) then
   get oauthClients(=host(as),sp,appId,appSecret,=uri(protocol(as),host(u),oauthServerPath(path(u)),nullParams())) in
   let reqUri = uri(protocol(as),host(as), path(as), oauthRequest(appId,cs,u)) in
   out(httpServerResponse,(u,httpRedirect(reqUri),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   let uri(=https(),=h,=oauthClientPath(app),q) = u in
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   let httpPost(socialDataFormReply(f,d)) = req in
   if (formTag(f) = socialDataForm) then
   if (socialDataCSRFCheck() = false || formId(f) = cs) then
   get serverSessions(=h,=cs,socialLogin(=as,appId,token)) in
   let ash = host(as) in
   let oauthServerPath(asapp) = path(as) in
   get oauthClients(=ash,sp,=appId,appSecret,=u) in
   let reqUri = uri(protocol(as), host(as), path(as), oauthDataSubmit(token,d)) in
   new b:Browser;
   new ncorr:bitstring;
   out(rawRequest(),(b, reqUri, httpGet(), headers(noneUri(), nullCookiePair(), notajax()), ncorr)))

| (in(httpServerRequest,(u:Uri,=httpGet(),hs:Headers,corr:bitstring));
   if oauthImplicitFlow() = true then
   let uri(=https(),=h,=oauthClientPath(app),q) = u in
   if oauthGetData() = params(u) then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,socialLogin(=as,appId,token)) in
   let ash = host(as) in
   let oauthServerPath(asapp) = path(as) in
   get oauthClients(=ash,sp,=appId,appSecret,=u) in
   let reqUri = uri(protocol(as),host(as),path(as),oauthDataRequest(token)) in
   new ncorr:bitstring;
   new b:Browser;
   out(rawRequest(),(b, reqUri, httpGet(), headers(noneUri(), nullCookiePair(), notajax()), ncorr));
   in(rawRequest(),(=b, resp:HttpResponse, c1:CookiePair, foo:XDR, =ncorr));
   let httpOk(dataIn)= resp in
   if socialDataDisplay = dataTag(dataIn) then
   out(httpServerResponse,(u,httpOk(dataIn), c, corr)))
).

let OAuthImplicitClientApp(h:Host,app:Path,as:Uri) =
(
  (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if oauthImplicitFlow() = true then
   let uri(=https(),=h,=oauthClientPath(app),q) = u in
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   let oauthToken(state,token) = q in
   if (stateSocialLoginCSRFCheck() = false || cs = state) then
   if checkSecureCookie(u,state) = true then
   get oauthClients(=host(as),sp,appId,appSecret,=u) in
   if oauthServerPath(path(u)) = path(as) then
   let reqUri = uri(protocol(as),host(as),path(as),oauthDataRequest(token)) in
   new ncorr:bitstring;
   new b:Browser;
   out(rawRequest(),(b, reqUri, httpGet(), headers(noneUri(), nullCookiePair(), notajax()), ncorr));
   in(rawRequest(),(=b, resp:HttpResponse, c1:CookiePair, foo:XDR, =ncorr));
   let httpOk(dataIn) = resp in
   if socialDataDisplay = dataTag(dataIn) then
   event Expect(SocialLoginAccepted(sp,token,u,as,state)); 
   insert serverSessions(h, cs, socialLogin(as,appId,token));
   out(httpServerResponse,(u, httpOk(socialLoginSuccess(as)), cookiePair(cs, nullCookie()), corr)))
).


let OAuthImplicitServerApp(h:Host,app:Path) =
( (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   let uri(=https(),=h,=oauthServerPath(slash()),q) = u in
   if oauthImplicitFlow() = true then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let oauthRequest(appId,CSRFToken,redirectURI) = params(u) in
   get authorizedClients(=h,=appId,=uId) in
   let code = authCode(u,appId,CSRFToken,uId,cs) in
   let token = authToken(code) in
   get oauthClients(=h,clp,=appId,appSecret,=redirectURI) in
   let tokenUri = uri(protocol(redirectURI),host(redirectURI),path(redirectURI),oauthToken(CSRFToken,token)) in
   out(httpServerResponse,(u,httpRedirect(tokenUri),c,corr)))
).

let OAuthCommonServerApp(h:Host,app:Path) =
( (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let oauthRequest(appId,state,redirectEp) = params(u) in
   let f = formGen(oauthForm(redirectEp),u,cs) in
   out(httpServerResponse,(u,httpOk(f),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let httpPost(oauthFormReply(d)) = req in
   let oauthRequest(appId,state,redirectEp) = params(u) in
   if (oauthForm(redirectEp) = formTag(d)) then
   if (loginCSRFCheck() = false || formId(d) = cs) then
   insert authorizedClients(h, appId, uId);
   out(httpServerResponse,(u,httpOk(nullBits()),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   let oauthDataRequest(token) = params(u) in
   let uId = tokenUId(token,u) in
(*   if assume(DataRequestAccepted(sp,h,app,token,uId)) then   *)
   get userData(p,=uId,=h,d) in
   out(httpServerResponse,(u,httpOk(dataGen(socialDataDisplay,d)),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   let oauthDataSubmit(token,d) = params(u) in
   let uId = tokenUId(token,u) in
   get userData(p,=uId,=h,data) in
(*   if assume(DataSubmitAccepted(sp,h,app,token,uId,d)) then*)
(*   get serverIdentities(=h,sp,xx,yy,zz) in
   if assume(DataStore(sp,h,uId,cs,d)) then  
*)
   insert userData(p,uId,h,d))
).


let OAuthPermissionsUserAgent(h:Host,a:Path,b:Browser) =
  (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   if h = host(u) && oauthServerPath(a) = path(u) && protocol(u) = https() then
   let p = principal(b) in
   let oauthForm(cl) = formTag(d) in
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
   if assume(Authorize(p,b,sid,cl,u)) then  
   out(pageClick(b),(p1,formAction(d),httpPost(oauthFormReply(d))))).

let OAuthPermissionsServerApp(h:Host,app:Path) =
( (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let oauthRequest(appId,state,redirectEp) = params(u) in
   let f = formGen(oauthForm(redirectEp),u,cs) in
   out(httpServerResponse,(u,httpOk(f),c,corr)))

| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   if h = host(u) && oauthServerPath(app) = path(u) && protocol(u) = https() then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let httpPost(oauthFormReply(d)) = req in
   let oauthRequest(appId,state,redirectEp) = params(u) in
   if (oauthForm(redirectEp) = formTag(d)) then
   if (loginCSRFCheck() = false || formId(d) = cs) then

   insert authorizedClients(h,appId,uId);
   out(httpServerResponse,(u,httpOk(nullBits()),c,corr)))
).

(* DataServer App *)

free dataDisplay: HTMLtag .
free dataForm : HTMLtag .
fun startDataApp(Host,Path): AppCommand [data].
fun startPermissionApp(Host,Path): AppCommand [data].
fun addUserData(Id,Host,Path): AppCommand [data].
fun dataPath(Path):Path [data].

let DataServerApp(h:Host,app:Path) =
( (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && dataPath(app) = path(u) then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   get userData(p,=uId,=h,data) in
   get serverIdentities(=h,sp,xx,yy,zz) in
   event Expect(DataReleaseAuthorized(sp,u,uId,cs));  
   out(httpServerResponse,(u,httpOk(dataGen(dataDisplay,data)),c,corr)))
| (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   if h = host(u) && dataPath(app) = path(u) then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   get serverSessions(=h,=cs,localLogin(uId)) in
   let df = formGen(dataForm,u,cs) in
   out(httpServerResponse,(u,httpOk(df),c,corr)))
| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   if h = host(u) && dataPath(app) = path(u) then
   let c = getCookie(hs) in
   let cookiePair(cs,ch) = c in
   if checkSecureCookie(u,cs) = true then
   let httpPost(dataFormReply(d,newData)) = req in
   if (formTag(d) = dataForm) then
   if (dataCSRFCheck() = false || formId(d) = cs) then
   get serverSessions(=h,=cs,localLogin(uId)) in
   get serverIdentities(=h,sp,xx,yy,zz) in
   event Expect(DataStoreAuthorized(sp,u,uId,cs,newData)); 
(*   if assume(DataStore(sp,h,app,uId,newData)) then   *)
   get credentials(=h,p,=uId,sec) in
   insert userData(p,uId,h,newData);  (* Use with care: Causes ProVerif to use up a lot of memory *)
   out(httpServerResponse,(u,httpOk(nullBits()),c,corr)))).


let OAuthClientUserAgent(b:Browser) =
(
  (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let socialLoginForm(as) = formTag(d) in
   let (=https(),h:Host,oauthClientPath(app)) = (protocol(u),host(u),path(u)) in
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
   if assume(SocialLogin(p,b,sid,u,as)) then  
   out(pageClick(b),(p1,formAction(d),httpPost(socialLoginFormReply(d)))))

| (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let (=https(),h:Host,oauthClientPath(app)) = (protocol(u),host(u),path(u)) in
   let socialLoginSuccess(as) = d in 
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
   event Expect(SocialLoginDone(p,b,sid,u,as)))

| (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let oauthClientPath(a) = path(u) in
   if (socialDataForm = formTag(d)) then
   in (getCookieStorage(b),(=p1,cookiePair(cs,ch),z:Data));
   let newData = genData(p,cs) in
   if assume(DataWrite(p,b,cs,u,newData)) then  
   out(pageClick(b),(p1,formAction(d),httpPost(socialDataFormReply(d,newData)))))

| (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let oauthClientPath(a) = path(u) in
   if(socialDataDisplay = dataTag(d)) then 
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
   event Expect(DataRead(p,b,sid,u,dataPayload(d))) 
  )
).

let DataServerUserAgent(b:Browser) =
 ( (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let dataPath(a) = path(u) in
   if(dataForm = formTag(d)) then
   in (getCookieStorage(b),(=p1,cookiePair(cs,ch),z:Data));
   let newData = genData(p,cs) in
   if assume(DataWrite(p,b,cs,u,newData)) then  
   out(pageClick(b),(p1,formAction(d),httpPost(dataFormReply(d,newData))))
  )
| (in (newPage(b),(p1:Page, u:Uri, d:bitstring));
   let p = principal(b) in
   let dataPath(a) = path(u) in
   if(dataDisplay = dataTag(d)) then 
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
   event Expect(DataRead(p,b,sid,u,dataPayload(d)))
  )
).

let LoginUserAgent(h:Host, a:Path, b:Browser) =
(
  (in(newPage(b),(p1:Page, loginURI:Uri, d:bitstring));
   let p = principal(b) in
   if loginForm = formTag(d) then
   let uri(=https(), =h, =a, =nullParams()) = loginURI in
   get credentials(=h,=p,uId,c) in
   let (=uId,pwd:Secret) = c in
(*   if assume(Login(p,b,loginURI,uId)) then *)
   out(pageClick(b),(p1,loginURI,httpPost(loginFormReply(d,uId,pwd))))
  )
|
 (in(newPage(b), (p1:Page,loginURI:Uri,d:bitstring));
   let p = principal(b) in
   if loginSuccess() = d then
   let uri(=https(), =h, =a, =nullParams()) = loginURI in
   in (getCookieStorage(b),(=p1,cookiePair(sid,ch),z:Data));
(*   event Expect(ValidSession(p,b,sid,loginURI));*)
   out(net, (h)))
 ).


let LoginApp(h:Host, a:Path) =
(
  (in(httpServerRequest,(u:Uri,hs:Headers,=httpGet(),corr:bitstring));
   let uri(=https(), =h, =a, q) = u in
   new state:Cookie;
   out(httpServerResponse,(u,httpOk(formGen(loginForm,u,state)),nullCookiePair(),corr)))
| (in(httpServerRequest,(u:Uri,hs:Headers,req:HttpRequest,corr:bitstring));
   let uri(=https(), =h, =a, q) = u in
   let httpPost(loginFormReply(d,uId,pwd)) = req in
   if (formTag(d) = loginForm) then
   get credentials(=h,p,=uId,(=uId,=pwd)) in
   let sid = mkHttpOnlySecureCookie((h,uId)) in
   get serverIdentities(=h,sp,xx,yy,zz) in
   event Expect(LoginAuthorized(sp,uId,u,sid));
   insert serverSessions(h,sid,localLogin(uId));
   let c = cookiePair(sid,nullCookie()) in
   out(httpServerResponse,(u,httpOk(loginSuccess()),c,corr)))).


let ServerFactory () =
(   (in (admin,appCommand(startLogin(h:Host,a:Path, b:Browser)));
     (LoginUserAgent(h,a,b)) | LoginApp(h, a))
(*
 |   (in (admin,appCommand(startOAuthPermissionApp(h:Host,a:Path, b:Browser)));
     (OAuthPermissionsUserAgent(h,a,b)) | OAuthPermissionsServerApp(h,a))
 |  (in (admin,appCommand(startDataApp(h:Host,a:Path)));
     DataServerApp(h,a))
 |  (in(admin,appCommand(addUserData(uId:Id,h:Host,a:Path)));
    get credentials(=h,p,=uId,sec) in
    new data:bitstring;
    get serverIdentities(=h,sp,xx,yy,zz) in
    if assume(DataStore(sp,h,a,uId,data)) then 
    insert userData(p,uId,h,data))
 |  (in (admin,appCommand(createOAuthClient(clp:Principal,cl:Uri,as:Uri)));
    if protocol(as) = https() && protocol(cl) = https() then
    let (appId:Id,appSecret:Secret) = makeClient(clp,cl,as) in
    let oauthClientPath(cla) = path(cl) in
    insert oauthClients(host(as),clp,appId,appSecret,cl))
 | (in (admin,appCommand(startOAuthClientApp(clh:Host,clp:Path,as:Uri)));
    get oauthClients(=host(as),p,appId,appSecret,cl) in
    if host(cl) = clh && path(cl) = oauthClientPath(clp) && protocol(cl) = https() && protocol(as) = https() then
     OAuthCommonClientApp(clh,clp,as) | OAuthImplicitClientApp(clh,clp,as))
 |  (in (admin,appCommand(startOAuthServerApp(h:Host,a:Path)));
     OAuthCommonServerApp(h,a) | OAuthImplicitServerApp(h,a))
*)
).

free alice:Principal.
free eve:Principal.
free yahoo:Principal.
free facebook:Principal.
free yahoocom:Domain.
free facebookcom:Domain.
free evecom:Domain.
event Check().

let Init() =
    (
        let h = topLevelHost(facebookcom) in
        out (admin,createServer(facebook, h, notxdr()));
        in (config,((=createServer(facebook, h, notxdr()),fbk:pubkey)));

        let h1 = topLevelHost(yahoocom) in
        out (admin,createServer(yahoo, h1, notxdr()));
        in (config,((=createServer(yahoo, h1, notxdr()),yahook:pubkey)));

        let eveHost = topLevelHost(evecom) in
        out (admin,createServer(eve, eveHost, notxdr()));
        in (config,(=createServer(eve, eveHost, notxdr()),eveKey:pubkey));

        out (admin,appCommand(startOAuthServerApp(h,slash())));

        (* Seed the Browser Cookie Jar *)
        out (pub,(orig(https(),h), oauthServerPath(slash())));
        out (pub,(orig(https(),h), oauthServerPath(permissionPath())));
        out (pub,(orig(https(),h), loginPath()));

        let(aliceId:Id,alicePwd:Secret) = makeUser(alice, h) in
        let userCmd = createUser(alice,h,aliceId,(aliceId,alicePwd)) in
        out (admin, userCmd);
        in (config,=userCmd);

        out (admin,openBrowser(alice));
        in (config,(=openBrowser(alice),aliceBrowser:Browser));
        out (admin,appCommand(startLogin(h,loginPath(),aliceBrowser)));

        let facebookLoginUri = uri(https(), h, loginPath(), nullParams()) in
        out (admin,surf(aliceBrowser,facebookLoginUri));

        let yahooUri = uri(https(), h1, oauthClientPath(slash()), nullParams()) in
        let facebookOAuthUri = uri(https(), h, oauthServerPath(slash()), nullParams()) in

        out (admin,appCommand(createOAuthClient(yahoo, yahooUri, facebookOAuthUri)));
        
        (* Seed Cookies *)
        out (pub,(orig(https(),h1), oauthClientPath(slash())));

        out (admin,appCommand(startOAuthPermissionApp(h,permissionPath(),aliceBrowser)));

        (*  Alice Authorizes Yahoo on Facebook *)
        new state:Cookie;
        get oauthClients(=h,clp,appId,xx,cl) in
        let facebookASUri = uri(https(), h, oauthServerPath(permissionPath()),oauthRequest(appId,state,yahooUri)) in
        out (admin,surf(aliceBrowser,facebookASUri));

        out (admin,startUntrustedApp(uri(http(),eveHost,slash(),nullParams())));
        let eveUri = uri(http(), eveHost, slash(), nullParams()) in

        (* Alice browses to Eve's website *)
        out (admin,surf(aliceBrowser,eveUri));

        out (admin,getServerRequest(eveUri));
        in (config,(=getServerRequest(eveUri),
                (u1:Uri,req:HttpRequest,hs:Headers,corr1:bitstring)));

        out (admin,startRedirector(uri(https(),h1,oauthClientPath(slash()),nullParams()), eveUri));

        (* Eve redirects Alice to Facebook's OAuth Server
           using redirect_uri=yahoo_eve_redirector *)
        new state1:Cookie;
        let authUri = uri(https(), h, oauthServerPath(slash()),
                oauthRequest(appId,state1,yahooUri)) in
        let C2 = sendServerResponse(eveUri,
                  (u1,httpRedirect(authUri),
               nullCookiePair(),corr1)) in
        out(admin,C2);

        (* Alice is redirected to yahoo_eve_redirector with
         her access token for Yahoo, which redirects her back to Eve *)
        let C5 = getServerRequest(eveUri) in
        out (admin,C5);
        in (config,(=C5,(u2:Uri,req2:HttpRequest,
                   hs2:Params,corr2:bitstring)));
        let oauthToken(=state1,token) = params(u2) in
        event Check()
    ).

pred endpointUri(Uri,Protocol,Host,Path).
clauses forall p:Protocol,h:Host,a:Path,x:Params; 
        endpointUri(uri(p,h,a,x),p,h,a).

(*
clauses 
	forall up:Principal,sp:Principal,x:Id,h:Host,app:Path,e:Uri,b:Browser,sid:Cookie;
	fact(Server(sp,h)) &&
	fact(User(up,x,h)) &&
	endpointUri(e,https(),h,loginPath()) &&
	fact(Says(up,Login(up,b,e,x))) ->    
	fact(LoginAuthorized(sp,x,e,sid)).
*)
(*
clauses 
	forall up:Principal,cp:Principal,sp:Principal,
	       x:Id,cl:Uri,as:Uri,b:Browser,sid:Cookie,
	       token:bitstring,state:Cookie,
	       h:Host,app:Path,h':Host,app':Path;

	fact(Server(sp,h)) && 
	fact(Server(cp,h')) && 
	fact(User(up,x,h)) &&
	endpointUri(as,https(),h,oauthServerPath(app)) &&
	endpointUri(cl,https(),h',oauthClientPath(app')) &&
	fact(Says(up,SocialLogin(up,b,sid,cl,as))) ->    
	fact(SocialLoginAccepted(cp,token,cl,as,state)).

clauses 
	forall up:Principal,cp:Principal,sp:Principal,
	       x:Id,cl:Uri,as:Uri,b:Browser,sid:Cookie,
	       token:bitstring,state:Cookie,
	       h:Host,app:Path,h':Host,app':Path;

	fact(Server(sp,h)) && 
	fact(Server(cp,h')) && 
	fact(User(up,x,h)) &&
	endpointUri(as,https(),h,oauthServerPath(app)) &&
	endpointUri(cl,https(),h',oauthClientPath(app')) &&
	fact(Says(up,SocialLogin(up,b,sid,cl,as))) &&   
	fact(Says(up,SocialLoginAccepted(cp,token,cl,as,state))) ->    
	fact(SocialLoginDone(up,b,sid,cl,as)).

clauses
	forall up:Principal,sp:Principal,x:Id,h:Host,app:Path,
	b:Browser,sid:Cookie,data:bitstring,e:Uri,b':Browser,
	e':Uri,h':Host,app':Path,sid':Cookie,e'':Uri;
	
	fact(Server(sp,h)) &&
	fact(User(up,x,h)) &&
	endpointUri(e,https(),h,oauthServerPath(app)) &&
	endpointUri(e,https(),h,oauthServerPath(app)) &&
	endpointUri(e'',https(),h',loginPath()) &&
	fact(Says(up,Login(up,b,sid,e'',x))) && 
	fact(Says(up,SocialLogin(up,b,sid,e',e))) &&
	fact(DataStore(sp,h,app,x,data))  ->
	fact(DataRead(up,b',sid',e',data)).

*)

query p:Principal,u:Id,sid:Cookie,e:Uri; 
      event(Expect(LoginAuthorized(p,u,e,sid))).

process(
    (* Init() | *) Network() | !ServerFactory()
)

(* EXPECTPV
RESULT not event(Expect(LoginAuthorized(p_33,u_23,e_2,sid_2))) cannot be proved.
0.384s (user 0.372s + system 0.012s), max rss 27100K
END *)
