(* Yubikey protocol.

  This example was originally generated by GSVerif from the following processes:

      let P_Server(d:channel,pid:bitstring,secretid:bitstring,k:bitstring) =
        !
        in(c, (=pid,nonce:bitstring,otp:bitstring));
        in(d, (i:nat,(=secretid,=k,otc:nat)));
        let (=secretid,tc:nat,npr:bitstring) = sdec(otp,k) in
        if otc < tc then
          event Login(pid,k,i+1,tc);
          out(c, MyLogin(pid,k,i+1,tc));
          out(d, (i+1,(secretid,k,tc)))
        else out(d, (i,(secretid,k,otc)))
        else out(d, (i,(secretid,k,otc))).

      let P_ButtonPress(cell:channel,pid:bitstring,secretid:bitstring,k:bitstring) =
        in(cell, tc:nat);
        new nonce:bitstring; new npr:bitstring;
        event YubiPress(pid,secretid,k,tc);
        out(c, (pid,nonce,senc((secretid,tc,npr),k)));
        out(cell, tc+1).

      let P_Yubikey =
        new k:bitstring;
        new pid:bitstring;
        new secretid:bitstring;
        new d_usr:channel [precise];
        new d_srv:channel [precise];
          out(d_srv, (0,(secretid,k,0)))
        | out(d_usr, 1)
        | (! in(d_srv, x:bitstring); out(d_srv,x))
        | (! in(d_usr, x:nat); out(d_usr, x))
        | out(c,pid)
        | P_Server(d_srv,pid,secretid,k)
        | ! P_ButtonPress(d_usr,pid,secretid,k).

  GSVerif modifies a query F => \phi with F => \phi || \phi' where \phi' is a property
  that is always false. Instead of using this transformed query, we can declare the
  negation of \phi' as an axiom. See the paper on GSVerif (CSF'18) for more details
  on these properties.

  For this file, we show that we can remove one of the axioms declared by GSVerif and
  still be able to prove the query using an intermediary lemma.
*)


free c:channel.

(* Symmetric encryption --------------------------------------------------- *)
fun senc(bitstring,bitstring):bitstring.
reduc forall K:bitstring,M:bitstring; sdec(senc(M,K),K) = M.

(* Events ----------------------------------------------------------------- *)
(* 1. The 'Login' event add a nonce to identify the time t from Login(..)@t *)
(* 2. The YubiPress event is executed every time the user press the button  *)
(* ------------------------------------------------------------------------ *)

event Login(bitstring,bitstring,nat,nat).
event YubiPress(bitstring,bitstring,bitstring,nat).

(* Precise axioms --------------------------------------------------------- *)
(* 1. The 'Login' event add a nonce to identify the time t from Login(..)@t *)
(* 2. The YubiPress event is executed every time the user press the button  *)
(* ------------------------------------------------------------------------ *)

type stamp.
free id:stamp [private].
free id1:stamp [private].

event Counter(channel,stamp,stamp,nat).
event VCell_bitstring(channel,bitstring).

set inductionLemmas = true.

axiom d:channel, st1:stamp, st2:stamp, st3:stamp, x:bitstring, x1:bitstring, x2:bitstring, x3:bitstring, i1:nat, i2:nat, i3:nat, i4:nat;
  event(VCell_bitstring(d,(i2,(x3,x,i1)))) && event(VCell_bitstring(d,(i2,(x1,x2,i3)))) ==> (x3,x,i1) = (x1,x2,i3);
  event(Counter(d,st1,st2,i1)) && event(Counter(d,st1,st2,i2)) ==> i2 = i1;
  event(Counter(d,st1,st2,i1)) && event(Counter(d,st1,st3,i1)) ==> st2 = st3.


