(* Noise NK: see http://www.noiseprotocol.org/noise.html <- s ... -> e, es <- e, ee -> [prove strong forward secrecy for this payload, sent by the initiator] *) (* To speed up proofs of correspondences *) set casesInCorresp = false. (* To speed up the proof *) set autoMergeBranches = false. (* To save memory *) set forgetOldGames = true. proof { (* Useful when using Curve25519 because we need to apply equalities pow8(..) = pow8(..) when testing whether a term matches another term, and CryptoVerif would not apply them by default. However, that is costly. *) set useKnownEqualitiesWithFunctionsInMatching = true; (* This is in fact the default; mentioned here just as a reminder. We unset it later for speed. *) set elsefindFactsInSimplify = true; out_game "g1.cv"; insert before_nth 3 "let concat_prf_output(ck_i2" "find j <= n_S suchthat defined(E_r_pub[j]) && (pow8(E_r_pub_rcvd_1) = pow8(E_r_pub[j])) then"; out_game "g2.cv"; insert before_nth 4 "out(c_wait_before_2nd_part" "event_abort bad_auth"; out_game "g3.cv"; focus "query event(bad_auth) ==> false"; success simplify; crypto prf_odh(HKDF8) [variables: E_i_priv_2 -> a, S_r_priv_2 -> b .]; crypto splitter(concat_prf_output) **; crypto prf_convert(HKDF) *; crypto prf(HKDF1) *; crypto splitter(concat_prf_output) **; crypto prf(HKDF2) *; crypto splitter(concat_two_keys) **; crypto int_ctxt(enc) *; success; (* Proved event(bad_auth) ==> false, going back to before focus *) SArename E_r_pub; crypto prf_odh(HKDF8) [variables: E_i_priv_2 -> a, E_r_priv_4 -> b, E_r_priv_5 -> b .]; crypto splitter(concat_prf_output) **; crypto prf(HKDF2) *; crypto splitter(concat_two_keys) **; crypto int_ctxt(enc) *; (* Show that the first key k_i2/k_r2 is the same on both sides, even though we do not show that it is random Useful to establish that the received decrypted payload1 is the same as the sent one *) replace at_nth 1 1 "k_r2: key_t) = {[0-9]+}HKDF8" "concat_prf_output(ck_i1_1[j_5], k_i2[j_5])"; simplify; success; simplify; crypto ind_cpa(enc) *; success } param n_S, n_M. (**** Gap Diffie-Hellman ****) type G_t [bounded,large]. (* type of public keys *) type G8_t [bounded,large]. (* type of { X^k, X \in F_p } *) fun G_to_bitstring(G_t): bitstring [data]. type Z_t [bounded,large,nonuniform]. (* type of exponents (must be "bounded" and "large", is the set of multiples of k prime to qq' modulo kqq'.) *) proba P_GDH. (* probability of breaking the GDH assumption *) expand DH_X25519( (* types *) G_t, (* Public keys *) Z_t, (* Exponents *) (* variables *) g, (* base point *) exp, (* exponentiation function *) mult, (* multiplication function for exponents *) G8_t, g8, exp_div8, exp_div8', (* a symbol that replaces exp_div8 after game transformation *) pow8, G8_to_G, is_zero, is_zero8 ). letfun DH(group_element: G_t, exponent: Z_t) = exp(group_element, exponent). (**** Symmetric encryption ****) type key_t [large,fixed]. type nonce_t [large,fixed]. (* 12 byte counter nonce for AEAD. *) const nonce_0: nonce_t. (* const value for the zero nonce *) proba P_enc. proba P_encctxt. expand AEAD_nonce( (* types *) key_t, (* keys *) bitstring, (* plaintext *) bitstring, (* ciphertext *) bitstring, (* additional data *) nonce_t, (* nonces *) (* functions *) enc, (* encryption: enc(plaintext, additional data, key, nonce): ciphertext *) dec, (* decryption: dec(ciphertext, additional data, key, nonce): bitstringbot *) injbot, (* injection from plaintext to bitstringbot: injbot(plaintext): bitstringbot *) Zero, (* returns a plaintext of same length, consisting of zeros: Zero(plaintext): plaintext *) (* probabilities *) P_enc, (* breaking IND-CPA *) P_encctxt (* breaking INT-CTXT *) ). (**** Hash and HKDF ****) type hashkey_t [fixed]. type hashoutput_t [large,fixed]. fun hashoutput_to_bitstring(hashoutput_t): bitstring [data]. (* HKDF0(k: hashoutput_t, x: G_t) = (k1,k2) = HKDF(k,x,2) if HASHLEN=64 then truncate k2 to 32 bytes return concatenation of k1 and k2 HKDF0(k,x) satisfies PRF-ODH with key x in the subset G8_t done by rewriting HKDF0(k,G8_to_G(x)) to HKDF8(x,k) *) type prfoutput_t [fixed]. letproba PCollKey = 2 * Pcoll1rand(Z_t). proba pPRF_ODH. expand PRF_ODH2( (* types *) G8_t, (* Group elements *) Z_t, (* Exponents *) hashoutput_t, (* PRF argument *) prfoutput_t, (* PRF output *) (* variables *) g8, (* a generator of the group *) exp_div8, (* exponentiation function *) exp_div8', (* exponentiation function after game transformation *) mult, (* multiplication function for exponents *) HKDF8, (* prf *) (* probabilities *) pPRF_ODH, (* probability of breaking the PRF-ODH assumption *) PCollKey ). fun HKDF0(hashoutput_t, G_t): prfoutput_t. equation forall x: G8_t, y: hashoutput_t; HKDF0(y, G8_to_G(x)) = HKDF8(x,y). (* HKDF0(k,G8_to_G(x)) = HKDF8(x,k) is a PRF with key k Specified by converting it to HKDF1(k,x). *) proba Pprf. expand PRF(hashoutput_t, G8_t, prfoutput_t, HKDF1, Pprf). param Nprf. equiv(prf_convert(HKDF)) k <-R hashoutput_t; foreach i <= Nprf do Oprf(x: G8_t) := return(HKDF8(x,k)) <=(0)=> k <-R hashoutput_t; foreach i <= Nprf do Oprf(x: G8_t) := return(HKDF1(k,x)). expand split_2( (* types *) prfoutput_t, (* intermediary output type of the actual PRF *) hashoutput_t, (* types of extracted values *) key_t, (* functions *) concat_prf_output, (* function that concatenates the 2 parts. *) (* variables *) ck, (* Names of the variables used when applying the equivalence *) k (* This makes the games in the proof much more readable. *) ). (* HKDF2(k, empty_val) = (k1,k2) = HKDF(k,empty_val,2) if HASHLEN=64, truncate k1,k2 to 32 bytes return concatenation of k1,k2 is a PRF with key k *) type empty_t [fixed]. const empty_val: empty_t. type twokeys_t [fixed]. expand PRF(hashoutput_t, empty_t, twokeys_t, HKDF2, Pprf). expand split_2( (* types *) twokeys_t, (* intermediary output type of the actual PRF *) key_t, (* types of extracted values *) key_t, (* functions *) concat_two_keys, (* function that concatenates the 2 parts. *) (* variables *) T_i_send, T_i_recv ). (* A collision resistant hash function is used for chaining hashes with parts of the protocol transcript. To the previous hash output, the next part of the transcript is appended. We model this as being a hash function that has two arguments, the first being of type hashoutput and the second of bitstring. *) proba P_hash. (* probability of breaking collision resistance *) expand CollisionResistant_hash_2( (* types *) hashkey_t, (* key of the hash function, models the choice of *) (* the hash function *) hashoutput_t, (* first argument that gets hashed. See the comment *) (* just above this macro for an explanation. *) bitstring, (* second argument that gets hashed. *) hashoutput_t, (* output type of the hash function *) (* functions *) hash, (* name of the hash function: *) (* hash(hashkey_t, hashoutput_t, bitstring): hashoutput_t *) (* processes *) hash_oracle, (* name of the oracle that will make available the *) (* hash key to the attacker *) (* parameters *) P_hash (* probability of breaking collision resistance *) ). (* constants used in the transcript hashing *) const hash_construction_identifier : hashoutput_t. (* This is hash( hash("Noise_IK…") || "WireGuard v1 …" ), and it's *) (* the same for all parties, so no need to calculate it with hash() *) (* channel names *) channel c_start. channel c_config_initiator. channel c_init2resp_send, c_resp2init_recv. channel c_init2resp_recv, c_resp2init_send. channel c_keyconfirm_send, c_keyconfirm_recv, c_wait_before_2nd_part. channel c_N_init_send_config, c_N_resp_send_config. channel c_N_init_send, c_N_resp_send. channel c_N_resp_recv, c_N_init_recv. channel c_N_resp_recv_res, c_N_init_recv_res. channel c_publickeys. channel c_corrupt_S_r. (* WireGuard specific types *) type counter_t [fixed]. (* 8 byte counter in the data message *) const counter_0: counter_t. (* constant for counter with value 0 *) fun counter_to_nonce(counter_t) : nonce_t [data]. (* This is [data] because it is an injection, from 8 byte counters to 12 bytes nonces. *) (* In the security game we want to be able to abort if the attacker wants us to encrypt with an already used nonce. *) type side. const is_initiator: side. const is_responder: side. (* the bitstring is used as tuple (side, replication_index) *) table sent_counters(bitstring, counter_t). table rcvd_counters(bitstring, counter_t). (* Convenience wrappers around hash that take care of type conversion. *) letfun mix_hash_G(key_hash: hashkey_t, prev_hash: hashoutput_t, value: G_t) = hash(key_hash, prev_hash, G_to_bitstring(value)). letfun mix_hash_bitstring(key_hash: hashkey_t, prev_hash: hashoutput_t, value: bitstring) = hash(key_hash, prev_hash, value). (* Convenience wrappers for enc and dec that take care of type conversions. *) letfun dec_ad_hash(ciphertext: bitstring, current_hash: hashoutput_t, k: key_t, n: nonce_t) = dec(ciphertext, hashoutput_to_bitstring(current_hash), k, n). letfun enc_bitstring(plaintext: bitstring, current_hash: hashoutput_t, k: key_t, n: nonce_t) = enc(plaintext, hashoutput_to_bitstring(current_hash), k, n). const empty_bitstring: bitstring. (* additional data for data messages *) (* Define a function for choosing from two attacker-provided plaintexts based on a bit. Also, defines some equations on it so CryptoVerif is able to reason about it. *) expand boolean_choice_for_encryption( (* types *) bitstring, (* type of the values *) (* functions *) Zero, (* the Zero function provided by the encryption scheme. *) (* Needed for some equations about the function. *) test (* Name of the choice function: *) (* test(bool, bitstring, bitstring): bitstring *) ). (* Authentication *) event sent_msg_initiator( (* values sent in the first protocol message *) G_t, (* initiator's ephemeral public key *) bitstring, (* payload1 *) bitstring, (* encrypted payload1 *) (* values sent in the second protocol message *) G_t, (* responder's ephemeral public key *) bitstring, (* payload2 *) bitstring, (* encrypted payload2 *) (* data keys *) key_t, key_t, (* new and non-constant values in the transport data message *) counter_t, (* the attacker-provided nonce *) bitstring, (* the ciphertext *) bitstring (* the plaintext *) ). event rcvd_msg_responder(G_t, bitstring, bitstring, G_t, bitstring, bitstring, key_t, key_t, counter_t, bitstring, bitstring). event sent_msg_responder(G_t, bitstring, bitstring, G_t, bitstring, bitstring, key_t, key_t, counter_t, bitstring, bitstring). event rcvd_msg_initiator(G_t, bitstring, bitstring, G_t, bitstring, bitstring, key_t, key_t, counter_t, bitstring, bitstring). query (* values sent in the first protocol message *) E_i_pub: G_t, payload1: bitstring, payload1_enc: bitstring, (* values sent in the second protocol message *) E_r_pub: G_t, payload2: bitstring, payload2_enc: bitstring, (* data keys *) T_r_recv: key_t, T_r_send: key_t, (* new and non-constant values in the transport data message *) counter: counter_t, (* the attacker-provided nonce *) ciphertext: bitstring, (* the ciphertext *) plaintext: bitstring; (* the plaintext *) inj-event(rcvd_msg_responder(E_i_pub, payload1, payload1_enc, E_r_pub, payload2, payload2_enc, T_r_recv, T_r_send, counter, ciphertext, plaintext)) ==> inj-event(sent_msg_initiator(E_i_pub, payload1, payload1_enc, E_r_pub, payload2, payload2_enc, T_r_recv, T_r_send, counter, ciphertext, plaintext)). query (* values sent in the first protocol message *) E_i_pub: G_t, payload1: bitstring, payload1_enc: bitstring, (* values sent in the second protocol message *) E_r_pub: G_t, payload2: bitstring, payload2_enc: bitstring, (* data keys *) T_r_recv: key_t, T_r_send: key_t, (* new and non-constant values in the transport data message *) counter: counter_t, (* the attacker-provided nonce *) ciphertext: bitstring, (* the ciphertext *) plaintext: bitstring; (* the plaintext *) inj-event(rcvd_msg_initiator(E_i_pub, payload1, payload1_enc, E_r_pub, payload2, payload2_enc, T_r_recv, T_r_send, counter, ciphertext, plaintext)) ==> inj-event(sent_msg_responder(E_i_pub, payload1, payload1_enc, E_r_pub, payload2, payload2_enc, T_r_recv, T_r_send, counter, ciphertext, plaintext)). (* Secrecy *) query secret secret_bit. (* Type definitions for return values of functions that prepare or process messages prepare1res_t is an "option" type: prepare1succ(...) is the success case prepare1fail is the failure case The disequation prepare1succ(x1,x2,x3,x4,x5,x6,x7,x8) <> prepare1fail guarantees that there is no confusion between these two cases. *) type prepare1res_t. fun prepare1succ(Z_t, G_t, bitstring, hashoutput_t, hashoutput_t): prepare1res_t [data]. const prepare1fail: prepare1res_t. equation forall x1: Z_t, x2: G_t, x3: bitstring, x4: hashoutput_t, x5: hashoutput_t; prepare1succ(x1,x2,x3,x4,x5) <> prepare1fail. type process1res_t. fun process1succ(hashoutput_t, hashoutput_t, bitstring):process1res_t [data]. const process1fail: process1res_t. equation forall x1: hashoutput_t, x2: hashoutput_t, x3: bitstring; process1succ(x1,x2,x3) <> process1fail. type prepare2res_t. fun prepare2succ(Z_t, G_t, key_t, key_t, hashoutput_t, bitstring):prepare2res_t [data]. const prepare2fail: prepare2res_t. equation forall x1: Z_t, x2: G_t, x3: key_t, x4: key_t, x5: hashoutput_t, x6: bitstring; prepare2succ(x1,x2,x3,x4,x5,x6) <> prepare2fail. type process2res_t. fun process2succ(key_t, key_t, hashoutput_t, bitstring):process2res_t [data]. const process2fail: process2res_t. equation forall x1: key_t, x2: key_t, x3: hashoutput_t, x4: bitstring; process2succ(x1,x2,x3,x4) <> process2fail. type preparemsgres_t. fun preparemsgsucc(bitstring, bitstring):preparemsgres_t [data]. const preparemsgfail: preparemsgres_t. equation forall x1: bitstring, x2: bitstring; preparemsgsucc(x1,x2) <> preparemsgfail. type processmsgres_t. fun processmsgsucc(bitstring):processmsgres_t [data]. const processmsgfail: processmsgres_t. equation forall x: bitstring; processmsgsucc(x) <> processmsgfail. (* Prepare the first message e, es, {p1} *) letfun prepare1( key_hash: hashkey_t, S_X_pub_rcvd: G_t, payload1: bitstring) = new E_i_priv: Z_t; let E_i_pub: G_t = exp(g, E_i_priv) in let H_i1: hashoutput_t = mix_hash_G(key_hash, hash_construction_identifier, S_X_pub_rcvd) in let H_i2: hashoutput_t = mix_hash_G(key_hash, H_i1, E_i_pub) in let es_i: G_t = DH(S_X_pub_rcvd, E_i_priv) in let concat_prf_output(ck_i1, k_i2: key_t) = HKDF0(hash_construction_identifier, es_i) in ( let payload1_enc: bitstring = enc_bitstring(payload1, H_i2, k_i2, nonce_0) in let H_i3: hashoutput_t = mix_hash_bitstring(key_hash, H_i2, payload1_enc) in prepare1succ(E_i_priv, E_i_pub, payload1_enc, H_i3, ck_i1) ) else (* weird case where the HKDF0 pattern matching did not work - actually never happens *) prepare1fail. (* Process a received first message *) letfun process1( key_hash: hashkey_t, S_r_priv: Z_t, S_r_pub: G_t, E_i_pub_rcvd: G_t, payload1_enc_rcvd: bitstring ) = let H_r1: hashoutput_t = mix_hash_G(key_hash, hash_construction_identifier, S_r_pub) in let H_r2: hashoutput_t = mix_hash_G(key_hash, H_r1, E_i_pub_rcvd) in let es_r: G_t = DH(E_i_pub_rcvd, S_r_priv) in let concat_prf_output(ck_r1, k_r2: key_t) = HKDF0(hash_construction_identifier, es_r) in ( let injbot(payload1_rcvd) = dec_ad_hash(payload1_enc_rcvd, H_r2, k_r2, nonce_0) in ( let H_r3: hashoutput_t = mix_hash_bitstring(key_hash, H_r2, payload1_enc_rcvd) in process1succ(H_r3, ck_r1, payload1_rcvd) ) else ( (* payload1 did not decrypt *) process1fail ) ) else (* weird case where the HKDF0 pattern matching did not work - actually never happens *) process1fail. (* Prepare the second message e, ee, {p2} *) letfun prepare2( key_hash: hashkey_t, E_i_pub_rcvd: G_t, H_r3: hashoutput_t, ck_r1: hashoutput_t, payload2: bitstring) = new E_r_priv: Z_t; let E_r_pub: G_t = exp(g, E_r_priv) in let ee_r: G_t = DH(E_i_pub_rcvd, E_r_priv) in let H_r4: hashoutput_t = mix_hash_G(key_hash, H_r3, E_r_pub) in let concat_prf_output(ck_r2, k_r4: key_t) = HKDF0(ck_r1, ee_r) in ( let concat_two_keys(T_r_recv: key_t, T_r_send: key_t) = HKDF2(ck_r2, empty_val) in ( let payload2_enc: bitstring = enc_bitstring(payload2, H_r4, k_r4, nonce_0) in let H_r5: hashoutput_t = mix_hash_bitstring(key_hash, H_r4, payload2_enc) in prepare2succ(E_r_priv, E_r_pub, T_r_recv, T_r_send, H_r5, payload2_enc) ) else ( (* weird case where the HKDF2 pattern matching did not work - actually never happens *) prepare2fail ) ) else ( (* weird case where the HKDF0 pattern matching did not work - actually never happens *) prepare2fail ). (* Process a received second message *) letfun process2( key_hash: hashkey_t, E_i_priv: Z_t, E_i_pub: G_t, E_r_pub_rcvd: G_t, payload2_enc_rcvd: bitstring, H_i3: hashoutput_t, ck_i1: hashoutput_t) = let ee_i: G_t = DH(E_r_pub_rcvd, E_i_priv) in let H_i4: hashoutput_t = mix_hash_G(key_hash, H_i3, E_r_pub_rcvd) in let concat_prf_output(ck_i2, k_i4: key_t) = HKDF0(ck_i1, ee_i) in ( let concat_two_keys(T_i_send: key_t, T_i_recv: key_t) = HKDF2(ck_i2, empty_val) in ( let injbot(payload2_rcvd) = dec_ad_hash(payload2_enc_rcvd, H_i4, k_i4, nonce_0) in ( let H_i5: hashoutput_t = mix_hash_bitstring(key_hash, H_i4, payload2_enc_rcvd) in process2succ(T_i_send, T_i_recv, H_i5, payload2_rcvd) ) else ( (* payload2_enc_rcvd did not decrypt *) process2fail ) ) else ( (* weird case where the HKDF2 pattern matching did not work - actually never happens *) process2fail ) ) else ( (* weird case where the HKDF0 pattern matching did not work - actually never happens *) process2fail ). (* Prepare the payload messages *) letfun prepare_msg( side_index: bitstring, secret_bit_I: bool, plaintext_0: bitstring, plaintext_1: bitstring, counter: counter_t, T_i_send: key_t) = if Zero(plaintext_0) = Zero(plaintext_1) then ( get sent_counters(=side_index, =counter) in preparemsgfail else insert sent_counters(side_index, counter); let plaintext = test(secret_bit_I, plaintext_0, plaintext_1) in let ciphertext = enc(plaintext, empty_bitstring, T_i_send, counter_to_nonce(counter)) in preparemsgsucc(ciphertext, plaintext) ) else ( preparemsgfail ). (* Process the payload messages *) letfun process_msg( side_index: bitstring, counter_rcvd: counter_t, ciphertext_rcvd: bitstring, T_i_recv: key_t) = get rcvd_counters(=side_index, =counter_rcvd) in processmsgfail else insert rcvd_counters(side_index, counter_rcvd); let injbot(plaintext) = dec(ciphertext_rcvd, empty_bitstring, T_i_recv, counter_to_nonce(counter_rcvd)) in ( processmsgsucc(plaintext) ) else ( (* decryption failed *) processmsgfail ). (* The initiator *) let initiator(key_hash: hashkey_t, S_r_pub: G_t, secret_bit_I: bool) = ! i_N_init_parties <= n_S (* Receive the payload the initiator should use. *) in(c_config_initiator, payload1: bitstring); let prepare1succ( E_i_priv: Z_t, E_i_pub_sent: G_t, payload1_enc: bitstring, H_i3: hashoutput_t, ck_i1: hashoutput_t) = prepare1(key_hash, S_r_pub, payload1) in out(c_init2resp_send, (E_i_pub_sent, payload1_enc)); in(c_resp2init_recv, (E_r_pub_rcvd: G_t, payload2_enc_rcvd: bitstring)); let honest_session_i = (if defined(S_r_is_corrupted) then (find j <= n_S suchthat defined(E_r_pub[j]) && pow8(E_r_pub_rcvd) = pow8(E_r_pub[j]) then (* This means we have an honest E_r. *) true else false) else true) in let process2succ(T_i_send: key_t, T_i_recv: key_t, H_i5: hashoutput_t, payload2_rcvd: bitstring) = process2(key_hash, E_i_priv, E_i_pub_sent, E_r_pub_rcvd, payload2_enc_rcvd, H_i3, ck_i1) in if not(honest_session_i) then out(c_wait_before_2nd_part, (T_i_send, T_i_recv)) (* The adversary can run the rest of the session by himself *) else out(c_wait_before_2nd_part, ()); (( ! i_Nis<=n_M in(c_N_init_send_config, (plaintext_data_0: bitstring, plaintext_data_1: bitstring, counter: counter_t)); let preparemsgsucc(ciphertext_data_send: bitstring, plaintext_data_send: bitstring) = prepare_msg((is_initiator, i_N_init_parties), secret_bit_I, plaintext_data_0, plaintext_data_1, counter, T_i_send) in event sent_msg_initiator(E_i_pub_sent, payload1, payload1_enc, E_r_pub_rcvd, payload2_rcvd, payload2_enc_rcvd, T_i_send, T_i_recv, counter, ciphertext_data_send, plaintext_data_send); out(c_N_init_send, (counter, ciphertext_data_send)) )|( ! i_Nir<=n_M in(c_N_init_recv, (counter_rcvd: counter_t, ciphertext_data_rcvd: bitstring)); let processmsgsucc(plaintext_data_rcvd: bitstring) = process_msg((is_initiator, i_N_init_parties), counter_rcvd, ciphertext_data_rcvd, T_i_recv) in event rcvd_msg_initiator(E_i_pub_sent, payload1, payload1_enc, E_r_pub_rcvd, payload2_rcvd, payload2_enc_rcvd, T_i_send, T_i_recv, counter_rcvd, ciphertext_data_rcvd, plaintext_data_rcvd) )). (* The responder *) let responder(key_hash: hashkey_t, S_r_priv: Z_t, S_r_pub: G_t, secret_bit_R: bool) = ! i_N_resp_parties <= n_S in(c_init2resp_recv, (E_i_pub_rcvd: G_t, payload1_enc_rcvd: bitstring, payload2: bitstring)); (* This means we have honest E_i and S_r. Thus, the attacker cannot get the secret key, because E_i_priv and S_r_priv are secret. *) let honest_session_r : bool = (find j <= n_S suchthat defined(E_i_pub_sent[j]) && pow8(E_i_pub_rcvd) = pow8(E_i_pub_sent[j]) then true else false) in let process1succ(H_r3: hashoutput_t, ck_r1: hashoutput_t, payload1_rcvd: bitstring) = process1( key_hash, S_r_priv, S_r_pub, E_i_pub_rcvd, payload1_enc_rcvd) in let prepare2succ(E_r_priv: Z_t, E_r_pub_sent: G_t, T_r_recv: key_t, T_r_send: key_t, H_r5: hashoutput_t, payload2_enc: bitstring) = prepare2(key_hash, E_i_pub_rcvd, H_r3, ck_r1, payload2) in if not(honest_session_r) then out(c_resp2init_send, (E_r_pub_sent, payload2_enc, T_r_recv, T_r_send)) (* The adversary can run the rest of the session by himself *) else out(c_resp2init_send, (E_r_pub_sent, payload2_enc)); (( ! i_Nrs<=n_M in(c_N_resp_send_config, (plaintext_0: bitstring, plaintext_1: bitstring, counter: counter_t)); let preparemsgsucc(ciphertext_data_send: bitstring, plaintext_data_send: bitstring) = prepare_msg((is_responder, i_N_resp_parties), secret_bit_R, plaintext_0, plaintext_1, counter, T_r_send) in event sent_msg_responder(E_i_pub_rcvd, payload1_rcvd, payload1_enc_rcvd, E_r_pub_sent, payload2, payload2_enc, T_r_recv, T_r_send, counter, ciphertext_data_send, plaintext_data_send); out(c_N_resp_send, (counter, ciphertext_data_send)) )|( ! i_Nrr<=n_M in(c_N_resp_recv, (counter_rcvd: counter_t, ciphertext_data_rcvd: bitstring)); let processmsgsucc(plaintext_data_rcvd: bitstring) = process_msg((is_responder, i_N_resp_parties), counter_rcvd, ciphertext_data_rcvd, T_r_recv) in event rcvd_msg_responder(E_i_pub_rcvd, payload1_rcvd, payload1_enc_rcvd, E_r_pub_sent, payload2, payload2_enc, T_r_recv, T_r_send, counter_rcvd, ciphertext_data_rcvd, plaintext_data_rcvd) )). (* Corruption process: the adversary uses it to corrupt the long-term key dynamically *) let corrupt_S_r(S_r_priv: Z_t) = in(c_corrupt_S_r, ()); let S_r_is_corrupted: bool = true in out(c_corrupt_S_r, S_r_priv). process in(c_start, ()); new key_hash: hashkey_t; new S_r_priv: Z_t; let S_r_pub = exp(g, S_r_priv) in (* Secret bit for the indistinguishability game. *) new secret_bit : bool; (* hand over control to the attacker *) out(c_publickeys, S_r_pub); (initiator(key_hash, S_r_pub, secret_bit) | responder(key_hash, S_r_priv, S_r_pub, secret_bit) | hash_oracle(key_hash) | corrupt_S_r(S_r_priv) ) (* EXPECTED All queries proved. 24.160s (user 24.044s + system 0.116s), max rss 58812K END *)