tamarin-prover-0.8.4.0: data/examples/ake/dh/UM_one_pass_attack.spthy
theory UM_one_pass_attack
begin
/*
The one-pass UM protocol. See
"Combined Security Analysis of the One- and Three-Pass Unified Model Key Agreement Protocols"
Sanjit Chatterjee, Alfred Menezes and Berkant Ustaoglu
The last property is not valid for the model since there
is a flaw in the definition of the model and the proof.
The problem is that the session id does not contain the
role and since this is a one-pass protocol, this means
that a responder session <A,B,X> can match another
responder session <B,A,X> if X is replayed.
*/
builtins: diffie-hellman, hashing
/* Key registration */
rule Register_key_honest:
let pkA = 'g'^~ea
in
[ Fr( ~ea ) // select random longterm private
]
--[ KeyReg( $A ) ]-> // a key for A has been registered
[ !Ltk( $A, ~ea ) // ~ea is the longterm private key of A
, !Pk( $A, pkA ) // pkA = 'g'^~ea is the longterm public key of A
, Out( pkA ) ] // the adversary can learn the public key
/* Initiator */
// Activate the session: choose ephemeral private key, compute ephemeral public key X.
rule I_Activate:
let X = 'g'^~ex // the ephemeral public key
sid = <$A, $B, X> // the session id, not unique because there might
// be responder session with actor B and peer A
// which receives X
in
[ Fr( ~ex ) ] // select random ephemeral private key
--[ Activate( sid ) ]-> // sid is 'Activate'd after this step
[ I_Act( ~ex, $A, $B ) // the state of this session, identified by ex (unique)
, !SessionState( sid, $A, ~ex ) // the session state of sid with actor $A consists only of ex,
// available for reveals
]
// Complete the session: send X, and compute key.
rule I_Complete:
let X = 'g'^~ex // recompute X
sid = <$A, $B, X> // recompute sid
pB = 'g'^~eb // we do not model key registration by adversary,
// hence key always of this form
k = h(pB^~ex, pB^~ea, $A, $B) // we do not include the public string
in
[ I_Act( ~ex, $A, $B ) // this session was activated
, !Ltk( $A, ~ea ) // lookup own longterm private key
, !Pk( $B, pB ) ] // lookup peer's public key
--[ Complete( sid, 'I', k ) ]-> // sid is 'Complete'd after this step
[ I_Comp( sid ) // state of this session
, Out( <$B, $A, X> ) // send message
, !SessionKey( sid, $A, k ) ] // the session key of sid with actor $A, available
// for reveals
// Expire the session
rule I_Expire:
[ I_Comp( sid ) ]
--[ Expire( sid ) ]-> // sid is expired after this step
[ ]
/* Responder */
// R has no Activated state since there is no ephemeral key.
rule R_Complete:
let pA = 'g'^~ea // we do not model key registration by adversary,
// hence key always of this form
sid = <$B, $A, X> // sid is not unique because of initiator sessions with
// same sid and replay
k = h(X^~eb, pA^~eb, $A, $B)
in
[ In( X )
, !Ltk( $B, ~eb )
, !Pk( $A, pA ) ]
--[ Activate(sid), Complete( sid, 'R', k ) ]-> // sid is 'Complete'd and 'Activate'd after this step
[ R_Comp( sid )
, !SessionKey( sid, $B, k )
]
rule R_Expire:
[ R_Comp( sid ) ]
--[ Expire( sid ) ]-> // sid is expired after this step
[ ]
/* Corrupt an agent:
We model corruption by three different rules.
We do not model static key selection for corrupted agents.
*/
// Corrupt and obtain longterm key
rule Corrupt_Ltk:
[ !Ltk( $A, ~ea ) ]
--[ Corrupt( $A ) ]->
[ Out( ~ea ) ]
// Corrupt and obtain session state. Must occur before complete which
// we ensure with BeforeComplete action and axiom.
rule Corrupt_SessionState:
[ !SessionState( sid, ~x, $A ) ]
--[ Corrupt( $A ), BeforeComplete( sid ) ]->
[ Out( ~x ) ]
// Corrupt and obtain session key. Must occur before expire which
// we ensure with BeforeExpire action and axiom.
rule Corrupt_SessionKey:
[ !SessionKey( sid, $A, k ) ]
--[ Corrupt( $A ), BeforeExpire( sid ) ]->
[ Out( k ) ]
/* Reveals for session state and session key */
rule Reveal_SessionState:
[ !SessionState( sid, ~x, $A ) ]
--[SessionStateReveal( sid ), BeforeComplete( sid ) ]->
[ Out( ~x ) ]
rule Reveal_SessionKey:
[ !SessionKey( sid, $A, k) ]
--[ SessionKeyReveal( sid ), BeforeExpire( sid ) ]->
[ Out( k ) ]
/* We only consider traces which satisfy these axioms */
// we enforce unique sids since the paper states that "At any point in time a session is
// in exactly one of the following states: active, completed, aborted, expired."
axiom sid_unique:
"All #i #j sid. Activate(sid) @ i & Activate(sid) @ j ==> #i = #j"
// every agent has at most one registered key
axiom keyreg_unique:
"All #i #j A. KeyReg(A) @ i & KeyReg(A) @ j ==> #i = #j"
// The reveals and the corrupt rules that reveal session state and session key are not performed
// too late.
axiom corrupt_and_reveal_not_too_early:
" (All #i #j s role_ k_. BeforeComplete( s ) @ i & Complete(s, role_, k_ ) @ j ==> #i < #j)
& (All #i #j s. BeforeExpire( s ) @ i & Expire( s ) @ j ==> #i < #j)"
// Key agreement for initiator and responder is reachable without any adversary interaction.
lemma key_agreement_reachable:
exists-trace
"Ex #i #j sid sidm k.
Complete( sid , 'I', k ) @ i
& Complete( sidm, 'R', k ) @ j
& (not (Ex #j C. Corrupt( C ) @ j))
& (not (Ex #j s. SessionKeyReveal( s ) @ j))
& (not (Ex #j s. SessionStateReveal( s ) @ j))"
lemma CK_secure:
"(All #i #j role A B X k s sp.
/* The key of a complete session 's' is known (whose partner is 'sp') */
Complete( <A,B,X> , role, k ) @ i & K( k ) @ j & s = <A,B,X> & sp = <B,A,X>
/* then one of the following must have happened */
==>
/* 1. (a) The session key of s was revealed */
(Ex #k. SessionKeyReveal( s ) @ k)
/* (b) the session key of some s* was revealed */
| (Ex #i1. SessionKeyReveal( sp ) @ i1)
| (/* 2. s is an initiator session */
(role = 'I') &
( /* (a) A was corrupted before expire*/
(Ex #k. Corrupt(A) @ k &
( (Ex #l. Expire(s) @ l & k < l)
| not (Ex #l. Expire(s) @ l )))
| /* (b) A was corrupted and s's session state was revealed */
(Ex #k #l. SessionStateReveal(s) @ k & Corrupt(A) @ l)
| /* (c) B was corrupted */
(Ex #k. Corrupt(B) @ k )))
| (/* 2. s is a responder session */
(role = 'R') &
( /* (a) A was corrupted */
Ex #k. Corrupt(A) @ k )
| /* (b) There is a matching session and */
(Ex #k k_ role_. Complete( sp, role_, k_ ) @ k
/* there is a corrupt B before expire s* */
& ( (Ex #k. Corrupt(B) @ k &
( (Ex #l. Expire(sp) @ l & k < l)
| not (Ex #l. Expire(sp) @ l )))
/* or both session state reveal s* and corrupt B */
| (Ex #k #l. SessionStateReveal(sp) @ k & Corrupt(B) @ l )))
| /* (c) There is no matching session and */
( (not (Ex #k k_ role_. Complete( sp, role_, k_) @ k))
/* there is corrupt B */
& (Ex #k. Corrupt(B) @ k))))"
end