CryptoVerif and Tamarin models, minor doc updates

Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
This commit is contained in:
Kamal Tufekcic 2026-04-13 01:51:32 +03:00
commit 3acaa0fa3f
No known key found for this signature in database
18 changed files with 2925 additions and 8 deletions

189
tamarin/LO_Auth.spthy Normal file
View file

@ -0,0 +1,189 @@
theory LO_Auth
begin
/*
* 1. CRYPTOGRAPHIC PRIMITIVES (Abstracted per §2)
*/
builtins: signing, hashing
// Hybrid KEM (X-Wing) abstraction per §2.1
// We treat it as a single IND-CCA2 KEM.
// kem_decaps recovers the encapsulation randomness r (a subterm of the
// ciphertext), and both sides derive the shared secret via kem_ss(pk, r).
// This formulation is subterm-convergent: the RHS `r` is a subterm of the LHS.
functions: kem_pk/1, kem_c/2, kem_ss/2, kem_decaps/2
equations: kem_decaps(sk, kem_c(kem_pk(sk), r)) = r
// MAC (HMAC-SHA3-256) per §2.3
// Modeled as an opaque function.
functions: mac/2
// Constant-time equality check helper
restriction Eq:
"All x y #i. Eq(x,y) @i ==> x = y"
/*
* 2. ADVERSARY MODEL & PKI (§8.2)
*/
// Generate Identity Keys (IK)
rule Generate_IK:
[ Fr(~sk_IK) ]
--[ LtkGen($P, ~sk_IK) ]->[ !Ltk($P, ~sk_IK), !Pk($P, kem_pk(~sk_IK)), Out(kem_pk(~sk_IK)) ]
// Corrupt IK (§8.2: Corrupt(IK, P))
rule Corrupt_IK:[ !Ltk($P, sk_IK) ]
--[ CorruptIK($P) ]->[ Out(sk_IK) ]
// Corrupt RNG (§8.2: Corrupt(RNG, P, t))
// We model this by allowing the adversary to learn the randomness `~r` used in a specific step.
rule Corrupt_RNG:
[ !RNG_State($P, ~r) ]
--[ CorruptRNG($P, ~r) ]->
[ Out(~r) ]
/*
* 3. LO-Auth PROTOCOL (§6)
*/
// Server -> Client: Challenge
rule LO_Auth_Challenge:
let
c = kem_c(pk_IK_client, ~r)
ss = kem_ss(pk_IK_client, ~r)
token = mac(ss, 'lo-auth-v1')
in[ !Pk($Client, pk_IK_client), Fr(~r) ]
--[
AuthChallenge($Server, $Client, ~r, token)
]->[
Out( c ),
// §8.3: Linear fact to enforce single-use challenge
AuthPending($Server, $Client, token),
// Expose RNG state for potential corruption
!RNG_State($Server, ~r)
]
// Client -> Server: Proof Generation
rule LO_Auth_Client_Response:
let
r = kem_decaps(sk_IK_client, c)
ss = kem_ss(kem_pk(sk_IK_client), r)
proof = mac(ss, 'lo-auth-v1')
in[ !Ltk($Client, sk_IK_client), In( c ) ]
--[
AuthResponse($Client, c, proof)
]->
[
Out( proof )
]
// Server: Verify
// Consumes AuthPending ensuring the challenge cannot be replayed successfully
rule LO_Auth_Verify_Success:
[ AuthPending($Server, $Client, token), In( proof ) ]
--[
Eq(token, proof), // Represents constant-time accept
AuthAccepted($Server, $Client, token)
]->
[ ]
// (Optional) Server: Timeout
// §8.3: AuthTimeout consumes the pending state without producing AuthAccepted
rule LO_Auth_Timeout:[ AuthPending($Server, $Client, token) ]
--[ AuthTimeout($Server, $Client, token) ]->
[ ]
/*
* 3b. CCA2 DECAPSULATION ORACLE (§8.3 compositional note)
*
* In the composed setting where LO-Auth and LO-KEX share sk_IK[XWing],
* each LO-Auth session gives the adversary an interactive Decaps oracle:
* submit arbitrary c, receive MAC(Decaps(sk_IK, c), 'lo-auth-v1').
* This accounts for the CCA2 queries in Theorem 1's advantage bound.
*/
// kem_decaps returns r (subterm-convergent), then kem_ss(pk, r) gives ss.
rule LO_Auth_Decaps_Oracle:
let
r_adv = kem_decaps(sk_IK_client, c_adv)
ss_adv = kem_ss(kem_pk(sk_IK_client), r_adv)
in
[ !Ltk($Client, sk_IK_client), In( c_adv ) ]
--[ DecapsOracle($Client, c_adv) ]->
[ Out( mac(ss_adv, 'lo-auth-v1') ) ]
/*
* 4. LEMMAS (§8.6)
*/
// Sanity: Can the protocol run to completion?
lemma Auth_Exists:
exists-trace
"Ex S C token #i. AuthAccepted(S, C, token) @ i"
// Sanity: Challenge temporally precedes acceptance.
lemma Auth_Ordering:
"All S C r token #chal #verify.
AuthChallenge(S, C, r, token) @ chal &
AuthAccepted(S, C, token) @ verify
==>
chal < verify
"
// §8.3 single-use: AuthPending is linear, so a given token is accepted at most once.
lemma Auth_Single_Use:
"All S C token #i #j.
AuthAccepted(S, C, token) @ i &
AuthAccepted(S, C, token) @ j
==>
#i = #j
"
// §8.3: If the challenge timed out, acceptance is impossible (AuthPending was consumed).
lemma Auth_No_Accept_After_Timeout:
"All S C token #t #a.
AuthTimeout(S, C, token) @ t &
AuthAccepted(S, C, token) @ a
==>
F
"
// Each Fr(~r) is unique, so distinct challenges produce distinct tokens.
lemma Auth_Unique_Challenge:
"All S1 S2 C1 C2 r token1 token2 #i #j.
AuthChallenge(S1, C1, r, token1) @ i &
AuthChallenge(S2, C2, r, token2) @ j
==>
#i = #j
"
// Theorem 6 (LO-Auth Key Possession):
// If the server accepts, then either the adversary corrupted the client's IK,
// corrupted the server's RNG for this challenge, used the Decaps oracle
// (which yields a valid MAC for any ciphertext), or the client responded.
lemma Theorem6_Key_Possession:
"All S C r token #chal #verify.
AuthChallenge(S, C, r, token) @ chal &
AuthAccepted(S, C, token) @ verify
==>
(Ex #j. CorruptIK(C) @ j) |
(Ex #j. CorruptRNG(S, r) @ j) |
(Ex c_adv #j. DecapsOracle(C, c_adv) @ j) |
(Ex c #resp. AuthResponse(C, c, token) @ resp)
"
// Theorem 6, strengthened: under an honest client, honest RNG, and no
// oracle use, the adversary cannot forge a proof.
lemma Theorem6_No_Oracle:
"All S C r token #chal #verify.
AuthChallenge(S, C, r, token) @ chal &
AuthAccepted(S, C, token) @ verify &
not (Ex #j. CorruptIK(C) @ j) &
not (Ex #j. CorruptRNG(S, r) @ j) &
not (Ex c_adv #j. DecapsOracle(C, c_adv) @ j)
==>
(Ex c #resp. AuthResponse(C, c, token) @ resp)
"
end