second iteration
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
Formal Analysis of the Nested Ratchet Protocol, and Symbolic Formal Verification of Sender Keys, Signal, Megolm, Olm, 3DH, and X3DH. The main models are written in [ProVerif](https://en.wikipedia.org/wiki/ProVerif); additional supplementary models (extra from the main results described in the paper) are provided in [VerifPal](https://en.wikipedia.org/wiki/ProVerif)
|
Formal Analysis of the Nested Ratchet Protocol, and Symbolic Formal Verification of Sender Keys, Signal, Megolm, Olm, 3DH, and X3DH. The main models are written in [ProVerif](https://en.wikipedia.org/wiki/ProVerif); additional supplementary models (extra from the main results described in the paper) are provided in [VerifPal](https://en.wikipedia.org/wiki/ProVerif)
|
||||||
|
|
||||||
|
**Note: this version of the artifact includes the post-quantum construction of sender keys present in Signal. For the original artifact at the time of submission, please see the first zenodo artifact version.**
|
||||||
|
|
||||||
# Environment Setup
|
# Environment Setup
|
||||||
1. Install the [nix package manager](https://nixos.org/download/)
|
1. Install the [nix package manager](https://nixos.org/download/)
|
||||||
2. Navigate to the current directory, and run `nix develop`. You will get dropped into a devshell with the correct versions of [ProVerif](https://en.wikipedia.org/wiki/ProVerif) and [VerifPal](https://verifpal.com/). Feel free to execute another shell (i.e. `zsh`) if you have something against `bash`.
|
2. Navigate to the current directory, and run `nix develop`. You will get dropped into a devshell with the correct versions of [ProVerif](https://en.wikipedia.org/wiki/ProVerif) and [VerifPal](https://verifpal.com/). Feel free to execute another shell (i.e. `zsh`) if you have something against `bash`.
|
||||||
@@ -13,8 +15,10 @@ Formal Analysis of the Nested Ratchet Protocol, and Symbolic Formal Verification
|
|||||||
- `megolm.pv`: a composition of Megolm and Olm.
|
- `megolm.pv`: a composition of Megolm and Olm.
|
||||||
- `megolm-olm-unsigned.pv`: a composition of Megolm and Olm, where Olm pre-keys are unsigned. Demonstrates the first Megolm session.
|
- `megolm-olm-unsigned.pv`: a composition of Megolm and Olm, where Olm pre-keys are unsigned. Demonstrates the first Megolm session.
|
||||||
- `sender-keys.pv`: a composition of fan-out layer session sharing and Signal. Includes secrecy, authentication, reachability, PCS, and PFS properties.
|
- `sender-keys.pv`: a composition of fan-out layer session sharing and Signal. Includes secrecy, authentication, reachability, PCS, and PFS properties.
|
||||||
|
- `pq/pqxdh.pv`: a complete model of the Post-Quantum Extended Triple Diffie-Hellman handshake. Proving authentication, secrecy, forward secrecy, and quantum forward secrecy.
|
||||||
|
- `pq/signal.pv` and `pq/signal-pcs.pv`: a complete model of the Signal protocol, including PQXDH and Double Ratchet. Proving secrecy, authentication, forward secrecy, quantum forward secrecy, and post-compromise security.
|
||||||
|
|
||||||
The `deniability` folder includes both initiator deniability and responder undeniability results for 3dh, x3dh, olm, signal, and megolm.
|
The `deniability` folder includes both initiator deniability and responder undeniability results for 3dh, x3dh, olm, signal (with x3dh), and megolm. The `pq` folder includes initiator deniability and responder undeniability for signal with pqxdh.
|
||||||
|
|
||||||
# Notes on Proverif reachability
|
# Notes on Proverif reachability
|
||||||
In each model, there exists several queries designed to ensure reachability and no deadlocks. First,
|
In each model, there exists several queries designed to ensure reachability and no deadlocks. First,
|
||||||
|
|||||||
195
proverif/pq/pqxdh.pv
Normal file
195
proverif/pq/pqxdh.pv
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
(*
|
||||||
|
PQXDH; proving authenticity, secrecy, forward secrecy, and quantum forward secrecy
|
||||||
|
Author: jake ginesin
|
||||||
|
|
||||||
|
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)).
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
|
||||||
|
(* events *)
|
||||||
|
event sendE1(bitstring, key).
|
||||||
|
event recvE1(bitstring, key).
|
||||||
|
|
||||||
|
event compromiseSKA(skey).
|
||||||
|
event compromiseSKB(skey).
|
||||||
|
event breakDH(key, key, key, key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) 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, ct)) in
|
||||||
|
event sendE1(m1, mak1);
|
||||||
|
|
||||||
|
out(c, (x1, x1_mac, gae1, ct));
|
||||||
|
|
||||||
|
phase 2;
|
||||||
|
event compromiseSKA(SK_A);
|
||||||
|
out(a, SK_A);
|
||||||
|
|
||||||
|
phase 3;
|
||||||
|
event breakDH(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1));
|
||||||
|
out(a, (dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1)));
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
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, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) 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, ct), 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 *)
|
||||||
|
|
||||||
|
|
||||||
|
(* forward secrecy *)
|
||||||
|
query sk1: skey, sk2: skey; (event(compromiseSKA(sk1)) && event(compromiseSKB(sk2)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* secrecy *)
|
||||||
|
query attacker(m1).
|
||||||
|
|
||||||
|
(* forward pq secrecy *)
|
||||||
|
query k1: key, k2: key, k3: key, k4: key; (event(breakDH(k1, k2, k3, k4)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* auth *)
|
||||||
|
query m: bitstring, rk: key; event(recvE1(m, rk)) ==> event(sendE1(m, rk)).
|
||||||
|
|
||||||
|
(* 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)))
|
||||||
302
proverif/pq/sender-keys-pqxdh.pv
Normal file
302
proverif/pq/sender-keys-pqxdh.pv
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
(*
|
||||||
|
Sender Keys protocol with PQXDH; proving secrecy, authentication, PFS, quantum 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): key.
|
||||||
|
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(key, key): key [data].
|
||||||
|
fun concat4(bitstring, pkey): bitstring [data].
|
||||||
|
|
||||||
|
fun khash(key): key.
|
||||||
|
fun hkdf1(bitstring): key [data].
|
||||||
|
|
||||||
|
fun hkdf2_dev1(key): key [data].
|
||||||
|
fun hkdf2_dev2(key): key [data].
|
||||||
|
letfun hkdf2(k: key) =
|
||||||
|
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||||
|
|
||||||
|
(* event declarations *)
|
||||||
|
|
||||||
|
event sendOE1(pkey, key, pkey).
|
||||||
|
event recvOE1(pkey, key, pkey).
|
||||||
|
|
||||||
|
event sendOE2(pkey, key, pkey).
|
||||||
|
event recvOE2(pkey, key, pkey).
|
||||||
|
|
||||||
|
event sendME1(bitstring, key).
|
||||||
|
event recvME1(bitstring, key).
|
||||||
|
|
||||||
|
event sendME2(bitstring, key).
|
||||||
|
event recvME2(bitstring, key).
|
||||||
|
|
||||||
|
event compromiseSKA(skey).
|
||||||
|
event compromiseSKB(skey).
|
||||||
|
event breakDH(key, key, key, key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, FSK_A: skey, FPK_A: 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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) in
|
||||||
|
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||||
|
|
||||||
|
let ak1 = khash(ca1) in
|
||||||
|
let (ak1_auth: key, ak1_enc: key) = hkdf2(ak1) in
|
||||||
|
|
||||||
|
new mra0: key; (* mra0 := fan out layer ratchet *)
|
||||||
|
|
||||||
|
let x1 = senc(ak1_enc, (FPK_A, mra0)) in
|
||||||
|
let x1_mac = mac(ak1_auth, concat1(x1, gae1, ct)) in
|
||||||
|
|
||||||
|
event sendOE1(FPK_A, mra0, gae1);
|
||||||
|
out(c, (x1, x1_mac, gae1, ct));
|
||||||
|
|
||||||
|
(* =================== PHASE 3: SENDER KEYS SESSION B -> A =================== *)
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ca2: key, ra2: key) = hkdf2(concat3(khash(ra1), dh(gtb2, ae1))) in
|
||||||
|
let ak2 = khash(ca2) in
|
||||||
|
let (ak2_auth: key, ak2_enc: key) = hkdf2(ak2) in
|
||||||
|
|
||||||
|
if checkmac(ak2_auth, concat4(x2, gtb2), x2_mac) = okay then
|
||||||
|
|
||||||
|
let (FPK_B: pkey, mrb0: key) = sdec(ak2_enc, x2) in
|
||||||
|
event recvOE2(FPK_B, mrb0, gae2);
|
||||||
|
|
||||||
|
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||||
|
|
||||||
|
let mra1 = khash(mra0) in
|
||||||
|
let (mak1_auth: key, mak1_enc: key) = hkdf2(mra1) in
|
||||||
|
|
||||||
|
let mx1 = senc(mak1_enc, m1) in
|
||||||
|
let mx1_sig = sign(FSK_A, mx1) in
|
||||||
|
event sendME1(m1, mra1);
|
||||||
|
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: key, mbk1_enc: key) = hkdf2(mrb1) in
|
||||||
|
|
||||||
|
if checksign(FPK_B, mx2, mx2_sig) = okay then
|
||||||
|
let m2 = sdec(mbk1_enc, mx2) in
|
||||||
|
event recvME2(m2, mrb1);
|
||||||
|
|
||||||
|
(* =================== PHASE 6: Compromise =================== *)
|
||||||
|
|
||||||
|
phase 5;
|
||||||
|
event compromiseSKA(SK_A);
|
||||||
|
out(a, SK_A);
|
||||||
|
|
||||||
|
phase 6;
|
||||||
|
event breakDH(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1));
|
||||||
|
out(a, (dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1)));
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, FSK_B: skey, FPK_B: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
phase 1; (* peer B commits first *)
|
||||||
|
|
||||||
|
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||||
|
|
||||||
|
in(c, (x1: bitstring, x1_mac: bitstring, gae1: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) in
|
||||||
|
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||||
|
let bk1 = khash(cb1) in
|
||||||
|
let (bk1_auth: key, bk1_enc: key) = hkdf2(bk1) in
|
||||||
|
if checkmac(bk1_auth, concat1(x1, gae1, ct), x1_mac) = okay then
|
||||||
|
let (mra0: key, FPK_A: pkey) = sdec(bk1_enc, x1) in
|
||||||
|
|
||||||
|
event recvOE1(FPK_A, mra0, gae1);
|
||||||
|
|
||||||
|
(* =================== PHASE 3: SENDER KEYS SESH B -> A =================== *)
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf2(concat3(khash(rb1), dh(gae1, tb2))) in
|
||||||
|
|
||||||
|
let bk2 = khash(cb2) in
|
||||||
|
let (bk2_auth: key, bk2_enc: key) = hkdf2(bk2) in
|
||||||
|
|
||||||
|
new mrb0: key; (* mrb0 := sender keys ratchet *)
|
||||||
|
|
||||||
|
let x2 = senc(bk2_enc, (FPK_B, mrb0)) in
|
||||||
|
let x2_mac = mac(bk2_auth, concat4(x2, gtb2)) in
|
||||||
|
event sendOE2(FPK_B, mrb0, gae1);
|
||||||
|
|
||||||
|
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: key, mak1_enc: key) = hkdf2(mra1) in
|
||||||
|
|
||||||
|
if checksign(FPK_A, mx1, mx1_sig) = okay then
|
||||||
|
let m1 = sdec(mak1_enc, mx1) in
|
||||||
|
event recvME1(m1, mra1);
|
||||||
|
|
||||||
|
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||||
|
|
||||||
|
let mrb1 = khash(mrb0) in
|
||||||
|
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mrb1) in
|
||||||
|
|
||||||
|
let mx2 = senc(mbk1_enc, m2) in
|
||||||
|
let mx2_sig = sign(FSK_B, mx2) in
|
||||||
|
event sendME2(m2, mrb1);
|
||||||
|
out(c, (mx2, mx2_sig));
|
||||||
|
|
||||||
|
(* =================== PHASE 6: Compromise =================== *)
|
||||||
|
|
||||||
|
phase 5;
|
||||||
|
event compromiseSKB(SK_B);
|
||||||
|
out(a, SK_B);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
query event(start()).
|
||||||
|
|
||||||
|
(* forward secrecy *)
|
||||||
|
query sk1: skey, sk2: skey; (event(compromiseSKA(sk1)) && event(compromiseSKB(sk2)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* secrecy *)
|
||||||
|
query attacker(m1).
|
||||||
|
query attacker(m2).
|
||||||
|
|
||||||
|
(* forward pq secrecy *)
|
||||||
|
query k1: key, k2: key, k3: key, k4: key; (event(breakDH(k1, k2, k3, k4)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* auth *)
|
||||||
|
query k1: pkey, rk: key, k2: pkey; inj-event(recvOE1(k1, rk, k2)) ==> inj-event(sendOE1(k1, rk, k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; inj-event(recvOE2(k1, rk, k2)) ==> inj-event(sendOE2(k1, rk, k2)).
|
||||||
|
query m: bitstring, rk: key; inj-event(recvME1(m, rk)) ==> inj-event(sendME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; inj-event(recvME2(m, rk)) ==> inj-event(sendME2(m, rk)).
|
||||||
|
|
||||||
|
(* reachability *)
|
||||||
|
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(sendOE1(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(recvOE1(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(sendOE2(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(recvOE2(k1,rk,k2)).
|
||||||
|
|
||||||
|
query m: bitstring, rk: key; event(sendME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(recvME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(sendME2(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(recvME2(m, rk)).
|
||||||
|
|
||||||
|
process
|
||||||
|
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||||
|
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||||
|
|
||||||
|
new FSK_A: skey; let FPK_A = pk(FSK_A) in
|
||||||
|
new FSK_B: skey; let FPK_B = pk(FSK_B) in
|
||||||
|
out(a, PK_A);
|
||||||
|
out(a, PK_B);
|
||||||
|
|
||||||
|
(*
|
||||||
|
out(a, FPK_A);
|
||||||
|
out(a, FPK_B);
|
||||||
|
*)
|
||||||
|
|
||||||
|
event start();
|
||||||
|
( (PeerA(SK_A, PK_A, PK_B, FSK_A, FPK_A)) |
|
||||||
|
(PeerB(SK_B, PK_B, PK_A, FSK_B, FPK_B)))
|
||||||
212
proverif/pq/signal-initiator-deny.pv
Normal file
212
proverif/pq/signal-initiator-deny.pv
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
(*
|
||||||
|
PQXDH + Double Ratchet; proving offline deniability for the initiator
|
||||||
|
Author: jake ginesin
|
||||||
|
|
||||||
|
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 selFun = Nounifset.
|
||||||
|
set simplifyProcess = false.
|
||||||
|
(*
|
||||||
|
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)).
|
||||||
|
|
||||||
|
|
||||||
|
fun hkdf4_dev1(key, key): key [data].
|
||||||
|
fun hkdf4_dev2(key, key): key [data].
|
||||||
|
letfun hkdf4(k1: key, k2: key) =
|
||||||
|
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(bitstring, pkey): 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 breakDH(key, key, key, key).
|
||||||
|
event masterLeak(key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) 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, gae2, ct)) in
|
||||||
|
event sendE1(m1, mak1);
|
||||||
|
|
||||||
|
out(c, (x1, x1_mac, gae1, gae2, ct));
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ae1)) in
|
||||||
|
let mak2 = khash(ca2) in
|
||||||
|
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||||
|
|
||||||
|
if checkmac(mak2_auth, concat3(x2, gtb2), x2_mac) = okay then
|
||||||
|
let m2 = sdec(mak2_enc, x2) in
|
||||||
|
event recvE2(m2, mak2);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
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, gae2: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) 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, gae2, ct), x1_mac) = okay then
|
||||||
|
let m1 = sdec(mbk1_enc, x1) in
|
||||||
|
event recvE1(m1, mbk1);
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gae2, 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, concat3(x2, gtb2)) in
|
||||||
|
event sendE2(m2, mbk2);
|
||||||
|
|
||||||
|
out(c, (x2, x2_mac, gtb2));
|
||||||
|
|
||||||
|
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 *)
|
||||||
229
proverif/pq/signal-pcs.pv
Normal file
229
proverif/pq/signal-pcs.pv
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
(*
|
||||||
|
PQXDH + Double Ratchet; proving post-compromise security
|
||||||
|
Author: jake ginesin
|
||||||
|
|
||||||
|
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 `pqxdh.pv`
|
||||||
|
*)
|
||||||
|
|
||||||
|
free m1: bitstring [private].
|
||||||
|
free m2: 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)).
|
||||||
|
|
||||||
|
|
||||||
|
fun hkdf4_dev1(key, key): key [data].
|
||||||
|
fun hkdf4_dev2(key, key): key [data].
|
||||||
|
letfun hkdf4(k1: key, k2: key) =
|
||||||
|
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(bitstring, pkey): 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 breakDH(key, key, key, key).
|
||||||
|
event masterLeak(key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) 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, gae2, ct)) in
|
||||||
|
event sendE1(m1, mak1);
|
||||||
|
|
||||||
|
out(c, (x1, x1_mac, gae1, gae2, ct));
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
phase 2;
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ae2)) in
|
||||||
|
let mak2 = khash(ca2) in
|
||||||
|
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||||
|
|
||||||
|
if checkmac(mak2_auth, concat3(x2, gtb2), x2_mac) = okay then
|
||||||
|
let m2 = sdec(mak2_enc, x2) in
|
||||||
|
event recvE2(m2, mak2);
|
||||||
|
|
||||||
|
phase 3;
|
||||||
|
event breakDH(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1));
|
||||||
|
out(a, (dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1)));
|
||||||
|
|
||||||
|
phase 4;
|
||||||
|
event masterLeak(amaster);
|
||||||
|
out(a, amaster);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
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, gae2: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) 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, gae2, ct), x1_mac) = okay then
|
||||||
|
let m1 = sdec(mbk1_enc, x1) in
|
||||||
|
event recvE1(m1, mbk1);
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
phase 2;
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gae2, 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, concat3(x2, gtb2)) in
|
||||||
|
event sendE2(m2, mbk2);
|
||||||
|
|
||||||
|
out(c, (x2, x2_mac, gtb2));
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
|
||||||
|
(* post-compromise security: compromise of the master key results in the compromise of the
|
||||||
|
message in the same ratchet session, but the next ratchet session's messages are fine *)
|
||||||
|
query k1: key; (event(masterLeak(k1)) && attacker(m2)) ==> false.
|
||||||
|
query k1: key; attacker(m1) ==> event(masterLeak(k1)).
|
||||||
|
|
||||||
|
query event(start()). (* reachable from all possible executions *)
|
||||||
|
|
||||||
|
(* 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)))
|
||||||
213
proverif/pq/signal-resp-nodeny.pv
Normal file
213
proverif/pq/signal-resp-nodeny.pv
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
(*
|
||||||
|
PQXDH + Double Ratchet; proving no offline deniability for the responder
|
||||||
|
Author: jake ginesin
|
||||||
|
|
||||||
|
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 `pqxdh.pv`
|
||||||
|
*)
|
||||||
|
|
||||||
|
free m1: bitstring [private].
|
||||||
|
free m2: bitstring [private].
|
||||||
|
|
||||||
|
set attacker = passive.
|
||||||
|
set selFun = Nounifset.
|
||||||
|
set simplifyProcess = false.
|
||||||
|
(*
|
||||||
|
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)).
|
||||||
|
|
||||||
|
|
||||||
|
fun hkdf4_dev1(key, key): key [data].
|
||||||
|
fun hkdf4_dev2(key, key): key [data].
|
||||||
|
letfun hkdf4(k1: key, k2: key) =
|
||||||
|
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(bitstring, pkey): 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 breakDH(key, key, key, key).
|
||||||
|
event masterLeak(key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) 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, gae2, ct)) in
|
||||||
|
event sendE1(m1, mak1);
|
||||||
|
|
||||||
|
out(c, (x1, x1_mac, gae1, gae2, ct));
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ae1)) in
|
||||||
|
let mak2 = khash(ca2) in
|
||||||
|
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||||
|
|
||||||
|
if checkmac(mak2_auth, concat3(x2, gtb2), x2_mac) = okay then
|
||||||
|
let m2 = sdec(mak2_enc, x2) in
|
||||||
|
event recvE2(m2, mak2);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
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, gae2: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) 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, gae2, ct), x1_mac) = okay then
|
||||||
|
let m1 = sdec(mbk1_enc, x1) in
|
||||||
|
event recvE1(m1, mbk1);
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gae2, 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, concat3(x2, gtb2)) in
|
||||||
|
event sendE2(m2, mbk2);
|
||||||
|
|
||||||
|
out(c, (x2, x2_mac, gtb2));
|
||||||
|
|
||||||
|
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))
|
||||||
243
proverif/pq/signal.pv
Normal file
243
proverif/pq/signal.pv
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
(*
|
||||||
|
PQXDH + Double Ratchet; proving authenticity, secrecy, forward secrecy, and post-quantum forward secrecy
|
||||||
|
Author: jake ginesin
|
||||||
|
|
||||||
|
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 `pqxdh.pv`
|
||||||
|
*)
|
||||||
|
|
||||||
|
free m1: bitstring [private].
|
||||||
|
free m2: 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)).
|
||||||
|
|
||||||
|
|
||||||
|
fun hkdf4_dev1(key, key): key [data].
|
||||||
|
fun hkdf4_dev2(key, key): key [data].
|
||||||
|
letfun hkdf4(k1: key, k2: key) =
|
||||||
|
(hkdf4_dev1(k1, k2), hkdf4_dev2(k1, k2)).
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(bitstring, pkey): 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 breakDH(key, key, key, key).
|
||||||
|
event masterLeak(key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) 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, gae2, ct)) in
|
||||||
|
event sendE1(m1, mak1);
|
||||||
|
|
||||||
|
out(c, (x1, x1_mac, gae1, gae2, ct));
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
|
||||||
|
phase 2;
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ra2: key, ca2: key) = hkdf4(ra1, dh(gtb2, ae1)) in
|
||||||
|
let mak2 = khash(ca2) in
|
||||||
|
let (mak2_auth: key, mak2_enc: key) = hkdf2(mak2) in
|
||||||
|
|
||||||
|
if checkmac(mak2_auth, concat3(x2, gtb2), x2_mac) = okay then
|
||||||
|
let m2 = sdec(mak2_enc, x2) in
|
||||||
|
event recvE2(m2, mak2);
|
||||||
|
|
||||||
|
phase 3;
|
||||||
|
event compromiseSKA(SK_A);
|
||||||
|
out(a, SK_A);
|
||||||
|
|
||||||
|
phase 4;
|
||||||
|
event breakDH(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1));
|
||||||
|
out(a, (dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1)));
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
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, gae2: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) 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, gae2, ct), x1_mac) = okay then
|
||||||
|
let m1 = sdec(mbk1_enc, x1) in
|
||||||
|
event recvE1(m1, mbk1);
|
||||||
|
|
||||||
|
(* second stage *)
|
||||||
|
phase 2;
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf4(rb1, dh(gae2, 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, concat3(x2, gtb2)) in
|
||||||
|
event sendE2(m2, mbk2);
|
||||||
|
|
||||||
|
out(c, (x2, x2_mac, gtb2));
|
||||||
|
|
||||||
|
phase 3;
|
||||||
|
event compromiseSKB(SK_B);
|
||||||
|
out(a, SK_B);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
query event(start()). (* reachable from all possible executions *)
|
||||||
|
|
||||||
|
|
||||||
|
(* forward secrecy *)
|
||||||
|
query sk1: skey, sk2: skey; (event(compromiseSKA(sk1)) && event(compromiseSKA(sk2)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* secrecy *)
|
||||||
|
query attacker(m1).
|
||||||
|
query attacker(m2).
|
||||||
|
|
||||||
|
(* forward pq secrecy *)
|
||||||
|
query k1: key, k2: key, k3: key, k4: key; (event(breakDH(k1, k2, k3, k4)) && attacker(m1)) ==> 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)).
|
||||||
|
|
||||||
|
(* 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)))
|
||||||
|
|
||||||
302
proverif/sender-keys-pqxdh.pv
Normal file
302
proverif/sender-keys-pqxdh.pv
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
(*
|
||||||
|
Sender Keys protocol with PQXDH; proving secrecy, authentication, PFS, quantum 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): key.
|
||||||
|
equation forall a: skey, b: skey; dh(pk(a), b) = dh(pk(b), a). (* symmetry of DH *)
|
||||||
|
|
||||||
|
(* KEM encapsulation *)
|
||||||
|
type kempriv.
|
||||||
|
type kempub.
|
||||||
|
|
||||||
|
fun kempk(kempriv):kempub.
|
||||||
|
fun penc(kempub, bitstring):bitstring.
|
||||||
|
(* fun pdec(kempriv,bitstring):bitstring. *)
|
||||||
|
reduc forall sk: kempriv, m:bitstring; pdec(sk, penc(kempk(sk), m)) = m.
|
||||||
|
|
||||||
|
letfun kempriv2pub(k:kempriv) = kempk(k).
|
||||||
|
|
||||||
|
letfun pqkem_enc(pk:kempub) =
|
||||||
|
new ss:bitstring;
|
||||||
|
(penc(pk,ss),ss).
|
||||||
|
|
||||||
|
letfun pqkem_dec(sk:kempriv,ct:bitstring) =
|
||||||
|
pdec(sk,ct).
|
||||||
|
|
||||||
|
fun qb(kempub): bitstring [data].
|
||||||
|
|
||||||
|
(* the concats *)
|
||||||
|
|
||||||
|
fun concat1(bitstring, pkey, bitstring): bitstring [data].
|
||||||
|
fun concat2(key, key, key, key, bitstring): bitstring [data].
|
||||||
|
fun concat3(key, key): key [data].
|
||||||
|
fun concat4(bitstring, pkey): bitstring [data].
|
||||||
|
|
||||||
|
fun khash(key): key.
|
||||||
|
fun hkdf1(bitstring): key [data].
|
||||||
|
|
||||||
|
fun hkdf2_dev1(key): key [data].
|
||||||
|
fun hkdf2_dev2(key): key [data].
|
||||||
|
letfun hkdf2(k: key) =
|
||||||
|
(hkdf2_dev1(k), hkdf2_dev2(k)).
|
||||||
|
|
||||||
|
(* event declarations *)
|
||||||
|
|
||||||
|
event sendOE1(pkey, key, pkey).
|
||||||
|
event recvOE1(pkey, key, pkey).
|
||||||
|
|
||||||
|
event sendOE2(pkey, key, pkey).
|
||||||
|
event recvOE2(pkey, key, pkey).
|
||||||
|
|
||||||
|
event sendME1(bitstring, key).
|
||||||
|
event recvME1(bitstring, key).
|
||||||
|
|
||||||
|
event sendME2(bitstring, key).
|
||||||
|
event recvME2(bitstring, key).
|
||||||
|
|
||||||
|
event compromiseSKA(skey).
|
||||||
|
event compromiseSKB(skey).
|
||||||
|
event breakDH(key, key, key, key).
|
||||||
|
|
||||||
|
event start().
|
||||||
|
|
||||||
|
let PeerA(SK_A: skey, PK_A: pkey, PK_B: pkey, FSK_A: skey, FPK_A: 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, gpqbo: kempub, gpqbsig: bitstring));
|
||||||
|
if checksign(PK_B, rb(gbs), gbssig) = okay then
|
||||||
|
if checksign(PK_B, qb(gpqbo), gpqbsig) = okay then
|
||||||
|
let (ct: bitstring, ss: bitstring) = pqkem_enc(gpqbo) in
|
||||||
|
let amaster = hkdf1(concat2(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1), ss)) in
|
||||||
|
let (ra1: key, ca1: key) = hkdf2(amaster) in (* derive the root and chain key *)
|
||||||
|
|
||||||
|
let ak1 = khash(ca1) in
|
||||||
|
let (ak1_auth: key, ak1_enc: key) = hkdf2(ak1) in
|
||||||
|
|
||||||
|
new mra0: key; (* mra0 := fan out layer ratchet *)
|
||||||
|
|
||||||
|
let x1 = senc(ak1_enc, (FPK_A, mra0)) in
|
||||||
|
let x1_mac = mac(ak1_auth, concat1(x1, gae1, ct)) in
|
||||||
|
|
||||||
|
event sendOE1(FPK_A, mra0, gae1);
|
||||||
|
out(c, (x1, x1_mac, gae1, ct));
|
||||||
|
|
||||||
|
(* =================== PHASE 3: SENDER KEYS SESSION B -> A =================== *)
|
||||||
|
|
||||||
|
in(c, (x2: bitstring, x2_mac: bitstring, gtb2: pkey));
|
||||||
|
|
||||||
|
let (ca2: key, ra2: key) = hkdf2(concat3(khash(ra1), dh(gtb2, ae1))) in
|
||||||
|
let ak2 = khash(ca2) in
|
||||||
|
let (ak2_auth: key, ak2_enc: key) = hkdf2(ak2) in
|
||||||
|
|
||||||
|
if checkmac(ak2_auth, concat4(x2, gtb2), x2_mac) = okay then
|
||||||
|
|
||||||
|
let (FPK_B: pkey, mrb0: key) = sdec(ak2_enc, x2) in
|
||||||
|
event recvOE2(FPK_B, mrb0, gae2);
|
||||||
|
|
||||||
|
(* =================== PHASE 4: SENDER KEYS MSG A -> B =================== *)
|
||||||
|
|
||||||
|
let mra1 = khash(mra0) in
|
||||||
|
let (mak1_auth: key, mak1_enc: key) = hkdf2(mra1) in
|
||||||
|
|
||||||
|
let mx1 = senc(mak1_enc, m1) in
|
||||||
|
let mx1_sig = sign(FSK_A, mx1) in
|
||||||
|
event sendME1(m1, mra1);
|
||||||
|
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: key, mbk1_enc: key) = hkdf2(mrb1) in
|
||||||
|
|
||||||
|
if checksign(FPK_B, mx2, mx2_sig) = okay then
|
||||||
|
let m2 = sdec(mbk1_enc, mx2) in
|
||||||
|
event recvME2(m2, mrb1);
|
||||||
|
|
||||||
|
(* =================== PHASE 6: Compromise =================== *)
|
||||||
|
|
||||||
|
phase 5;
|
||||||
|
event compromiseSKA(SK_A);
|
||||||
|
out(a, SK_A);
|
||||||
|
|
||||||
|
phase 6;
|
||||||
|
event breakDH(dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1));
|
||||||
|
out(a, (dh(gbs, SK_A), dh(PK_B, ae1), dh(gbs, ae1), dh(gbo, ae1)));
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
let PeerB(SK_B: skey, PK_B: pkey, PK_A: pkey, FSK_B: skey, FPK_B: pkey) =
|
||||||
|
new bo: skey;
|
||||||
|
new bs: skey;
|
||||||
|
new pqbo: kempriv;
|
||||||
|
|
||||||
|
let gbs = pk(bs) in
|
||||||
|
let gbo = pk(bo) in
|
||||||
|
let gpqbo = kempk(pqbo) in
|
||||||
|
let gbssig = sign(SK_B, rb(gbs)) in
|
||||||
|
let gpqbsig = sign(SK_B, qb(gpqbo)) in
|
||||||
|
out(c, (gbssig, gbs, gbo, gpqbo, gpqbsig));
|
||||||
|
phase 1; (* peer B commits first *)
|
||||||
|
|
||||||
|
(* =================== PHASE 2: DERIVE P2P MASTER + SENDER KEYS SESSION A -> B =================== *)
|
||||||
|
|
||||||
|
in(c, (x1: bitstring, x1_mac: bitstring, gae1: pkey, ct: bitstring));
|
||||||
|
let ss = pqkem_dec(pqbo, ct) in
|
||||||
|
|
||||||
|
let bmaster = hkdf1(concat2(dh(PK_A, bs), dh(gae1, SK_B), dh(gae1, bs), dh(gae1, bo), ss)) in
|
||||||
|
let (rb1: key, cb1: key) = hkdf2(bmaster) in (* derive the root and chain key *)
|
||||||
|
let bk1 = khash(cb1) in
|
||||||
|
let (bk1_auth: key, bk1_enc: key) = hkdf2(bk1) in
|
||||||
|
if checkmac(bk1_auth, concat1(x1, gae1, ct), x1_mac) = okay then
|
||||||
|
let (mra0: key, FPK_A: pkey) = sdec(bk1_enc, x1) in
|
||||||
|
|
||||||
|
event recvOE1(FPK_A, mra0, gae1);
|
||||||
|
|
||||||
|
(* =================== PHASE 3: SENDER KEYS SESH B -> A =================== *)
|
||||||
|
|
||||||
|
new tb2: skey;
|
||||||
|
let gtb2 = pk(tb2) in
|
||||||
|
|
||||||
|
let (rb2: key, cb2: key) = hkdf2(concat3(khash(rb1), dh(gae1, tb2))) in
|
||||||
|
|
||||||
|
let bk2 = khash(cb2) in
|
||||||
|
let (bk2_auth: key, bk2_enc: key) = hkdf2(bk2) in
|
||||||
|
|
||||||
|
new mrb0: key; (* mrb0 := sender keys ratchet *)
|
||||||
|
|
||||||
|
let x2 = senc(bk2_enc, (FPK_B, mrb0)) in
|
||||||
|
let x2_mac = mac(bk2_auth, concat4(x2, gtb2)) in
|
||||||
|
event sendOE2(FPK_B, mrb0, gae1);
|
||||||
|
|
||||||
|
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: key, mak1_enc: key) = hkdf2(mra1) in
|
||||||
|
|
||||||
|
if checksign(FPK_A, mx1, mx1_sig) = okay then
|
||||||
|
let m1 = sdec(mak1_enc, mx1) in
|
||||||
|
event recvME1(m1, mra1);
|
||||||
|
|
||||||
|
(* =================== PHASE 5: SENDER KEYS MSG B -> A =================== *)
|
||||||
|
|
||||||
|
let mrb1 = khash(mrb0) in
|
||||||
|
let (mbk1_auth: key, mbk1_enc: key) = hkdf2(mrb1) in
|
||||||
|
|
||||||
|
let mx2 = senc(mbk1_enc, m2) in
|
||||||
|
let mx2_sig = sign(FSK_B, mx2) in
|
||||||
|
event sendME2(m2, mrb1);
|
||||||
|
out(c, (mx2, mx2_sig));
|
||||||
|
|
||||||
|
(* =================== PHASE 6: Compromise =================== *)
|
||||||
|
|
||||||
|
phase 5;
|
||||||
|
event compromiseSKB(SK_B);
|
||||||
|
out(a, SK_B);
|
||||||
|
|
||||||
|
0.
|
||||||
|
|
||||||
|
query event(start()).
|
||||||
|
|
||||||
|
(* forward secrecy *)
|
||||||
|
query sk1: skey, sk2: skey; (event(compromiseSKA(sk1)) && event(compromiseSKB(sk2)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* secrecy *)
|
||||||
|
query attacker(m1).
|
||||||
|
query attacker(m2).
|
||||||
|
|
||||||
|
(* forward pq secrecy *)
|
||||||
|
query k1: key, k2: key, k3: key, k4: key; (event(breakDH(k1, k2, k3, k4)) && attacker(m1)) ==> false.
|
||||||
|
|
||||||
|
(* auth *)
|
||||||
|
query k1: pkey, rk: key, k2: pkey; inj-event(recvOE1(k1, rk, k2)) ==> inj-event(sendOE1(k1, rk, k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; inj-event(recvOE2(k1, rk, k2)) ==> inj-event(sendOE2(k1, rk, k2)).
|
||||||
|
query m: bitstring, rk: key; inj-event(recvME1(m, rk)) ==> inj-event(sendME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; inj-event(recvME2(m, rk)) ==> inj-event(sendME2(m, rk)).
|
||||||
|
|
||||||
|
(* reachability *)
|
||||||
|
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(sendOE1(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(recvOE1(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(sendOE2(k1,rk,k2)).
|
||||||
|
query k1: pkey, rk: key, k2: pkey; event(recvOE2(k1,rk,k2)).
|
||||||
|
|
||||||
|
query m: bitstring, rk: key; event(sendME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(recvME1(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(sendME2(m, rk)).
|
||||||
|
query m: bitstring, rk: key; event(recvME2(m, rk)).
|
||||||
|
|
||||||
|
process
|
||||||
|
new SK_A: skey; let PK_A = pk(SK_A) in
|
||||||
|
new SK_B: skey; let PK_B = pk(SK_B) in
|
||||||
|
|
||||||
|
new FSK_A: skey; let FPK_A = pk(FSK_A) in
|
||||||
|
new FSK_B: skey; let FPK_B = pk(FSK_B) in
|
||||||
|
out(a, PK_A);
|
||||||
|
out(a, PK_B);
|
||||||
|
|
||||||
|
(*
|
||||||
|
out(a, FPK_A);
|
||||||
|
out(a, FPK_B);
|
||||||
|
*)
|
||||||
|
|
||||||
|
event start();
|
||||||
|
( (PeerA(SK_A, PK_A, PK_B, FSK_A, FPK_A)) |
|
||||||
|
(PeerB(SK_B, PK_B, PK_A, FSK_B, FPK_B)))
|
||||||
Reference in New Issue
Block a user