#define CHAINING_KEY_EVENTS 1 #define MESSAGE_TRANSMISSION_EVENTS 0 #define SESSION_START_EVENTS 0 #define RANDOMIZED_CALL_IDS 0 #define COOKIE_EVENTS 1 #define KEM_EVENTS 1 #include "config.mpv" #include "prelude/basic.mpv" #include "crypto/key.mpv" #include "crypto/kem.mpv" #include "rosenpass/handshake_state.mpv" /* The cookie data structure is implemented based on the WireGuard protocol. * The ip and port is based purely on the public key and the implementation of the private cookie key is intended to mirror the biscuit key. * The code tests the response to a possible DOS attack by setting up alternative branches for the protocol * processes: Oinit_conf, Oinit_hello and resp_hello to simulate what happens when the responder or initiator is overloaded. * When under heavy load a valid cookie is required. When such a cookie is not present a cookie message is sent as a response. * Queries then test to make sure that expensive KEM operations are only conducted after a cookie has been successfully validated. */ type CookieMsg_t. fun CookieMsg( SessionId, // sender bits, // nonce bits // cookie ) : CookieMsg_t [data]. #define COOKIE_EVENTS(eventLbl) \ COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (SessionId, SessionId, Atom).) \ COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (SessionId, SessionId, Atom).) \ COOKIE_EV(event MCAT(eventLbl, _CookieSent) (SessionId, SessionId, Atom, CookieMsg_t).) fun cookie_key(kem_sk) : key [private]. fun ip_and_port(kem_pk):bits. letfun create_mac2_key(sskm:kem_sk, spkt:kem_pk) = prf(cookie_key(sskm), ip_and_port(spkt)). letfun create_cookie(sskm:kem_sk, spkm:kem_pk, spkt:kem_pk, nonce:bits, msg:bits) = xaead_enc(lprf2(COOKIE, kem_pk2b(spkm), nonce), k2b(create_mac2_key(sskm, spkm)), msg). #define COOKIE_PROCESS(eventLbl, innerFunc) \ new nonce:bits; \ in(C, Ccookie(mac1, mac2)); \ COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (sidi, sidr, call);) \ msgB <- Envelope(mac1, msg); \ mac2_key <- create_mac2_key(sskm, spkt); \ if k2b(create_mac2(mac2_key, msgB)) = mac2 then \ COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (sidi, sidr, call);) \ innerFunc \ else \ cookie <- create_cookie(sskm, spkm, spkt, nonce, msg); \ cookie_msg <- CookieMsg(sidi, nonce, cookie); \ COOKIE_EV(event MCAT(eventLbl, _CookieSent) (sidi, sidr, call, cookie_msg);) \ out(C, cookie_msg). \ #include "rosenpass/oracles.mpv" #include "rosenpass/responder.macro" COOKIE_EVENTS(Oinit_conf) let Oinit_conf_underLoad() = in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic)); in(C, last_cookie:bits); msg <- IC2b(ic); let InitConf(sidi, sidr, biscuit, auth) = ic in new call:Atom; SETUP_HANDSHAKE_STATE() COOKIE_PROCESS(Oinit_conf, Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic, call)) #include "rosenpass/responder.macro" COOKIE_EVENTS(Oinit_hello) let Oinit_hello_underLoad() = in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih)); in(C, Oinit_hello_last_cookie:key); new call:Atom; msg <- IH2b(ih); let InitHello(sidi, epki, sctr, pidic, auth) = ih in SETUP_HANDSHAKE_STATE() COOKIE_PROCESS(Oinit_hello, Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, Oinit_hello_last_cookie, C, call)) let rosenpass_dos_main() = 0 | !Oreveal_kem_pk | REP(INITIATOR_BOUND, Oinitiator) | REP(RESPONDER_BOUND, Oinit_hello) | REP(RESPONDER_BOUND, Oinit_conf) | REP(RESPONDER_BOUND, Oinit_hello_underLoad) | REP(RESPONDER_BOUND, Oinit_conf_underLoad). let main = rosenpass_dos_main. select cookie:CookieMsg_t; attacker(cookie)/6220[hypothesis]. nounif v:key; attacker(prepare_key( v ))/6217[hypothesis]. nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. // nounif Spk:kem_sk_tmpl; // attacker(Creveal_kem_pk(Spk))/6110[conclusion]. // nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; // attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. // nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; // attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. nounif rh:RespHello_t; attacker(Cresp_hello( *rh ))/6107[conclusion]. nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. @reachable "DOS protection: cookie sent" query sidi:SessionId, sidr:SessionId, call:Atom, cookieMsg:CookieMsg_t; event (Oinit_hello_CookieSent(sidi, sidr, call, cookieMsg)). @lemma "DOS protection: Oinit_hello kem use when under load implies validated cookie" lemma sidi:SessionId, sidr:SessionId, call:Atom; event(Oinit_hello_UnderLoadEV(sidi, sidr, call)) && event(Oinit_hello_KemUse(sidi, sidr, call)) ==> event(Oinit_hello_CookieValidated(sidi, sidr, call)). @lemma "DOS protection: Oinit_conf kem use when under load implies validated cookie" lemma sidi:SessionId, sidr:SessionId, call:Atom; event(Oinit_conf_UnderLoadEV(sidi, sidr, call)) && event(Oinit_conf_KemUse(sidi, sidr, call)) ==> event(Oinit_conf_CookieValidated(sidi, sidr, call)). @lemma "DOS protection: Oresp_hello kem use when under load implies validated cookie" lemma sidi:SessionId, sidr:SessionId, call:Atom; event(Oresp_hello_UnderLoadEV(sidi, sidr, call)) && event(Oresp_hello_KemUse(sidi, sidr, call)) ==> event(Oresp_hello_CookieValidated(sidi, sidr, call)).