hash h

name secret : message
name key : message

abstract error : message
abstract myZero : message


mutable d : message = myZero

channel cA
channel cB.

TOY-COUNTER

V. Cheval, V. Cortier, and M. Turuani,
A Little More Conversation, a Little Less Action, a Lot More Satisfaction: Global States in ProVerif,
in 2018 IEEE 31st Computer Security Foundations Symposium (CSF), Oxford, Jul. 2018, pp. 344–358,
doi: 10.1109/CSF.2018.00032.

  • A = in(d, i : nat); out(c, h(i, s)); out(d, i + 1)
  • B = in(d, i : nat); in(c, y);
    if y = h(i, s) then
    out(c, s); out(d, i + 1)
    else
    out(d, i + 1)
  • P = ! A | ! B | out(d, 0) | ! in(d, i : nat); out(d, i)

COMMENTS

  • In this model, we do not use private channels since actions (input/condition/ update/output) are atomic.
  • The goal is to prove that the secret s is never leaked because B receives only hashes with old values of the counter.

SECURITY PROPERTIES

  • monotonicity of the counter
  • secrecy (as a reachability property)

We declare here a mutable state symbol, initialized with the public constant myZero.

abstract mySucc : message->message
abstract (~<) : message -> message -> boolean.

In order to model counter values, we use:

  • a function mySucc modelling the successor of a value;
  • an order relation ~< modelling the usual order on natural numbers.
process A =
let m = h(<d,secret>,key) in
d := mySucc(d);
out(cA, m)

process B =
in(cA,y);
if y = h(<d,secret>,key) then
d := mySucc(d);
out(cB,secret)
else
d := mySucc(d);
out(cB,error)

system ((!_i A) | (!_j B)).
System before processing:

(!_i A ) | (!_j B )

System after processing:

(!_i let m = h(pair(d,secret),key) in
d := mySucc(d); A: out(cA,m(i)); null) |
(!_j
in(cA,y);
if (y = h(pair(d,secret),key)) then
d := mySucc(d); B: out(cB,secret); null else
d := mySucc(d); B1: out(cB,error); null)

System default registered with actions (init,A,B,B1).

Processes A and B are defined as follows. They both access to the mutable state d.

axiom orderSucc (n:message): n ~< mySucc(n).

AXIOMS

We now axiomatize the order relation ~< defined above in order to be able to reason on counter values.


axiom orderTrans (n1,n2,n3:message): n1 ~< n2 && n2 ~< n3 => n1 ~< n3.

axiom orderStrict (n1,n2:message): n1 = n2 => n1 ~< n2 => false.
goal counterIncreasePred (t:timestamp):
t > init => d@pred(t) ~< d@t.
Goal counterIncreasePred :
t > init => d@pred(t) ~< d@t

SECURITY PROPERTIES

We first show that the counter increases strictly at each update.


Proof.
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t:timestamp
----------------------------------------
t > init => d@pred(t) ~< d@t


