(* 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)))