FormalProof/proverif/RogueAS_otp.pv

188 lines
5.9 KiB
Plaintext
Raw Normal View History

(*
* Formal definition to verify the Fenrir protocol.
* to be used with ProVerif
*
* This is to verify the correctness of the full 3-RTT handshake.
* DNSSEC is NOT verified, as it is assumed to be correct.
*
* compared to the handshakes, we have a lot less to check, so the tests are very quick
*)
(*
* ==============
* Type definition
* ==============
*)
type pubKey. (* public key for asymmetric key encryption *)
type privKey. (* private key for asymmetric key encryption *)
type sKey. (* symmetric key *)
type nonce. (* just a nonce *)
const leak: channel. (* to leak private material *)
const cs_xor: channel. (* service-fenrir channel for xor otp auth *)
(* various stuff that should remain private *)
const prS: privKey [private]. (* service private key *)
const OTP: sKey [private].
free csKey: sKey [private].
(*
* ==============
* Function definition
* ==============
*)
(* any to bitstring conversion *)
fun fromKey(sKey) : bitstring.
reduc forall a : sKey; toKey(fromKey(a)) = a.
(* symmetric key encription *)
fun sEnc(bitstring, sKey): bitstring. (* encrypt with symmetric key *)
reduc forall c: bitstring, k: sKey; sDec(sEnc(c,k),k) = c. (* decrypt with symmetric key *)
(* public key definition *)
fun genPub(privKey): pubKey. (* public keys are generated from private ones *)
fun aEnc(bitstring, pubKey): bitstring. (* encrypt with asymmetric public key *)
reduc forall c: bitstring, k: privKey; aDec(aEnc(c,genPub(k)),k) = c. (* decrypt with private key *)
(* public key signing *)
fun sign(bitstring, privKey): bitstring.
reduc forall c: bitstring, k: privKey; ver(c, sign(c, k), genPub(k)) = true. (* verify signature *)
(* integrity for encrypt then mac *)
fun int(bitstring, sKey): bitstring.
reduc forall c: bitstring, k: sKey; verint(c, int(c, k), k) = true.
(* some key exchange based on public keys *)
fun exchange(privKey, pubKey) : sKey.
equation forall p1 : privKey, p2 : privKey; exchange(p1, genPub(p2)) = exchange(p2, genPub(p1)).
(* hash used by OTP, sKey based *)
fun hash(sKey) : sKey.
(* xor function *)
(* note: this xor function doe not handle associativity as proverif would loop,
* so we pay attention to use it in a way that does not require associativity
*)
fun xor(sKey, sKey) : sKey.
reduc forall a : sKey, b : sKey; unxor(xor(a, b), b) = a.
reduc forall a : sKey, b : sKey; unxor2(xor(a, b), a) = b. (* created for proverif, not used for simplicity *)
(*
* ==============
* Events -- used for correlation
* ==============
*)
event clientConnect(sKey).
event serviceConnect(sKey).
(* rekeying algorithm *)
let client_rekey (key: sKey, chan: channel, pb : pubKey) =
new rekey_p1: privKey;
new rekey_n1: nonce;
out (chan, sEnc((rekey_n1, genPub(rekey_p1)), key));
in (chan, enc: bitstring);
let (=rekey_n1, srv_eph : pubKey, signed : bitstring) = sDec(enc, key) in
if ver((rekey_n1, srv_eph), signed, pb) then
let (new_Key) = exchange(rekey_p1, srv_eph) in
out (chan, sEnc((OTP, OTP), new_Key)) (* test secrecy *)
.
let server_rekey (key: sKey, chan: channel, pr : privKey) =
new rekey_s_p: privKey;
in (chan, enc: bitstring);
let (client_n1: nonce, client_p: pubKey) = sDec (enc, key) in
out (chan, sEnc((client_n1, genPub(rekey_s_p), sign((client_n1, genPub(rekey_s_p)), pr)), key));
let (new_Key) = exchange(rekey_s_p, client_p) in
in (chan, enc2: bitstring);
let (=OTP, =OTP) = sDec(enc2, new_Key)
.
(* test the OTP between the service and client. Assume that the authentication server
* has been compromised. *)
(* assume that the client and service have not yet setup an OTP *)
let service_no_otp() =
(* this first sent/received messages are relayed to the service/client by the AS *)
in (cs_xor, (enc: bitstring, pb: pubKey));
let key = exchange(prS, pb) in
let bitOTP = sDec(enc, key) in
let sOTP = toKey(bitOTP) in
(* this should be cskey XOR hash(OTP), but proverif can't handle XOR.
* so we treat it as encryption, as it has the same properties in our case *)
let xored = xor(csKey, hash(sOTP)) in
out (cs_xor, xored);
in (cs_xor, enc2 : bitstring);
let is_OTP = sDec(enc2, csKey) in
if sOTP = toKey(is_OTP) then
event clientConnect(csKey);
server_rekey(csKey, cs_xor, prS).
let client_no_otp(OTP: sKey, pbS: pubKey) =
(* this first message is actually sent to the AS, but gets relayed to the service *)
new pr : privKey;
out (cs_xor, sEnc(fromKey(OTP), exchange(pr, pbS)));
in (cs_xor, xored : sKey);
let xorKey = unxor(xored, hash(OTP)) in
event serviceConnect(xorKey);
out (cs_xor, sEnc(fromKey(OTP), xorKey));
client_rekey(xorKey, cs_xor, pbS).
(* now assume that there is a shared OTP *)
let service_otp(OTP : sKey) =
(* this should be cskey XOR hash(OTP), but proverif can't handle XOR.
* so we treat it as encryption, as it has the same properties in our case *)
let xorK = sEnc(fromKey(csKey), hash(OTP)) in
out (cs_xor, (xorK, xorK));
in (cs_xor, enc2 : bitstring);
let is_OTP = sDec(enc2, csKey) in
if OTP = toKey(is_OTP) then
event clientConnect(csKey);
server_rekey(csKey, cs_xor, prS).
let client_otp(OTP: sKey, pbS: pubKey) =
in (cs_xor, enc : bitstring);
let xorKey = sDec(enc, hash(OTP)) in
event serviceConnect(toKey(xorKey));
out (cs_xor, sEnc(fromKey(OTP), toKey(xorKey)));
client_rekey(toKey(xorKey), cs_xor, pbS).
(*
* Event correlation:
*)
query k : sKey; event(clientConnect(k)) ==> event(serviceConnect(k)).
(*
* ==============
* What should be secret?
* ==============
*)
query attacker(OTP). (* client-service OTP data. *)
(*
* ==============
* Run the verification
* ==============
*)
(* make it easy to deactivate single parts, like the 2-RTT login, which has unproven events problems *)
(* TODO: fix PFS: must leak both server and client public key *)
process
(!service_no_otp()) | (!client_no_otp(OTP, genPub(prS)))
| phase 1; (!service_otp(OTP)) | (!client_otp(OTP, genPub(prS)))