init
This commit is contained in:
153
proverif/3dh.pv
Normal file
153
proverif/3dh.pv
Normal file
@@ -0,0 +1,153 @@
|
||||
(*
|
||||
3DH; proving authenticity and secrecy
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey).
|
||||
event recvE1(bitstring, key, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao)) in
|
||||
event sendE1(m1, mak1, gao);
|
||||
|
||||
phase 2;
|
||||
|
||||
out(c, (x1, x1_mac, gao))
|
||||
|
||||
).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* auth *)
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvE1(m, rk, k1)) ==> inj-event(sendE1(m, rk, k1)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvE1(m, rk, k1)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendE1(m, rk, k1)). (* reachable from all executions *)
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
event start();
|
||||
( (!PeerA(SK_A, PK_A, PK_B)) |
|
||||
(!PeerB(SK_B, PK_B, PK_A)))
|
||||
150
proverif/deniability/3dh-initiator-deny.pv
Normal file
150
proverif/deniability/3dh-initiator-deny.pv
Normal file
@@ -0,0 +1,150 @@
|
||||
(*
|
||||
3DH INITIATOR DENY
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
(*
|
||||
set simpEqAll = false.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey).
|
||||
event recvE1(bitstring, key, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao)) in
|
||||
event sendE1(m1, mak1, gao);
|
||||
|
||||
phase 2;
|
||||
|
||||
out(c, (x1, x1_mac, gao))
|
||||
|
||||
).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
|
||||
event start();
|
||||
( (PeerA(k_A, pk(k_A), PK_B)) |
|
||||
(PeerB(SK_B, PK_B, PK_A)) |
|
||||
out(a, m1)) (* transcript publish *)
|
||||
167
proverif/deniability/3dh-rep-nodeny.pv
Normal file
167
proverif/deniability/3dh-rep-nodeny.pv
Normal file
@@ -0,0 +1,167 @@
|
||||
(*
|
||||
3DH MUTUAL DENIABILITY
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set simplifyProcess = false.
|
||||
(*
|
||||
set simpEqAll = false.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey).
|
||||
event recvE1(bitstring, key, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao)) in
|
||||
event sendE1(m1, mak1, gao);
|
||||
|
||||
phase 2;
|
||||
|
||||
out(c, (x1, x1_mac, gao))
|
||||
|
||||
).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
(*
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* auth *)
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvE1(m, rk, k1)) ==> inj-event(sendE1(m, rk, k1)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvE1(m, rk, k1)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendE1(m, rk, k1)). (* reachable from all executions *)
|
||||
*)
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
|
||||
event start();
|
||||
( (PeerA(SK_B, PK_A, pk(k_B))) |
|
||||
(PeerB(k_B, pk(k_B), PK_A)) |
|
||||
out(a, m1))
|
||||
135
proverif/deniability/fan-out-deny.pv
Normal file
135
proverif/deniability/fan-out-deny.pv
Normal file
@@ -0,0 +1,135 @@
|
||||
(*
|
||||
Showing the compromise of a fan-out session signing key results in a loss of deniability,
|
||||
in isolation from the peer-to-peer layer
|
||||
*)
|
||||
set simplifyProcess = false.
|
||||
set preciseActions = true.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(pkey, key): bitstring [data].
|
||||
fun concat2(bitstring, bitstring): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey).
|
||||
event recvE1(bitstring, key, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free k: key [private].
|
||||
free m1: bitstring [private].
|
||||
|
||||
let PeerA() =
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new rk: key;
|
||||
|
||||
new fib1: skey;
|
||||
let sk_A = choice [fib1, SK_A] in
|
||||
|
||||
let x1 = senc(k, (pk(sk_A), rk)) in
|
||||
let x1_mac = mac(k, x1) in
|
||||
out(c, (x1, x1_mac));
|
||||
|
||||
phase 1;
|
||||
|
||||
let mx1 = senc(rk, m1) in
|
||||
let mx1_mac = mac(rk, mx1) in
|
||||
let mx1_sig = sign(sk_A, concat2(mx1, mx1_mac)) in
|
||||
out(c, (mx1, mx1_mac, mx1_sig));
|
||||
|
||||
phase 2;
|
||||
|
||||
phase 3;
|
||||
|
||||
out(a, PK_A);
|
||||
|
||||
0.
|
||||
|
||||
let PeerB() =
|
||||
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
|
||||
phase 1;
|
||||
in(c, (x1: bitstring, x1_mac: bitstring));
|
||||
if checkmac(k, x1, x1_mac) = okay then
|
||||
let (PK_A: pkey, rk: key) = sdec(k, x1) in
|
||||
|
||||
phase 2;
|
||||
|
||||
in(c, (mx1: bitstring, mx1_mac: bitstring, mx1_sig: bitstring));
|
||||
(* in(c, (mx1: bitstring, mx1_mac: bitstring)); *)
|
||||
|
||||
if checksign(PK_A, concat2(mx1, mx1_mac), mx1_sig) = okay then
|
||||
if checkmac(rk, mx1, mx1_mac) = okay then
|
||||
let m1 = sdec(rk, mx1) in
|
||||
event start();
|
||||
|
||||
phase 3;
|
||||
|
||||
0.
|
||||
|
||||
(* query event(start()). (* reachable from all possible executions *) *)
|
||||
|
||||
process
|
||||
(*
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
*)
|
||||
|
||||
new fib1: skey;
|
||||
|
||||
(* let (kM_A: skey) = choice [fib1, SK_A] in *)
|
||||
(* let kM_A = (SK_A) in *)
|
||||
|
||||
( (!PeerA()) |
|
||||
(!PeerB()))
|
||||
|
||||
232
proverif/deniability/megolm-initiator-deny.pv
Normal file
232
proverif/deniability/megolm-initiator-deny.pv
Normal file
@@ -0,0 +1,232 @@
|
||||
(*
|
||||
Olm + Megolm initiator deniability
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = passive.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
(*
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_mac = mac(mak1_auth, mx1) in
|
||||
let mx1_sig = sign(MSK_A, concat3(mx1, mx1_mac)) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_mac, mx1_sig));
|
||||
|
||||
phase 3 (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_mac: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, concat3(mx1, mx1_mac), mx1_sig) = okay then (
|
||||
if checkmac(mak1_auth, mx1, mx1_mac) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B)
|
||||
|
||||
))).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
|
||||
new fib3: skey;
|
||||
new fib4: skey;
|
||||
let (kO_A: skey, kM_A: skey) = choice [(OSK_A, MSK_A), (fib1, fib2) ] in
|
||||
let (kO_B: skey, kM_B: skey) = choice [(OSK_B, MSK_B), (fib1, fib2) ] in
|
||||
|
||||
( (!PeerA(kO_A, pk(kO_A), OPK_B, OLM_KEYS_STR, OLM_ROOT_STR, kM_A, pk(kM_A))) |
|
||||
(!PeerB(OSK_B, OPK_B, OPK_A, OLM_KEYS_STR, OLM_ROOT_STR, MSK_B, MPK_B)))
|
||||
232
proverif/deniability/megolm-resp-nodeny.pv
Normal file
232
proverif/deniability/megolm-resp-nodeny.pv
Normal file
@@ -0,0 +1,232 @@
|
||||
(*
|
||||
Olm + Megolm responder deniability
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = active.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
(*
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_mac = mac(mak1_auth, mx1) in
|
||||
let mx1_sig = sign(MSK_A, concat3(mx1, mx1_mac)) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_mac, mx1_sig));
|
||||
|
||||
phase 3 (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_mac: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, concat3(mx1, mx1_mac), mx1_sig) = okay then (
|
||||
if checkmac(mak1_auth, mx1, mx1_mac) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B)
|
||||
|
||||
))).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
|
||||
new fib3: skey;
|
||||
new fib4: skey;
|
||||
let (kO_A: skey, kM_A: skey) = choice [(OSK_A, MSK_A), (fib1, fib2) ] in
|
||||
let (kO_B: skey, kM_B: skey) = choice [(OSK_B, MSK_B), (fib1, fib2) ] in
|
||||
|
||||
( (!PeerA(OSK_A, OPK_A, OPK_B, OLM_KEYS_STR, OLM_ROOT_STR, MSK_A, MPK_A)) |
|
||||
(!PeerB(kO_B, pk(kO_B), OPK_A, OLM_KEYS_STR, OLM_ROOT_STR, kM_A, pk(kM_A))))
|
||||
203
proverif/deniability/olm-initiator-deny.pv
Normal file
203
proverif/deniability/olm-initiator-deny.pv
Normal file
@@ -0,0 +1,203 @@
|
||||
(*
|
||||
Olm 3DH+Double Ratchet; initiator deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = passive.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf3_dev1(key, bitstring): key.
|
||||
fun hkdf3_dev2(key, bitstring): key.
|
||||
letfun hkdf3(k: key, b: bitstring) =
|
||||
(hkdf3_dev1(k, b), hkdf3_dev2(k, b)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey, pkey).
|
||||
event recvE1(bitstring, key, pkey, pkey).
|
||||
|
||||
event sendE2(bitstring, key, pkey, pkey).
|
||||
event recvE2(bitstring, key, pkey, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf3(amaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendE1(m1, mak1, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
(
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2, gta1, gtb2);
|
||||
phase 2;
|
||||
|
||||
event compromiseSKA(SK_A);
|
||||
out(c, SK_A)
|
||||
|
||||
)).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf3(bmaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao, gta1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2, gta1, gtb2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
event start();
|
||||
( (PeerA(k_A, pk(k_A), PK_B, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
(PeerB(SK_B, PK_B, PK_A, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
out(a, (m1, m2))) (* transcript publish *)
|
||||
204
proverif/deniability/olm-resp-nodeny.pv
Normal file
204
proverif/deniability/olm-resp-nodeny.pv
Normal file
@@ -0,0 +1,204 @@
|
||||
(*
|
||||
Olm 3DH+Double Ratchet; responder deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = passive.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf3_dev1(key, bitstring): key.
|
||||
fun hkdf3_dev2(key, bitstring): key.
|
||||
letfun hkdf3(k: key, b: bitstring) =
|
||||
(hkdf3_dev1(k, b), hkdf3_dev2(k, b)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey, pkey).
|
||||
event recvE1(bitstring, key, pkey, pkey).
|
||||
|
||||
event sendE2(bitstring, key, pkey, pkey).
|
||||
event recvE2(bitstring, key, pkey, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf3(amaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendE1(m1, mak1, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
(
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2, gta1, gtb2);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKA(SK_A);
|
||||
out(c, SK_A)
|
||||
)).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf3(bmaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao, gta1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2, gta1, gtb2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
event start();
|
||||
|
||||
event start();
|
||||
( (PeerA(SK_B, PK_A, PK_B, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
(PeerB(k_B, pk(k_B), PK_A, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
out(a, (m1, m2))) (* transcript publish *)
|
||||
281
proverif/deniability/sender-keys-initiator-deny.pv
Normal file
281
proverif/deniability/sender-keys-initiator-deny.pv
Normal file
@@ -0,0 +1,281 @@
|
||||
(*
|
||||
Sender Keys protocol; proving secrecy, authentication, PCS, and PFS
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESSION B -> A =================== *)
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let ca2 = hkdf(concat3(concat0(khash(ra1)), dh(gtb2, ta1))) in
|
||||
let ak2 = khash(ca2) in
|
||||
let ak2_auth = hkdf(concat0(ak2)) in
|
||||
|
||||
if checkmac(ak2_auth, concat2(x2, gtb2), x2_mac) = okay then (
|
||||
|
||||
let mak2_enc = hkdf(concat0(ak2)) in
|
||||
|
||||
let (MPK_B: pkey, mrb0: key) = sdec(mak2_enc, x2) in
|
||||
event recvOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_sig = sign(MSK_A, mx1) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_sig));
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
in(c, (mx2: bitstring, mx2_sig: bitstring));
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
if checksign(MPK_B, mx2, mx2_sig) = okay then
|
||||
let m2 = sdec(mbk1_enc, mx2) in
|
||||
event recvME2(m2, mrb1, MPK_B);
|
||||
|
||||
phase 3; (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
event compromiseOSKA(OSK_A);
|
||||
out(c, OSK_A)
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESH B -> A =================== *)
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
let rb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
let cb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
|
||||
let bk2 = khash(cb2) in
|
||||
let bk2_auth = hkdf(concat0(bk2)) in
|
||||
let bk2_enc = hkdf(concat0(bk2)) in
|
||||
|
||||
new mrb0: key; (* mrb0 := sender keys ratchet *)
|
||||
|
||||
let x2 = senc(bk2_enc, (MPK_B, mrb0)) in
|
||||
let x2_mac = mac(bk2_auth, concat2(x2, gtb2)) in
|
||||
event sendOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, mx1, mx1_sig) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B);
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
|
||||
let mx2 = senc(mbk1_enc, m2) in
|
||||
let mx2_sig = sign(MSK_B, mx2) in
|
||||
event sendME2(m2, mrb1, MPK_B);
|
||||
out(c, (mx2, mx2_sig))
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
)).
|
||||
|
||||
process
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
|
||||
new fib3: skey;
|
||||
new fib4: skey;
|
||||
let (kO_A: skey, kM_A: skey) = choice [(OSK_A, MSK_A), (fib1, fib2) ] in
|
||||
let (kO_B: skey, kM_B: skey) = choice [(OSK_B, MSK_B), (fib1, fib2) ] in
|
||||
|
||||
( (!PeerA(kO_A, pk(kO_A), OPK_B, kM_A, pk(kM_A))) |
|
||||
(!PeerB(OSK_B, OPK_B, OPK_A, MSK_B, MPK_B)))
|
||||
|
||||
281
proverif/deniability/sender-keys-resp-nodeny.pv
Normal file
281
proverif/deniability/sender-keys-resp-nodeny.pv
Normal file
@@ -0,0 +1,281 @@
|
||||
(*
|
||||
Sender Keys protocol; proving secrecy, authentication, PCS, and PFS
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESSION B -> A =================== *)
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let ca2 = hkdf(concat3(concat0(khash(ra1)), dh(gtb2, ta1))) in
|
||||
let ak2 = khash(ca2) in
|
||||
let ak2_auth = hkdf(concat0(ak2)) in
|
||||
|
||||
if checkmac(ak2_auth, concat2(x2, gtb2), x2_mac) = okay then (
|
||||
|
||||
let mak2_enc = hkdf(concat0(ak2)) in
|
||||
|
||||
let (MPK_B: pkey, mrb0: key) = sdec(mak2_enc, x2) in
|
||||
event recvOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_sig = sign(MSK_A, mx1) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_sig));
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
in(c, (mx2: bitstring, mx2_sig: bitstring));
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
if checksign(MPK_B, mx2, mx2_sig) = okay then
|
||||
let m2 = sdec(mbk1_enc, mx2) in
|
||||
event recvME2(m2, mrb1, MPK_B);
|
||||
|
||||
phase 3; (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
event compromiseOSKA(OSK_A);
|
||||
out(c, OSK_A)
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESH B -> A =================== *)
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
let rb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
let cb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
|
||||
let bk2 = khash(cb2) in
|
||||
let bk2_auth = hkdf(concat0(bk2)) in
|
||||
let bk2_enc = hkdf(concat0(bk2)) in
|
||||
|
||||
new mrb0: key; (* mrb0 := sender keys ratchet *)
|
||||
|
||||
let x2 = senc(bk2_enc, (MPK_B, mrb0)) in
|
||||
let x2_mac = mac(bk2_auth, concat2(x2, gtb2)) in
|
||||
event sendOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, mx1, mx1_sig) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B);
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
|
||||
let mx2 = senc(mbk1_enc, m2) in
|
||||
let mx2_sig = sign(MSK_B, mx2) in
|
||||
event sendME2(m2, mrb1, MPK_B);
|
||||
out(c, (mx2, mx2_sig))
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
)).
|
||||
|
||||
process
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
|
||||
new fib3: skey;
|
||||
new fib4: skey;
|
||||
let (kO_A: skey, kM_A: skey) = choice [(OSK_A, MSK_A), (fib1, fib2) ] in
|
||||
let (kO_B: skey, kM_B: skey) = choice [(OSK_B, MSK_B), (fib1, fib2) ] in
|
||||
|
||||
( (!PeerA(OSK_A, OPK_A, OPK_B, MSK_A, MPK_A)) |
|
||||
(!PeerB(kO_B, pk(kO_B), OPK_A, kM_A, pk(kM_A))))
|
||||
|
||||
194
proverif/deniability/signal-initiator-deny.pv
Normal file
194
proverif/deniability/signal-initiator-deny.pv
Normal file
@@ -0,0 +1,194 @@
|
||||
(*
|
||||
Signal X3DH+Double Ratchet; initiator deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = passive.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key.
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
fun concat3(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event sendE2(bitstring, key).
|
||||
event recvE2(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
new ae1: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
|
||||
let amaster = hkdf1(concat3(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gta1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gta1, gae1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2);
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbs = pk(bs) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gta1: pkey, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat3(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gta1, gae1), x1_mac) = okay then
|
||||
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
( (!PeerA(k_A, pk(k_A), PK_B)) |
|
||||
(!PeerB(SK_B, PK_B, PK_A)))
|
||||
193
proverif/deniability/signal-resp-nodeny.pv
Normal file
193
proverif/deniability/signal-resp-nodeny.pv
Normal file
@@ -0,0 +1,193 @@
|
||||
(*
|
||||
Signal X3DH+Double Ratchet; initiator deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set attacker = passive.
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = false.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key.
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
fun concat3(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event sendE2(bitstring, key).
|
||||
event recvE2(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
new ae1: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
|
||||
let amaster = hkdf1(concat3(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gta1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gta1, gae1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2);
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbs = pk(bs) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gta1: pkey, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat3(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gta1, gae1), x1_mac) = okay then
|
||||
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
( (PeerA(SK_A, PK_A, PK_B)) |
|
||||
(PeerB(k_B, pk(k_B), PK_A)))
|
||||
168
proverif/deniability/x3dh-initiator-deny.pv
Normal file
168
proverif/deniability/x3dh-initiator-deny.pv
Normal file
@@ -0,0 +1,168 @@
|
||||
(*
|
||||
X3DH; initiator deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set attacker = passive.
|
||||
(*
|
||||
set simpEqAll = false.
|
||||
set simpEqAll = false.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring [data].
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key [data].
|
||||
fun hkdf2_dev2(key): key [data].
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
fun concat2(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
equation forall a1: key, a2: key, a3: key, a4: key; hkdf1(concat2(a1,a2,a3,a4)) = hkdf1(concat2(a1,a2,a3,a4)).
|
||||
|
||||
(*
|
||||
k1: bs
|
||||
k2: SK_A
|
||||
k3: SK_B
|
||||
k4: ae1
|
||||
k5: bo
|
||||
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
|
||||
equation forall k1: skey, k2: skey, k3: skey, k4: skey, k5: skey; hkdf1(concat2(dh(pk(k1), k2),dh(pk(k3), k4),dh(pk(k1), k4),dh(pk(k5), k4))) = hkdf1(concat2(dh(pk(k2), k1),dh(pk(k4), k3),dh(pk(k4), k1),dh(pk(k4), k5))) [convergent].
|
||||
*)
|
||||
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
|
||||
new ae1: skey;
|
||||
new ae2: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
let gae2 = pk(ae2) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
phase 1;
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gae1));
|
||||
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbs = pk(bs) in
|
||||
let gbo = pk(bo) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
phase 1; (* peer B commits first *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gae1), x1_mac) = okay then
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(a, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
|
||||
event start();
|
||||
( (PeerA(k_A, pk(k_A), PK_B)) |
|
||||
(PeerB(SK_B, PK_B, PK_A)) |
|
||||
out(a, m1)) (* transcript publish *)
|
||||
168
proverif/deniability/x3dh-resp-nodeny.pv
Normal file
168
proverif/deniability/x3dh-resp-nodeny.pv
Normal file
@@ -0,0 +1,168 @@
|
||||
(*
|
||||
X3DH; responder deniability
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set attacker = passive.
|
||||
set simplifyProcess = false.
|
||||
(*
|
||||
set simpEqAll = false.
|
||||
set simpEqAll = false.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring [data].
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key [data].
|
||||
fun hkdf2_dev2(key): key [data].
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
fun concat2(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
equation forall a1: key, a2: key, a3: key, a4: key; hkdf1(concat2(a1,a2,a3,a4)) = hkdf1(concat2(a1,a2,a3,a4)).
|
||||
|
||||
(*
|
||||
k1: bs
|
||||
k2: SK_A
|
||||
k3: SK_B
|
||||
k4: ae1
|
||||
k5: bo
|
||||
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
|
||||
equation forall k1: skey, k2: skey, k3: skey, k4: skey, k5: skey; hkdf1(concat2(dh(pk(k1), k2),dh(pk(k3), k4),dh(pk(k1), k4),dh(pk(k5), k4))) = hkdf1(concat2(dh(pk(k2), k1),dh(pk(k4), k3),dh(pk(k4), k1),dh(pk(k4), k5))) [convergent].
|
||||
*)
|
||||
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
|
||||
new ae1: skey;
|
||||
new ae2: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
let gae2 = pk(ae2) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
phase 1;
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gae1));
|
||||
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbs = pk(bs) in
|
||||
let gbo = pk(bo) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
phase 1; (* peer B commits first *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gae1), x1_mac) = okay then
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(a, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
|
||||
new fib1: skey;
|
||||
new fib2: skey;
|
||||
let k_A = choice [ SK_A, fib1 ] in
|
||||
let k_B = choice [ SK_B, fib2 ] in
|
||||
|
||||
event start();
|
||||
( (PeerA(SK_B, PK_A, pk(k_B))) |
|
||||
(PeerB(k_B, pk(k_B), PK_A)) |
|
||||
out(a, m1))
|
||||
249
proverif/megolm-olm-unsigned.pv
Normal file
249
proverif/megolm-olm-unsigned.pv
Normal file
@@ -0,0 +1,249 @@
|
||||
(*
|
||||
Olm + Megolm protocol
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
(*
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
(* if checksign(OPK_B, rb(gbo), gbo_sig) = okay then ( *)
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_mac = mac(mak1_auth, mx1) in
|
||||
let mx1_sig = sign(MSK_A, concat3(mx1, mx1_mac)) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_mac, mx1_sig));
|
||||
|
||||
phase 3; (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
(* out(c, PK_A); *)
|
||||
|
||||
0.
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_mac: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, concat3(mx1, mx1_mac), mx1_sig) = okay then (
|
||||
if checkmac(mak1_auth, mx1, mx1_mac) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B)
|
||||
|
||||
))).
|
||||
|
||||
|
||||
|
||||
query event(start()). (* reachable from all possible executions: trivially false *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseOSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
|
||||
query sk1: skey, sk2: skey; (event(compromiseOSKB(sk1)) && attacker(m1)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; inj-event(recvOE1(k1, rk, k2, k3)) ==> inj-event(sendOE1(k1, rk, k2, k3)).
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvME1(m, rk, k1)) ==> inj-event(sendME1(m, rk, k1)).
|
||||
|
||||
(* secrecy *)
|
||||
query attacker(m1). (* expected to be false *)
|
||||
|
||||
(* reachability: all expected to be false *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(sendOE1(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(recvOE1(k1,rk,k2,k3)).
|
||||
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvME1(m, rk, k1)).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
do not publish sender keys material
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
event start();
|
||||
( (!PeerA(OSK_A, OPK_A, OPK_B, OLM_KEYS_STR, OLM_ROOT_STR, MSK_A, MPK_A)) |
|
||||
(!PeerB(OSK_B, OPK_B, OPK_A, OLM_KEYS_STR, OLM_ROOT_STR, MSK_B, MPK_B)))
|
||||
319
proverif/megolm.pv
Normal file
319
proverif/megolm.pv
Normal file
@@ -0,0 +1,319 @@
|
||||
(*
|
||||
Olm + Megolm protocol; proving secrecy, authentication, PCS, and PFS
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
free mra0: key [private].
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 3: MEGOLM SESH B -> A =================== *)
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let ca2 = hkdf(concat3(concat0(khash(ra1)), dh(gtb2, ta1))) in
|
||||
let ak2 = khash(ca2) in
|
||||
let ak2_auth = hkdf(concat0(ak2)) in
|
||||
|
||||
if checkmac(ak2_auth, concat2(x2, gtb2), x2_mac) = okay then (
|
||||
|
||||
let mak2_enc = hkdf(concat0(ak2)) in
|
||||
|
||||
let (MPK_B: pkey, mrb0: key) = sdec(mak2_enc, x2) in
|
||||
event recvOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_mac = mac(mak1_auth, mx1) in
|
||||
let mx1_sig = sign(MSK_A, concat3(mx1, mx1_mac)) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_mac, mx1_sig));
|
||||
|
||||
(* =================== PHASE 5: MEGOLM MSG B -> A =================== *)
|
||||
|
||||
in(c, (mx2: bitstring, mx2_mac: bitstring, mx2_sig: bitstring));
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
if checksign(MPK_B, concat3(mx2, mx2_mac), mx2_sig) = okay then
|
||||
if checkmac(mbk1_auth, mx2, mx2_mac) = okay then
|
||||
let m2 = sdec(mbk1_enc, mx2) in
|
||||
event recvME2(m2, mrb1, MPK_B);
|
||||
|
||||
phase 3; (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
event compromiseOSKA(OSK_A);
|
||||
out(c, OSK_A)
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE OLM MASTER + MEGOLM SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 3: MEGOLM SESH B -> A =================== *)
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
let rb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
let cb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
|
||||
let bk2 = khash(cb2) in
|
||||
let bk2_auth = hkdf(concat0(bk2)) in
|
||||
let bk2_enc = hkdf(concat0(bk2)) in
|
||||
|
||||
new mrb0: key; (* mrb0 := megolm ratchet *)
|
||||
|
||||
|
||||
let x2 = senc(bk2_enc, (MPK_B, mrb0)) in
|
||||
let x2_mac = mac(bk2_auth, concat2(x2, gtb2)) in
|
||||
event sendOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
|
||||
(* =================== PHASE 4: MEGOLM MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_mac: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, concat3(mx1, mx1_mac), mx1_sig) = okay then (
|
||||
if checkmac(mak1_auth, mx1, mx1_mac) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B);
|
||||
|
||||
(* =================== PHASE 5: MEGOLM MSG B -> A =================== *)
|
||||
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
|
||||
let mx2 = senc(mbk1_enc, m2) in
|
||||
let mx2_mac = mac(mbk1_auth, mx2) in
|
||||
let mx2_sig = sign(MSK_B, concat3(mx2, mx2_mac)) in
|
||||
event sendME2(m2, mrb1, MPK_B);
|
||||
out(c, (mx2, mx2_mac, mx2_sig))
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
))).
|
||||
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseOSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
|
||||
query sk1: skey, sk2: skey; (event(compromiseOSKB(sk1)) && event(compromiseOSKA(sk2)) && attacker(m1)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; inj-event(recvOE1(k1, rk, k2, k3)) ==> inj-event(sendOE1(k1, rk, k2, k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; inj-event(recvOE2(k1, rk, k2, k3)) ==> inj-event(sendOE2(k1, rk, k2, k3)).
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvME1(m, rk, k1)) ==> inj-event(sendME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvME2(m, rk, k1)) ==> inj-event(sendME2(m, rk, k1)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
query attacker(m2).
|
||||
|
||||
(* reachability *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(sendOE1(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(recvOE1(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(sendOE2(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(recvOE2(k1,rk,k2,k3)).
|
||||
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendME2(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvME2(m, rk, k1)).
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
event start();
|
||||
( (!PeerA(OSK_A, OPK_A, OPK_B, OLM_KEYS_STR, OLM_ROOT_STR, MSK_A, MPK_A)) |
|
||||
(!PeerB(OSK_B, OPK_B, OPK_A, OLM_KEYS_STR, OLM_ROOT_STR, MSK_B, MPK_B)))
|
||||
|
||||
211
proverif/olm-unsigned.pv
Normal file
211
proverif/olm-unsigned.pv
Normal file
@@ -0,0 +1,211 @@
|
||||
(*
|
||||
Olm 3DH+Double Ratchet without olm signing;
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric keys *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf3_dev1(key, bitstring): key.
|
||||
fun hkdf3_dev2(key, bitstring): key.
|
||||
letfun hkdf3(k: key, b: bitstring) =
|
||||
(hkdf3_dev1(k, b), hkdf3_dev2(k, b)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey, pkey).
|
||||
event recvE1(bitstring, key, pkey, pkey).
|
||||
|
||||
event sendE2(bitstring, key, pkey, pkey).
|
||||
event recvE2(bitstring, key, pkey, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
in(c, (gbo: pkey));
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf3(amaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendE1(m1, mak1, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
(
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2, gta1, gtb2);
|
||||
phase 2
|
||||
).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
|
||||
out(c, (gbo));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf3(bmaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao, gta1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2, gta1, gtb2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
).
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
query sk: skey; (event(compromiseSKB(sk)) && attacker(m2)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; inj-event(recvE1(m, rk, k1, k2)) ==> inj-event(sendE1(m, rk, k1, k2)).
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey, k3: pkey, k4: pkey; inj-event(recvE2(m, rk, k1, k2)) ==> inj-event(sendE2(m, rk, k1, k2)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
query attacker(m2).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(recvE1(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(recvE2(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(sendE1(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(sendE2(m, rk, k1, k2)). (* rechable from all executions *)
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
event start();
|
||||
( (!PeerA(SK_A, PK_A, PK_B, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
(!PeerB(SK_B, PK_B, PK_A, OLM_KEYS_STR, OLM_ROOT_STR)))
|
||||
220
proverif/olm.pv
Normal file
220
proverif/olm.pv
Normal file
@@ -0,0 +1,220 @@
|
||||
(*
|
||||
Olm 3DH+Double Ratchet; proving authenticity, secrecy, forward secrecy, and post-compromise security
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(key, key, key): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf3_dev1(key, bitstring): key.
|
||||
fun hkdf3_dev2(key, bitstring): key.
|
||||
letfun hkdf3(k: key, b: bitstring) =
|
||||
(hkdf3_dev1(k, b), hkdf3_dev2(k, b)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key, pkey, pkey).
|
||||
event recvE1(bitstring, key, pkey, pkey).
|
||||
|
||||
event sendE2(bitstring, key, pkey, pkey).
|
||||
event recvE2(bitstring, key, pkey, pkey).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(PK_B, rb(gbo), gbo_sig) = okay then (
|
||||
|
||||
let amaster = hkdf1(dh(PK_B, SK_A), dh(gbo, SK_A), dh(PK_B, ao)) in
|
||||
let (ra1: key, ca1: key) = hkdf3(amaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendE1(m1, mak1, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
(
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2, gta1, gtb2);
|
||||
phase 2
|
||||
)).
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, OLM_KEYS_STR:bitstring, OLM_ROOT_STR: bitstring) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(SK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf1(dh(PK_A, SK_B), dh(PK_A, bo), dh(gao, SK_B)) in
|
||||
let (rb1: key, cb1: key) = hkdf3(bmaster, OLM_ROOT_STR) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1, gao, gta1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2, gta1, gtb2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B)
|
||||
|
||||
).
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
query sk: skey; (event(compromiseSKB(sk)) && attacker(m2)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; inj-event(recvE1(m, rk, k1, k2)) ==> inj-event(sendE1(m, rk, k1, k2)).
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey, k3: pkey, k4: pkey; inj-event(recvE2(m, rk, k1, k2)) ==> inj-event(sendE2(m, rk, k1, k2)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
query attacker(m2).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(recvE1(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(recvE2(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(sendE1(m, rk, k1, k2)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key, k1: pkey, k2: pkey; event(sendE2(m, rk, k1, k2)). (* rechable from all executions *)
|
||||
|
||||
process
|
||||
new OLM_KEYS_STR:bitstring; out(a, OLM_KEYS_STR);
|
||||
new OLM_ROOT_STR:bitstring; out(a, OLM_ROOT_STR);
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
event start();
|
||||
( (!PeerA(SK_A, PK_A, PK_B, OLM_KEYS_STR, OLM_ROOT_STR)) |
|
||||
(!PeerB(SK_B, PK_B, PK_A, OLM_KEYS_STR, OLM_ROOT_STR)))
|
||||
310
proverif/sender-keys.pv
Normal file
310
proverif/sender-keys.pv
Normal file
@@ -0,0 +1,310 @@
|
||||
(*
|
||||
Sender Keys protocol; proving secrecy, authentication, PCS, and PFS
|
||||
Author: [redacted]
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set simpEqAll = false.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): bitstring.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun khash(key): key.
|
||||
fun hkdf(bitstring): key [data].
|
||||
|
||||
fun concat0(key): bitstring [data, typeConverter].
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey, k2: pkey; split1(concat1(m1, k1, k2)) = (m1, k1, k2).
|
||||
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
reduc forall m1: bitstring, k1: pkey; split2(concat2(m1, k1)) = (m1, k1).
|
||||
|
||||
fun concat3(bitstring, bitstring): bitstring [data].
|
||||
reduc forall m1: bitstring, m2: bitstring; split3(concat3(m1, m2)) = (m1, m2).
|
||||
fun concat4(bitstring, bitstring, bitstring): bitstring [data].
|
||||
|
||||
reduc forall m1: bitstring, m2: bitstring, m3:bitstring; split4(concat4(m1, m2, m3)) = (m1, m2, m3).
|
||||
|
||||
equation forall k: key, a: skey, b: skey; hkdf(concat3(concat0(khash(k)), dh(pk(a), b))) = hkdf(concat3(concat0(khash(k)), dh(pk(b), a))).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
(* events *)
|
||||
event sendOE1(pkey, key, pkey, pkey).
|
||||
event recvOE1(pkey, key, pkey, pkey).
|
||||
|
||||
event sendOE2(pkey, key, pkey, pkey).
|
||||
event recvOE2(pkey, key, pkey, pkey).
|
||||
|
||||
event sendME1(bitstring, key, pkey).
|
||||
event recvME1(bitstring, key, pkey).
|
||||
|
||||
event sendME2(bitstring, key, pkey).
|
||||
event recvME2(bitstring, key, pkey).
|
||||
|
||||
event compromiseOSKA(skey).
|
||||
|
||||
event compromiseOSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(OSK_A: skey, OPK_A: pkey, OPK_B: pkey, MSK_A: skey, MPK_A: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
let gao = pk(ao) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
(* in(c, gbo: pkey); *)
|
||||
in(c, (gbo: pkey, gbo_sig: bitstring));
|
||||
if checksign(OPK_B, rb(gbo), gbo_sig) = okay then
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
let amaster = hkdf(concat4(dh(OPK_B, OSK_A), dh(gbo, OSK_A), dh(OPK_B, ao))) in
|
||||
let ra1 = hkdf(concat0(amaster)) in
|
||||
let ca1 = hkdf(concat0(amaster)) in
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let ak1 = khash(ca1) in
|
||||
let ak1_auth = hkdf(concat0(ak1)) in
|
||||
let ak1_enc = hkdf(concat0(ak1)) in
|
||||
|
||||
new mra0: key; (* mra0 := megolm ratchet *)
|
||||
|
||||
let x1 = senc(ak1_enc, (MPK_A, mra0)) in
|
||||
let x1_mac = mac(ak1_auth, concat1(x1, gao, gta1)) in
|
||||
event sendOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
out(c, (x1, x1_mac, gao, gta1));
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESSION B -> A =================== *)
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let ca2 = hkdf(concat3(concat0(khash(ra1)), dh(gtb2, ta1))) in
|
||||
let ak2 = khash(ca2) in
|
||||
let ak2_auth = hkdf(concat0(ak2)) in
|
||||
|
||||
if checkmac(ak2_auth, concat2(x2, gtb2), x2_mac) = okay then (
|
||||
|
||||
let mak2_enc = hkdf(concat0(ak2)) in
|
||||
|
||||
let (MPK_B: pkey, mrb0: key) = sdec(mak2_enc, x2) in
|
||||
event recvOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
let mx1 = senc(mak1_enc, m1) in
|
||||
let mx1_sig = sign(MSK_A, mx1) in
|
||||
event sendME1(m1, mra1, MPK_A);
|
||||
out(c, (mx1, mx1_sig));
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
in(c, (mx2: bitstring, mx2_sig: bitstring));
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
if checksign(MPK_B, mx2, mx2_sig) = okay then
|
||||
let m2 = sdec(mbk1_enc, mx2) in
|
||||
event recvME2(m2, mrb1, MPK_B);
|
||||
|
||||
phase 3; (* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
event compromiseOSKA(OSK_A);
|
||||
out(c, OSK_A)
|
||||
).
|
||||
|
||||
let PeerB(OSK_B: skey, OPK_B: pkey, OPK_A: pkey, MSK_B: skey, MPK_B: pkey) =
|
||||
new bo: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbo_sig = sign(OSK_B, rb(gbo)) in
|
||||
|
||||
out(c, (gbo, gbo_sig));
|
||||
phase 1;
|
||||
|
||||
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gao: pkey, gta1: pkey));
|
||||
|
||||
let bmaster = hkdf(concat4(dh(OPK_A, OSK_B), dh(OPK_A, bo), dh(gao, OSK_B))) in
|
||||
let rb1 = hkdf(concat0(bmaster)) in
|
||||
let cb1 = hkdf(concat0(bmaster)) in
|
||||
|
||||
let bk1 = khash(cb1) in
|
||||
let bk1_auth = hkdf(concat0(bk1)) in
|
||||
let bk1_enc = hkdf(concat0(bk1)) in
|
||||
if checkmac(bk1_auth, concat1(x1, gao, gta1), x1_mac) = okay then
|
||||
(
|
||||
let (MPK_A: pkey, mra0: key) = sdec(bk1_enc, x1) in
|
||||
event recvOE1(MPK_A, mra0, gao, gta1);
|
||||
|
||||
(* =================== PHASE 3: SENDER KEYS SESH B -> A =================== *)
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
let rb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
let cb2 = hkdf(concat3(concat0(khash(rb1)), dh(gta1, tb2))) in
|
||||
|
||||
let bk2 = khash(cb2) in
|
||||
let bk2_auth = hkdf(concat0(bk2)) in
|
||||
let bk2_enc = hkdf(concat0(bk2)) in
|
||||
|
||||
new mrb0: key; (* mrb0 := sender keys ratchet *)
|
||||
|
||||
let x2 = senc(bk2_enc, (MPK_B, mrb0)) in
|
||||
let x2_mac = mac(bk2_auth, concat2(x2, gtb2)) in
|
||||
event sendOE2(MPK_B, mrb0, gao, gta1);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
|
||||
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||
|
||||
in(c, (mx1: bitstring, mx1_sig: bitstring));
|
||||
|
||||
let mra1 = khash(mra0) in
|
||||
let mak1_auth = hkdf(concat0(mra1)) in
|
||||
let mak1_enc = hkdf(concat0(mra1)) in
|
||||
|
||||
if checksign(MPK_A, mx1, mx1_sig) = okay then (
|
||||
let m1 = sdec(mak1_enc, mx1) in
|
||||
event recvME1(m1, mra1, MPK_A);
|
||||
|
||||
phase 3;
|
||||
|
||||
event compromiseOSKB(OSK_B);
|
||||
out(c, OSK_B);
|
||||
|
||||
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||
|
||||
let mrb1 = khash(mrb0) in
|
||||
let mbk1_auth = hkdf(concat0(mrb1)) in
|
||||
let mbk1_enc = hkdf(concat0(mrb1)) in
|
||||
|
||||
let mx2 = senc(mbk1_enc, m2) in
|
||||
let mx2_sig = sign(MSK_B, mx2) in
|
||||
event sendME2(m2, mrb1, MPK_B);
|
||||
out(c, (mx2, mx2_sig))
|
||||
|
||||
(* =================== PHASE 6: PCS =================== *)
|
||||
|
||||
)).
|
||||
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseOSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
|
||||
query sk1: skey, sk2: skey; (event(compromiseOSKB(sk1)) && event(compromiseOSKA(sk2)) && attacker(m1)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; inj-event(recvOE1(k1, rk, k2, k3)) ==> inj-event(sendOE1(k1, rk, k2, k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; inj-event(recvOE2(k1, rk, k2, k3)) ==> inj-event(sendOE2(k1, rk, k2, k3)).
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvME1(m, rk, k1)) ==> inj-event(sendME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; inj-event(recvME2(m, rk, k1)) ==> inj-event(sendME2(m, rk, k1)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
query attacker(m2).
|
||||
|
||||
(* reachability *)
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(sendOE1(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(recvOE1(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(sendOE2(k1,rk,k2,k3)).
|
||||
query k1: pkey, rk: key, k2: pkey, k3: pkey; event(recvOE2(k1,rk,k2,k3)).
|
||||
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvME1(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(sendME2(m, rk, k1)).
|
||||
query m: bitstring, rk: key, k1: pkey; event(recvME2(m, rk, k1)).
|
||||
|
||||
process
|
||||
new OSK_A: skey; let OPK_A = pk(OSK_A) in
|
||||
new OSK_B: skey; let OPK_B = pk(OSK_B) in
|
||||
|
||||
new MSK_A: skey; let MPK_A = pk(MSK_A) in
|
||||
new MSK_B: skey; let MPK_B = pk(MSK_B) in
|
||||
out(a, OPK_A);
|
||||
out(a, OPK_B);
|
||||
(*
|
||||
out(a, MPK_A);
|
||||
out(a, MPK_B);
|
||||
*)
|
||||
event start();
|
||||
( (!PeerA(OSK_A, OPK_A, OPK_B, MSK_A, MPK_A)) |
|
||||
(!PeerB(OSK_B, OPK_B, OPK_A, MSK_B, MPK_B)))
|
||||
|
||||
217
proverif/signal.pv
Normal file
217
proverif/signal.pv
Normal file
@@ -0,0 +1,217 @@
|
||||
(*
|
||||
Signal X3DH+Double Ratchet; proving authenticity, secrecy, forward secrecy, and post-compromise security
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
model assumption #2: authentication for the first message holds, and is thus omitted from this model. authentication was proved standalone in `x3dh.pv`
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
free m2: bitstring [private].
|
||||
|
||||
set simpEqAll = false.
|
||||
set selFun = Nounifset.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring.
|
||||
fun pk(skey): pkey.
|
||||
fun aenc(bitstring, pkey): bitstring.
|
||||
|
||||
reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key.
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key.
|
||||
fun hkdf2_dev2(key): key.
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
fun hkdf4_dev1(key, key): key.
|
||||
fun hkdf4_dev2(key, key): key.
|
||||
letfun hkdf4(k1: key, k2: key) =
|
||||
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey, pkey): bitstring [data].
|
||||
fun concat2(bitstring, pkey): bitstring [data].
|
||||
fun concat3(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event sendE2(bitstring, key).
|
||||
event recvE2(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
free tag_oe1: bitstring [private].
|
||||
free tag_oe2: bitstring [private].
|
||||
free tag_me1: bitstring [private].
|
||||
free tag_me2: bitstring [private].
|
||||
free tag_b_eph: bitstring [private].
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
phase 1;
|
||||
|
||||
new ao: skey;
|
||||
new ae1: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
|
||||
let amaster = hkdf1(concat3(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
new ta1: skey;
|
||||
let gta1 = pk(ta1) in
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gta1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gta1, gae1));
|
||||
|
||||
(* second stage: now, decrypt the received message from bob *)
|
||||
|
||||
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||
|
||||
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ta1)) in
|
||||
let mak2 = khash(ca2) in
|
||||
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||
|
||||
if checkmac(mak2_auth, concat2(x2, gtb2), x2_mac) = okay then
|
||||
let m2 = sdec(mak2_enc, x2) in
|
||||
event recvE2(m2, mak2);
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbo = pk(bo) in
|
||||
let gbs = pk(bs) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
|
||||
phase 1;
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gta1: pkey, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat3(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gta1, gae1), x1_mac) = okay then
|
||||
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
new tb2: skey;
|
||||
let gtb2 = pk(tb2) in
|
||||
|
||||
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gta1, tb2)) in
|
||||
let mbk2 = khash(cb2) in
|
||||
let (mbk2_auth: key, mbk2_enc: key) = hkdf2(mbk2) in
|
||||
|
||||
let x2 = senc(mbk2_enc, m2) in
|
||||
let x2_mac = mac(mbk2_auth, concat2(x2, gtb2)) in
|
||||
event sendE2(m2, mbk2);
|
||||
|
||||
out(c, (x2, x2_mac, gtb2));
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(c, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
|
||||
(* pre-compromise security, aka forward secrecy. the only way
|
||||
m1 can be compromised is if alice's sk is compromised
|
||||
|
||||
NOTE: if signed, this is trivially true since m1 is never compromised
|
||||
*)
|
||||
query sk: skey; attacker(m1) ==> event(compromiseSKB(sk)).
|
||||
|
||||
(* post-compromise security. even if the secret key is compromised, message two remains secret
|
||||
*)
|
||||
query sk: skey; (event(compromiseSKB(sk)) && attacker(m2)) ==> false.
|
||||
|
||||
(* auth *)
|
||||
(* query m: bitstring, rk: key; event(recvE1(m, rk)) ==> event(sendE1(m, rk)). *)
|
||||
query m: bitstring, rk: key; inj-event(recvE2(m, rk)) ==> inj-event(sendE2(m, rk)).
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
query attacker(m2).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key; event(recvE1(m, rk)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key; event(recvE2(m, rk)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key; event(sendE1(m, rk)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key; event(sendE2(m, rk)). (* rechable from all executions *)
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
event start();
|
||||
( (!PeerA(SK_A, PK_A, PK_B)) |
|
||||
(!PeerB(SK_B, PK_B, PK_A)))
|
||||
174
proverif/x3dh.pv
Normal file
174
proverif/x3dh.pv
Normal file
@@ -0,0 +1,174 @@
|
||||
(*
|
||||
X3DH; proving authenticity and secrecy
|
||||
Author: [redacted]
|
||||
|
||||
model assumption #1: same key is used for signing and encryption (i.e. X25519)
|
||||
*)
|
||||
|
||||
free m1: bitstring [private].
|
||||
|
||||
set selFun = Nounifset.
|
||||
(*
|
||||
set simpEqAll = false.
|
||||
set simpEqAll = false.
|
||||
set redundancyElim = best.
|
||||
set redundantHypElim = true.
|
||||
set simplifyProcess = true.
|
||||
set stopTerm = false.
|
||||
*)
|
||||
|
||||
free c: channel.
|
||||
free a: channel. (* channel for the attacker *)
|
||||
free p: channel [private]. (* For the distribution of public keys with integrity and authenticity - verification happens out of band. This is a standard assumption. *)
|
||||
|
||||
(* Symmetric key encryption *)
|
||||
|
||||
type key.
|
||||
|
||||
fun senc(key, bitstring): bitstring.
|
||||
reduc forall m: bitstring, k: key; sdec(k, senc(k,m)) = m.
|
||||
|
||||
(* Asymmetric key encryption *)
|
||||
|
||||
type skey.
|
||||
type pkey.
|
||||
|
||||
fun rb(pkey): bitstring [data].
|
||||
fun pk(skey): pkey.
|
||||
|
||||
(* Digital signatures *)
|
||||
|
||||
fun sign(skey, bitstring): bitstring.
|
||||
fun okay():bitstring.
|
||||
reduc forall m: bitstring, sk: skey; checksign(pk(sk), m, sign(sk, m)) = okay.
|
||||
|
||||
(* MACs *)
|
||||
fun mac(key, bitstring): bitstring.
|
||||
reduc forall k: key, m: bitstring; checkmac(k, m, mac(k, m)) = okay.
|
||||
|
||||
(* Diffie-Hellman *)
|
||||
(* DH -> Public^Private *)
|
||||
|
||||
fun dh(pkey, skey): key.
|
||||
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||
|
||||
(* the concat functions *)
|
||||
fun hkdf1(bitstring): key [data].
|
||||
|
||||
fun khash(key): key.
|
||||
|
||||
fun hkdf2_dev1(key): key [data].
|
||||
fun hkdf2_dev2(key): key [data].
|
||||
letfun hkdf2(k: key) =
|
||||
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||
|
||||
(* the concats *)
|
||||
|
||||
fun concat1(bitstring, pkey): bitstring [data].
|
||||
fun concat2(key, key, key, key): bitstring [data].
|
||||
|
||||
(* events *)
|
||||
event sendE1(bitstring, key).
|
||||
event recvE1(bitstring, key).
|
||||
|
||||
event compromiseSKA(skey).
|
||||
|
||||
event compromiseSKB(skey).
|
||||
|
||||
event start().
|
||||
|
||||
(*
|
||||
equation forall a1: key, a2: key, a3: key, a4: key; hkdf1(concat2(a1,a2,a3,a4)) = hkdf1(concat2(a1,a2,a3,a4)).
|
||||
*)
|
||||
|
||||
(*
|
||||
k1: bs
|
||||
k2: SK_A
|
||||
k3: SK_B
|
||||
k4: ae1
|
||||
k5: bo
|
||||
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
|
||||
equation forall k1: skey, k2: skey, k3: skey, k4: skey, k5: skey; hkdf1(concat2(dh(pk(k1), k2),dh(pk(k3), k4),dh(pk(k1), k4),dh(pk(k5), k4))) = hkdf1(concat2(dh(pk(k2), k1),dh(pk(k4), k3),dh(pk(k4), k1),dh(pk(k4), k5))) [convergent].
|
||||
*)
|
||||
|
||||
|
||||
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey) =
|
||||
|
||||
new ae1: skey;
|
||||
new ae2: skey;
|
||||
let gae1 = pk(ae1) in
|
||||
let gae2 = pk(ae2) in
|
||||
|
||||
(* generate amaster and enc msg (PHASE 1) *)
|
||||
phase 1;
|
||||
|
||||
in(c, (gbssig: bitstring, gbs: pkey, gbo: pkey));
|
||||
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1))) in
|
||||
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||
|
||||
let mak1 = khash(ca1) in
|
||||
let (mak1_auth: key, mak1_enc: key) = hkdf2(mak1) in
|
||||
|
||||
let x1 = senc(mak1_enc, m1) in
|
||||
let x1_mac = mac(mak1_auth, concat1(x1, gae1)) in
|
||||
event sendE1(m1, mak1);
|
||||
|
||||
out(c, (x1, x1_mac, gae1));
|
||||
|
||||
phase 2;
|
||||
0.
|
||||
|
||||
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||
new bo: skey;
|
||||
new bs: skey;
|
||||
|
||||
let gbs = pk(bs) in
|
||||
let gbo = pk(bo) in
|
||||
let gbssig = sign(SK_B, rb(gbs)) in
|
||||
out(c, (gbssig, gbs, gbo));
|
||||
phase 1; (* peer B commits first *)
|
||||
|
||||
(* first stage: derive bmaster, verfiy a's msgs, decrypt prekey message, reply *)
|
||||
|
||||
in(c, (x1: bitstring, x1_mac: bitstring, gae1: pkey));
|
||||
|
||||
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo))) in
|
||||
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||
|
||||
let mbk1 = khash(cb1) in
|
||||
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mbk1) in
|
||||
if checkmac(mbk1_auth, concat1(x1, gae1), x1_mac) = okay then
|
||||
let m1 = sdec(mbk1_enc, x1) in
|
||||
event recvE1(m1, mbk1);
|
||||
|
||||
phase 2;
|
||||
event compromiseSKB(SK_B);
|
||||
out(a, SK_B);
|
||||
|
||||
0.
|
||||
|
||||
query event(start()). (* reachable from all possible executions *)
|
||||
query m: bitstring, rk: key; event(recvE1(m, rk)) ==> event(sendE1(m, rk)).
|
||||
|
||||
(* auth *)
|
||||
|
||||
(* secrecy *)
|
||||
|
||||
query attacker(m1).
|
||||
|
||||
(* reachability *)
|
||||
query m: bitstring, rk: key; event(recvE1(m, rk)). (* reachable from all executions *)
|
||||
query m: bitstring, rk: key; event(sendE1(m, rk)). (* reachable from all executions *)
|
||||
|
||||
process
|
||||
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||
out(a, PK_A);
|
||||
out(a, PK_B);
|
||||
event start();
|
||||
( (PeerA(SK_A, PK_A, PK_B)) |
|
||||
(PeerB(SK_B, PK_B, PK_A)))
|
||||
Reference in New Issue
Block a user