tamarin-prover-0.8.4.0: data/examples/related_work/YubiSecure_KS_STM12/Yubikey.spthy
theory Yubikey
begin
section{* The Yubikey-Protocol *}
/*
* Protocol: Yubikey Protocol
* Modeler: Robert Kunnemann, Graham Steel
* Date: August 2012
*
* Status: working
*/
builtins: symmetric-encryption
functions: S/1,zero/0
/* We to model the Yubikey protocol, described in
* http://www.yubico.com/documentation
* http://www.yubico.com/developers-intro
* This is simplified version, in particular:
* - timestamps are not modelled
* - We do not distinguish the session and token counter. We describe them
* as one single counter, that represents the pair (session counter, token
* counter) with a lexicographical order on the pair. This implies that
* a) pressing the button on the Yubikey increases this counter by 1, and
* b) a plugin of the Yubikey increases it by an arbitrary amount the
* adversary gets to choose (giving him more power).
*/
/* The following rules model two binary relations between integers. !Succ
* is functional. If !Succ(a,b), then the adversary was able to show that b
* is the successor of b. Similarly, albeit !Smaller is not functional, if
* !Smaller(a,b), then the adversary was able to show that a is smaller
* than b.
* The Theory() action is used to enforce that this relation (to the extend
* it is needed in this trace) has to be build up before running the first
* protocol actions.
*/
rule InitSucc:
[In(zero),In(S(zero))]
--[Theory(), IsSucc(zero,S(zero)),IsZero(zero)]->
[!Succ(zero,S(zero))]
rule StepSucc:
[In(y),In(S(y)), !Succ(x,y)]
--[Theory(), IsSucc(y,S(y)) ]->
[!Succ(y,S(y))]
rule SimpleSmaller:
[!Succ(x,y)]
--[Theory(), IsSmaller(x,y)]->
[!Smaller(x,y)]
rule ZExtendedSmaller:
[!Smaller(x,y),!Succ(y,z)]
--[Theory(), IsSmaller(x,z)]->
[!Smaller(x,z)]
/* A Yubikey is initialised with a zero counter, a key and a public, as well as a
* secret identifier, ~pid and ~sid. This information is shared with the
* Authentication Server, so we assume a trusted way of installing a
* Yubikey
*/
rule BuyANewYubikey:
[Fr(~k),Fr(~pid),Fr(~sid)] //for fresh k, public and secret id..
--[Protocol(), Init(~pid,~k),ExtendedInit(~pid,~sid,~k),IsZero(zero)]->
[!Y(~pid,~sid), Y_counter(~pid,zero),
//..store public and secret id along with the starting counter
//(zero) on the Yubikey..
Server(~pid,~sid,zero),!SharedKey(~pid,~k), //and on the server
Out(~pid)] //and make the public id public
//On plugin, the session counter is increased and the token counter reset
rule Yubikey_Plugin:
[Y_counter(pid,sc),!Smaller(sc, Ssc) ]
//The old counter value sc is removed
--[ Yubi(pid,Ssc) ]->
[Y_counter(pid, Ssc)]
//and substituted by a new counter value, larger, Ssc
//If the Button is pressed, the token counter is increased
rule Yubikey_PressButton:
[!Y(pid,sid), Y_counter(pid,tc),!SharedKey(pid,k),
!Succ(tc,Stc),Fr(~npr),Fr(~nonce) ]
//The old countervalue tc is removed
--[ YubiPress(pid,tc), YubiOTP(pid,senc(<sid,tc,~npr>,k)),
YubiSid(pid,sid,k) ]->
[Y_counter(pid, Stc), //and substituted by its successor
Out(<pid,~nonce,senc(<sid,tc,~npr>,k)>)
//in addition, an encrypted otp is output along with a nonce and
//the public id of the Yubikey used.
]
/* Upon receiving an encrypted OTP, the Server compares the (unencrypted)
* public id to his data base to identify the key to decrypt the OTP. After
* making sure that the secret id is correct, the Server verifies that the
* received counter value is larger than the last one stored. If the Login
* is successful, i.e., the previous conditions were fulfilled, the counter
* value on the Server that is associated to the Yubikey is updated.
*/
rule Server_ReceiveOTP_NewSession:
[Server(pid,sid,otc), In(<pid,nonce,senc(<sid,tc,~pr>,k)>),
!SharedKey(pid,k), !Smaller(otc,tc) ]
//if the Server receives an OTP encrypted with k that belongs to
//the (unencrypted) public id, and the OTP has the right format,
//contains the correct secret id as well as a counter tc that is
//larger than the current counter otc, then...
--[ Login(pid,sid,tc,senc(<sid,tc,~pr>,k)),
LoginCounter(pid,otc,tc) //..the Login is accepted..
]->
[Server(pid,sid,tc)] //..and the counter value updated.
/* The following three axioms are conditions on the traces that make sure
* that : */
//a) the !Smaller relation is transitive
axiom transitivity: //axiomatic
"All #t1 #t2 a b c. IsSmaller(a,b)@t1 & IsSmaller(b,c)@t2
==> Ex #t3 . IsSmaller(a,c)@t3 "
//b) !Smaller implies unequality
axiom smaller_implies_unequal: //axiomatic
"not (Ex a #t . IsSmaller(a,a)@t)"
//c) The protocol runs only after the IsSmaller and IsSuccessor relation is
// build up
axiom theory_before_protocol:
"All #i #j. Theory() @ i & Protocol() @ j ==> i < j"
// For sanity: Ensure that a successful login is reachable.
lemma Login_reachable:
exists-trace
"Ex #i pid sid x otp1. Login(pid,sid,x,otp1)@i"
// Each succesful login with counter value x was preceeded by a PressButton
// event with the same counter value
lemma one_count_foreach_login[reuse,use_induction]:
"All pid sid x otp #t2 . Login(pid,sid,x,otp)@t2 ==>
( Ex #t1 . YubiPress(pid,x)@#t1 & #t1<#t2 )"
// If a succesful Login happens before a second sucesfull Login, the
// counter value of the first is smaller than the counter value of the
// second
lemma slightly_weaker_invariant[reuse, use_induction]:
"(All pid otc1 tc1 otc2 tc2 #t1 #t2 .
LoginCounter(pid,otc1,tc1)@#t1 & LoginCounter(pid,otc2,tc2)@#t2
==> ( #t1<#t2 & ( Ex #t3 . IsSmaller(tc1,tc2)@t3 ))
| #t2<#t1 | #t1=#t2)
"
induction
case empty_trace
by contradiction // from formulas
next
case non_empty_trace
simplify
solve( (∀ pid otc1 tc1 otc2 tc2 #t1 #t2.
(LoginCounter( pid, otc1, tc1 ) @ #t1) ∧
(LoginCounter( pid, otc2, tc2 ) @ #t2)
⇒
(last(#t2)) ∨
(last(#t1)) ∨
((#t1 < #t2) ∧
(∃ #t3. (IsSmaller( tc1, tc2 ) @ #t3) ∧ ¬(last(#t3)))) ∨
(#t2 < #t1) ∨
(#t1 = #t2)) ∥
(∃ #t1 #t2 a b c.
(IsSmaller( a, b ) @ #t1) ∧ (IsSmaller( b, c ) @ #t2)
∧
(¬(last(#t2))) ∧
(¬(last(#t1))) ∧
(∀ #t3. (IsSmaller( a, c ) @ #t3) ⇒ last(#t3))) )
case case_1
solve( (last(#t2)) ∥ (last(#t1)) ∥
((#t1 < #t2) ∧
(∃ #t3. (IsSmaller( tc1, tc2 ) @ #t3) ∧ ¬(last(#t3)))) ∥
(#t2 < #t1) ∥ (#t1 = #t2) )
case case_1
solve( Server( pid, sid, otc1 ) ▶₀ #t1 )
case BuyANewYubikey
solve( Server( ~pid, sid.1, otc2 ) ▶₀ #t2 )
by sorry
next
case Server_ReceiveOTP_NewSession_case_1
solve( Server( ~pid, sid.1, otc2 ) ▶₀ #t2 )
by sorry
next
case Server_ReceiveOTP_NewSession_case_2
solve( Server( ~pid, sid.1, otc2 ) ▶₀ #t2 )
by sorry
next
case Server_ReceiveOTP_NewSession_case_3
solve( Server( ~pid, sid.1, otc2 ) ▶₀ #t2 )
by sorry
next
case Server_ReceiveOTP_NewSession_case_4
solve( Server( ~pid, sid.1, otc2 ) ▶₀ #t2 )
by sorry
qed
next
case case_2
by contradiction // cyclic
next
case case_3
by contradiction // from formulas
next
case case_4
by contradiction // from formulas
next
case case_5
by contradiction // from formulas
qed
next
case case_2
by sorry
qed
qed
// It is not possible to have to distinct logins with the same counter
// value
lemma no_replay:
"not (Ex #i #j pid sid x otp1 otp2 .
Login(pid,sid,x,otp1)@i & Login(pid,sid,x,otp2)@j
& not(#i=#j))"
lemma injective_correspondance:
"All pid sid x otp #t2 . Login(pid,sid,x,otp)@t2 ==>
( Ex #t1 . YubiPress(pid,x)@#t1 & #t1<#t2
& All otp2 #t3 . Login(pid,sid,x,otp2)@t3 ==> #t3=#t2
)"
lemma Login_invalidates_smaller_counters:
"All pid otc1 tc1 otc2 tc2 #t1 #t2 #t3 .
LoginCounter(pid,otc1,tc1)@#t1 & LoginCounter(pid,otc2,tc2)@#t2
& IsSmaller(tc1,tc2)@t3
==> #t1<#t2 "
end