177 lines
5.0 KiB
Plaintext
177 lines
5.0 KiB
Plaintext
|
|
(*
|
|
* Formal definition to verify the Fenrir protocol.
|
|
* to be used with ProVerif
|
|
*
|
|
* Here we check the security of the connection between client
|
|
* and service in the case of a compromised authentication server
|
|
*)
|
|
|
|
(*
|
|
* ==============
|
|
* 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 cs: channel. (* simulates service-fenrir channel for authentication *)
|
|
|
|
(* various stuff that should remain private *)
|
|
const prS: privKey [private]. (* service private key *)
|
|
free csKey: sKey [private].
|
|
free shared: sKey [private].
|
|
|
|
|
|
(*
|
|
* ==============
|
|
* Function definition
|
|
* ==============
|
|
*)
|
|
|
|
(* key 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 *)
|
|
(* public key signing *)
|
|
fun sign(bitstring, privKey): bitstring.
|
|
reduc forall c: bitstring, k: privKey; ver(c, sign(c, k), genPub(k)) = true. (* verify signature *)
|
|
|
|
(* 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((shared, shared), 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 (=shared, =shared) = sDec(enc2, new_Key)
|
|
.
|
|
|
|
|
|
(* test the connection between the service and client. Assume that the authentication server
|
|
* has been compromised. *)
|
|
|
|
|
|
let service_shared() =
|
|
let xored = xor(csKey, shared) in
|
|
out (cs, xored);
|
|
in (cs, enc : bitstring);
|
|
let data = sDec(enc, csKey) in
|
|
if data = fromKey(shared) then
|
|
event clientConnect(csKey);
|
|
server_rekey(csKey, cs, prS).
|
|
|
|
let client_shared(pbS: pubKey) =
|
|
in (cs, xored : sKey);
|
|
let xorKey = unxor(xored, shared) in
|
|
event serviceConnect(xorKey);
|
|
out (cs, sEnc(fromKey(shared), xorKey)); (* test 'shared' secrecy *)
|
|
client_rekey(xorKey, cs, pbS).
|
|
|
|
|
|
(* assume that the client and service have not yet setup a shared secret *)
|
|
(* DATA is the secret data *)
|
|
|
|
let service_no_shared() =
|
|
in (cs, pbC : pubKey);
|
|
let shared2 = exchange(prS, pbC) in
|
|
let xored = xor(csKey, shared2) in
|
|
out (cs, xored);
|
|
in (cs, enc : bitstring);
|
|
let data = sDec(enc, csKey) in
|
|
if data = fromKey(shared) then
|
|
event clientConnect(csKey);
|
|
server_rekey(csKey, cs, prS).
|
|
|
|
let client_no_shared(pbS: pubKey) =
|
|
new prC : privKey;
|
|
out (cs, genPub(prC));
|
|
let shared2 = exchange(prC, pbS) in
|
|
in (cs, xored : sKey);
|
|
let xorKey = unxor(xored, shared2) in
|
|
event serviceConnect(xorKey);
|
|
out (cs, sEnc(fromKey(shared), xorKey)); (* test 'shared' secrecy *)
|
|
client_rekey(xorKey, cs, pbS).
|
|
|
|
|
|
(* assume that the client and service have not yet setup a shared secret *)
|
|
(* DATA is the secret data *)
|
|
|
|
(*
|
|
* Event correlation:
|
|
*)
|
|
query k : sKey; event(clientConnect(k)) ==> event(serviceConnect(k)).
|
|
|
|
(*
|
|
* ==============
|
|
* What should be secret?
|
|
* ==============
|
|
*)
|
|
|
|
query attacker(shared). (* client-service shared key. *)
|
|
query attacker(csKey). (* client-service key. *)
|
|
|
|
(*
|
|
* ==============
|
|
* Run the verification
|
|
* ==============
|
|
*)
|
|
|
|
(* make it easy to deactivate single parts, like the 2-RTT login, which has unproven events problems *)
|
|
|
|
process
|
|
(!service_no_shared()) | (!client_no_shared(genPub(prS)))
|
|
| phase 1; (!service_shared()) | (!client_shared(genPub(prS)))
|
|
|
|
|
|
|
|
|