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).
event UAction_bitstring(stamp,bitstring).

event CheckNat(nat).

nounif x:nat; event(CheckNat(x)) [conclusion].

set inductionLemmas = true.

axiom d:channel, st:stamp, st1:stamp, st2:stamp, st3:stamp, x:bitstring, x1:bitstring, x2:bitstring, x3:bitstring, i1:nat, i2:nat, i3:nat, i4:nat;
  event(UAction_bitstring(st1,x1)) && event(UAction_bitstring(st1,x2)) ==> x1 = x2;
  event(VCell_bitstring(d,(i4,(x3,x,i1)))) && event(VCell_bitstring(d,(i2,(x1,x2,i1)))) ==> (i4,x3,x) = (i2,x1,x2);
  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,st,st1,i1)) && event(Counter(d,st,st2,i2)) ==> (i2 = i1 && st1 = st2) || (i2 <> i1 && st1 <> st2).

lemma x:nat; event(CheckNat(x)) ==> is_nat(x) [maxSubset].


(* 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));
  new st[]:stamp;
  event UAction_bitstring(st,(pid,nonce,otp));
  in(d, (i:nat,(=secretid,=k,otc:nat)));
  event CheckNat(otc);
  event CheckNat(i);
  event VCell_bitstring(d,(i,(secretid,k,otc)));
  let (=secretid,tc:nat,npr:bitstring) = sdec(otp,k) in
    event CheckNat(tc);
    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);
  event CheckNat(tc);
  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); event CheckNat(x); 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.
 --------------------------------------------------------------------------------------------------------------- *)



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' && 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' [induction].

process !P_Yubikey

(* EXPECTPV
RESULT event(CheckNat(x_2)) ==> is_nat(x_2) 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')) ==> (i_1 = i' && x_2 = x') || (i' ≥ i_1 + 1 && x_2 ≥ x' + 1) || (i_1 ≥ i' + 1 && x' ≥ x_2 + 1) is false.
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.
1.224s (user 1.160s + system 0.064s), max rss 13304K
END *)
