tamarin-prover-0.8.4.0: data/examples/ake/dh/UM_three_pass.spthy
theory UM_three_pass
begin
/*
The three-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
*/
builtins: diffie-hellman, hashing, multiset
functions: MAC/2, first/1, second/1, concat/2
equations: concat(first(x), second(x)) = x
/* 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 */
// UM3 session creation for initiator:
// choose ephemeral private key, compute ephemeral public key X, and send
rule I_Activate:
let X = 'g'^~ex // the ephemeral public key
sid = <'UM3', $A, $B, X> // the session id is unique
in
[ Fr( ~ex ) ] // select random ephemeral private key
--[ Activate( sid )
, Sid( sid )
, Agents($A,$B) ]-> // 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
, Out( X ) // send message
]
// UM3 session update for initiator:
// receive key confirmation and Y, compute key, check key confirmation.
rule I_Complete:
let X = 'g'^~ex // recompute X
pB = 'g'^~eb // we do not model key registration by adversary,
// hence key always of this form
kstring = h(Y^~ex, pB^~ea, $A, $B, X, Y) // we do not include the public string Lambda
key = second(kstring)
conf = MAC(first(kstring), <'R', $B, $A, Y, X>) // we do not include Lambda_1
confB = MAC(first(kstring), <'I', $A, $B, X, Y>) // we do not include Lambda_2
sidOld = <'UM3',$A, $B, <'1',X>>
sid = <'UM3',$A, $B, <'1',X> + <'2', <Y,confB>> + <'3', conf> > // new sid
in
[ I_Act( ~ex, $A, $B ) // this session was activated
, In( <Y, confB> )
, !Ltk( $A, ~ea ) // lookup own longterm private key
, !Pk( $B, pB ) ] // lookup peer's public key
--[ Complete( sid, 'I', key ) // sid is 'Complete'd after this step
, SidUpdated( sidOld )
, Sid( sid ) ]->
[ I_Comp( sid ) // state of this session
, Out( conf )
, !SessionKey( sid, $A, key ) ] // the session key of sid with actor $A, available
// for reveals
// UM3 session expiration
rule I_Expire:
[ I_Comp( sid ) ]
--[ Expire( sid ) ]-> // sid is expired after this step
[ ]
/* Responder */
// Session creation:
rule R_Activate:
let pA = 'g'^~ea // we do not model key registration by adversary,
// hence key always of this form
Y = 'g'^~ey
kstring = h(X^~ey, pA^~eb, $A, $B, X, Y )
key = second(kstring)
conf = MAC(first(kstring), <'I', $A, $B, X, Y>) // we do not include Lambda_2
sid = <'UM3', $B, $A, <'1',X> + <'2',<Y, conf>> > // sid is unique
in
[ Fr( ~ey )
, In( X )
, !Ltk( $B, ~eb )
, !Pk( $A, pA ) ]
--[ Activate(sid)
, Sid( sid )
, Agents($A,$B) ]->
[ Out( <Y,conf>)
, R_Act( ~ey, $B, $A, X )
, !SessionState( sid, $A, ~ey )
]
// Session update:
rule R_Complete:
let pA = 'g'^~ea
Y = 'g'^~ey
kstring = h(X^~ey, pA^~eb, $A, $B, X, Y )
key = second(kstring)
confA = MAC(first(kstring), <'R', $B, $A, Y, X>) // we do not include Lambda_1
conf = MAC(first(kstring), <'I', $A, $B, X, Y>) // we do not include Lambda_2
sidOld = <'UM3', $B, $A, <'1',X> + <'2', <Y, conf>>> // sid is unique
sid = <'UM3', $B, $A, <'1',X> + <'2', <Y, conf>> + <'3', confA> > // sid is unique
in
[ R_Act( ~ey, $B, $A, X )
, In( confA )
, !Ltk( $B, ~eb )
, !Pk( $A, pA )
]
--[ Complete( sid, 'R', key ) // sid is 'Complete'd after this step
, SidUpdated( sidOld )
, Sid( sid ) ]->
[ R_Comp( sid )
, !SessionKey( sid, $B, key ) ] // the session key of sid with actor $B, available
// for reveals
rule R_Expire:
[ R_Comp( sid ) ]
--[ Expire( sid ) ]->
[ ]
/* 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"
// this assumption is used in the proof. Without this restriction, a completed
// session with actor and peer A is its own matching session.
//axiom actor_ineq_peer:
// "not (Ex #i A. Agents(A,A) @ i)"
// 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_UM3:
"(All #i #j role A B k s comm.
/* The key of a complete session 's' is known */
Complete( <'UM3',A,B,comm> , role, k ) @ i & K( k ) @ j & s = <'UM3',A,B,comm>
/* then one of the following must have happened */
==>
/* 1. (a) The session key of s was revealed (see 4. for 1. (b)) */
(Ex #k. SessionKeyReveal( s ) @ k)
/* 2. Corrupt(A) before Expire(s) */
| (Ex #k. Corrupt(A) @ k &
( not (Ex #l. Expire(s) @ l & l < k)))
/* 3. SessionStateReveal(s) and either Corrupt(A) or Corrupt(B) */
| (Ex #k. SessionStateReveal(s) @ k
& ((Ex #l. Corrupt(A) @ l) | (Ex #l. Corrupt(B) @ l)))
/* 4. s* exists and */
| (Ex #l ms matchingComm.
Sid(<'UM3',B,A,matchingComm>) @ l
& ms = <'UM3',B,A,matchingComm>
& ((Ex rest. matchingComm + rest = comm) | matchingComm = comm)
// the session has not been updated
& not (Ex #v. SidUpdated(ms) @ v)
& (
/* SessionKeyReveal(s*) (see 1. (b)) */
(Ex #k. SessionKeyReveal( ms ) @ k)
/* (a) Corrupt(B) before Expire(s*) */
| (Ex #k. Corrupt(B) @ k &
(not (Ex #l. Expire(ms) @ l & l < k)))
/* (b) SessionStateReveal(s*) and either Corrupt(A) or Corrupt(B) */
| (Ex #k. SessionStateReveal(ms) @ k
& ((Ex #l. Corrupt(A) @ l) | (Ex #l. Corrupt(B) @ l)))))
/* 5. s* does not exist and Corrupt(B) before Expire(s) */
| ((not (Ex #l matchingComm.
Sid(<'UM3',B,A,matchingComm>) @ l
& ((Ex rest. matchingComm + rest = comm) | matchingComm = comm)
// the session has not been updated
& not (Ex #v. SidUpdated(<'UM3',B,A,matchingComm>) @ v)))
& (Ex #k. Corrupt(B) @ k &
(not (Ex #l. Expire(s) @ l & l < k)))))"
end