intro Hc.
[> Line 93: (intro Hc)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t:timestamp
Hc: t > init
----------------------------------------
d@pred(t) ~< d@t


use orderSucc with d@pred(t).
[> Line 94: (have ... as )
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t:timestamp
H: d@pred(t) ~< mySucc(d@pred(t))
Hc: t > init
----------------------------------------
d@pred(t) ~< d@t


case t => //.
[> Line 95: ((case t);(intro //))
[goal> Goal counterIncreasePred is proved

Qed.
Exiting proof mode.


goal counterIncrease (t,t':timestamp):
t' < t => d@t' ~< d@t.
Goal counterIncrease :
t' < t => d@t' ~< d@t

We also show a more general result than counterIncreasePred, stating here that the counter strictly increases between two distinct timestamps.

The proof is done by induction, and relies on the previous result counterIncreasePred.


Proof.
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
----------------------------------------
t' < t => d@t' ~< d@t


induction t => t Hind Ht.
[> Line 108: ((induction t);(intro t Hind Ht))
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

assert (t' < pred(t) || t' >= pred(t)) as H0 by case t. [> Line 110: ((have ((t' < pred(t)) || (t' >= pred(t))), H0); 1: by (case t))
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
H0: t' < pred(t) || t' >= pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

We use the assert tactic to introduce two cases.


case H0.
[> Line 111: (case H0)
[goal> Focused goal (1/2):
System: left:default/left, right:default/right
Variables: t,t':timestamp
H0: t' < pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

+ apply Hind in H0 => //. [> Line 118: ((apply ... in H0);(intro //))
[goal> Focused goal (1/2):
System: left:default/left, right:default/right
Variables: t,t':timestamp
H0: d@t' ~< d@pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

Case where t’ < pred(t): We first apply the induction hypothesis on t' to get d@t' ~< d@pred(t), then use the lemma counterIncreasePred with t to get d@pred(t) ~< d@t. It then remains to conclude by transitivity (applying orderTrans).


use counterIncreasePred with t; 2: by constraints.
[> Line 119: ((have ... as ); 2: by constraints)
[goal> Focused goal (1/2):
System: left:default/left, right:default/right
Variables: t,t':timestamp
H: d@pred(t) ~< d@t
H0: d@t' ~< d@pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t


by apply orderTrans _ (d@pred(t)).
[> Line 120: by (apply ... )
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
H0: t' >= pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

+ assert t' = pred(t) as Ceq by constraints. [> Line 126: ((have (t' = pred(t)), Ceq); 1: by constraints)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
Ceq: t' = pred(t)
H0: t' >= pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t

Case where t’ >= pred(t). Since t' < t we can deduce that t' = pred(t). It is then directly a consequence of the counterIncreasePred lemma.


use counterIncreasePred with t; 2: auto.
[> Line 127: ((have ... as ); 2: auto)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: t,t':timestamp
Ceq: t' = pred(t)
H: d@pred(t) ~< d@t
H0: t' >= pred(t)
Hind: forall (t0:timestamp), t0 < t => t' < t0 => d@t' ~< d@t0
Ht: t' < t
----------------------------------------
d@t' ~< d@t


by rewrite Ceq; auto.
[> Line 128: by ((rewrite ...);auto)
[goal> Goal counterIncrease is proved

Qed.
Exiting proof mode.


goal secretReach (j:index):
happens(B(j)) => cond@B(j) => false.
Goal secretReach :
happens(B(j)) => cond@B(j) => false

The following reachability property states that the secret s is never leaked (i.e. the condition of the action B(j) is never satisfied).

The proof relies on the EUF assumption: if cond@B(j) is satisfied, it would mean that the attacker has been able to forge the message h(<d,secret>,key) with d corresponding to the value of the counter at timepoint B(j), because all messages outputted so far correspond to older values of d.


Proof.
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: j:index
----------------------------------------
happens(B(j)) => cond@B(j) => false

intro Hap Hcond. [> Line 144: (intro Hap Hcond)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: j:index
Hap: happens(B(j))
Hcond: cond@B(j)
----------------------------------------
false

We start by introducing the hypotheses and expanding the cond macro.


expand cond.
[> Line 145: (expand cond)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: j:index
Hap: happens(B(j))
Hcond: input@B(j) = h(<d@pred(B(j)),secret>,key)
----------------------------------------
false

euf Hcond => Ht Heq. [> Line 147: ((euf Hcond);(intro Ht Heq))
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: i,j:index
Hap: happens(B(j))
Hcond: input@B(j) = h(<d@pred(B(j)),secret>,key)
Heq: <d@pred(A(i)),secret> = <d@pred(B(j)),secret>
Ht: A(i) < B(j)
----------------------------------------
false

Applying the euf tactic generates two new hypotheses, Ht and Heq.

assert pred(A(i)) < pred(B(j)) as H by constraints. [> Line 150: ((have (pred(A(i)) < pred(B(j))), H); 1: by constraints)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: i,j:index
H: pred(A(i)) < pred(B(j))
Hap: happens(B(j))
Hcond: input@B(j) = h(<d@pred(B(j)),secret>,key)
Heq: <d@pred(A(i)),secret> = <d@pred(B(j)),secret>
Ht: A(i) < B(j)
----------------------------------------
false

We use here the counterIncrease lemma to show that the equality Heq is not possible.


apply counterIncrease in H.
[> Line 151: (apply ... in H)
[goal> Focused goal (1/1):
System: left:default/left, right:default/right
Variables: i,j:index
H: d@pred(A(i)) ~< d@pred(B(j))
Hap: happens(B(j))
Hcond: input@B(j) = h(<d@pred(B(j)),secret>,key)
Heq: <d@pred(A(i)),secret> = <d@pred(B(j)),secret>
Ht: A(i) < B(j)
----------------------------------------
false


by apply orderStrict in H.
[> Line 152: by (apply ... in H)
[goal> Goal secretReach is proved

Qed.
Exiting proof mode.


Press the left and right arrows to do and undo an instruction.

Alternatively, you can double-click on an instruction.

This zone shows a Squirrel file. You can double-click on a comment to collapse it for better readabilility.

This zone shows the output given by Squirrel.

This zone shows the output of the previous instruction, to help identifying the change caused by the instruction.

Previously: