tamarin-prover-0.8.2.0: data/examples/loops/TESLA_Scheme2.spthy
theory TESLA_Scheme2 begin
/*
Protocol: The TESLA protocol, scheme 2
Modeler: Simon Meier
Date: September 2012
Status: not yet working
(We have trouble reasoning about the authenticity check. More
precisely, we cannot prove that a successful check implies that
the key is a key of the hash-chain. See Minimal_HashChain.spthy
for an example of the core problem.)
Original descrption in [1]. This model is based on the following description
from [2].
Msg 0a. R -> S : nR
Msg 0b. S -> R : {k0 , nR }SK (S )
Msg 1. S -> R : m1 , MAC (k1 , m1 ).
And for n > 1:
Msg n. S -> R : Dn , MAC (kn , Dn ) where Dn = mn , kn-1 .
One aim of this second version is to be able to tolerate an arbitrary number of
packet losses, and to drop unauthenticated packets, yet continue to authenticate
later packets.
We verify that the use of cryptography is correct under the assumption that
the security condition holds. We do not verify that the timing schedule
works, as we do not have a notion of time. For a manual, but machine-checked
verification of the Scheme 2 of the TESLA protocol with time see [3].
[1] Perrig, Adrian, Ran Canetti, Dawn Song, and Doug Tygar. "The TESLA
Broadcast Authentication Protocol." In RSA Cryptobytes, Summer 2002.
[2] Philippa J. Hopcroft, Gavin Lowe: Analysing a stream authentication
protocol using model checking. Int. J. Inf. Sec. 3(1): 2-13 (2004)
[3] David A. Basin, Srdjan Capkun, Patrick Schaller, Benedikt Schmidt:
Formal Reasoning about Physical Properties of Security Protocols. ACM Trans.
Inf. Syst. Secur. 14(2): 16 (2011)
*/
builtins: signing
functions: MAC/2, f/1
// PKI
//////
rule Generate_Keypair:
[ Fr(~ltk) ]
-->
[ !Ltk($A, ~ltk), !Pk($A, pk(~ltk)), Out(pk(~ltk)) ]
// We assume an active adversary.
// rule Reveal_Ltk:
// [ !Ltk(A, ltk) ]
// --[ RevealLtk(A) ]->
// [ Out(ltk) ]
// Chain setup phase
////////////////////
// Hash chain generation
rule Gen_Start:
[ Fr(~seed) ] --> [ Gen(~seed, ~seed) ]
// The NextKey-facts are used by the sender rules to store the link between
// the keys in the chain.
rule Gen_Step:
[ Gen(seed, chain) ]
--[ ChainKey(f(chain))
]->
[ Gen(seed, f(chain) ) , NextKey( f(chain) , chain ) ]
// At some point the sender decides to stop the hash-chain precomputation.
rule Gen_Stop:
[ Gen(seed, kZero) ]
--[ Expired(kZero), KeyZero(seed, kZero) ]->
[ Sender(kZero) , !Sender0($S, kZero) ]
// Intial chain key distribution
////////////////////////////////
// Everybody can listen in by sending a request for k_0.
rule Sender0:
let msgZero = <nR, kZero>
in
[ !Sender0(S, kZero), !Ltk(S, ltkS), In(nR) ]
-->
[ Out( <msgZero, sign{msgZero}ltkS> ) ]
// Receivers start by requesting key k_0 adn verifying the signature on this
// response.
rule Receiver0a:
[ Fr(~nR) ] --> [ Receiver0b($S, ~nR), Out(<$S, ~nR>) ]
rule Receiver0b:
let msgZero = <nR, kZero>
in
[ Receiver0b(S,nR), !Pk(S, pkS), In(<msgZero, signature>) ]
--[ Eq( verify(signature, msgZero, pkS), true() ) ]->
[ !Receiver(S, kZero) ]
// Sending
//////////
// We use the convention that k_{n-1} is denoted as kNp, where the 'p' stands
// for predecessor.
rule Sender:
let msgN = <mN, MAC( kN, mN )>
in
[ Sender( kNp ), NextKey( kNp, kN), Fr(mN) ]
--[ Expired( kNp )
, Sent( msgN )
]->
[ Sender( kN ), Out(kNp), Out(msgN) ]
// Receiving
////////////
rule Receiver:
let msg = <m, mac>
in
[ !Receiver(S, kZero), In( msg )
, Fr(expiryCheck)
]
--[ NotExpiredHere( expiryCheck ) ]->
[ CheckAuth(expiryCheck, S, kZero, msg ) ]
rule CheckAuth0:
let args = <kZero, expiryCheck, S, k, msg>
in
[ CheckAuth(expiryCheck, S, kZero, msg)
, In(k)
, Fr(loopId)
]
--[ CheckStart(loopId, args)
]->
[ CheckAuthLoop(loopId, k, args) ]
rule CheckAuth:
[ CheckAuthLoop(loopId, k, args) ]
--[ CheckLoop( loopId, f(k), args )
]->
[ CheckAuthLoop(loopId, f(k), args) ]
rule CheckAuthClaim:
let msg = <m, MAC(kRef,m)>
in
[ CheckAuthLoop(loopId, kZero, <kZero, expiryCheck, S, kRef, msg>) ]
--[ FromSender(msg, S, kRef, expiryCheck)
, Success(loopId)
]-> []
// Axioms; i.e., universal restrictions on the traces of interest
/////////////////////////////////////////////////////////////////
axiom Eq_checks_succeed: "(All x y #j. Eq(x, y) @ j ==> x = y)"
axiom Neq_checks_succeed: "(All x #j. Neq(x, x) @ j ==> F)"
// The security condition of TESLA guarantees that
axiom Security_condition:
"All m S k check #i #j #e.
FromSender(m, S, k, check) @ i
& NotExpiredHere(check) @ j
& Expired(k) @ e
==>
j < e"
// Security properties
//////////////////////
// The following two lemmas constraint the search space strongly enough to
// allow reasoning about the authenticity of the received messages.
lemma chain_keys_unique [use_induction, reuse]:
"All k #i #j. ChainKey(k) @ i & ChainKey(k) @ j ==> #i = #j"
lemma knows_only_expired_chain_keys [use_induction, reuse]:
"All k #i #j. ChainKey(k) @i & KU(k) @ j ==>
(Ex #e. Expired(k) @ e & e < j)"
// The current proof idea is to assume this axiom because we cannot yet prove
// it. Proving it requires support for multisets or repeated function
// application.
axiom FromSender_charn: // [use_induction]:
// "All m S k0 lid k args check #i.
"All lid k args #i #j.
// FromSender(m, S, k0, check) @ i
Success(lid) @ i
& CheckLoop(lid, k, args) @ j
==>
(Ex #c. ChainKey(k) @ c)
"
lemma authentic [use_induction]:
" (All m S k check #i.
FromSender(m, S, k, check) @ i
==>
(Ex #j. Sent(m) @ j & j < i)
// | (Ex #j. RevealLtk(S) @ j & j < i)
)
"
end