(* The Server process -------------------------------------------------------------------------------------------- *)
(* 1. P_Server created inside P_Yubikey and given the appropriate (pid, secretid, k) parameters, to help ProVerif. *)
(* 2. Corrected message checks w.r.t the use of explicit destructors (like decryption, de-pairing, etc..)          *)
(* 3. Replaced the 'smaller' test by a simple predecessor test, because ProVerif would not allow it.               *)
(* 4. The stamp st' is used as a uniq marker to identify the time when the Login (or MyLogin) event is raised.     *)
(* 5. Due to iteration, the MyLogin message/event is placed after the cell update, while Login is placed before.   *)
(* --------------------------------------------------------------------------------------------------------------- *)
let P_Server(d:channel,pid:bitstring,secretid:bitstring,k:bitstring) =
  !
  in(c, (=pid,nonce:bitstring,otp:bitstring));
  in(d, (i:nat,(=secretid,=k,otc:nat)));
  event VCell_bitstring(d,(i,(secretid,k,otc)));
  let (=secretid,tc:nat,npr:bitstring) = sdec(otp,k) in
    if otc < tc then
      new st1[]:stamp;
      event Counter(d,id1,st1,otc);
      event Counter(d,id,st1,i);
      event VCell_bitstring(d,(i + 1,(secretid,k,tc)));
      event Login(pid,k,i+1,tc);
      out(d, (i+1,(secretid,k,tc)))
    else out(d, (i,(secretid,k,otc)))
  else out(d, (i,(secretid,k,otc))).

(* The ButtonPress process --------------------------------------------------------------------------------------- *)
(* 1. Moved the cell update to the last step before the cell unlock, because it cannot be used before that.        *)
(* --------------------------------------------------------------------------------------------------------------- *)

let P_ButtonPress(cell:channel,pid:bitstring,secretid:bitstring,k:bitstring) =
  in(cell, tc:nat);
  new st[]:stamp;
  event Counter(cell,id,st,tc);
  new nonce:bitstring; new npr:bitstring;
  event YubiPress(pid,secretid,k,tc);
  out(c, (pid,nonce,senc((secretid,tc,npr),k)));
  out(cell, tc+1).

(* The Yubikey process ------------------------------------------------------------------------------------------- *)
(* 1. Starts the Server process with the appropriate initialisation data;                                          *)
(* 2. Ignores the Plugin / Unplugin processes: same as in the paper.                                               *)
(* 3. Assumes that all the YubiKey data are created by this Yubikey process.                                       *)
(* --------------------------------------------------------------------------------------------------------------- *)
let P_Yubikey =
  new k:bitstring;
  new pid:bitstring;
  new secretid:bitstring;
  new d_usr:channel;
  new d_srv:channel;
    out(d_srv, (0,(secretid,k,0))) (* Initialise server cell *)
  | out(d_usr, 1) (* Initialise client cell *)
  | (! in(d_srv, x:bitstring); out(d_srv,x))
  | (! in(d_usr, x:nat); out(d_usr, x))
  | out(c,pid)
  | P_Server(d_srv,pid,secretid,k)
  | ! P_ButtonPress(d_usr,pid,secretid,k).


(* Security property : Part 1 ------------------------------------------------------------------------------------ *)

query pid:bitstring, secretid:bitstring, k:bitstring, tc:nat, i:nat;
  event(Login(pid,k,i,tc)) ==> event(YubiPress(pid,secretid,k,tc)).

(* Security property : Part 2 ------------------------------------------------------------------------------------
    for all i, i', if Login(pid,k,i,x) && Login(pid,k,i',x') && i' <= i then either i = i' and x = x' or x' < x

    This proves that two different successful authentications are necessarily with different value.
      Moreover we prove that if a succesful authentication occurs before another one then the value
      of the former is stictly smaller than the valye of the latter.
 --------------------------------------------------------------------------------------------------------------- *)


lemma pid:bitstring, k:bitstring, i:nat, i':nat, x:nat, x':nat;
 event(Login(pid,k,i,x)) && event(Login(pid,k,i',x')) ==> (i = i' && x = x') || (i < i' && x < x') || (i > i' && x > x').

query pid:bitstring, k:bitstring, i:nat, i':nat, x:nat, x':nat;
  event(Login(pid,k,i,x)) && event(Login(pid,k,i',x)) ==> i = i'.

process !P_Yubikey

(* EXPECTPV
RESULT event(Login(pid_3,k_3,i_1,x_2)) && event(Login(pid_3,k_3,i',x')) ==> (i_1 = i' && x_2 = x') || (i' ≥ i_1 + 1 && x' ≥ x_2 + 1) || (i_1 ≥ i' + 1 && x_2 ≥ x' + 1) is true.
RESULT event(Login(pid_3,k_3,i_1,tc_2)) ==> event(YubiPress(pid_3,secretid_3,k_3,tc_2)) is true.
RESULT event(Login(pid_3,k_3,i_1,x_2)) && event(Login(pid_3,k_3,i',x_2)) ==> i_1 = i' is true.
0.080s (user 0.060s + system 0.020s), max rss 9728K
END *)
