equation forall x:bool; not(not(x)) = x. equation forall x:bool,y:bool; (not(x && y)) = (not(x) || not(y)). equation forall x:bool,y:bool; (not(x || y)) = (not(x) && not(y)). equation not(true) = false. equation not(false) = true. equation forall x:bool; (x = true) = x. equation forall x:bool; (x <> false) = x. equation forall x:bool; (x <> true) = not(x). equation forall x:bool; (x = false) = not(x). equation forall x,y,z:bool; ((x || y) && z) = ((x && z) || (y && z)). const bottom:bitstringbot. (******************************** Key generation ************************************************) (* The symmetric primitives no longer include a key generation function. If you want to add one, you can use the following macro keyseed: type of key seeds, must be "bounded" or "nonuniform" (to be able to generate random numbers from it), typically "fixed" and "large". key: type of keys, must be "bounded" or "nonuniform" kgen: key generation function The types keyseed and key must be declared before this macro is expanded. The function kgen is declared by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. *) def keygen(keyseed, key, kgen) { fun kgen(keyseed): key. equiv(keygen(kgen)) r <-R keyseed; Okey() := return(kgen(r)) <=(0)=> k <-R key; Okey() := return(k). } (***************************** Symmetric encryption *********************************************) (* IND-CPA probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). enc_r': symbol that replaces enc_r after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l The types key, cleartext, ciphertext, enc_seed and the probability Penc must be declared before this macro is expanded. The functions enc, enc_r, enc_r', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CPA_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc) { param N. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun enc_r'(cleartext, key, enc_seed): ciphertext. fun injbot(cleartext):bitstringbot [data]. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall x:cleartext; injbot(x) <> bottom. equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). equiv(ind_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r'(Z(x), k, r)). } def IND_CPA_sym_enc(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc) { type enc_seed [bounded]. expand IND_CPA_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc). } (* IND-CPA encryption with a nonce. This is similar to the IND_CPA_sym_enc macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts nonce: type of the nonce enc: encryption function enc': symbol that replaces enc after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l The types key, cleartext, ciphertext, nonce and the probability Penc must be declared before this macro is expanded. The functions enc, enc', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CPA_sym_enc_nonce_all_args(key, cleartext, ciphertext, nonce, enc, enc', dec, injbot, Z, Penc) { param N, N2, N3. fun enc(cleartext, key, nonce): ciphertext. fun dec(ciphertext, key, nonce): bitstringbot. fun enc'(cleartext, key, nonce): ciphertext. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. equation forall m:cleartext, k:key, n: nonce; dec(enc(m, k, n), k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND-CPA *) equiv(ind_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc'(Z(x), k, n) in return(r). } def IND_CPA_sym_enc_nonce(key, cleartext, ciphertext, nonce, enc, dec, injbot, Z, Penc) { expand IND_CPA_sym_enc_nonce_all_args(key, cleartext, ciphertext, nonce, enc, enc', dec, injbot, Z, Penc). } (* IND-CPA and INT-CTXT probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). enc_r': symbol that replaces enc_r after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, enc_seed and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc_r, enc_r', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CPA_INT_CTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc, Pencctxt) { param N, N2, N3. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun enc_r'(cleartext, key, enc_seed): ciphertext. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). (* IND-CPA *) equiv(ind_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r'(Z(x), k, r)). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := z:ciphertext <- enc_r(x, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],z[j]) && z[j] = y then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := z:ciphertext <- enc_r(x, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := if defined(corrupt) then return(dec(y,k)) else find j <= N suchthat defined(x[j],z[j]) && z[j] = y then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def IND_CPA_INT_CTXT_sym_enc(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc, Pencctxt) { type enc_seed [bounded]. expand IND_CPA_INT_CTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc, Pencctxt). } (* Authenticated encryption with a nonce. This is similar to the IND_CPA_INT_CTXT_sym_enc macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts nonce: type of the nonce enc: encryption function enc': symbol that replaces enc after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, nonce and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CPA_INT_CTXT_sym_enc_nonce_all_args(key, cleartext, ciphertext, nonce, enc, enc', dec, injbot, Z, Penc, Pencctxt) { param N, N2, N3. fun enc(cleartext, key, nonce): ciphertext. fun dec(ciphertext, key, nonce): bitstringbot. fun enc'(cleartext, key, nonce): ciphertext. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. equation forall m:cleartext, k:key, n: nonce; dec(enc(m, k, n), k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND-CPA *) equiv(ind_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc'(Z(x), k, n) in return(r). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) [useful_change] := return(dec(y,k,c_n))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc(x, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) := find j <= N suchthat defined(x[j],n[j],r[j]) && r[j] = y && n[j] = c_n then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) [useful_change] := return(dec(y, k,c_n)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc(x, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) := if defined(corrupt) then return(dec(y,k,c_n)) else find j <= N suchthat defined(x[j],n[j],r[j]) && r[j] = y && n[j] = c_n then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def IND_CPA_INT_CTXT_sym_enc_nonce(key, cleartext, ciphertext, nonce, enc, dec, injbot, Z, Penc, Pencctxt) { expand IND_CPA_INT_CTXT_sym_enc_nonce_all_args(key, cleartext, ciphertext, nonce, enc, enc', dec, injbot, Z, Penc, Pencctxt). } (* AEAD (authenticated encryption with additional data) This is similar to IND-CPA and INT-CTXT authenticated encryption, except that some additional data is just authenticated. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data that is just authenticated enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). enc_r': symbol that replaces enc_r after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, add_data, enc_seed and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc_r, enc_r', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_all_args(key, cleartext, ciphertext, add_data, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc, Pencctxt) { param N, N2, N3. fun enc_r(cleartext, add_data, key, enc_seed): ciphertext. fun dec(ciphertext, add_data, key): bitstringbot. fun enc_r'(cleartext, add_data, key, enc_seed): ciphertext. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, d: add_data, k: key) = r <-R enc_seed; enc_r(m, d, k, r). equation forall m:cleartext, d: add_data, k:key, r:enc_seed; dec(enc_r(m, d, k, r), d, k) = injbot(m). (* IND-CPA *) equiv(ind_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r'(Z(x), d, k, r)). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) [useful_change] := return(dec(y,c_d,k))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext, d: add_data) := z:ciphertext <- enc_r(x, d, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) := find j <= N suchthat defined(x[j],d[j],z[j]) && z[j] = y && d[j] = c_d then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) [useful_change] := return(dec(y,c_d,k)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext, d: add_data) := z:ciphertext <- enc_r(x, d, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) := if defined(corrupt) then return(dec(y,c_d,k)) else find j <= N suchthat defined(x[j],d[j],z[j]) && z[j] = y && d[j] = c_d then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def AEAD(key, cleartext, ciphertext, add_data, enc, dec, injbot, Z, Penc, Pencctxt) { type enc_seed [bounded]. expand AEAD_all_args(key, cleartext, ciphertext, add_data, enc_seed, enc, enc_r, enc_r', dec, injbot, Z, Penc, Pencctxt). } (* AEAD (authenticated encryption with additional data) with a nonce. This is similar to the AEAD macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. A typical example is AES-GCM. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data that is just authenticated nonce: type of the nonce enc: encryption function enc': symbol that replaces enc after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, add_data, nonce and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc', dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_nonce_all_args(key, cleartext, ciphertext, add_data, nonce, enc, enc', dec, injbot, Z, Penc, Pencctxt) { param N, N2, N3. fun enc(cleartext, add_data, key, nonce): ciphertext. fun dec(ciphertext, add_data, key, nonce): bitstringbot. fun enc'(cleartext, add_data, key, nonce): ciphertext. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. equation forall m:cleartext, d: add_data, k:key, n: nonce; dec(enc(m, d, k, n), d, k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND-CPA *) equiv(ind_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],r[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else let r: ciphertext = enc'(Z(x), d, k, n) in return(r). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) [useful_change] := return(dec(y,c_d,k,c_n))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],r[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else let r: ciphertext = enc(x, d, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) := find j <= N suchthat defined(x[j],d[j],n[j],r[j]) && r[j] = y && d[j] = c_d && n[j] = c_n then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) [useful_change] := return(dec(y,c_d,k,c_n)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],r[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else let r: ciphertext = enc(x, d, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) := if defined(corrupt) then return(dec(y,c_d,k,c_n)) else find j <= N suchthat defined(x[j],d[j],n[j],r[j]) && r[j] = y && d[j] = c_d && n[j] = c_n then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def AEAD_nonce(key, cleartext, ciphertext, add_data, nonce, enc, dec, injbot, Z, Penc, Pencctxt) { expand AEAD_nonce_all_args(key, cleartext, ciphertext, add_data, nonce, enc, enc', dec, injbot, Z, Penc, Pencctxt). } (* IND$-CPA probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l The types key, cleartext, ciphertext, enc_seed, cipher_stream and the probability Penc must be declared before this macro is expanded. The functions enc, enc_r, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def INDdollar_CPA_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc) { param N. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall x:cleartext; injbot(x) <> bottom. equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do s <-R cipher_stream; Oenc(x:cleartext) := return(truncate(s, enc_len(Z(x)))). } def INDdollar_CPA_sym_enc(key, cleartext, ciphertext, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc) { type enc_seed [bounded]. expand INDdollar_CPA_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc). } (* IND$-CPA encryption with a nonce. This is similar to the INDdollar_CPA_sym_enc macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts nonce: type of the nonce cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l The types key, cleartext, ciphertext, nonce, cipher_stream and the probability Penc must be declared before this macro is expanded. The functions enc, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def INDdollar_CPA_sym_enc_nonce(key, cleartext, ciphertext, nonce, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc) { param N, N2, N3. fun enc(cleartext, key, nonce): ciphertext. fun dec(ciphertext, key, nonce): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. equation forall m:cleartext, k:key, n: nonce; dec(enc(m, k, n), k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND$-CPA *) equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],s[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else s <-R cipher_stream; return(truncate(s, enc_len(Z(x)))). } (* IND$-CPA and INT-CTXT probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, enc_seed, cipher_stream and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc_r, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def INDdollar_CPA_INT_CTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { param N, N2, N3. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). (* IND$-CPA *) equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do s <-R cipher_stream; Oenc(x:cleartext) := return(truncate(s, enc_len(Z(x)))). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := z:ciphertext <- enc_r(x, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],z[j]) && z[j] = y then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := z:ciphertext <- enc_r(x, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := if defined(corrupt) then return(dec(y,k)) else find j <= N suchthat defined(x[j],z[j]) && z[j] = y then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def INDdollar_CPA_INT_CTXT_sym_enc(key, cleartext, ciphertext, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { type enc_seed [bounded]. expand INDdollar_CPA_INT_CTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt). } (* IND$-CPA and INT-CTXT Authenticated encryption with a nonce. This is similar to the INDdollar_CPA_INT_CTXT_sym_enc macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts nonce: type of the nonce cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function enc': symbol that replaces enc after game transformation dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, nonce, cipher_stream and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def INDdollar_CPA_INT_CTXT_sym_enc_nonce(key, cleartext, ciphertext, nonce, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { param N, N2, N3. fun enc(cleartext, key, nonce): ciphertext. fun dec(ciphertext, key, nonce): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. equation forall m:cleartext, k:key, n: nonce; dec(enc(m, k, n), k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND$-CPA *) equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],s[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else s <-R cipher_stream; return(truncate(s, enc_len(Z(x)))). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) [useful_change] := return(dec(y,k,c_n))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc(x, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) := find j <= N suchthat defined(x[j],n[j],r[j]) && r[j] = y && n[j] = c_n then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := return(enc(x, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) [useful_change] := return(dec(y, k,c_n)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, n: nonce) := find u <= N suchthat defined(x[u],n[u],r[u]) && n = n[u] && x <> x[u] then event_abort repeated_nonce else let r: ciphertext = enc(x, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_n: nonce) := if defined(corrupt) then return(dec(y,k,c_n)) else find j <= N suchthat defined(x[j],n[j],r[j]) && r[j] = y && n[j] = c_n then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } (* AEAD (authenticated encryption with additional data) IND$-CPA and INT-CTXT This is similar to INDdollar-CPA and INT-CTXT authenticated encryption, except that some additional data is just authenticated. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data that is just authenticated enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, add_data, enc_seed, cipher_stream and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc_r, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_INDdollar_CPA_all_args(key, cleartext, ciphertext, add_data, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { param N, N2, N3. fun enc_r(cleartext, add_data, key, enc_seed): ciphertext. fun dec(ciphertext, add_data, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, d: add_data, k: key) = r <-R enc_seed; enc_r(m, d, k, r). equation forall m:cleartext, d: add_data, k:key, r:enc_seed; dec(enc_r(m, d, k, r), d, k) = injbot(m). (* IND$-CPA *) equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do s <-R cipher_stream; Oenc(x:cleartext, d: add_data) := return(truncate(s, enc_len(Z(x)))). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) [useful_change] := return(dec(y,c_d,k))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext, d: add_data) := z:ciphertext <- enc_r(x, d, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) := find j <= N suchthat defined(x[j],d[j],z[j]) && z[j] = y && d[j] = c_d then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) [useful_change] := return(dec(y,c_d,k)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext, d: add_data) := z:ciphertext <- enc_r(x, d, k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data) := if defined(corrupt) then return(dec(y,c_d,k)) else find j <= N suchthat defined(x[j],d[j],z[j]) && z[j] = y && d[j] = c_d then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } def AEAD_INDdollar_CPA(key, cleartext, ciphertext, add_data, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { type enc_seed [bounded]. expand AEAD_INDdollar_CPA_all_args(key, cleartext, ciphertext, add_data, enc_seed, cipher_stream, enc, enc_r, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt). } (* AEAD (authenticated encryption with additional data) IND$-CPA and INT-CTXT with a nonce. This is similar to the AEAD_INDdollar_CPA macro, but it uses a nonce (which must have a different value in each call to encryption) instead of random coins generated by encryption. A typical example is AES-GCM. key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data that is just authenticated nonce: type of the nonce cipher_stream: type of unbounded streams (must be "nonuniform") enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. enc_len: function that returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. truncate: truncate(s,x) is the truncation of s to the length of x, where s is a stream of unbounded length. Penc(t, N, l): probability of breaking the IND-CPA property in time t for one key and N encryption queries with cleartexts of length at most l Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, add_data, nonce, cipher_stream and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, dec, injbot, Z, enc_len, and truncate are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_INDdollar_CPA_nonce(key, cleartext, ciphertext, add_data, nonce, cipher_stream, enc, dec, injbot, Z, enc_len, truncate, Penc, Pencctxt) { param N, N2, N3. fun enc(cleartext, add_data, key, nonce): ciphertext. fun dec(ciphertext, add_data, key, nonce): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The function enc_len returns, for each bitstring x, a bitstring of the same length as the encryption of x, consisting only of zeroes. *) fun enc_len(cleartext): ciphertext. (* truncate(s,x) is the truncation of s to the length of x. s is assumed to be a stream of unbounded length. *) fun truncate(cipher_stream, ciphertext): ciphertext. equation forall m:cleartext, d: add_data, k:key, n: nonce; dec(enc(m, d, k, n), d, k, n) = injbot(m). (* Event raised when some nonce is used several times with the same key, which breaks security. *) event repeated_nonce. (* IND$-CPA *) equiv(inddollar_cpa(enc)) k <-R key; foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) <=(Penc(time, N, maxlength(x)))=> k <-R key; foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],s[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else s <-R cipher_stream; return(truncate(s, enc_len(Z(x)))). (* INT-CTXT *) equiv(int_ctxt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) [useful_change] := return(dec(y,c_d,k,c_n))) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],r[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else let r: ciphertext = enc(x, d, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) := find j <= N suchthat defined(x[j],d[j],n[j],r[j]) && r[j] = y && d[j] = c_d && n[j] = c_n then return(injbot(x[j])) else return(bottom)). equiv(int_ctxt_corrupt(enc)) k <-R key; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) [useful_change] := return(dec(y,c_d,k,c_n)) | Ocorrupt() [10] := return(k)) <=(Pencctxt(time, N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := find u <= N suchthat defined(x[u],d[u],n[u],r[u]) && n = n[u] && (x <> x[u] || d <> d[u]) then event_abort repeated_nonce else let r: ciphertext = enc(x, d, k, n) in return(r) | foreach i3 <= N3 do Odec(y:ciphertext, c_d: add_data, c_n: nonce) := if defined(corrupt) then return(dec(y,c_d,k,c_n)) else find j <= N suchthat defined(x[j],d[j],n[j],r[j]) && r[j] = y && d[j] = c_d && n[j] = c_n then return(injbot(x[j])) else return(bottom) | Ocorrupt() := let corrupt: bool = true in return(k)). } (* IND-CCA2 probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function enc_r', dec': symbols that replace enc_r and dec respectively after game transformation injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, Nu, N', l, l'): probability of breaking the IND-CCA2 property in time t for one key, N modified encryption queries, N unchanged encryption queries, and N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, enc_seed and the probability Penc must be declared before this macro is expanded. The functions enc, enc_r, enc_r', dec, dec', injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CCA2_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, dec', injbot, Z, Penc) { param N, N', N'', N2, N3. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun enc_r'(cleartext, key, enc_seed): ciphertext. fun dec'(ciphertext, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). (* IND-CCA2 *) equiv(ind_cca2(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := return(dec(y,k))) <=(Penc(time, N, 0, N3, maxlength(x), maxlength(y)))=> k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := z:ciphertext <- enc_r'(Z(x), k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],r[j],z[j]) && y = z[j] then return(injbot(x[j])) else return(dec'(y, k))). (* Manual version, which can transform only some occurrences of encryption: use oracle Oenc_unchanged to discharge the occurrences of encryption that you do not want to transform. It renames only the encryption function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(ind_cca2_partial(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) [useful_change] := return(enc_r(x, k, r)) | foreach i <= N' do r' <-R enc_seed; Oenc_unchanged(x':cleartext) := return(enc_r(x', k, r')) | foreach i <= N'' do r'' <-R enc_seed; Oenc_unchanged'(x'':cleartext) := return(enc_r'(x'', k, r'')) | foreach i3 <= N3 do Odec(y:ciphertext) := return(dec(y,k))) <=(Penc(time, N, N'+N'', N3, max(maxlength(x),maxlength(x'),maxlength(x'')), maxlength(y)))=> [manual] k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := z:ciphertext <- enc_r'(Z(x), k, r); return(z) | foreach i <= N' do r' <-R enc_seed; Oenc_unchanged(x':cleartext) := return(enc_r(x', k, r')) | foreach i <= N'' do r'' <-R enc_seed; Oenc_unchanged'(x'':cleartext) := return(enc_r'(x'', k, r'')) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],r[j],z[j]) && y = z[j] then return(injbot(x[j])) else return(dec(y, k))). } def IND_CCA2_sym_enc(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc) { type enc_seed [bounded]. expand IND_CCA2_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, dec', injbot, Z, Penc). } (* INT-PTXT probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function dec': symbol that replaces dec after game transformation injbot: natural injection from cleartext to bitstringbot Pencptxt(t, N, N', Nu', l, l'): probability of breaking the INT-PTXT property in time t for one key, N encryption queries, N' modified decryption queries, Nu' unchanged decryption queries, with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, enc_seed and the probability Pencptxt must be declared before this macro is expanded. The functions enc, enc_r, dec, dec', and injbot are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def INT_PTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, dec, dec', injbot, Pencptxt) { param N, N2, N3, N3', N3''. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun dec'(ciphertext, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). (* INT-PTXT *) equiv(int_ptxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k))) <=(Pencptxt(time, N, N3, 0, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := z <- dec'(y, k); find j <= N suchthat defined(x[j]) && z = injbot(x[j]) then return(injbot(x[j])) else return(bottom)). (* Manual version, which supports corruption of the key and can transform only some occurrences of decryption: use oracle Odec_unchanged to discharge the occurrences of decryption that you do not want to transform. It renames only the decryption function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(int_ptxt_corrupt_partial(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k)) | foreach i3 <= N3' do Odec_unchanged(y':ciphertext) [8] := return(dec(y',k)) | foreach i3 <= N3'' do Odec_unchanged'(y'':ciphertext) := return(dec'(y'',k)) | Ocorrupt() [10] := return(k)) <=(Pencptxt(time, N, N3, N3'+N3'', maxlength(x), max(maxlength(y),maxlength(y'),maxlength(y''))))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := z <- dec'(y, k); if defined(corrupt) then return(z) else find j <= N suchthat defined(x[j]) && z = injbot(x[j]) then return(injbot(x[j])) else return(bottom) | foreach i3 <= N3' do Odec_unchanged(y':ciphertext) := return(dec(y',k)) | foreach i3 <= N3'' do Odec_unchanged'(y'':ciphertext) := return(dec'(y'',k)) | Ocorrupt() := let corrupt: bool = true in return(k)). } def INT_PTXT_sym_enc(key, cleartext, ciphertext, enc, dec, injbot, Pencptxt) { type enc_seed [bounded]. expand INT_PTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, dec, dec', injbot, Pencptxt). } (* IND-CCA2 and INT-PTXT probabilistic symmetric encryption key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc_r without mentioning the length of the key), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function enc_r', dec': symbols that replace enc_r and dec respectively after game transformation injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, Nu, N', l, l'): probability of breaking the IND-CCA2 property in time t for one key, N modified encryption queries, Nu unchanged encryption queries, and N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. Pencptxt(t, N, N', Nu', l, l'): probability of breaking the INT-PTXT property in time t for one key, N encryption queries, N' modified decryption queries, and Nu' unchanged decryption queries with cleartexts of length at most l and ciphertexts of length at most l'. The types key, cleartext, ciphertext, enc_seed and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, enc_r, enc_r', dec, dec', injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. CryptoVerif often needs manual guidance with this property, because it does not know which property (IND-CCA2 or INT-PTXT) to apply first. Moreover, when empty plaintexts are not allowed, IND-CCA2 and INT-PTXT is equivalent to IND-CPA and INT-CTXT, which is much easier to use for CryptoVerif, so we recommend using the latter property when possible. *) def IND_CCA2_INT_PTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, dec', injbot, Z, Penc, Pencptxt) { param N, N', N2, N3, N3'. fun enc_r(cleartext, key, enc_seed): ciphertext. fun dec(ciphertext, key): bitstringbot. fun enc_r'(cleartext, key, enc_seed): ciphertext. fun dec'(ciphertext, key): bitstringbot. fun injbot(cleartext):bitstringbot [data]. equation forall x:cleartext; injbot(x) <> bottom. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. (* The encryption function is probabilistic *) letfun enc(m: cleartext, k: key) = r <-R enc_seed; enc_r(m, k, r). equation forall m:cleartext, k:key, r:enc_seed; dec(enc_r(m, k, r), k) = injbot(m). (* IND-CCA2 *) equiv(ind_cca2(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := return(dec(y,k))) <=(Penc(time, N, 0, N3, maxlength(x), maxlength(y)))=> k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := z:ciphertext <- enc_r'(Z(x), k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],r[j],z[j]) && y = z[j] then return(injbot(x[j])) else return(dec(y, k))). equiv(ind_cca2_after_int_ptxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := return(dec'(y,k))) <=(Penc(time, N, 0, N3, maxlength(x), maxlength(y)))=> k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := z:ciphertext <- enc_r'(Z(x), k, r); return(z) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],r[j],z[j]) && y = z[j] then return(injbot(x[j])) else return(dec'(y, k))). (* Manual version, which can transform only some occurrences of encryption: use oracle Oenc_unchanged to discharge the occurrences of encryption that you do not want to transform. It does not rename function symbols, so it can be applied again, and before or after INT-PTXT. *) equiv(ind_cca2_partial(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) [useful_change] := return(enc_r(x, k, r)) | foreach i <= N' do r' <-R enc_seed; Oenc_unchanged(x':cleartext) := return(enc_r(x', k, r')) | foreach i3 <= N3 do Odec(y:ciphertext) := return(dec(y,k))) <=(Penc(time, N, N', N3, max(maxlength(x),maxlength(x')), maxlength(y)))=> [manual] k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := z:ciphertext <- enc_r(Z(x), k, r); return(z) | foreach i <= N' do r' <-R enc_seed; Oenc_unchanged(x':cleartext) := return(enc_r(x', k, r')) | foreach i3 <= N3 do Odec(y:ciphertext) := find j <= N suchthat defined(x[j],r[j],z[j]) && y = z[j] then return(injbot(x[j])) else return(dec(y, k))). (* INT-PTXT *) equiv(int_ptxt(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k))) <=(Pencptxt(time, N, N3, 0, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := z <- dec'(y, k); find j <= N suchthat defined(x[j]) && z = injbot(x[j]) then return(injbot(x[j])) else return(bottom)). equiv(int_ptxt_after_ind_cca2(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r'(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k))) <=(Pencptxt(time, N, N3, 0, maxlength(x), maxlength(y)))=> [computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := return(enc_r'(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := z <- dec'(y, k); find j <= N suchthat defined(x[j]) && z = injbot(x[j]) then return(injbot(x[j])) else return(bottom)). (* Manual version, which supports corruption of the key and can transform only some occurrences of decryption: use oracle Odec_unchanged to discharge the occurrences of decryption that you do not want to transform. It does not rename function symbols, so it can be applied again, and before or after IND-CCA2. *) equiv(int_ptxt_corrupt_partial(enc)) k <-R key; ( foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) [useful_change] := return(dec(y,k)) | foreach i3 <= N3' do Odec_unchanged(y':ciphertext) [8] := return(dec(y',k)) | Ocorrupt() [10] := return(k)) <=(Pencptxt(time, N, N3, N3', maxlength(x), max(maxlength(y),maxlength(y'))))=> [manual,computational] k <-R key [unchanged]; ( foreach i <= N do r <-R enc_seed [unchanged]; Oenc(x:cleartext) := return(enc_r(x, k, r)) | foreach i3 <= N3 do Odec(y:ciphertext) := z <- dec(y, k); if defined(corrupt) then return(z) else find j <= N suchthat defined(x[j]) && z = injbot(x[j]) then return(injbot(x[j])) else return(bottom) | foreach i3 <= N3' do Odec_unchanged(y':ciphertext) := return(dec(y',k)) | Ocorrupt() := let corrupt: bool = true in return(k)). } def IND_CCA2_INT_PTXT_sym_enc(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc, Pencptxt) { type enc_seed [bounded]. expand IND_CCA2_INT_PTXT_sym_enc_all_args(key, cleartext, ciphertext, enc_seed, enc, enc_r, enc_r', dec, dec', injbot, Z, Penc, Pencptxt). } (* SPRP block cipher key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". blocksize: type of the input and output of the cipher, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the block; typically "fixed") and "large". (The modeling of SPRP block ciphers is not perfect in that, in order to encrypt a new message, one chooses a fresh random number, not necessarily different from previously generated random numbers. Then CryptoVerif needs to eliminate collisions between those random numbers, so blocksize must really be "large".) enc: encryption function dec: decryption function Penc(t, N, N'): probability of breaking the SPRP property in time t for one key, N encryption queries, and N' decryption queries. The types key, blocksize and the probability Penc must be declared before this macro is expanded. The functions enc, dec are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def SPRP_cipher(key, blocksize, enc, dec, Penc) { param Ne, Nd, Necoll, Ndcoll, Nk. fun enc(blocksize, key): blocksize. fun dec(blocksize, key): blocksize. equation forall m:blocksize, k:key; dec(enc(m, k), k) = m. equation forall m:blocksize, k:key; enc(dec(m, k), k) = m. equation forall m1:blocksize, m2:blocksize, k:key; (dec(m1, k) = dec(m2, k)) = (m1 = m2). equation forall m1:blocksize, m2:blocksize, k:key; (enc(m1, k) = enc(m2, k)) = (m1 = m2). equiv(sprp(enc)) special sprp(("msg", "key"), enc, dec, Penc, (k, m, c, u), ("large")). equiv(sprp_partial(enc)) special sprp_partial(("msg", "key"), enc, dec, Penc, (k, m, c, u), ("large")) [manual]. } (* PRP block cipher key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the key), typically "fixed" and "large". blocksize: type of the input and output of the cipher, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of enc without mentioning the length of the block; typically "fixed") and "large". (The modeling of PRP block ciphers is not perfect in that, in order to encrypt a new message, one chooses a fresh random number, not necessarily different from previously generated random numbers. In other words, we model a PRF rather than a PRP, and apply the PRF/PRP switching lemma to make sure that this is sound. Then CryptoVerif needs to eliminate collisions between those random numbers, so blocksize must really be "large".) enc: encryption function dec: decryption function Penc(t, N): probability of breaking the PRP property in time t for one key and N encryption queries. The types key, blocksize and the probability Penc must be declared before this macro is expanded. The functions enc, dec are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def PRP_cipher(key, blocksize, enc, dec, Penc) { param N, Ncoll, Nk. fun enc(blocksize, key): blocksize. fun dec(blocksize, key): blocksize. equation forall m:blocksize, k:key; dec(enc(m, k), k) = m. equation forall c:blocksize, k:key; enc(dec(c, k), k) = c. equation forall m1:blocksize, m2:blocksize, k:key; (dec(m1, k) = dec(m2, k)) = (m1 = m2). equation forall m1:blocksize, m2:blocksize, k:key; (enc(m1, k) = enc(m2, k)) = (m1 = m2). equiv(prp(enc)) special prp("key_last", enc, Penc, (k, re, x, u), ("large")). equiv(prp_partial(enc)) special prp_partial("key_last", enc, Penc, (k, re, x, u), ("large")) [manual]. } (*************************************** MACs ***************************************) (* SUF-CMA deterministic mac (strongly unforgeable MAC) The difference between a UF-CMA MAC and a SUF-CMA MAC is that, for a UF-CMA MAC, the adversary may easily forge a new MAC for a message for which he has already seen a MAC. Such a forgery is guaranteed to be hard for a SUF-CMA MAC. For deterministic MACs, the verification can be done by recomputing the MAC, and in this case, an UF-CMA MAC is always SUF-CMA, so we model only SUF-CMA deterministic MACs. This macro transforms tests mac(k,m) = m' into check(k, m, m'), so that the MAC verification can also be written mac(k,m) = m'. mkey: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of mac without mentioning the length of the key), typically "fixed" and "large". macinput: type of inputs of MACs macres: type of result of MACs mac: MAC function mac': symbol that replaces mac after game transformation check: verification function Pmac(t, N, N', Nu', l): probability of breaking the SUF-CMA property in time t for one key, N MAC queries, N' modified verification queries and Nu' unchanged verification queries for messages of length at most l. The types mkey, macinput, macres and the probability Pmac must be declared before this macro is expanded. The functions mac, mac', check are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def SUF_CMA_det_mac_all_args(mkey, macinput, macres, mac, mac', check, Pmac) { param N, N2, N2', N3. fun mac(macinput, mkey):macres. fun check(macinput, mkey, macres): bool. fun mac'(macinput, mkey):macres. equation forall m:macinput, k:mkey; check(m, k, mac(m, k)). equation forall m:macinput, k:mkey, m':macres; (mac(m,k) = m') = check(m, k, m'). equiv(suf_cma(mac)) k <-R mkey;( foreach i <= N do Omac(x: macinput) := return(mac(x, k)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma))) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] k <-R mkey [unchanged];( foreach i <= N do Omac(x: macinput) := let ma2:macres = mac'(x, k) in return(ma2) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false)). equiv(suf_cma_corrupt(mac)) k <-R mkey;( foreach i <= N do Omac(x: macinput) := return(mac(x, k)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do Omac(x: macinput) := let ma2:macres = mac'(x, k) in return(ma2) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check(m, k, ma)) else find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). (* Manual version, which transforms only some occurrences of verification. When you use this version, you should specify which verification terms are transformed (using Ocheck) and which ones are left unchanged (using Ocheck_unchanged). It does not rename the function symbols: there is no need to avoid a loop here since the transformation is manual, and it can be applied several times in case we want to transform a new occurrence of check. *) equiv(suf_cma_corrupt_partial(mac)) k <-R mkey;( foreach i <= N do Omac(x: macinput) := return(mac(x, k)) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, N2', max(maxlength(x), max(maxlength(m),maxlength(m')))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do Omac(x: macinput) := let ma2:macres = mac(x, k) in return(ma2) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check(m, k, ma)) else find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). } def SUF_CMA_det_mac(mkey, macinput, macres, mac, check, Pmac) { expand SUF_CMA_det_mac_all_args(mkey, macinput, macres, mac, mac', check, Pmac). } (* UF-CMA probabilistic mac mkey: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of mac without mentioning the length of the key), typically "fixed" and "large". macinput: type of inputs of MACs macres: type of result of MACs mac_seed: type of random coins for MAC (must be "bounded"; omitted in the second version of the macro). mac: MAC function that generates coins internally mac_r: MAC function that takes coins as argument (omitted in the second version of the macro). check: verification function mac_r', check': symbols that replace mac_r and check respectively after game transformation Pmac(t, N, N', Nu', l): probability of breaking the UF-CMA property in time t for one key, N MAC queries, N' modified verification queries, and Nu' unchanged verification queries for messages of length at most l. The types mkey, macinput, macres, mac_seed and the probability Pmac must be declared before this macro is expanded. The functions mac, mac_r, mac_r', check, check' are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def UF_CMA_proba_mac_all_args(mkey, macinput, macres, mac_seed, mac, mac_r, mac_r', check, check', Pmac) { param N, N2, N2', N3. fun mac_r(macinput, mkey, mac_seed):macres. fun check(macinput, mkey, macres): bool. fun mac_r'(macinput, mkey, mac_seed):macres. fun check'(macinput, mkey, macres): bool. letfun mac(m: macinput, k: mkey) = r <-R mac_seed; mac_r(m, k, r). equation forall m:macinput, k:mkey, r:mac_seed; check(m, k, mac_r(m, k, r)). equation forall m:macinput, k:mkey, r:mac_seed; check'(m, k, mac_r'(m, k, r)). equiv(uf_cma(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput):= return(mac_r(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma))) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r'(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := find j <= N suchthat defined(x[j]) && (m = x[j]) && check'(x[j], k, ma) then return(true) else return(false)). equiv(uf_cma_corrupt(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r'(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check'(m, k, ma)) else find j <= N suchthat defined(x[j]) && (m = x[j]) && check'(x[j], k, ma) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). (* Manual version, which transforms only some occurrences of verification. When you use this version, you should specify which verification terms are transformed (using Ocheck) and which ones are left unchanged (using Ocheck_unchanged). It does not rename the function symbols: there is no need to avoid a loop here since the transformation is manual, and it can be applied several times in case we want to transform a new occurrence of check. *) equiv(uf_cma_corrupt_partial(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r(x, k, r)) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, N2', max(maxlength(x), maxlength(m),maxlength(m'))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r(x, k, r)) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check(m, k, ma)) else find j <= N suchthat defined(x[j]) && (m = x[j]) && check(x[j], k, ma) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). } def UF_CMA_proba_mac(mkey, macinput, macres, mac, check, Pmac) { type mac_seed [bounded]. expand UF_CMA_proba_mac_all_args(mkey, macinput, macres, mac_seed, mac, mac_r, mac_r', check, check', Pmac). } (* SUF-CMA probabilistic mac (strongly unforgeable MAC) The difference between a UF-CMA MAC and a SUF-CMA MAC is that, for a UF-CMA MAC, the adversary may easily forge a new MAC for a message for which he has already seen a MAC. Such a forgery is guaranteed to be hard for a SUF-CMA MAC. mkey: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of mac without mentioning the length of the key), typically "fixed" and "large". macinput: type of inputs of MACs macres: type of result of MACs mac_seed: type of random coins for MAC (must be "bounded"; omitted in the second version of the macro). mac: MAC function that generates coins internally mac_r: MAC function that takes coins as argument (omitted in the second version of the macro). mac_r': symbol that replaces mac_r after game transformation check: verification function Pmac(t, N, N', Nu', l): probability of breaking the SUF-CMA property in time t for one key, N MAC queries, N' modified verification queries, Nu' unchanged verification queries for messages of length at most l. The types mkey, macinput, macres, mac_seed and the probability Pmac must be declared before this macro is expanded. The functions mac, mac_r, mac_r', check are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def SUF_CMA_proba_mac_all_args(mkey, macinput, macres, mac_seed, mac, mac_r, mac_r', check, Pmac) { param N, N2, N2', N3. fun mac_r(macinput, mkey, mac_seed):macres. fun check(macinput, mkey, macres): bool. fun mac_r'(macinput, mkey, mac_seed):macres. letfun mac(m: macinput, k: mkey) = r <-R mac_seed; mac_r(m, k, r). equation forall m:macinput, k:mkey, r:mac_seed; check(m, k, mac_r(m, k, r)). equiv(suf_cma(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput):= return(mac_r(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma))) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := let ma2:macres = mac_r'(x, k, r) in return(ma2) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false)). equiv(suf_cma_corrupt(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r(x, k, r)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := let ma2:macres = mac_r'(x, k, r) in return(ma2) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check(m, k, ma)) else find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). (* Manual version, which transforms only some occurrences of verification. When you use this version, you should specify which verification terms are transformed (using Ocheck) and which ones are left unchanged (using Ocheck_unchanged). It does not rename the function symbols: there is no need to avoid a loop here since the transformation is manual, and it can be applied several times in case we want to transform a new occurrence of check. *) equiv(suf_cma_corrupt_partial(mac)) k <-R mkey;( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := return(mac_r(x, k, r)) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(Pmac(time, N, N2, N2', max(maxlength(x), maxlength(m), maxlength(m'))))=> [manual,computational] k <-R mkey [unchanged];( foreach i <= N do r <-R mac_seed; Omac(x: macinput) := let ma2:macres = mac_r(x, k, r) in return(ma2) | foreach i2 <= N2' do Ocheck_unchanged(m': macinput, ma': macres) := return(check(m', k, ma')) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := if defined(corrupt) then return(check(m, k, ma)) else find j <= N suchthat defined(x[j], ma2[j]) && (m = x[j]) && ma = ma2[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(k)). } def SUF_CMA_proba_mac(mkey, macinput, macres, mac, check, Pmac) { type mac_seed [bounded]. expand SUF_CMA_proba_mac_all_args(mkey, macinput, macres, mac_seed, mac, mac_r, mac_r', check, Pmac). } (******************************* Public-key encryption *******************************) (* IND-CCA2 probabilistic public-key encryption keyseed: type of key seeds, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of pkgen without mentioning the length of the keyseed), typically "fixed" and "large". pkey: type of public keys, must be "bounded" skey: type of secret keys, must be "bounded" cleartext: type of cleartexts ciphertext: type of ciphertexts enc_seed: type of random coins for encryption (must be "bounded"; omitted in the second version of the macro). pkgen: public-key generation function skgen: secret-key generation function enc: encryption function that generates coins internally enc_r: encryption function that takes coins as argument (omitted in the second version of the macro). dec: decryption function pkgen2, skgen2, enc_r2, dec2: symbols that replace pkgen, skgen, enc_r, and dec respectively after game transformation injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N): probability of breaking the IND-CCA2 property in time t for one key and N decryption queries. Penccoll: probability of collision between independently generated keys The types keyseed, pkey, skey, cleartext, ciphertext, enc_seed and the probabilities Penc, Penccoll must be declared before this macro is expanded. The functions pkgen, skgen, enc, enc_r, dec, pkgen2, skgen2, enc_r2, dec2, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def IND_CCA2_public_key_enc_all_args(keyseed, pkey, skey, cleartext, ciphertext, enc_seed, skgen, skgen2, pkgen, pkgen2, enc, enc_r, enc_r2, dec, dec2, injbot, Z, Penc, Penccoll) { param N, N', N2, N3, N4. fun enc_r(cleartext, pkey, enc_seed): ciphertext. fun skgen(keyseed):skey. fun pkgen(keyseed):pkey. fun dec(ciphertext, skey): bitstringbot. fun enc_r2(cleartext, pkey, enc_seed): ciphertext. fun skgen2(keyseed):skey. fun pkgen2(keyseed):pkey. fun dec2(ciphertext, skey): bitstringbot. letfun enc(m: cleartext, pk: pkey) = r <-R enc_seed; enc_r(m,pk,r). fun injbot(cleartext):bitstringbot [data]. (* The function Z returns for each bitstring, a bitstring of the same length, consisting only of zeroes. *) fun Z(cleartext):cleartext. equation forall m:cleartext, r:keyseed, r2:enc_seed; dec(enc_r(m, pkgen(r), r2), skgen(r)) = injbot(m). equation forall m:cleartext, r:keyseed, r2:enc_seed; dec2(enc_r2(m, pkgen2(r), r2), skgen2(r)) = injbot(m). table cipher(N3,cleartext,ciphertext). equiv(ind_cca2(enc)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [2] := return(pkgen(r)) | foreach i2 <= N2 do Odec(m:ciphertext) := return(dec(m, skgen(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) := return(enc_r(x1, pkgen(r),r1))) | foreach i4 <= N4 do r2 <-R enc_seed; Oenc2(x:cleartext, y:pkey) [3] := return(enc_r(x,y,r2)) [all] <=(N3 * (N + N4) * Penc(time + (N4+N-1) * time(enc_r, max(maxlength(x),maxlength(x1))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(dec, maxlength(m)) + N * time(enc_r, max(maxlength(x),maxlength(x1)))), N2))=> foreach i3 <= N3 do r <-R keyseed; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do Odec(m:ciphertext) := get cipher(=i3, x, =m) in return(injbot(x)) else return(dec2(m, skgen2(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) := m1 <- enc_r2(Z(x1), pkgen2(r), r1); insert cipher(i3, x1, m1); return(m1)) | foreach i4 <= N4 do Oenc2(x:cleartext, y:pkey) := find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then (r2 <-R enc_seed; m2 <- enc_r2(Z(x), y, r2); insert cipher(k, x, m2); return(m2)) else r3 <-R enc_seed; return(enc_r(x,y,r3)). (* Manual version, which can transform only some occurrences of encryption: just use oracle Opk to discharge the public key in occurrences of encryption that you do not want to transform. It renames only the encryption function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) table cipher1(cleartext, ciphertext). equiv(ind_cca2_partial(enc)) r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Odec(m:ciphertext) := return(dec(m, skgen(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) [useful_change] := return(enc_r(x1, pkgen(r),r1))) <=(N * Penc(time + (N-1) * time(enc_r, maxlength(x1)), N2))=> [manual] r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Odec(m:ciphertext) := get cipher1(x, =m) in return(injbot(x)) else return(dec(m, skgen(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) := m1 <- enc_r2(Z(x1), pkgen(r), r1); insert cipher1(x1, m1); return(m1)). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen(r2)) <=(Penccoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen2(r2)) <=(Penccoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen2(r1) = pkgen2(r2)) <=(Penccoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen(r2)) <=(Penccoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen2(r2)) <=(Penccoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen2(r1) = skgen2(r2)) <=(Penccoll)=> return(false). } def IND_CCA2_public_key_enc(keyseed, pkey, skey, cleartext, ciphertext, skgen, pkgen, enc, dec, injbot, Z, Penc, Penccoll) { type enc_seed [bounded]. expand IND_CCA2_public_key_enc_all_args(keyseed, pkey, skey, cleartext, ciphertext, enc_seed, skgen, skgen2, pkgen,pkgen2, enc, enc_r, enc_r2, dec, dec2, injbot, Z, Penc, Penccoll). } (*************************************** Signatures ******************************) (* UF-CMA deterministic signatures keyseed: type of key seeds, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of pkgen without mentioning the length of the keyseed), typically "fixed" and "large". pkey: type of public keys, must be "bounded" skey: type of secret keys, must be "bounded" signinput: type of inputs of signatures signature: type of signatures pkgen: public-key generation function skgen: secret-key generation function sign: signature function check: verification function pkgen2, skgen2, sign2, check2: symbols that replace pkgen, skgen, sign, and check respectively after game transformation Psign(t, N, l): probability of breaking the UF-CMA property in time t, for one key, N signature queries with messages of length at most l. Psigncoll: probability of collision between independently generated keys The types keyseed, pkey, skey, signinput, signature and the probabilities Psign, Psigncoll must be declared before this macro is expanded. The functions pkgen, skgen, sign, check, pkgen2, skgen2, sign2, check2 are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def UF_CMA_det_signature_all_args(keyseed, pkey, skey, signinput, signature, skgen, skgen2, pkgen, pkgen2, sign, sign2, check, check2, Psign, Psigncoll) { param N, N2, N3, N4. fun sign(signinput, skey): signature. fun skgen(keyseed):skey. fun pkgen(keyseed):pkey. fun check(signinput, pkey, signature): bool. fun sign2(signinput, skey): signature. fun skgen2(keyseed):skey. fun pkgen2(keyseed):pkey. fun check2(signinput, pkey, signature): bool. equation forall m:signinput, r:keyseed; check(m, pkgen(r), sign(m, skgen(r))) = true. equation forall m:signinput, r:keyseed; check2(m, pkgen2(r), sign2(m, skgen2(r))) = true. equiv(uf_cma(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [2] := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := return(check(m1, pkgen(r), si1))) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m1), maxlength(m)), max(maxlength(si1), maxlength(si))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign2(x, skgen2(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen2(r), si1) then return(true) else return(false)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find j <= N2, k <= N3 suchthat defined(x[j,k],r[k]) && y = pkgen2(r[k]) && m = x[j,k] && check2(m, y, si) then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). equiv(uf_cma_corrupt(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [useful_change] [2] := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) [useful_change] := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m1), maxlength(m)), max(maxlength(si1), maxlength(si))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [manual,computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign2(x, skgen2(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen2(r), si1)) else find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen2(r), si1) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find k <= N3 suchthat defined(r[k],corrupt[k]) && y = pkgen2(r[k]) then return(check2(m, y, si)) else find j <= N2, k <= N3 suchthat defined(x[j,k],r[k]) && y = pkgen2(r[k]) && m = x[j,k] && check2(m, y, si) then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). (* Manual version, which can transform only some occurrences of verification: just use oracle Opk to discharge the public key in occurrences of verification that you do not want to transform. It renames only the check function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(uf_cma_corrupt_partial(sign)) r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) [useful_change] := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) <=(Psign(time + (N-1) * time(check, maxlength(m1), maxlength(si1)), N2, maxlength(x)))=> [manual,computational] r <-R keyseed [unchanged]; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen(r), si1)) else find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen(r), si1) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen2(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen2(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). } def UF_CMA_det_signature(keyseed, pkey, skey, signinput, signature, skgen, pkgen, sign, check, Psign, Psigncoll) { expand UF_CMA_det_signature_all_args(keyseed, pkey, skey, signinput, signature, skgen, skgen2, pkgen, pkgen2, sign, sign2, check, check2, Psign, Psigncoll). } (* SUF-CMA deterministic signatures keyseed: type of key seeds, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of pkgen without mentioning the length of the keyseed), typically "fixed" and "large". pkey: type of public keys, must be "bounded" skey: type of secret keys, must be "bounded" signinput: type of inputs of signatures signature: type of signatures pkgen: public-key generation function skgen: secret-key generation function sign: signature function check: verification function pkgen2, skgen2, sign2, check2: symbols that replace pkgen, skgen, sign, and check respectively after game transformation Psign(t, N, l): probability of breaking the SUF-CMA property in time t, for one key, N signature queries with messages of length at most l. Psigncoll: probability of collision between independently generated keys The types keyseed, pkey, skey, signinput, signature and the probabilities Psign, Psigncoll must be declared before this macro is expanded. The functions pkgen, skgen, sign, check, pkgen2, skgen2, sign2, check2 are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def SUF_CMA_det_signature_all_args(keyseed, pkey, skey, signinput, signature, skgen, skgen2, pkgen, pkgen2, sign, sign2, check, check2, Psign, Psigncoll) { param N, N2, N3, N4. fun sign(signinput, skey): signature. fun skgen(keyseed):skey. fun pkgen(keyseed):pkey. fun check(signinput, pkey, signature): bool. fun sign2(signinput, skey): signature. fun skgen2(keyseed):skey. fun pkgen2(keyseed):pkey. fun check2(signinput, pkey, signature): bool. equation forall m:signinput, r:keyseed; check(m, pkgen(r), sign(m, skgen(r))) = true. equation forall m:signinput, r:keyseed; check2(m, pkgen2(r), sign2(m, skgen2(r))) = true. equiv(suf_cma(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [2] := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := return(check(m1, pkgen(r), si1))) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m), maxlength(m1)), max(maxlength(si), maxlength(si1))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do Osign(x: signinput) := let s: signature = sign2(x, skgen2(r)) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find j <= N2, k <= N3 suchthat defined(x[j,k],r[k],s[j,k]) && y = pkgen2(r[k]) && m = x[j,k] && si = s[j,k] then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). equiv(suf_cma_corrupt(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [useful_change] [2] := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) [useful_change] := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m), maxlength(m1)), max(maxlength(si), maxlength(si1))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [manual,computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do Osign(x: signinput) := let s: signature = sign2(x, skgen2(r)) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen2(r), si1)) else find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find k <= N3 suchthat defined(r[k],corrupt[k]) && y = pkgen2(r[k]) then return(check2(m, y, si)) else find j <= N2, k <= N3 suchthat defined(x[j,k],r[k],s[j,k]) && y = pkgen2(r[k]) && m = x[j,k] && si = s[j,k] then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). (* Manual version, which can transform only some occurrences of verification: just use oracle Opk to discharge the public key in occurrences of verification that you do not want to transform. It renames only the check function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(suf_cma_corrupt_partial(sign)) r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) := return(sign(x, skgen(r))) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) <=(Psign(time + (N-1) * time(check, maxlength(m1), maxlength(si1)), N2, maxlength(x)))=> [manual,computational] r <-R keyseed [unchanged]; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Osign(x: signinput) := let s: signature = sign(x, skgen(r)) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen(r), si1)) else find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen2(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen2(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). } def SUF_CMA_det_signature(keyseed, pkey, skey, signinput, signature, skgen, pkgen, sign, check, Psign, Psigncoll) { expand SUF_CMA_det_signature_all_args(keyseed, pkey, skey, signinput, signature, skgen, skgen2, pkgen, pkgen2, sign, sign2, check, check2, Psign, Psigncoll). } (* UF-CMA probabilistic signatures keyseed: type of key seeds, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of pkgen without mentioning the length of the keyseed), typically "fixed" and "large". pkey: type of public keys, must be "bounded" skey: type of secret keys, must be "bounded" signinput: type of inputs of signatures signature: type of signatures sign_seed: type of random coins for signature (must be "bounded"; omitted in the second version of the macro). pkgen: public-key generation function skgen: secret-key generation function sign: signature function that generates coins internally sign_r: signature function that takes coins as argument (omitted in the second version of the macro). check: verification function pkgen2, skgen2, sign_r2, check2: symbols that replace pkgen, skgen, sign, and check respectively after game transformation Psign(t, N, l): probability of breaking the UF-CMA property in time t, for one key, N signature queries with messages of length at most l. Psigncoll: probability of collision between independently generated keys The types keyseed, pkey, skey, signinput, signature, seed and the probabilities Psign, Psigncoll must be declared before this macro is expanded. The functions pkgen, skgen, sign, check, pkgen2, skgen2, sign_r2, check2 are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def UF_CMA_proba_signature_all_args(keyseed, pkey, skey, signinput, signature, sign_seed, skgen, skgen2, pkgen, pkgen2, sign, sign_r, sign_r2, check, check2, Psign, Psigncoll) { param N, N2, N3, N4. fun sign_r(signinput, skey, sign_seed): signature. fun skgen(keyseed):skey. fun pkgen(keyseed):pkey. fun check(signinput, pkey, signature): bool. fun sign_r2(signinput, skey, sign_seed): signature. fun skgen2(keyseed):skey. fun pkgen2(keyseed):pkey. fun check2(signinput, pkey, signature): bool. letfun sign(m: signinput, sk: skey) = r <-R sign_seed; sign_r(m,sk,r). equation forall m:signinput, r:keyseed, r2:sign_seed; check(m, pkgen(r), sign_r(m, skgen(r), r2)) = true. equation forall m:signinput, r:keyseed, r2:sign_seed; check2(m, pkgen2(r), sign_r2(m, skgen2(r), r2)) = true. equiv(uf_cma(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [2] := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := return(check(m1, pkgen(r), si1))) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m1), maxlength(m)), max(maxlength(si1), maxlength(si))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign_r, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := return(sign_r2(x, skgen2(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen2(r), si1) then return(true) else return(false)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find j <= N2, k <= N3 suchthat defined(x[j,k],r[k]) && y = pkgen2(r[k]) && m = x[j,k] && check2(m, y, si) then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). equiv(uf_cma_corrupt(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [useful_change] [2] := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) [useful_change] := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m1), maxlength(m)), max(maxlength(si1), maxlength(si))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign_r, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [manual,computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := return(sign_r2(x, skgen2(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen2(r), si1)) else find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen2(r), si1) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find k <= N3 suchthat defined(r[k],corrupt[k]) && y = pkgen2(r[k]) then return(check2(m, y, si)) else find j <= N2, k <= N3 suchthat defined(x[j,k],r[k]) && y = pkgen2(r[k]) && m = x[j,k] && check2(m, y, si) then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). (* Manual version, which can transform only some occurrences of verification: just use oracle Opk to discharge the public key in occurrences of verification that you do not want to transform. It renames only the check function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(uf_cma_corrupt_partial(sign)) r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) <=(Psign(time + (N-1) * time(check, maxlength(m1), maxlength(si1)), N2, maxlength(x)))=> [manual,computational] r <-R keyseed [unchanged]; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen(r), si1)) else find j <= N2 suchthat defined(x[j]) && m1 = x[j] && check2(m1, pkgen(r), si1) then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen2(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen2(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). } def UF_CMA_proba_signature(keyseed, pkey, skey, signinput, signature, skgen, pkgen, sign, check, Psign, Psigncoll) { type sign_seed [bounded]. expand UF_CMA_proba_signature_all_args(keyseed, pkey, skey, signinput, signature, sign_seed, skgen, skgen2, pkgen, pkgen2, sign, sign_r, sign_r2, check, check2, Psign, Psigncoll). } (* SUF-CMA probabilistic signatures keyseed: type of key seeds, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of pkgen without mentioning the length of the keyseed), typically "fixed" and "large". pkey: type of public keys, must be "bounded" skey: type of secret keys, must be "bounded" signinput: type of inputs of signatures signature: type of signatures sign_seed: type of random coins for signature (must be "bounded"; omitted in the second version of the macro). pkgen: public-key generation function skgen: secret-key generation function sign: signature function that generates coins internally sign_r: signature function that takes coins as argument (omitted in the second version of the macro). check: verification function pkgen2, skgen2, sign_r2, check2: symbols that replace pkgen, skgen, sign, and check respectively after game transformation Psign(t, N, l): probability of breaking the SUF-CMA property in time t, for one key, N signature queries with messages of length at most l. Psigncoll: probability of collision between independently generated keys The types keyseed, pkey, skey, signinput, signature, seed and the probabilities Psign, Psigncoll must be declared before this macro is expanded. The functions pkgen, skgen, sign, check, pkgen2, skgen2, sign_r2, check2 are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def SUF_CMA_proba_signature_all_args(keyseed, pkey, skey, signinput, signature, sign_seed, skgen, skgen2, pkgen, pkgen2, sign, sign_r, sign_r2, check, check2, Psign, Psigncoll) { param N, N2, N3, N4. fun sign_r(signinput, skey, sign_seed): signature. fun skgen(keyseed):skey. fun pkgen(keyseed):pkey. fun check(signinput, pkey, signature): bool. fun sign_r2(signinput, skey, sign_seed): signature. fun skgen2(keyseed):skey. fun pkgen2(keyseed):pkey. fun check2(signinput, pkey, signature): bool. letfun sign(m: signinput, sk: skey) = r <-R sign_seed; sign_r(m,sk,r). equation forall m:signinput, r:keyseed, r2:sign_seed; check(m, pkgen(r), sign_r(m, skgen(r), r2)) = true. equation forall m:signinput, r:keyseed, r2:sign_seed; check2(m, pkgen2(r), sign_r2(m, skgen2(r), r2)) = true. equiv(suf_cma(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [2] := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := return(check(m1, pkgen(r), si1))) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m), maxlength(m1)), max(maxlength(si), maxlength(si1))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign_r, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := let s: signature = sign_r2(x, skgen2(r), r2) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find j <= N2, k <= N3 suchthat defined(x[j,k],r[k],s[j,k]) && y = pkgen2(r[k]) && m = x[j,k] && si = s[j,k] then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). equiv(suf_cma_corrupt(sign)) foreach i3 <= N3 do r <-R keyseed; ( Opk() [useful_change] [2] := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) [useful_change] := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) [3] := return(check(m, y, si)) [all] <=(N3 * Psign(time + (N4+N-1) * time(check, max(maxlength(m), maxlength(m1)), max(maxlength(si), maxlength(si1))) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(sign_r, maxlength(x)) + N * time(check, maxlength(m1), maxlength(si1))), N2, maxlength(x)))=> [manual,computational] foreach i3 <= N3 do r <-R keyseed [unchanged]; ( Opk() := return(pkgen2(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := let s: signature = sign_r2(x, skgen2(r), r2) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen2(r), si1)) else find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)) | foreach i4 <= N4 do Ocheck2(m: signinput, y: pkey, si: signature) := find k <= N3 suchthat defined(r[k],corrupt[k]) && y = pkgen2(r[k]) then return(check2(m, y, si)) else find j <= N2, k <= N3 suchthat defined(x[j,k],r[k],s[j,k]) && y = pkgen2(r[k]) && m = x[j,k] && si = s[j,k] then return(true) else find k <= N3 suchthat defined(r[k]) && y = pkgen2(r[k]) then return(false) else return(check(m,y,si)). (* Manual version, which can transform only some occurrences of verification: just use oracle Opk to discharge the public key in occurrences of verification that you do not want to transform. It renames only the check function symbol for the occurrences that are transformed, so it can be applied again to other occurrences. *) equiv(suf_cma_corrupt_partial(sign)) r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed; Osign(x: signinput) := return(sign_r(x, skgen(r), r2)) | foreach i <= N do Ocheck(m1: signinput, si1:signature) [useful_change] := return(check(m1, pkgen(r), si1)) | Ocorrupt() [10] := return(r)) <=(Psign(time + (N-1) * time(check, maxlength(m1), maxlength(si1)), N2, maxlength(x)))=> [manual,computational] r <-R keyseed [unchanged]; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do r2 <-R sign_seed [unchanged]; Osign(x: signinput) := let s: signature = sign_r(x, skgen(r), r2) in return(s) | foreach i <= N do Ocheck(m1: signinput, si1:signature) := if defined(corrupt) then return(check2(m1, pkgen(r), si1)) else find j <= N2 suchthat defined(x[j],s[j]) && m1 = x[j] && si1 = s[j] then return(true) else return(false) | Ocorrupt() := let corrupt: bool = true in return(r)). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(pkgen2(r1) = pkgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). collision r1 <-R keyseed; r2 <-R keyseed; return(skgen2(r1) = skgen2(r2)) <=(Psigncoll)=> return(false). } def SUF_CMA_proba_signature(keyseed, pkey, skey, signinput, signature, skgen, pkgen, sign, check, Psign, Psigncoll) { type sign_seed [bounded]. expand SUF_CMA_proba_signature_all_args(keyseed, pkey, skey, signinput, signature, sign_seed, skgen, skgen2, pkgen, pkgen2, sign, sign_r, sign_r2, check, check2, Psign, Psigncoll). } (******************************** Diffie-Hellman ***************************) (* DH_basic defines a part common for most Diffie-Hellman assumptions. G: type of group elements (must be "bounded" or "nonuniform", and "large") Z: type of exponents (must be "bounded" or "nonuniform", and "large") g: generator exp: the exponentiation function exp': symbol that replaces exp after game transformation mult: the multiplication function for exponents The types G and Z must be declared before expanding this macro. The functions g, exp, exp', and mult are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def DH_basic(G, Z, g, exp, exp', mult) { fun exp(G,Z): G. fun exp'(G,Z): G. const g:G. fun mult(Z,Z): Z. equation builtin commut(mult). (* exponents multiply *) equation forall a:G, x:Z, y:Z; exp(exp(a,x), y) = exp(a, mult(x,y)). equation forall a:G, x:Z, y:Z; exp'(exp'(a,x), y) = exp'(a, mult(x,y)). } (* DH_basic_with_is_neutral(G, Z, g, exp, exp', mult, is_neutral) defines a Diffie-Hellman structure like DH_basic(G, Z, g, exp, exp', mult) with additionally * for X in G, is_neutral(X^y) if and only if is_neutral(X); * not(is_neutral(g)). is_neutral(G):bool is defined by this macro. It must not be declared elsewhere, and can be used only after expanding the macro. Prime-order groups with the neutral element included satisfy this assumption, for instance. *) def DH_basic_with_is_neutral(G, Z, g, exp, exp', mult, is_neutral) { expand DH_basic(G, Z, g, exp, exp', mult). fun is_neutral(G):bool. equation is_neutral(g) = false. equation forall x:G, y:Z; is_neutral(exp(x,y)) = is_neutral(x). equation forall x:G, y:Z; is_neutral(exp'(x,y)) = is_neutral(x). } (* DH_subgroup(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG) defines a Diffie-Hellman structure that satisfies the following properties: Z is a set of integers multiple of k, prime to n (possibly modulo kn). k is prime to n. G is a set of elements g is an element of G There is an exponentiation function ^: for X in G, y integer, we have X^y in G. with the following properties: * (X^x)^y = X^(xy) * for X in subG, for any x prime to n, X^x = X'^x => X = X' * ^ yields the same results for exponents equal modulo kn. mult(Z,Z):Z is the product of integers. subG = { X^k | X \in G } is a subset of G. g_k = g^k \in subG exp(G,Z):G exp(X,y) = X^y exp_div_k(subG,Z):subG exp_div_k(X,y) = X^{y/k} exp_div_k' is defined like exp_div_k; it replaces exp_div_k after games transformations. pow_k(G):subG pow_k(X) = X^k subGtoG(subG):G is the injection from subG to G The types G, Z, and subG must be declared before expanding this macro. The constants g and g_k, and the functions exp, mult, exp_div_k, exp_div_k', pow_k, subGtoG are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. Curve25519 satisfies these properties with k = 8, n = pp' where the curve has order kp and the quadratic twist has order k'p' (k' = 4). See https://hal.inria.fr/hal-02100345 Curve448 does not satisfy these properties because kp may be chosen as a private key and it is not prime to n = pp' where the curve has order kp and the quadratic twist has order k'p' (k = k' = 4) (weak private key). In order to use DH_subgroup, one should exclude the weak private key using DH_exclude_weak_keys as follows: expand DH_subgroup(G, Znw, g, expnw, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG). letproba Pweak_key = 2^-445. expand DH_exclude_weak_keys(G, Z, Znw, ZnwtoZ, exp, expnw, Pweak_key). Groups of prime order p also satisfy these properties, with k = 1, n = p, subG = G, g_k = g, pow_k and subGtoG are the identity (assuming private keys are chosen in [1,p-1]). *) def DH_subgroup(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG) { fun mult(Z,Z): Z. equation builtin commut(mult). fun pow_k(G):subG. fun subGtoG(subG): G [data]. equation forall x:subG, x':subG; (pow_k(subGtoG(x)) = pow_k(subGtoG(x'))) = (x = x'). const g: G. const g_k:subG. equation pow_k(g) = g_k. fun exp_div_k(subG,Z): subG. fun exp_div_k'(subG,Z): subG. (* ((X^(y/k))^k)^(z/k) = X^(y.z/k) Used when expanding exp(exp(g,y),z). *) equation forall X:subG, y:Z, z:Z; exp_div_k(pow_k(subGtoG(exp_div_k(X,y))), z) = exp_div_k(X, mult(y,z)). equation forall X:subG, y:Z, z:Z; exp_div_k'(pow_k(subGtoG(exp_div_k'(X,y))), z) = exp_div_k'(X, mult(y,z)). equation forall x:subG, x':subG, y:Z; (exp_div_k(x,y) = exp_div_k(x',y)) = (x = x'). equation forall x:subG, x':subG, y:Z; (exp_div_k'(x,y) = exp_div_k'(x',y)) = (x = x'). (* exp_div_k(g_k, mult(x',y)) = exp_div_k'(X',y) where X' = pow_k(subGtoG(exp_div_k(g_k,x')), we can show the next equalities using the previous ones *) equation forall x:subG, x':Z, y:Z; (exp_div_k(x,y) = exp_div_k(g_k, mult(x',y))) = (x = pow_k(subGtoG(exp_div_k(g_k,x')))). equation forall x:subG, x':Z, y:Z; (exp_div_k'(x,y) = exp_div_k'(g_k, mult(x',y))) = (x = pow_k(subGtoG(exp_div_k'(g_k,x')))). fun exp(G, Z): G. (* X^y = (X^k)^(y/k) *) equation forall X:G, y:Z; exp(X,y) = subGtoG(exp_div_k(pow_k(X),y)). } (* DH_subgroup_with_is_neutral(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_neutral_G, is_neutral_subG) defines a Diffie-Hellman structure with the same properties as DH_subgroup(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG) and additionally * for X in subG, is_neutral(X^y) if and only if is_neutral(X); * not(is_neutral(g^k)). is_neutral_G(G):bool is_neutral_subG(subG):bool correspond to the function is_neutral, respectively on G and on subG. These functions are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. Curve22519 satisfies these properties with is_neutral(X) = (X = 0). Curve448 also satisfies these properties with is_neutral(X) = (X = 0), after removing the weak private key as follows: expand DH_subgroup_with_is_neutral(G, Znw, g, expnw, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_neutral_G, is_neutral_subG). letproba Pweak_key = 2^-445. expand DH_exclude_weak_keys(G, Z, Znw, ZnwtoZ, exp, expnw, Pweak_key). Prime order groups satisfy these properties with is_neutral(X) = false. *) def DH_subgroup_with_is_neutral(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_neutral_G, is_neutral_subG) { expand DH_subgroup(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG). fun is_neutral_G(G):bool. fun is_neutral_subG(subG):bool. equation forall X:subG; is_neutral_G(subGtoG(X)) = is_neutral_subG(X). equation is_neutral_subG(g_k) = false. equation forall x:subG, y:Z; is_neutral_subG(exp_div_k(x,y)) = is_neutral_subG(x). equation forall x:subG, y:Z; is_neutral_subG(exp_div_k'(x,y)) = is_neutral_subG(x). equation forall x:subG; is_neutral_subG(pow_k(subGtoG(x))) = is_neutral_subG(x). } (* DH_exclude_weak_keys allows excluding weak private keys. Z is a set of Diffie-Hellman private keys (exponents), possibly containing weak private keys Znw is the subset of Z obtained by removing weak keys. ZnwtoZ is the injection from Znw to Z. Pweak_key is the probability that a weak private key is chosen. This macro defines an equivalence exclude_weak_keys(Z) which replaces the choice of private keys in Z with a choice in Znw, so that there are no weak private keys. It should be applied early in the proof, before applying Diffie-Hellman properties. The type of exponentiation functions is a follows: exp(G,Z):G expnw(G,Znw):G The types G, Z, Znw, the function expnw, and the probability Pweak_key must be declared before expanding this macro. (The function expnw should be defined by expanding one of the macros DH_basic, DH_subgroup, or DH_good_group with Znw instead of Z and expnw instead of exp.) The functions ZnwtoZ and exp are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. This is useful for Curve448 which has a weak key kp, with k = 4 where the curve has order kp, so Znw = Z \ { kp }, Pweak_key = 2^-445. This is also useful for groups of prime order p in case private keys are chosen in [0,p-1]: one should eliminate the weak private key 0, so Z = [0,p-1], Znw = [1,p-1], Pweak_key = 1/p. *) def DH_exclude_weak_keys(G, Z, Znw, ZnwtoZ, exp, expnw, Pweak_key) { fun ZnwtoZ(Znw): Z [data]. param N. equiv(exclude_weak_keys(Z)) foreach i<=N do x <-R Z; O() := return(x) <=(N*Pweak_key)=> foreach i<=N do x <-R Znw; O() := return(ZnwtoZ(x)). fun exp(G, Z): G. equation forall X:G, y:Znw; exp(X,ZnwtoZ(y)) = expnw(X,y). equation forall X:G, y:Z, z:Znw; expnw(exp(X,y),z) = exp(expnw(X,z),y). } (* DH_proba_collision says that the probability that exp(g, x) = Y for random x and Y independent of x is at most PCollKey1, and the probability that exp(g, mult(x,y)) = Y where x and y are independent random private keys and Y is independent of x or y is at most PCollKey2. Note: we do not bound the probability that exp(g, mult(x,y)) = Y with random x and with y and Y independent of x, because this probability may be high when y is a weak private key. Moreover, exponents (such as y) are always chosen randomly, so the properties above should be sufficient. The other arguments are as in DH_basic. *) def DH_proba_collision(G, Z, g, exp, exp', mult, PCollKey1, PCollKey2) { collision x <-R Z; forall Y: G; return(exp(g, x) = Y) <=(PCollKey1)=> return(false) if Y independent-of x. collision x <-R Z; forall Y: G; return(exp'(g, x) = Y) <=(PCollKey1)=> return(false) if Y independent-of x. collision x <-R Z; y <-R Z; forall Y: G; return(exp(g, mult(x,y)) = Y) <=(PCollKey2)=> return(false) if Y independent-of x || Y independent-of y. collision x <-R Z; y <-R Z; forall Y: G; return(exp'(g, mult(x,y)) = Y) <=(PCollKey2)=> return(false) if Y independent-of x || Y independent-of y. } (* square_DH_proba_collision is similar to DH_proba_collision, but additionally says that the probability that exp(g, mult(x,x)) = Y with random x and Y independent of x is at most PCollKey3, with PCollKey3 >= PCollKey2. *) def square_DH_proba_collision(G, Z, g, exp, exp', mult, PCollKey1, PCollKey2, PCollKey3) { expand DH_proba_collision(G, Z, g, exp, exp', mult, PCollKey1, PCollKey2). (* The option "random_choices_may_be_equal" allows x and y to be the same random choice (or not). It still makes sure that the array indices of x and y do not depend on x and y. Hence this collision groups two cases: - x and y are different random choices: exp(g, mult(x,y)) = Y with y and Y independent of x or x and Y independent of y, already seen in DH_proba_collision; ok because PCollKey3 >= PCollKey2 - x and y are the same random choice: exp(g, mult(x,x)) = Y with Y independent of x *) collision x <-R Z; y <-R Z; [random_choices_may_be_equal] forall Y: G; return(exp(g, mult(x,y)) = Y) <=(PCollKey3)=> return(false) if Y independent-of x || Y independent-of y. collision x <-R Z; y <-R Z; [random_choices_may_be_equal] forall Y: G; return(exp'(g, mult(x,y)) = Y) <=(PCollKey3)=> return(false) if Y independent-of x || Y independent-of y. (* The next collisions are consequences of the previous one, though this requires some reasoning. The random choices x1,y1,x2,y2 may be either independent of each other or equal (because the option "random_choices_may_be_equal" still makes sure that the array indices of x1,y1,x2,y2 do not depend on x1,y1,x2,y2). Hence x1 independent-of x2 actually implies that x2 is also independent of x1. The condition (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2) yields 4 cases: 1/ x1 independent-of x2 and x1 independent-of y2 x2 and y2 independent of x1, so exp(g, mult(x2,y2)) independent of x1 Apply previous collision with Y = exp(g, mult(x2,y2)) 2/ x1 independent-of x2 and y1 independent-of x2 x1 and y1 independent of x2, so exp(g, mult(x1,y1)) independent of x2 Apply previous collision with Y = exp(g, mult(x1,y1)) 3/ y1 independent-of y2 and x1 independent-of y2 x1 and y1 independent of y2, so exp(g, mult(x1,y1)) independent of y2 Apply previous collision with Y = exp(g, mult(x1,y1)) 4/ y1 independent-of y2 and y1 independent-of x2 x2 and y2 independent of y1, so exp(g, mult(x2,y2)) independent of y1 Apply previous collision with Y = exp(g, mult(x2,y2)) The formulation below allows CryptoVerif to replace exp(g, mult(x1,y1)) = exp(g, mult(x2,y2)) with a condition corresponding to (x1 = x2 && y1 = y2) || (x1 = y2 && y1 = x2) where = here means that they are the same random choice (i.e. same variable with the same indices). This condition is the negation of the independence condition (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). It corresponds to the only case that makes the equality succeed with non-negligible probability. *) collision x1 <-R Z; y1 <-R Z; x2 <-R Z; y2 <-R Z; [random_choices_may_be_equal] return(exp(g, mult(x1,y1)) = exp(g, mult(x2,y2))) <=(PCollKey3)=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x1 <-R Z; y1 <-R Z; x2 <-R Z; y2 <-R Z; [random_choices_may_be_equal] return(exp'(g, mult(x1,y1)) = exp'(g, mult(x2,y2))) <=(PCollKey3)=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). } (* is_neutral_DH_proba_collision is meant to be used with DH_subgroup_with_is_neutral as follows: expand DH_subgroup_with_is_neutral(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_neutral_G, is_neutral_subG). expand is_neutral_DH_proba_collision(subG, Z, g_k, exp_div_k, exp_div_k', mult, is_neutral_subG, PCollKey2, PCollKey3, PCollKey4). or with DH_basic_with_is_neutral as follows: expand DH_basic_with_is_neutral(G, Z, g, exp, exp', mult, is_neutral). expand is_neutral_DH_proba_collision(G, Z, g, exp, exp', mult, is_neutral, PCollKey2, PCollKey3, PCollKey4). In both cases, naming the arguments of is_neutral_DH_proba_collision as follows: is_neutral_DH_proba_collision(G, Z, g, exp, exp', mult, is_neutral, PCollKey2, PCollKey3, PCollKey4) we have * mult is commutative; * if X = g^x, then exp(X, y) = exp(g, mult(x,y)); * for X in G, is_neutral(X^y) if and only if is_neutral(X); * for X in G, is_neutral(exp(X,y)) if and only if is_neutral(X); * not(is_neutral(g)). is_neutral_DH_proba_collision assumes the following properties: - if is_neutral(X) and is_neutral(Y) then X = Y (in other words, there is 0 or 1 neutral element in G); - the probability that exp(X,x) = Y where x is random and X and Y are independent of x and not neutral is at most PCollKey2; - the probability that exp(g, mult(x,x)) = Y with random x and Y independent of x is at most PCollKey3, with PCollKey3 >= PCollKey2. - the probability that exp(X,y) = exp(X,z) with y,z random independent of each other and X is not neutral is at most PCollKey4. *) def is_neutral_DH_proba_collision(G, Z, g, exp, exp', mult, is_neutral, PCollKey2, PCollKey3, PCollKey4) { (* The hypothesis implies that - the probability that exp(g, x) = Y for random x and Y independent of x is at most PCollKey2 (using the probability that exp(X,x) = Y with X = g not neutral and independent of x; the equality can hold only when Y is not neutral); - the probability that exp(g, mult(x,y)) = Y where x and y are independent random private keys and Y is independent of x or y is at most PCollKey2 (assuming Y is independent of x, we take X = g^y, not neutral and independent of x and use the probability that exp(X,x) = Y; the equality can hold only when Y is not neutral; the other case is symmetric). - the probability that exp(g, mult(x,x)) = Y with random x and Y independent of x is at most PCollKey3, with PCollKey3 >= PCollKey2. *) expand square_DH_proba_collision(G, Z, g, exp, exp', mult, PCollKey2, PCollKey2, PCollKey3). (* This collision groups 2 cases: - y and z are the same random choice: both sides are true - y and z are independent random choices: implied by hypothesis *) collision y <-R Z; z <-R Z; [random_choices_may_be_equal] forall X: G; return(exp(X, y) = exp(X, z)) <=(PCollKey4)=> return(is_neutral(X) || (y = z)). collision y <-R Z; z <-R Z; [random_choices_may_be_equal] forall X: G; return(exp'(X, y) = exp'(X, z)) <=(PCollKey4)=> return(is_neutral(X) || (y = z)). (* The next collision is a consequence of the previous one by taking X = g^x. *) collision x <-R Z; y <-R Z; y' <-R Z; [random_choices_may_be_equal] return(exp(g,mult(x,y)) = exp(g,mult(x,y'))) <=(PCollKey4)=> return(y = y'). collision x <-R Z; y <-R Z; y' <-R Z; [random_choices_may_be_equal] return(exp'(g,mult(x,y)) = exp'(g,mult(x,y'))) <=(PCollKey4)=> return(y = y'). collision x <-R Z; forall X: G, Y: G; return(exp(X, x) = Y) <=(PCollKey2)=> return(is_neutral(X) && is_neutral(Y)) if X independent-of x && Y independent-of x. collision x <-R Z; forall X: G, Y: G; return(exp'(X, x) = Y) <=(PCollKey2)=> return(is_neutral(X) && is_neutral(Y)) if X independent-of x && Y independent-of x. (* The next collision is a consequence of the previous one by taking X = g^y *) collision x <-R Z; forall y: Z, Y: G; return(exp(g,mult(x,y)) = Y) <=(PCollKey2)=> return(false) if y independent-of x && Y independent-of x. collision x <-R Z; forall y: Z, Y: G; return(exp'(g,mult(x,y)) = Y) <=(PCollKey2)=> return(false) if y independent-of x && Y independent-of x. (* The next collision is a consequence of the previous one collision x <-R Z; y <-R Z; forall Y: G; return(exp(g,mult(x,y)) = Y) <=(PCollKey2)=> return(false) if Y independent-of x || Y independent-of y. collision x <-R Z; y <-R Z; forall Y: G; return(exp'(g,mult(x,y)) = Y) <=(PCollKey2)=> return(false) if Y independent-of x || Y independent-of y. It is included in square_DH_proba_collision. *) } (* DH_dist_random_group_element_vs_exponent says that the probability of distinguishing a random group element from an exponentiation exp(g,x) with a random exponent is at most PDist. The other arguments are as in DH_basic and must all be defined before. *) def DH_dist_random_group_element_vs_exponent(G, Z, g, exp, exp', mult, PDist) { (* replace a random group element with an exponentiation, and conversely. *) param N, N'. equiv(group_to_exp_strict(exp)) foreach iX <= N do X <-R G; (OX() := return(X) | foreach iXm <= N' do OXm(m:Z) [useful_change] := return(exp(X,m))) <=(N * PDist)=> [computational] foreach iX <= N do x <-R Z; (OX() := return(exp(g,x)) | foreach iXm <= N' do OXm(m:Z) := return(exp(g,mult(x,m)))). (* This equivalence is very general, apply it only manually, because otherwise it might be applied too often. The equivalence above is particular case applied only when X is inside exp, and good for automatic proofs. *) equiv(group_to_exp(exp)) foreach iX <= N do X <-R G; OX() := return(X) <=(N * PDist)=> [manual, computational] foreach iX <= N do x <-R Z; OX() := return(exp(g,x)). equiv(exp_to_group(exp)) foreach iX <= N do x <-R Z; OX() := return(exp(g,x)) <=(N * PDist)=> [computational] foreach iX <= N do X <-R G; OX() := return(X). equiv(exp'_to_group(exp)) foreach iX <= N do x <-R Z; OX() := return(exp'(g,x)) <=(N * PDist)=> [computational] foreach iX <= N do X <-R G; OX() := return(X). } (* DH_good_group defines Diffie-Hellman assumptions that hold when - G is a group of prime order q, with the neutral element excluded. - the set of exponents Z is {1, ..., q-1} - g is a generator of G - mult is the product modulo q in {1, ..., q-1}, i.e. in the group (Z/qZ)* Warnings: * It may not be obvious when an element is received on the network whether it really belongs to the group G generated by g. That should be checked for the results to apply. * Random choices of exponents must be made uniformly in {1, ..., q-1} for the results to apply; random choices in G are also uniform. The arguments are as in DH_basic. *) def DH_good_group(G, Z, g, exp, exp', mult) { expand DH_basic(G, Z, g, exp, exp', mult). (* Since the group has prime order, exp(x,y) = exp(x',y) implies x = x', by raising both sides to y^{-1}. Note that y is invertible since it belongs to (Z/qZ)* with q prime. *) equation forall x:G, x':G, y:Z; (exp(x,y) = exp(x',y)) = (x = x'). equation forall x:G, x':G, y:Z; (exp'(x,y) = exp'(x',y)) = (x = x'). equation forall x:G, x':Z, y:Z; (exp(x,y) = exp(g, mult(x',y))) = (x = exp(g,x')). equation forall x:G, x':Z, y:Z; (exp'(x,y) = exp'(g, mult(x',y))) = (x = exp(g,x')). (* Injectivity: Since G has prime order, all its elements x are generators. All elements x^y for y \in Z = { 1, ..., q-1 } are then distinct. *) equation forall x:G, y:Z, y':Z; (exp(x,y) = exp(x,y')) = (y = y'). equation forall x:G, y:Z, y':Z; (exp'(x,y) = exp'(x,y')) = (y = y'). (* collision between products *) (* The collision x.y = z happens when x = z/y (y is invertible since we work in (Z/qZ)* with q prime). Since z/y is independent of x, this has probability 1/|Z| to happen. *) collision x <-R Z; forall y: Z, z: Z; return(mult(x,y) = z) <=(1/|Z|)=> return(false) if y independent-of x && z independent-of x. collision x <-R Z; y <-R Z; forall z: Z; return(mult(x,y) = z) <=(1/|Z|)=> return(false) if z independent-of x || z independent-of y. (* The next collision groups 2 cases: - mult(x,y) = z with x, y independent of each other, seen above - mult(x,x) = z. This case is justified as follows. In (Z/qZ)*, half of the elements are square, and each square has 2 square roots. If z is not a square, the equality x.x = z is false. If z is a square z = x'.x', then x.x = x'.x' iff x = x' or x = -x' (mod q), so with probability 2/|Z|. *) collision x <-R Z; y <-R Z; [random_choices_may_be_equal] forall z: Z; return(mult(x,y) = z) <=(2/|Z|)=> return(false) if z independent-of x || z independent-of y. (* The next collision is a consequence of the previous one, though this requires some reasoning. The random choices x1,y1,x2,y2 may be either independent of each other or equal (because the option "random_choices_may_be_equal" still makes sure that the array indices of x1,y1,x2,y2 do not depend on x1,y1,x2,y2). Hence x1 independent-of x2 actually implies that x2 is also independent of x1. The condition (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2) yields 4 cases: 1/ x1 independent-of x2 and x1 independent-of y2 x2 and y2 independent of x1, so mult(x2,y2) independent of x1 Apply previous collision with z = mult(x2,y2) 2/ x1 independent-of x2 and y1 independent-of x2 x1 and y1 independent of x2, so mult(x1,y1) independent of x2 Apply previous collision with z = mult(x1,y1) 3/ y1 independent-of y2 and x1 independent-of y2 x1 and y1 independent of y2, so mult(x1,y1) independent of y2 Apply previous collision with z = mult(x1,y1) 4/ y1 independent-of y2 and y1 independent-of x2 x2 and y2 independent of y1, so mult(x2,y2) independent of y1 Apply previous collision with z = mult(x2,y2) The formulation below allows CryptoVerif to replace mult(x1,y1) = mult(x2,y2) with a condition corresponding to (x1 = x2 && y1 = y2) || (x1 = y2 && y1 = x2) where = here means that they are the same random choice (i.e. same variable with the same indices). This condition is the negation of the independence condition (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). It corresponds to the only case that makes the equality succeed with non-negligible probability. *) collision x1 <-R Z; y1 <-R Z; x2 <-R Z; y2 <-R Z; [random_choices_may_be_equal] return(mult(x1,y1) = mult(x2,y2)) <=(2/|Z|)=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). (* x is invertible since we work in (Z/qZ)* with q prime *) equation forall x:Z, y:Z, y':Z; (mult(x,y) = mult(x,y')) = (y = y'). (* The following properties are true, but CryptoVerif should be able to infer them from the equations above. If Y independent of x, g^x = Y succeeds for one value of x, so with probability 1/|Z|. collision x <-R Z; forall Y: G; return(exp(g, x) = Y) <=(1/|Z|)=> return(false) if Y independent-of x. collision x <-R Z; forall Y: G; return(exp'(g, x) = Y) <=(1/|Z|)=> return(false) if Y independent-of x. If Y independent of y, g^(x.y) = Y succeeds for one value of y, so with probability 1/|Z|. collision x <-R Z; y <-R Z; forall Y: G; return(exp(g, mult(x,y)) = Y) <=(1/|Z|)=> return(false) if Y independent-of y. collision x <-R Z; y <-R Z; forall Y: G; return(exp'(g, mult(x,y)) = Y) <=(1/|Z|)=> return(false) if Y independent-of y. *) (* X^x = Y X is a generator of the group, so Y = X^y for some y independent of x. The equality X^x = Y = X^y holds only when x = y (mod q) with y independent of x, so this has probability 1/|Z| of happening. *) collision x <-R Z; forall X: G, Y: G; return(exp(X, x) = Y) <=(1/|Z|)=> return(false) if X independent-of x && Y independent-of x. collision x <-R Z; forall X: G, Y: G; return(exp'(X, x) = Y) <=(1/|Z|)=> return(false) if X independent-of x && Y independent-of x. (* replace a random group element with an exponentiation, and conversely. This is valid because G is exactly the group generated by g of cardinal q, and Z = {1, ..., q-1} so when x varies in Z, exp(g,x) covers each element of G exactly once. *) letproba PDist = 0. expand DH_dist_random_group_element_vs_exponent(G, Z, g, exp, exp', mult, PDist). } (* DH_single_coord_ladder models an elliptic curve defined by the equation Y^2 = X^3 + A X^2 + X in the field F_p of non-zero integers modulo the large prime p, where A^2 - 4 is not a square modulo p. This curve must form a commutative group of order kq where k is a small integer and q is a large prime. Its quadratic twist must form a commutative group of order k'q' where k' is a small integer and q' is a large prime. k must be a multiple of k'. We must use a single coordinate ladder defined as follows: we consider the elliptic curve E(F_{p^2}) defined by the equation Y^2 = X^3 + A X^2 + X in a quadratic extension F_{p^2} of F_p, we define X_0 : E(F_{p^2}) -> F_{p^2} by X_0(\infty) = 0 and X_0(X,Y) = X, and for X \in F_p and y an integer, we define X^y \in F_p as X^y = X_0(yQ) for all Q \in E(F_{p^2}) such that X_0(Q) = X. g = X_0(g_0) represents the base point g_0, which must have order q. The public keys (bitstrings) are mapped to elements of F_p by the function red and conversely, elements of F_p are mapped to public keys by the function repr, such that red o repr is the identity. The Diffie-Hellman "exponentiation" is defined by exp(X,y) = repr((red(X))^y). The private keys are chosen uniformly in { kn | n \in [n_min,n_max] } where n_min < n_max, n_max-n_min < q, and n_max-n_min < q'. Therefore the set of private keys may contain a multiple of q (resp. q'). Such keys are weak, in the sense that they yield 0 for all public keys on the curve (resp. on the twist). We exclude them. G: type of public keys (must be "bounded" or "nonuniform", and "large") subG: type of { X^k, X \in F_p } (must be "bounded", "nonuniform", and "large") Random choices in subG are done by choosing uniformly in { g^x | x \in {1, ..., q-1 } }. (This set is not the whole subG, since subG also contains elements of the twist.) This is important when the DDH assumption or the square DDH assumption is used. Z, Znw: types of exponents (must be "bounded","nonuniform", and "large") Znw is the set of integers multiple of k, prime to qq' modulo kqq'. Random choices in Znw are done by choosing uniformly in { kn | n \in [n_min,n_max], n prime to qq' }. Z is the set of integers multiple of k, modulo kqq'. Random choices in Z are done by choosing uniformly in { kn | n \in [n_min,n_max] }, hence Pcoll1rand(Z) = 1/(n_max-n_min+1). ZnwtoZ is the injection from Znw to Z. mult(x,y) = x.y mod kqq' (remains in Znw) g_k = red(g)^k. exp(G, Z): G. exp(X,y) = repr((red(X))^y). exp_div_k(subG,Znw): subG. exp_div_k(X,y) = exp_div_k'(X,y) = X^(y/k) exp_div_k': symbol that replaces exp_div_k after game transformation pow_k(G):subG. pow_k(X) = red(X)^k. subGtoG is repr restricted to subG to G. is_zero_G(G):bool is_zero_G(X) is true when X is the public key 0. is_zero_subG(subG): bool is defined as is_zero_G, but for arguments of type subG. The types G, subG, Z, and Znw must be declared before this macro. The functions g, exp, mult, ZnwtoZ, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. This model is justified in https://hal.inria.fr/hal-02100345 *) def DH_single_coord_ladder(G, Z, g, exp, mult, subG, Znw, ZnwtoZ, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG) { expand DH_subgroup_with_is_neutral(G, Znw, g, expnw, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG). letproba Pweak_key = 2*Pcoll1rand(Z). expand DH_exclude_weak_keys(G, Z, Znw, ZnwtoZ, exp, expnw, Pweak_key). letproba PCollKey2 = 2*Pcoll1rand(Znw). letproba PCollKey3 = 4*Pcoll1rand(Znw). letproba PCollKey4 = Pcoll1rand(Znw). expand is_neutral_DH_proba_collision(subG, Znw, g_k, exp_div_k, exp_div_k', mult, is_zero_subG, PCollKey2, PCollKey3, PCollKey4). } (* DH_X25519 models Curve25519 as defined in RFC 7748. https://tools.ietf.org/html/rfc7748 More generally, it supports the same curves as DH_single_coord_ladder with the additional assumption that all private keys are prime to qq'. Therefore, we do not need to exclude weak private keys, so the parameters Znw and ZnwtoZ are removed, and we use Z instead of Znw. Curve25519 satisfies these assumptions with - p = 2^{255}-19 - k = 8, k' = 4, q = 2^{252} + \delta with 0 < \delta < 2^{128}, q' = 2^{253} - 9 - 2\delta - red(X) = (X \modop 2^{255})\modop p, repr(X) is the representation of X as an element of {0, \dots, p-1} - n_min = 2^{251}, n_max = 2^{252}-1, so Pcoll1rand(Z) = 2^{-251}. This model is justified in detail in https://hal.inria.fr/hal-02100345 *) def DH_X25519(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG) { expand DH_subgroup_with_is_neutral(G, Z, g, exp, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG). letproba PCollKey2 = 2*Pcoll1rand(Z). letproba PCollKey3 = 4*Pcoll1rand(Z). letproba PCollKey4 = Pcoll1rand(Z). expand is_neutral_DH_proba_collision(subG, Z, g_k, exp_div_k, exp_div_k', mult, is_zero_subG, PCollKey2, PCollKey3, PCollKey4). } (* DH_X448 models Curve448 as defined in RFC 7748. https://tools.ietf.org/html/rfc7748 More generally, it supports the same curves as DH_single_coord_ladder with the additional assumptions that there is at most one private key multiple of q or q' and that q = -1 \mod 4, so -1 is not a square modulo q. That allows to reduce some probabilities. This model is justified in https://hal.inria.fr/hal-02100345 *) def DH_X448(G, Z, g, exp, mult, subG, Znw, ZnwtoZ, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG) { expand DH_subgroup_with_is_neutral(G, Znw, g, expnw, mult, subG, g_k, exp_div_k, exp_div_k', pow_k, subGtoG, is_zero_G, is_zero_subG). letproba Pweak_key = Pcoll1rand(Z). expand DH_exclude_weak_keys(G, Z, Znw, ZnwtoZ, exp, expnw, Pweak_key). letproba PCollKey2 = 2*Pcoll1rand(Znw). letproba PCollKey3 = 2*Pcoll1rand(Znw). letproba PCollKey4 = Pcoll1rand(Znw). expand is_neutral_DH_proba_collision(subG, Znw, g_k, exp_div_k, exp_div_k', mult, is_zero_subG, PCollKey2, PCollKey3, PCollKey4). } (* Computational Diffie-Hellman pCDH(t): the probability of breaking the CDH assumption in time t Other arguments as in DH_basic. All arguments must be declared before this macro. *) def CDH(G, Z, g, exp, exp', mult, pCDH) { (* the CDH assumption *) param na, naDDH, nb, nbDDH, naDH9, nbDH9. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) [useful_change] := return(m = exp(g, mult(b[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := return(m = exp(g, mult(a[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=((naDDH + nbDDH) * na * nb * pCDH(time + (na + nb + #ODDHa + #ODDHb - 3 + #ODHa9 + #ODHb9) * time(exp)))=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) := if defined(kb[j]) then return(m = exp'(g, mult(b[j], a))) else if defined(ka) then return(m = exp'(g, mult(b[j], a))) else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z [unchanged]; ( OB() := return(exp'(g,b)) | Ob() := let kb:bool = true in return(b) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], b))) else if defined(kb) then return(m = exp'(g, mult(a[j], b))) else return(false) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Same as CDH, but with a single family of exponents instead of two. More powerful, but may lead to a higher probability. *) def CDH_single(G, Z, g, exp, exp', mult, pCDH) { (* the CDH assumption *) param na, naDDH, naDH9. event square. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(naDDH * na * (na-1) * pCDH(time + (na + #ODDHa - 3 + #ODHa9) * time(exp)))=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else if j = ia then event_abort square else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* Variant of CDH with random self reducibility. It may yield lower probabilities but requires the rerandomization to be feasible. pCDH(t): the probability of breaking the CDH assumption in time t pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). Other arguments as in DH_basic. All arguments must be declared before this macro. *) def CDH_RSR(G, Z, g, exp, exp', mult, pCDH, pDistRerandom) { (* the CDH assumption *) param na, naDDH, nb, nbDDH, naDH9, nbDH9. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) [useful_change] := return(m = exp(g, mult(b[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := return(m = exp(g, mult(a[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=((#ODDHa + #ODDHb) * (3*#Oa+1) * (3*#Ob+1) * pCDH(time + (na + nb + (optim-if #Oa = 0 && #Ob = 0 then 0 else #ODDHa + #ODDHb) + 1 + #ODHa9 + #ODHb9) * time(exp)) + (na + nb) * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) := if defined(kb[j]) then return(m = exp'(g, mult(b[j], a))) else if defined(ka) then return(m = exp'(g, mult(b[j], a))) else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z [unchanged]; ( OB() := return(exp'(g,b)) | Ob() := let kb:bool = true in return(b) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], b))) else if defined(kb) then return(m = exp'(g, mult(a[j], b))) else return(false) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Same as CDH_RSR, but with a single family of exponents instead of two. More powerful, but leads to a higher probability. *) def CDH_RSR_single(G, Z, g, exp, exp', mult, pCDH, pDistRerandom) { (* the CDH assumption *) param na, naDDH, naDH9. event square. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(#ODDHa * (23*(#Oa+1)^2-11) / 6 * pCDH(time + (na + (optim-if #Oa = 0 then 0 else #ODDHa) + 1 + #ODHa9) * time(exp)) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else if j = ia then event_abort square else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* Decisional Diffie-Hellman pDDH(t): the probability of breaking the DDH assumption in time t Other arguments as in DH_basic. All arguments must be declared before this macro. *) def DDH(G, Z, g, exp, exp', mult, pDDH) { (* the DDH assumption *) event ev_abort. param na, naDH, nb, nbDH, naDH9, nbDH9. table dhval(na, nb, G). equiv(ddh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return (a) | foreach iaDH <= naDH do ODHa(jb<=nb) [useful_change] := return (exp(g, mult(b[jb], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return (exp(g,b)) | Ob() [10] := return(b) | foreach ibDH <= nbDH do ODHb(ja<=na) := return(exp(g, mult(a[ja], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=(na * nb * pDDH(time + (na + nb + #ODHa + #ODHb - 3 + #ODHa9 + #ODHb9) * time(exp)))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := get dhval(=ia, jb, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach iaDH <= naDH do ODHa(jb<=nb) := if defined(kb[jb]) then return(exp'(g, mult(b[jb], a))) else if defined(ka) then return(exp'(g, mult(b[jb], a))) else get[unique] dhval(=ia, =jb, c) in return(c) else ca <-R G; insert dhval(ia, jb, ca); return(ca) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp'(g,b)) | Ob() := get dhval(ja, =ib, c) in event_abort ev_abort else let kb:bool = true in return(b) | foreach ibDH <= nbDH do ODHb(ja<=na) := if defined(ka[ja]) then return(exp'(g, mult(a[ja], b))) else if defined(kb) then return(exp'(g, mult(a[ja], b))) else get[unique] dhval(=ja, =ib, c) in return(c) else cb <-R G; insert dhval(ja, ib, cb); return(cb) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Same as DDH, but with a single family of exponents instead of two. More powerful, but may lead to a higher probability. *) def DDH_single(G, Z, g, exp, exp', mult, pDDH) { (* the DDH assumption *) event ev_abort. event square. param na, naDH, naDH9. table dhval(na, na, G). equiv(ddh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return (a) | foreach iaDH <= naDH do ODHa(j<=na) [useful_change] := return (exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na*(na-1)/2 * pDDH(time + (na + #ODHa - 3 + #ODHa9) * time(exp)))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := get dhval(=ia, j, c) in event_abort ev_abort else get dhval(j, =ia, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach iaDH <= naDH do ODHa(j<=na) := if defined(ka[j]) then return(exp'(g, mult(a[j], a))) else if defined(ka) then return(exp'(g, mult(a[j], a))) else if j = ia then event_abort square else get[unique] dhval(=ia, =j, c) in return(c) else get[unique] dhval(=j, =ia, c) in return(c) else ca <-R G; insert dhval(ia, j, ca); return(ca) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* Decisional Diffie-Hellman, with random self reducibility. This macro yields a better probability but is much more restricted than the other DDH macros: - it does not support corruption - it supports only a single Diffie-Hellman query for each exponent a_i, associated with an arbitrary b_j and no Diffie-Hellman queries for b_j. - the default distribution on G must be as follows: There is an underlying prime-order group (the Diffie-Hellman group itself when it has prime order; the prime-order subgroup of the curve generated by the base point for Curve25519/Curve448). The default distribution on G is obtained by choosing uniformly an element in that group minus its neutral element and taking the associated public key in G (the group element itself for prime-order Diffie-Hellman groups; the encoding of its X coordinate for Curve25519/Curve448). pDDH(t): the probability of breaking the DDH assumption in time t pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 Other arguments as in DH_basic. All arguments must be declared before this macro. *) def DDH_RSR(G, Z, g, exp, exp', mult, pDDH, pDistRerandom) { (* the DDH assumption *) param na, nb, naDH9, nbDH9. equiv(ddh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | ODHa(jb<=nb) [useful_change] := return (exp(g, mult(b[jb], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return (exp(g,b)) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=(pDDH(time + (2*na + 2*nb + 4*#ODHa + #ODHa9 + #ODHb9) * time(exp)) + (2*na+2*nb+#ODHa+1)*Pcoll1rand(G) + (na+nb)*pDistRerandom)=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | ODHa(jb<=nb) := ca <-R G; return(ca) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp'(g,b)) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Gap Diffie-Hellman pGDH(t, n): the probability of breaking the GDH assumption in time t, with at most n calls to the DDH oracle. pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). It is needed because, for curve25519/448, to make the DH decision oracle unambiguous, we generate private keys in [(p+1)/2,p-1] instead of the set used for generating private keys in the curve25519/448 implementation. (The latter set yields equivalent private keys with small probability.) Other arguments as in DH_basic. All arguments must be declared before this macro. *) def GDH(G, Z, g, exp, exp', mult, pGDH, pDistRerandom) { (* the GDH assumption This equivalence says that, when exp(g,a[i]) and exp(g,b[j]) are known to the adversary, the adversary can compute exp(g, mult(a[i], b[j])) only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDDH6, naDDH7, naDDH8, naDH9, nb, nbeq, nbDDH, nbDDH1, nbDDH2, nbDDH3, nbDDH4, nbDDH5, nbDDH6, nbDDH7, nbDDH8, nbDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH2 <= naDDH2 do ODDHa2(m:G, m':G,j<=nb) := return(exp(m,b[j]) = exp(m', a)) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH4 <= naDDH4 do ODDHa4(m:G, j'<=nb,j<=nb) := return(exp(m,b[j]) = exp(g, mult(b[j'], a))) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := return(exp(m,a[j]) = exp(g, mult(b[j'], a))) | foreach iaDDH6 <= naDDH6 do ODDHa6(m:G, j'<=na,j<=nb) := return(exp(m,b[j]) = exp(g, mult(a[j'], a))) | foreach iaDDH7 <= naDDH7 do ODDHa7(m:G, j'<=na,j<=na) := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | (* We put the oracle m = exp(m', a) after exp(m,b[j]) = exp(m', a) to apply in priority the latter. Otherwise, CryptoVerif sometimes uses m = exp(m', a) to discharge exp(m'',b[j]) = exp(m', a) with m equal to exp(m'',b[j]) but then fails to discharge exp(m'',b[j]). *) foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) [useful_change] := return(m = exp(g, mult(b[j], a))) | foreach iaDDH8 <= naDDH8 do ODDHa8(m:G,j<=na) [3] := return(m = exp(g,mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ibDDH2 <= nbDDH2 do ODDHb2(m:G, m':G,j<=nb) := return(exp(m,b[j]) = exp(m', b)) | foreach ibDDH3 <= nbDDH3 do ODDHb3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', b)) | foreach ibDDH4 <= nbDDH4 do ODDHb4(m:G, j'<=nb,j<=nb) := return(exp(m,b[j]) = exp(g, mult(b[j'], b))) | foreach ibDDH5 <= nbDDH5 do ODDHb5(m:G, j'<=nb,j<=na) := return(exp(m,a[j]) = exp(g, mult(b[j'], b))) | foreach ibDDH6 <= nbDDH6 do ODDHb6(m:G, j'<=na,j<=nb) := return(exp(m,b[j]) = exp(g, mult(a[j'], b))) | foreach ibDDH7 <= nbDDH7 do ODDHb7(m:G, j'<=na,j<=na) := return(exp(m,a[j]) = exp(g, mult(a[j'], b))) | foreach ibeq <= nbeq do OBeq(m:G) := return(m = exp(g,b)) | foreach ibDDH1 <= nbDDH1 do ODDHb1(m:G, m':G) := return(m = exp(m', b)) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := return(m = exp(g, mult(a[j], b))) | foreach ibDDH8 <= nbDDH8 do ODDHb8(m:G,j<=nb) [3] := return(m = exp(g,mult(b[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=(na * nb * pGDH(time + (na + nb - 1 + #ODHa9 + #ODHb9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa2 + #ODDHa3 + #ODDHa4 + #ODDHa5 + #ODDHa6 + #ODDHa7 + #ODDHa8 + #ODDHb + #ODDHb1 + #ODDHb2 + #ODDHb3 + #ODDHb4 + #ODDHb5 + #ODDHb6 + #ODDHb7 + #ODDHb8) + (na + nb) * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH2 <= naDDH2 do ODDHa2(m:G, m':G,j<=nb) := return(exp'(m,b[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, b[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,b[j]), m, m') Indeed, D(exp(g,a),exp(g,b[j]),m,m') = (log_{g^a}(g^b[j]) * log_{g^a}(m) = log_{g^a}(m')) = (b[j]/a * log_g(m)/a = log_g(m')/a) = (b[j] * log_g(m) = a log_g(m')) = (m^b[j] = m'^a). *) foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* Similar to ODDHa2 *) foreach iaDDH4 <= naDDH4 do ODDHa4(m:G, j'<=nb,j<=nb) := if defined(kb[j']) then return(exp'(m,b[j]) = exp'(g, mult(b[j'], a))) else if defined(ka) then return(exp'(m,b[j]) = exp'(g, mult(b[j'], a))) else return(j = j' && exp'(m,b[j']) = exp'(g, mult(b[j'], a))) | (* GDH always allows to compute exp(m, b[j]) = exp(g,mult(b[j'],a)) as a particular case of ODDHa2. When a or b[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, b[j]) = exp'(g,mult(b[j'],a)). Otherwise, we distinguish two cases: - When j = j', we also keep the value of the left-hand side. - Otherwise, we apply the CDH assumption considering an adversary that knows b[j] and computes exp(m, b[j]). This adversary cannot compute exp(g,mult(b[j'],a)) by CDH, so the equality exp(m, b[j]) = exp(g,mult(b[j'],a)) is false in this case. Hence, the equality exp(m, b[j]) = exp(g,mult(b[j'],a)) reduces to j = j' && exp'(m,b[j']) = exp'(g, mult(b[j'], a)). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := if defined(kb[j']) then return(exp'(m,a[j]) = exp'(g, mult(b[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(b[j'], a))) else return (j = ia && exp'(m,a) = exp'(g, mult(b[j'], a))) | (* This case is similar to ODDHa4. *) foreach iaDDH6 <= naDDH6 do ODDHa6(m:G, j'<=na,j<=nb) := return(exp'(m,b[j]) = exp'(g, mult(a[j'], a))) | foreach iaDDH7 <= naDDH7 do ODDHa7(m:G, j'<=na,j<=na) := return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* ODDHa4..7 are particular cases of ODDHa2 or ODDHa3, with m' = exp(g, b[j']) or m' = exp(g, a[j']). We need to consider all these forms because CryptoVerif rewrites exp(exp(g,b[j']),a) into exp(g,mult(b[j'],a)), and it would not detect exp(g,mult(b[j'],a)) as an instance of exp(m',a). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) := if defined(kb[j]) then return(m = exp'(g, mult(b[j], a))) else if defined(ka) then return(m = exp'(g, mult(b[j], a))) else return(false) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and b[j] are not leaked. *) foreach iaDDH8 <= naDDH8 do ODDHa8(m:G,j<=na) [3] := return(m = exp'(g,mult(a[j], a))) | (* ODDHa8 is a particular case of ODDHa1 in which we do not apply the CDH assumption, since we apply it between a's and b's *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z [unchanged]; ( OB() := return(exp'(g,b)) | Ob() := let kb:bool = true in return(b) | foreach ibDDH2 <= nbDDH2 do ODDHb2(m:G, m':G,j<=nb) := return(exp'(m,b[j]) = exp'(m', b)) | foreach ibDDH3 <= nbDDH3 do ODDHb3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', b)) | foreach ibDDH4 <= nbDDH4 do ODDHb4(m:G, j'<=nb,j<=nb) := return(exp'(m,b[j]) = exp'(g, mult(b[j'], b))) | foreach ibDDH5 <= nbDDH5 do ODDHb5(m:G, j'<=nb,j<=na) := return(exp'(m,a[j]) = exp'(g, mult(b[j'], b))) | foreach ibDDH6 <= nbDDH6 do ODDHb6(m:G, j'<=na,j<=nb) := if defined(ka[j']) then return(exp'(m,b[j]) = exp'(g, mult(a[j'], b))) else if defined(kb) then return(exp'(m,b[j]) = exp'(g, mult(a[j'], b))) else return(j = ib && exp'(m,b) = exp'(g, mult(a[j'], b))) | foreach ibDDH7 <= nbDDH7 do ODDHb7(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], b))) else if defined(kb) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], b))) else return(j = j' && exp'(m,a[j']) = exp'(g, mult(a[j'], b))) | foreach ibeq <= nbeq do OBeq(m:G) := return(m = exp'(g,b)) | foreach ibDDH1 <= nbDDH1 do ODDHb1(m:G, m':G) := return(m = exp'(m', b)) (* GDH allows to compute m = exp(m',b) for any m and m', without leaking b *) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], b))) else if defined(kb) then return(m = exp'(g, mult(a[j], b))) else return(false) | foreach ibDDH8 <= nbDDH8 do ODDHb8(m:G,j<=nb) [3] := return(m = exp'(g,mult(b[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(b[j], a)) in the equivalence, because, when m' is known to be exp(g, b[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(b[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(b[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* Same as GDH, but with a single family of exponents instead of two. More powerful, but may lead to a higher probability. *) def GDH_single(G, Z, g, exp, exp', mult, pGDH, pDistRerandom) { (* the GDH assumption This equivalence says that, when exp(g,a[i]) are known to the adversary, the adversary can compute exp(g, mult(a[i], a[j])) for i<>j only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) [useful_change] := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * (na-1)/2 * pGDH(time + (na + #ODHa9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa3 + #ODDHa5) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, a[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,a[j]), m, m') Indeed, D(exp(g,a),exp(g,a[j]),m,m') = (log_{g^a}(g^a[j]) * log_{g^a}(m) = log_{g^a}(m')) = (a[j]/a * log_g(m)/a = log_g(m')/a) = (a[j] * log_g(m) = a log_g(m')) = (m^a[j] = m'^a). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else return ((j = ia || j = j' || j' = ia) && exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* GDH always allows to compute exp(m, a[j]) = exp(g,mult(a[j'],a)) as a particular case of ODDHa2. When a or a[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). Otherwise, we distinguish two cases: - When j = ia || j = j' || j' = ia, we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). - Otherwise, we apply the CDH assumption considering an adversary that knows a[j] and computes exp(m, a[j]). This adversary cannot compute exp(g,mult(a[j'],a)) by CDH, so the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) is false in this case. Hence, the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) reduces to (j = ia || j = j' || j' = ia) && exp'(m, a[j]) = exp'(g,mult(a[j'],a)). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(j = ia && m = exp'(g, mult(a[j], a))) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and a[j] are not leaked. *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(a[j], a)) in the equivalence, because, when m' is known to be exp(g, a[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(a[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(a[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* Variant of GDH with random self reducibility. It may yield lower probabilities but requires the rerandomization to be feasible. pGDH(t,n): the probability of breaking the GDH assumption in time t, with at most n calls to the DDH oracle. pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). Other arguments as in DH_basic. All arguments must be declared before this macro. *) def GDH_RSR(G, Z, g, exp, exp', mult, pGDH, pDistRerandom) { (* the GDH assumption This equivalence says that, when exp(g,a[i]) and exp(g,b[j]) are known to the adversary, the adversary can compute exp(g, mult(a[i], b[j])) only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDDH6, naDDH7, naDDH8, naDH9, nb, nbeq, nbDDH, nbDDH1, nbDDH2, nbDDH3, nbDDH4, nbDDH5, nbDDH6, nbDDH7, nbDDH8, nbDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH2 <= naDDH2 do ODDHa2(m:G, m':G,j<=nb) := return(exp(m,b[j]) = exp(m', a)) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH4 <= naDDH4 do ODDHa4(m:G, j'<=nb,j<=nb) := return(exp(m,b[j]) = exp(g, mult(b[j'], a))) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := return(exp(m,a[j]) = exp(g, mult(b[j'], a))) | foreach iaDDH6 <= naDDH6 do ODDHa6(m:G, j'<=na,j<=nb) := return(exp(m,b[j]) = exp(g, mult(a[j'], a))) | foreach iaDDH7 <= naDDH7 do ODDHa7(m:G, j'<=na,j<=na) := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) [useful_change] := return(m = exp(g, mult(b[j], a))) | foreach iaDDH8 <= naDDH8 do ODDHa8(m:G,j<=na) [3] := return(m = exp(g,mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ibDDH2 <= nbDDH2 do ODDHb2(m:G, m':G,j<=nb) := return(exp(m,b[j]) = exp(m', b)) | foreach ibDDH3 <= nbDDH3 do ODDHb3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', b)) | foreach ibDDH4 <= nbDDH4 do ODDHb4(m:G, j'<=nb,j<=nb) := return(exp(m,b[j]) = exp(g, mult(b[j'], b))) | foreach ibDDH5 <= nbDDH5 do ODDHb5(m:G, j'<=nb,j<=na) := return(exp(m,a[j]) = exp(g, mult(b[j'], b))) | foreach ibDDH6 <= nbDDH6 do ODDHb6(m:G, j'<=na,j<=nb) := return(exp(m,b[j]) = exp(g, mult(a[j'], b))) | foreach ibDDH7 <= nbDDH7 do ODDHb7(m:G, j'<=na,j<=na) := return(exp(m,a[j]) = exp(g, mult(a[j'], b))) | foreach ibeq <= nbeq do OBeq(m:G) := return(m = exp(g,b)) | foreach ibDDH1 <= nbDDH1 do ODDHb1(m:G, m':G) := return(m = exp(m', b)) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := return(m = exp(g, mult(a[j], b))) | foreach ibDDH8 <= nbDDH8 do ODDHb8(m:G,j<=nb) [3] := return(m = exp(g,mult(b[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=((3*(#Oa+optim-if #ODDHa5+#ODDHb7=0 then 0 else 1)+1) * (3*(#Ob+optim-if #ODDHa4+#ODDHb6=0 then 0 else 1)+1) * pGDH(time + (na + nb + 1 + #ODHa9 + #ODHb9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa2 + #ODDHa3 + #ODDHa4 + #ODDHa5 + #ODDHa6 + #ODDHa7 + #ODDHa8 + #ODDHb + #ODDHb1 + #ODDHb2 + #ODDHb3 + #ODDHb4 + #ODDHb5 + #ODDHb6 + #ODDHb7 + #ODDHb8) + (na + nb) * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH2 <= naDDH2 do ODDHa2(m:G, m':G,j<=nb) := return(exp'(m,b[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, b[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,b[j]), m, m') Indeed, D(exp(g,a),exp(g,b[j]),m,m') = (log_{g^a}(g^b[j]) * log_{g^a}(m) = log_{g^a}(m')) = (b[j]/a * log_g(m)/a = log_g(m')/a) = (b[j] * log_g(m) = a log_g(m')) = (m^b[j] = m'^a). *) foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* Similar to ODDHa2 *) foreach iaDDH4 <= naDDH4 do ODDHa4(m:G, j'<=nb,j<=nb) := if defined(kb[j']) then return(exp'(m,b[j]) = exp'(g, mult(b[j'], a))) else if defined(ka) then return(exp'(m,b[j]) = exp'(g, mult(b[j'], a))) else return(j = j' && exp'(m,b[j']) = exp'(g, mult(b[j'], a))) | (* GDH always allows to compute exp(m, b[j]) = exp(g,mult(b[j'],a)) as a particular case of ODDHa2. When a or b[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, b[j]) = exp'(g,mult(b[j'],a)). Otherwise, we distinguish two cases: - When j = j', we also keep the value of the left-hand side. - Otherwise, we apply the CDH assumption considering an adversary that knows b[j] and computes exp(m, b[j]). This adversary cannot compute exp(g,mult(b[j'],a)) by CDH, so the equality exp(m, b[j]) = exp(g,mult(b[j'],a)) is false in this case. Hence, the equality exp(m, b[j]) = exp(g,mult(b[j'],a)) reduces to j = j' && exp'(m,b[j']) = exp'(g, mult(b[j'], a)). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := if defined(kb[j']) then return(exp'(m,a[j]) = exp'(g, mult(b[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(b[j'], a))) else return (j = ia && exp'(m,a) = exp'(g, mult(b[j'], a))) | (* This case is similar to ODDHa4. *) foreach iaDDH6 <= naDDH6 do ODDHa6(m:G, j'<=na,j<=nb) := return(exp'(m,b[j]) = exp'(g, mult(a[j'], a))) | foreach iaDDH7 <= naDDH7 do ODDHa7(m:G, j'<=na,j<=na) := return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* ODDHa4..7 are particular cases of ODDHa2 or ODDHa3, with m' = exp(g, b[j']) or m' = exp(g, a[j']). We need to consider all these forms because CryptoVerif rewrites exp(exp(g,b[j']),a) into exp(g,mult(b[j'],a)), and it would not detect exp(g,mult(b[j'],a)) as an instance of exp(m',a). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=nb) := if defined(kb[j]) then return(m = exp'(g, mult(b[j], a))) else if defined(ka) then return(m = exp'(g, mult(b[j], a))) else return(false) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and b[j] are not leaked. *) foreach iaDDH8 <= naDDH8 do ODDHa8(m:G,j<=na) [3] := return(m = exp'(g,mult(a[j], a))) | (* ODDHa8 is a particular case of ODDHa1 in which we do not apply the CDH assumption, since we apply it between a's and b's *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z [unchanged]; ( OB() := return(exp'(g,b)) | Ob() := let kb:bool = true in return(b) | foreach ibDDH2 <= nbDDH2 do ODDHb2(m:G, m':G,j<=nb) := return(exp'(m,b[j]) = exp'(m', b)) | foreach ibDDH3 <= nbDDH3 do ODDHb3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', b)) | foreach ibDDH4 <= nbDDH4 do ODDHb4(m:G, j'<=nb,j<=nb) := return(exp'(m,b[j]) = exp'(g, mult(b[j'], b))) | foreach ibDDH5 <= nbDDH5 do ODDHb5(m:G, j'<=nb,j<=na) := return(exp'(m,a[j]) = exp'(g, mult(b[j'], b))) | foreach ibDDH6 <= nbDDH6 do ODDHb6(m:G, j'<=na,j<=nb) := if defined(ka[j']) then return(exp'(m,b[j]) = exp'(g, mult(a[j'], b))) else if defined(kb) then return(exp'(m,b[j]) = exp'(g, mult(a[j'], b))) else return(j = ib && exp'(m,b) = exp'(g, mult(a[j'], b))) | foreach ibDDH7 <= nbDDH7 do ODDHb7(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], b))) else if defined(kb) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], b))) else return(j = j' && exp'(m,a[j']) = exp'(g, mult(a[j'], b))) | foreach ibeq <= nbeq do OBeq(m:G) := return(m = exp'(g,b)) | foreach ibDDH1 <= nbDDH1 do ODDHb1(m:G, m':G) := return(m = exp'(m', b)) (* GDH allows to compute m = exp(m',b) for any m and m', without leaking b *) | foreach ibDDH <= nbDDH do ODDHb(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], b))) else if defined(kb) then return(m = exp'(g, mult(a[j], b))) else return(false) | foreach ibDDH8 <= nbDDH8 do ODDHb8(m:G,j<=nb) [3] := return(m = exp'(g,mult(b[j], b))) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(b[j], a)) in the equivalence, because, when m' is known to be exp(g, b[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(b[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(b[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* Same as GDH_RSR, but with a single family of exponents instead of two. More powerful, but leads to a higher probability. *) def GDH_RSR_single(G, Z, g, exp, exp', mult, pGDH, pDistRerandom) { (* the GDH assumption This equivalence says that, when exp(g,a[i]) are known to the adversary, the adversary can compute exp(g, mult(a[i], a[j])) for i<>j only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) [useful_change] := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=((23*(1+#Oa+optim-if #ODDHa5=0 then 0 else 1)^2-11) / 6 * pGDH(time + (na+1 + #ODHa9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa3 + #ODDHa5) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, a[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,a[j]), m, m') Indeed, D(exp(g,a),exp(g,a[j]),m,m') = (log_{g^a}(g^a[j]) * log_{g^a}(m) = log_{g^a}(m')) = (a[j]/a * log_g(m)/a = log_g(m')/a) = (a[j] * log_g(m) = a log_g(m')) = (m^a[j] = m'^a). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else return ((j = ia || j = j' || j' = ia) && exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* GDH always allows to compute exp(m, a[j]) = exp(g,mult(a[j'],a)) as a particular case of ODDHa2. When a or a[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). Otherwise, we distinguish two cases: - When j = ia || j = j' || j' = ia, we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). - Otherwise, we apply the CDH assumption considering an adversary that knows a[j] and computes exp(m, a[j]). This adversary cannot compute exp(g,mult(a[j'],a)) by CDH, so the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) is false in this case. Hence, the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) reduces to (j = ia || j = j' || j' = ia) && exp'(m, a[j]) = exp'(g,mult(a[j'],a)). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(j = ia && m = exp'(g, mult(a[j], a))) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and a[j] are not leaked. *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(a[j], a)) in the equivalence, because, when m' is known to be exp(g, a[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(a[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(a[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* square Computational Diffie-Hellman and Computational Diffie-Hellman. When the group is of prime order, the square CDH assumption is equivalent to the CDH assumption (but CryptoVerif can prove more using the square variant). pCDH(t): the probability of breaking the CDH assumption in time t pSQCDH(t): the probability of breaking the square CDH assumption in time t Other arguments as in DH_basic. All arguments must be declared before this macro. *) def square_CDH(G, Z, g, exp, exp', mult, pCDH, pSQCDH) { (* the (square) CDH assumption *) param na, naDDH, naDH9. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * naDDH * pSQCDH(time + (na + #ODDHa - 2 + #ODHa9) * time(exp)) + na * (na-1) * naDDH * pCDH(time + (na + #ODDHa - 3 + #ODHa9) * time(exp)))=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* Variant of square CDH with random self reducibility. It may yield lower probabilities but requires the rerandomization to be feasible. pSQCDH(t): the probability of breaking the square CDH assumption in time t pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). Other arguments as in DH_basic. All arguments must be declared before this macro. *) def square_CDH_RSR(G, Z, g, exp, exp', mult, pSQCDH, pDistRerandom) { (* the (square) CDH assumption *) param na, naDDH, naDH9. equiv(cdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(#ODDHa * (23*(1+#Oa)^2-11) / 12 * pSQCDH(time + (na + (optim-if #Oa = 0 then 0 else #ODDHa) + 1 + #ODHa9) * time(exp)) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(false) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* square Decisional Diffie-Hellman and Decisional Diffie-Hellman pDDH(t): the probability of breaking the DDH assumption in time t pSQDDH(t): the probability of breaking the square DDH assumption in time t Other arguments as in DH_basic. All arguments must be declared before this macro. *) def square_DDH(G, Z, g, exp, exp', mult, pDDH, pSQDDH) { (* the (square) DDH assumption *) event ev_abort. param na, naDH, naDH9. table dhval(na, na, G). equiv(ddh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return (a) | foreach iaDH <= naDH do ODHa(j<=na) [useful_change] := return (exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * pSQDDH(time + (na + #ODHa - 2 + #ODHa9) * time(exp)) + na*(na-1)/2 * pDDH(time + (na + #ODHa - 3 + #ODHa9) * time(exp)))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := get dhval(=ia, j, c) in event_abort ev_abort else get dhval(j, =ia, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach iaDH <= naDH do ODHa(j<=na) := if defined(ka[j]) then return(exp'(g, mult(a[j], a))) else if defined(ka) then return(exp'(g, mult(a[j], a))) else get[unique] dhval(=ia, =j, c) in return(c) else get[unique] dhval(=j, =ia, c) in return(c) else ca <-R G; insert dhval(ia, j, ca); return(ca) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* square Gap Diffie-Hellman and Gap Diffie-Hellman. When the group is of prime order, the square GDH assumption is equivalent to the GDH assumption (but CryptoVerif can prove more using the square variant). pGDH(t,n): the probability of breaking the GDH assumption in time t, with at most n calls to the DDH oracle. pSQGDH(t,n): the probability of breaking the square GDH assumption in time t, with at most n calls to the DDH oracle. pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). It is needed because, for curve25519/448, to make the DH decision oracle unambiguous, we generate private keys in [(p+1)/2,p-1] instead of the set used for generating private keys in the curve25519/448 implementation. (The latter set yields equivalent private keys with small probability.) Other arguments as in DH_basic. All arguments must be declared before this macro. *) def square_GDH(G, Z, g, exp, exp', mult, pGDH, pSQGDH, pDistRerandom) { (* the square GDH assumption This equivalence says that, when exp(g,a[i]) are known to the adversary, the adversary can compute exp(g, mult(a[i], a[j])) only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) [useful_change] := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * pSQGDH(time + (na + #ODHa9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa3 + #ODDHa5) + na * (na-1)/2 * pGDH(time + (na + #ODHa9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa3 + #ODDHa5) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, a[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,a[j]), m, m') Indeed, D(exp(g,a),exp(g,a[j]),m,m') = (log_{g^a}(g^a[j]) * log_{g^a}(m) = log_{g^a}(m')) = (a[j]/a * log_g(m)/a = log_g(m')/a) = (a[j] * log_g(m) = a log_g(m')) = (m^a[j] = m'^a). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else return ((j = ia || j = j') && exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* GDH always allows to compute exp(m, a[j]) = exp(g,mult(a[j'],a)) as a particular case of ODDHa2. When a or a[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). Otherwise, we distinguish two cases: - When j = ia or j = j', we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). - Otherwise, we apply the CDH assumption considering an adversary that knows a[j] and computes exp(m, a[j]). This adversary cannot compute exp(g,mult(a[j'],a)) by CDH, so the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) is false in this case. Hence, the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) reduces to (j = ia || j = j') && exp'(m, a[j]) = exp'(g,mult(a[j'],a)). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(false) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and a[j] are not leaked. *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(a[j], a)) in the equivalence, because, when m' is known to be exp(g, a[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(a[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(a[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* Variant of square GDH with random self reducibility. It may yield lower probabilities but requires the rerandomization to be feasible. pSQGDH(t,n): the probability of breaking the square GDH assumption in time t, with at most n calls to the DDH oracle. pDistRerandom: the probability that rerandomization can be distinguished from the original distribution It is 0 when exponents are chosen uniformly in (Z/qZ)^*. 2^-126 for curve25519 2^-221 for curve448 (see https://tools.ietf.org/html/draft-barnes-cfrg-mult-for-7748-00 for the rerandomization for curve25519 and curve448). Other arguments as in DH_basic. All arguments must be declared before this macro. *) def square_GDH_RSR(G, Z, g, exp, exp', mult, pSQGDH, pDistRerandom) { (* the square GDH assumption This equivalence says that, when exp(g,a[i]) are known to the adversary, the adversary can compute exp(g, mult(a[i], a[j])) only with negligible probability, even in the presence of a DDH oracle DDH(G,A,B,C) tells whether A = G^a, B = G^b, and C = G^{ab} for some a,b, that is DDH(G,A,B,C) is (log_G(A) * log_G(B) = log_G(C)). *) param na, naeq, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDH9. equiv(gdh(exp)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp(m,a[j]) = exp(m', a)) | foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) [useful_change] := return(exp(m,a[j]) = exp(g, mult(a[j'], a))) | foreach iaeq <= naeq do OAeq(m:G) := return(m = exp(g,a)) | (* We put the oracle above before ODDHa1, so that ODDHa1 is not used when m' = g, which would lead to additional calls to the DDH oracle when in fact we can simply compare with the public key *) foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp(m', a)) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) [useful_change] := return(m = exp(g, mult(a[j], a))) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=((23*(1+#Oa+optim-if #ODDHa5=0 then 0 else 1)^2-11) / 12 * pSQGDH(time + (na+1 + #ODHa9) * time(exp), #ODDHa + #ODDHa1 + #ODDHa3 + #ODDHa5) + na * pDistRerandom)=> [computational] foreach ia <= na do a <-R Z [unchanged]; ( OA() := return(exp'(g,a)) | Oa() := let ka:bool = true in return(a) | foreach iaDDH3 <= naDDH3 do ODDHa3(m:G, m':G,j<=na) := return(exp'(m,a[j]) = exp'(m', a)) | (* GDH allows to compute exp(m, a[j]) = exp(m',a) for any m and m', without leaking a, as it is DDH(exp(g,a), exp(g,a[j]), m, m') Indeed, D(exp(g,a),exp(g,a[j]),m,m') = (log_{g^a}(g^a[j]) * log_{g^a}(m) = log_{g^a}(m')) = (a[j]/a * log_g(m)/a = log_g(m')/a) = (a[j] * log_g(m) = a log_g(m')) = (m^a[j] = m'^a). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=na,j<=na) := if defined(ka[j']) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else if defined(ka) then return(exp'(m,a[j]) = exp'(g, mult(a[j'], a))) else return ((j = ia || j = j') && exp'(m,a[j]) = exp'(g, mult(a[j'], a))) | (* GDH always allows to compute exp(m, a[j]) = exp(g,mult(a[j'],a)) as a particular case of ODDHa2. When a or a[j'] is leaked, that is all we use and we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). Otherwise, we distinguish two cases: - When j = ia or j = j', we keep the value that occurs in the left-hand side exp'(m, a[j]) = exp'(g,mult(a[j'],a)). - Otherwise, we apply the CDH assumption considering an adversary that knows a[j] and computes exp(m, a[j]). This adversary cannot compute exp(g,mult(a[j'],a)) by CDH, so the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) is false in this case. Hence, the equality exp(m, a[j]) = exp(g,mult(a[j'],a)) reduces to (j = ia || j = j') && exp'(m, a[j]) = exp'(g,mult(a[j'],a)). *) foreach iaeq <= naeq do OAeq(m:G) := return(m = exp'(g,a)) | foreach iaDDH1 <= naDDH1 do ODDHa1(m:G, m':G) := return(m = exp'(m', a)) (* GDH allows to compute m = exp(m',a) for any m and m', without leaking a, as it is DDH(g, exp(g,a), m', m) *) | foreach iaDDH <= naDDH do ODDHa(m:G, j<=na) := if defined(ka[j]) then return(m = exp'(g, mult(a[j], a))) else if defined(ka) then return(m = exp'(g, mult(a[j], a))) else return(false) | (* ODDHa is a particular case of ODDHa1 in which can apply the CDH assumption, provided a and a[j] are not leaked. *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). (* We need to consider both forms m = exp(m', a) and m = exp(g, mult(a[j], a)) in the equivalence, because, when m' is known to be exp(g, a[j]), CryptoVerif is going to simplify m = exp(m', a) into m = exp(g, mult(a[j], a)), and the procedure that tests whether a term in the game matches a term in the equivalence would not recognize that m = exp(g, mult(a[j], a)) in the game matches m = exp(m', a) in the equivalence. *) } (* PRF-ODH assumption We propose two variants of PRF-ODH, PRF-ODH1 and PRF-ODH2 defined below. PRF-ODH1 is a consequence of CDH and ROM or of DDH and PRF. It corresponds to nnPRF-ODH in https://eprint.iacr.org/2017/517, but with several challenge queries using the same or different DH pairs and argument of the PRF. G, Z, g, exp, exp', mult are as in DH_basic. prf: pseudo-random function that takes as argument a group element (in G) and an element in prf_in, and produces a result in prf_out. The type prf_out must be "bounded" or "nonuniform". pPRF_ODH(t, n): probability of breaking the PRF-ODH1 assumption in time t with n queries to the PRF (prf(g^ab, m)). The function prf is defined by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. All other arguments must be declared before this macro. *) def PRF_ODH1(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH) { fun prf(G, prf_in): prf_out. (* The PRF-ODH assumption *) event ev_abort. param na, na1, na2, nb, nb1, nb2, naDH9, nbDH9. table prf_dh_val(na, nb, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia2 <= na2 do Oa2(jb <= nb, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(b[jb], a)), xa2)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ib2 <= nb2 do Ob2(ja <= na, xb2: prf_in) := return(prf(exp(g, mult(a[ja], b)), xb2)) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=(na * nb * pPRF_ODH(time + (na + nb + #Oa2 + #Ob2 - 3 + #ODHa9 + #ODHb9)*time(exp)+ (#Oa2 + #Ob2 - 1)*time(prf, max(maxlength(xa2), maxlength(xb2))), na2 + nb2))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, jb, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia2 <= na2 do Oa2(jb <= nb, xa2: prf_in) := if defined(kb[jb]) then (* b[jb] compromised *) return(prf(exp'(g, mult(b[jb], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(b[jb], a)), xa2)) else (* At this point, a and b[jb] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =jb, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, jb, xa2, ca2); return(ca2) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp'(g,b)) | Ob() := (* Abort when b must not be compromised *) get prf_dh_val(ja, =ib, x, c) in event_abort ev_abort else let kb:bool = true in return(b) | foreach ib2 <= nb2 do Ob2(ja <= na, xb2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], b)), xb2)) else if defined(kb) then (* b compromised *) return(prf(exp'(g, mult(a[ja], b)), xb2)) else (* At this point, b and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ja, =ib, =xb2, c) in return(c) else cb2 <-R prf_out; insert prf_dh_val(ja, ib, xb2, cb2); return(cb2) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Same as PRF_ODH1, but with a single family of exponents instead of two. More powerful, but may lead to a higher probability. *) def PRF_ODH1_single(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH) { fun prf(G, prf_in): prf_out. (* The PRF-ODH1 assumption *) event ev_abort. event square. param na, na1, na2, naDH9. table prf_dh_val(na, na, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(a[ja], a)), xa2)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na*(na-1)/2 * pPRF_ODH(time + (na + #Oa2 - 3 + #ODHa9)*time(exp) + (#Oa2 - 1) * time(prf, maxlength(xa2)), 2*na2))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, ja, x, c) in event_abort ev_abort else get prf_dh_val(ja, =ia, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if ja = ia then event_abort square else (* At this point, a and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =ja, =xa2, c) in return(c) else get[unique] prf_dh_val(=ja, =ia, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, ja, xa2, ca2); return(ca2) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* PRF-ODH2 is a consequence of GDH and ROM. It corresponds to mmPRF-ODH in https://eprint.iacr.org/2017/517, but again with several challenge queries using the same or different DH pairs and argument of the PRF. This assumption requires that it is possible to test efficiently whether exp(Y,a) = exp(g, ab) knowing just Y and B = exp(g,b) (so the result does not depend on a). This is possible for prime-order groups as well as Curve25519 and Curve448 when the weak private key is excluded. When this is true, we say that the keys Y and B are equivalent. G, Z, g, exp, exp', mult are as in DH_basic. prf(G, prf_in): prf_out: pseudo-random function that takes as argument a group element (in G) and an element in prf_in, and produces a result in prf_out. The type prf_out must be "bounded" or "nonuniform". pPRF_ODH(t, n, n'): probability of breaking the PRF-ODH2 assumption in time t with n queries to the PRF (prf(g^ab, m)) and n' queries to prf(X^a, m) or prf(X^b, m). PCollKey1: probability that two randomly generated public keys exp(g,a) and exp(g,b) are equivalent. The function prf is defined by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. All other arguments must be declared before this macro. *) def PRF_ODH2(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH, PCollKey1) { fun prf(G, prf_in): prf_out. (* The PRF-ODH assumption *) event ev_abort. param na, na1, na2, na3, nb, nb1, nb2, nb3, naDH9, nbDH9. table prf_dh_val(na, nb, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) [useful_change] := return(prf(exp(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(jb <= nb, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(b[jb], a)), xa2)) | foreach ia3 <= na3 do Oa3(ja <= na, xa3: prf_in) := return(prf(exp(g, mult(a[ja], a)), xa3)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp(g,b)) | Ob() [10] := return(b) | foreach ib1 <= nb1 do Ob1(mb1:G, xb1:prf_in) := return(prf(exp(mb1, b), xb1)) | foreach ib2 <= nb2 do Ob2(ja <= na, xb2: prf_in) := return(prf(exp(g, mult(a[ja], b)), xb2)) | foreach ib3 <= nb3 do Ob3(jb <= nb, xb3: prf_in) := return(prf(exp(g, mult(b[jb], b)), xb3)) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) [2] := return(exp(g, mult(b, x))) ) <=(na * nb * pPRF_ODH(time + (na + nb + #Oa1 + #Ob1 + #Oa2 + #Ob2 + #Oa3 + #Ob3 - 3 + #ODHa9 + #ODHb9)*time(exp)+ (#Oa1 + #Ob1 + #Oa2 + #Ob2 + #Oa3 + #Ob3 - 1)* time(prf, max(maxlength(xa1), maxlength(xb1), maxlength(xa2), maxlength(xb2))), na1 + nb1 + na2 + nb2 + na3 + nb3, na1 + nb1 + na3 + nb3) + optim-if na3+nb3 = 0 then 0 else na * nb * PCollKey1)=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, jb, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) := find j' <= nb suchthat defined(b[j']) && exp'(ma1,a) = exp'(g, mult(a,b[j'])) then ( (* In this case, that's the same as Oa2 *) if defined(kb[j']) then (* b[j'] compromised *) return(prf(exp'(ma1, a), xa1)) else if defined(ka) then (* a compromised *) return(prf(exp'(ma1, a), xa1)) else (* At this point, a and b[j'] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =j', =xa1, c) in return(c) else ca1 <-R prf_out; insert prf_dh_val(ia, j', xa1, ca1); return(ca1) ) else return(prf(exp'(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(jb <= nb, xa2: prf_in) := if defined(kb[jb]) then (* b[jb] compromised *) return(prf(exp'(g, mult(b[jb], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(b[jb], a)), xa2)) else (* At this point, a and b' are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =jb, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, jb, xa2, ca2); return(ca2) | foreach ia3 <= na3 do Oa3(ja <= na, xa3: prf_in) := return(prf(exp'(g, mult(a[ja], a)), xa3)) | (* Oa3 can be considered as prf(exp(OA[ja], a), xa3) and there is a negligible probability of collision between exp(OA[ja], a) and exp(g, mult(b[.], a)) *) foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp'(g,b)) | Ob() := (* Abort when b must not be compromised *) get prf_dh_val(ja, =ib, x, c) in event_abort ev_abort else let kb:bool = true in return(b) | foreach ib1 <= nb1 do Ob1(mb1:G, xb1:prf_in) := find j' <= na suchthat defined(a[j']) && exp'(mb1,b) = exp'(g, mult(a[j'],b)) then ( (* In this case, that's the same as Oa2 *) if defined(ka[j']) then (* a[j'] compromised *) return(prf(exp'(mb1, b), xb1)) else if defined(kb) then (* b compromised *) return(prf(exp'(mb1, b), xb1)) else (* At this point, b and a[j'] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=j', =ib, =xb1, c) in return(c) else cb1 <-R prf_out; insert prf_dh_val(j', ib, xb1, cb1); return(cb1) ) else return(prf(exp'(mb1, b), xb1)) | foreach ib2 <= nb2 do Ob2(ja <= na, xb2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], b)), xb2)) else if defined(kb) then (* b compromised *) return(prf(exp'(g, mult(a[ja], b)), xb2)) else (* At this point, b and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ja, =ib, =xb2, c) in return(c) else cb2 <-R prf_out; insert prf_dh_val(ja, ib, xb2, cb2); return(cb2) | foreach ib3 <= nb3 do Ob3(jb <= nb, xb3: prf_in) := return(prf(exp'(g, mult(b[jb], b)), xb3)) | foreach ibDH9 <= nbDH9 do ODHb9(x:Z) := return(exp'(g, mult(b, x))) ). } (* Same as PRF_ODH2, but with a single family of exponents instead of two. More powerful, but may lead to a higher probability. *) def PRF_ODH2_single(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH, PCollKey1) { fun prf(G, prf_in): prf_out. (* The PRF-ODH2 assumption *) event ev_abort. param na, na1, na2, naDH9. table prf_dh_val(na, na, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) [useful_change] := return(prf(exp(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(a[ja], a)), xa2)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na*(na-1)/2 * pPRF_ODH(time + (na + #Oa1 + #Oa2 - 3 + #ODHa9)*time(exp) + (#Oa1 + #Oa2 - 1) * time(prf, max(maxlength(xa1), maxlength(xa2))), 2*na2, 2*na1) + na*(na-1)/2 * PCollKey1)=> (* proba that g^{a_i a_i} = g^{a_i a_j}, that is, g^a_i is equivalent to g^{a_j}, needed to support the square *) foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, ja, x, c) in event_abort ev_abort else get prf_dh_val(ja, =ia, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) := find j' <= na suchthat defined(a[j']) && exp'(ma1,a) = exp'(g, mult(a[j'],a)) then ( (* In this case, that's the same as Oa2 *) if defined(ka[j']) then (* a[j'] compromised *) return(prf(exp'(ma1, a), xa1)) else if defined(ka) then (* a compromised *) return(prf(exp'(ma1, a), xa1)) else if j' = ia then (* square *) return(prf(exp'(ma1, a), xa1)) else (* At this point, a and a[j'] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =j', =xa1, c) in return(c) else get[unique] prf_dh_val(=j', =ia, =xa1, c) in return(c) else ca1 <-R prf_out; insert prf_dh_val(ia, j', xa1, ca1); return(ca1) ) else return(prf(exp'(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if ja = ia then (* square *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else (* At this point, a and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =ja, =xa2, c) in return(c) else get[unique] prf_dh_val(=ja, =ia, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, ja, xa2, ca2); return(ca2) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* square PRF-ODH1 and PRF-ODH1 This is a "square" variant of PRF_ODH1. G, Z, g, exp, exp', mult are as in DH_basic. prf: pseudo-random function that takes as argument a group element (in G) and an element in prf_in, and produces a result in prf_out. The type prf_out must be "bounded" or "nonuniform". pPRF-ODH: probability of breaking the PRF-ODH1 assumption in time t with n queries to the PRF (prf(g^ab, m)). pSQPRF-ODH: probability of breaking the square PRF-ODH1 assumption in time t with n queries to the PRF (prf(g^aa, m)). The function prf is defined by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. All other arguments must be declared before this macro. *) def square_PRF_ODH1(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH, pSQPRF_ODH) { fun prf(G, prf_in): prf_out. (* The (square) PRF-ODH1 assumption *) event ev_abort. param na, na1, na2, naDH9. table prf_dh_val(na, na, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(a[ja], a)), xa2)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * pSQPRF_ODH(time + (na + #Oa2 - 2 + #ODHa9)*time(exp) + (#Oa2 - 1) * time(prf, maxlength(xa2)), na2) + na*(na-1)/2 * pPRF_ODH(time + (na + #Oa2 - 3 + #ODHa9)*time(exp) + (#Oa2 - 1) * time(prf, maxlength(xa2)), 2*na2))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, ja, x, c) in event_abort ev_abort else get prf_dh_val(ja, =ia, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else (* At this point, a and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =ja, =xa2, c) in return(c) else get[unique] prf_dh_val(=ja, =ia, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, ja, xa2, ca2); return(ca2) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (* square PRF-ODH2 and PRF-ODH2 This is a "square" variant of PRF_ODH2. This assumption requires that it is possible to test efficiently whether exp(Y,a) = exp(g, ab) knowing just Y and B = exp(g,b) (so the result does not depend on a). This is possible for prime-order groups as well as Curve25519 and Curve448 when the weak private key is excluded. G, Z, g, exp, exp', mult are as in DH_basic. prf(G, prf_in): prf_out: pseudo-random function that takes as argument a group element (in G) and an element in prf_in, and produces a result in prf_out. The type prf_out must be "bounded" or "nonuniform". pPRF_ODH(t, n, n'): probability of breaking the PRF-ODH2 assumption in time t with n queries to the PRF (prf(g^ab, m)) and n' queries to prf(X^a, m) or prf(X^b, m). pSQPRF_ODH(t, n, n'): probability of breaking the square PRF-ODH2 assumption in time t with n queries to the PRF (prf(g^aa, m)) and n' queries to prf(X^a, m). The function prf is defined by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. All other arguments must be declared before this macro. *) def square_PRF_ODH2(G, Z, prf_in, prf_out, g, exp, exp', mult, prf, pPRF_ODH, pSQPRF_ODH) { fun prf(G, prf_in): prf_out. (* The (square) PRF-ODH2 assumption *) event ev_abort. param na, na1, na2, naDH9. table prf_dh_val(na, na, prf_in, prf_out). equiv(prf_odh(prf)) foreach ia <= na do a <-R Z; ( OA() := return(exp(g,a)) | Oa() [10] := return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) [useful_change] := return(prf(exp(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) [useful_change] := return(prf(exp(g, mult(a[ja], a)), xa2)) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) [2] := return(exp(g, mult(a, x))) ) <=(na * pSQPRF_ODH(time + (na + #Oa1 + #Oa2 - 2 + #ODHa9)*time(exp) + (#Oa1 + #Oa2 - 1) * time(prf, max(maxlength(xa1),maxlength(xa2))), na2, na1) + na*(na-1)/2 * pPRF_ODH(time + (na + #Oa1 + #Oa2 - 3 + #ODHa9)*time(exp) + (#Oa1 + #Oa2 - 1) * time(prf, max(maxlength(xa1), maxlength(xa2))), 2*na2, 2*na1))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := (* Abort when a must not be compromised *) get prf_dh_val(=ia, ja, x, c) in event_abort ev_abort else get prf_dh_val(ja, =ia, x, c) in event_abort ev_abort else let ka:bool = true in return(a) | foreach ia1 <= na1 do Oa1(ma1:G, xa1:prf_in) := find j' <= na suchthat defined(a[j']) && exp'(ma1,a) = exp'(g, mult(a[j'],a)) then ( (* In this case, that's the same as Oa2 *) if defined(ka[j']) then (* a[j'] compromised *) return(prf(exp'(ma1, a), xa1)) else if defined(ka) then (* a compromised *) return(prf(exp'(ma1, a), xa1)) else (* At this point, a and a[j'] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =j', =xa1, c) in return(c) else get[unique] prf_dh_val(=j', =ia, =xa1, c) in return(c) else ca1 <-R prf_out; insert prf_dh_val(ia, j', xa1, ca1); return(ca1) ) else return(prf(exp'(ma1, a), xa1)) | foreach ia2 <= na2 do Oa2(ja <= na, xa2: prf_in) := if defined(ka[ja]) then (* a[ja] compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else if defined(ka) then (* a compromised *) return(prf(exp'(g, mult(a[ja], a)), xa2)) else (* At this point, a and a[ja] are not compromised, and must never be compromised in the future *) get[unique] prf_dh_val(=ia, =ja, =xa2, c) in return(c) else get[unique] prf_dh_val(=ja, =ia, =xa2, c) in return(c) else ca2 <-R prf_out; insert prf_dh_val(ia, ja, xa2, ca2); return(ca2) | foreach iaDH9 <= naDH9 do ODHa9(x:Z) := return(exp'(g, mult(a, x))) ). } (********************************* Miscellaneous ***************************) (* One-way trapdoor permutation seed: type of random seeds to generate keys, must be "bounded", typically "fixed" pkey: type of public keys, must be "bounded" skey: type of private keys, must be "bounded" D: type of the input and output of the permutation, must be "bounded", typically "fixed" pkgen: public-key generation function skgen: private-key generation function f: the permutation (taking as argument the public key) invf: the inverse permutation of f (taking as argument the private key, i.e. the trapdoor) pkgen', f': symbols that replace pkgen and f respectively after game transformation POW(t): probability of breaking the one-wayness property in time t, for one key and one permuted value. The types seed, pkey, skey, D, and the probability POW must be declared before this macro. The functions pkgen, skgen, f, invf are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def OW_trapdoor_perm_all_args(seed, pkey, skey, D, pkgen, pkgen', skgen, f, f', invf, POW) { param nK, nF, n1. fun pkgen(seed):pkey. fun pkgen'(seed):pkey. fun skgen(seed):skey. fun f(pkey, D):D. fun f'(pkey, D):D. fun invf(skey, D):D. (* invf is the inverse of f *) equation forall r:seed, x:D; invf(skgen(r), f(pkgen(r), x)) = x. (* f is the inverse of invf *) equation forall r:seed, x:D; f(pkgen(r), invf(skgen(r), x)) = x. (* Injectivity of f *) equation forall k:pkey, x:D, x':D; (f(k,x) = f(k,x')) = (x = x'). equation forall k:pkey, x:D, x':D; (f'(k,x) = f'(k,x')) = (x = x'). (* injectivity of invf *) equation forall k:skey, x:D, x':D; (invf(k,x) = invf(k,x')) = (x = x'). (* f/invf are inverse permutations; use this to remove some occurrences of invf in equality tests *) equation forall r:seed, x:D, x':D; (x' = invf(skgen(r),x)) = (f(pkgen(r),x') = x). (* We can permute the distribution, for uniformly distributed random numbers x. Do that only when x is used in invf(skgen(r),x) *) equiv(remove_invf(f)) r <-R seed; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(invf(skgen(r),x)) | Oim() := return(x))) <=(0)=> [computational] r <-R seed [unchanged]; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(x) | Oim() := return(f(pkgen(r), x)))). (* One-wayness *) equiv(ow(f)) foreach iK <= nK do r <-R seed; ( Opk() [2] := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oy() := return(f(pkgen(r), x)) | foreach i1 <= n1 do Oeq (x' : D) := return(x' = x) | Ox() := return(x))) <=(nK * nF * POW(time + (nK-1) * time(pkgen) + (#Oy-1) * time(f)))=> [computational] foreach iK <= nK do r <-R seed [unchanged]; ( Opk() := return(pkgen'(r)) | foreach iF <= nF do x <-R D [unchanged]; (Oy() := return(f'(pkgen'(r), x)) | foreach i1 <= n1 do Oeq(x':D) := if defined(k) then return(x' = x) else return(false) | Ox() := let k:bool = true in return(x))). } def OW_trapdoor_perm(seed, pkey, skey, D, pkgen, skgen, f, invf, POW) { expand OW_trapdoor_perm_all_args(seed, pkey, skey, D, pkgen, pkgen', skgen, f, f', invf, POW). } (* One-way trapdoor permutation, with random self-reducibility. Same as above, but with a smaller probability of attack *) def OW_trapdoor_perm_RSR_all_args(seed, pkey, skey, D, pkgen, pkgen', skgen, f, f', invf, POW) { param nK, nF, n1. fun pkgen(seed):pkey. fun pkgen'(seed):pkey. fun skgen(seed):skey. fun f(pkey, D):D. fun f'(pkey, D):D. fun invf(skey, D):D. (* invf is the inverse of f *) equation forall r:seed, x:D; invf(skgen(r), f(pkgen(r), x)) = x. (* f is the inverse of invf *) equation forall r:seed, x:D; f(pkgen(r), invf(skgen(r), x)) = x. (* Injectivity of f *) equation forall k:pkey, x:D, x':D; (f(k,x) = f(k,x')) = (x = x'). equation forall k:pkey, x:D, x':D; (f'(k,x) = f'(k,x')) = (x = x'). (* injectivity of invf *) equation forall k:skey, x:D, x':D; (invf(k,x) = invf(k,x')) = (x = x'). (* f/invf are inverse permutations; use this to remove some occurrences of invf in equality tests *) equation forall r:seed, x:D, x':D; (x' = invf(skgen(r),x)) = (f(pkgen(r),x') = x). (* We can permute the distribution, for uniformly distributed random numbers x. Do that only when x is used in invf(skgen(r),x) *) equiv(remove_invf(f)) r <-R seed; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(invf(skgen(r),x)) | Oim() := return(x))) <=(0)=> [computational] r <-R seed [unchanged]; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(x) | Oim() := return(f(pkgen(r), x)))). (* One-wayness *) equiv(ow_rsr(f)) foreach iK <= nK do r <-R seed; ( Opk() [2] := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oy() := return(f(pkgen(r), x)) | foreach i1 <= n1 do Oeq (x' : D) := return(x' = x) | Ox() := return(x))) <=(nK * ((3 * #(Ox foreach r) + 1) * POW(time + (nK-1) * time(pkgen) + (#Oy+#(Oeq foreach r)) * time(f))))=> [computational] foreach iK <= nK do r <-R seed [unchanged]; ( Opk() := return(pkgen'(r)) | foreach iF <= nF do x <-R D [unchanged]; (Oy() := return(f'(pkgen'(r), x)) | foreach i1 <= n1 do Oeq(x':D) := if defined(k) then return(x' = x) else return(false) | Ox() := let k:bool = true in return(x))). } def OW_trapdoor_perm_RSR(seed, pkey, skey, D, pkgen, skgen, f, invf, POW) { expand OW_trapdoor_perm_RSR_all_args(seed, pkey, skey, D, pkgen, pkgen', skgen, f, f', invf, POW). } (* Set partial-domain one-way trapdoor permutation seed: type of random seeds to generate keys, must be "bounded", typically "fixed" pkey: type of public keys, must be "bounded" skey: type of private keys, must be "bounded" D: type of the input and output of the permutation, must be "bounded", typically "fixed" The domain D consists of the concatenation of bitstrings in Dow and Dr. Dow is the set of sub-bitstrings of D on which one-wayness holds (it is difficult to compute the random element x of Dow knowing f(pk, concat(x,y)) where y is a random element of Dr). Dow and Dr must be "bounded", typically "fixed". pkgen: public-key generation function skgen: private-key generation function f: the permutation (taking as argument the public key) invf: the inverse permutation of f (taking as argument the private key, i.e. the trapdoor) concat(Dow, Dr):D is bitstring concatenation pkgen', f': symbols that replace pkgen and f respectively after game transformation P_PD_OW(t,l): probability of breaking the set partial-domain one-wayness property in time t, for one key, one permuted value, and l tries. The types seed, pkey, skey, D, Dow, Dr and the probability P_PD_OW must be declared before this macro. The functions pkgen, skgen, f, invf, concat are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def set_PD_OW_trapdoor_perm_all_args(seed, pkey, skey, D, Dow, Dr, pkgen, pkgen', skgen, f, f', invf, concat, P_PD_OW) { param nK, nF, n1. fun pkgen(seed):pkey. fun pkgen'(seed):pkey. fun skgen(seed):skey. fun f(pkey, D):D. fun f'(pkey, D):D. fun invf(skey, D):D. fun concat(Dow,Dr):D [data]. (* invf is the inverse of f *) equation forall r:seed, x:D; invf(skgen(r), f(pkgen(r), x)) = x. (* f is the inverse of invf *) equation forall r:seed, x:D; f(pkgen(r), invf(skgen(r), x)) = x. (* Injectivity of f *) equation forall k:pkey, x:D, x':D; (f(k,x) = f(k,x')) = (x = x'). equation forall k:pkey, x:D, x':D; (f'(k,x) = f'(k,x')) = (x = x'). (* injectivity of invf *) equation forall k:skey, x:D, x':D; (invf(k,x) = invf(k,x')) = (x = x'). (* f/invf are inverse permutations; use this to remove some occurrences of invf in equality tests *) equation forall r:seed, x:D, x':D; (x' = invf(skgen(r),x)) = (f(pkgen(r),x') = x). (* We can permute the distribution, for uniformly distributed random numbers x. Do that only when x is used in invf(skgen(r),x) *) equiv(remove_invf(f)) r <-R seed; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(invf(skgen(r),x)) | Oim() := return(x))) <=(0)=> [computational] r <-R seed [unchanged]; ( Opk() := return(pkgen(r)) | foreach iF <= nF do x <-R D; (Oant() := return(x) | Oim() := return(f(pkgen(r), x)))). (* One-wayness *) equiv(pd_ow(f)) foreach iK <= nK do r <-R seed; ( Opk() [2] := return(pkgen(r)) | foreach iF <= nF do xow <-R Dow; xr <-R Dr; (Oy() := return(f(pkgen(r), concat(xow, xr))) | foreach i1 <= n1 do Oeq(xow' : Dow) := return(xow' = xow) | Oxow() := return(xow) | Oxr() := return(xr))) <=(nK * nF * P_PD_OW(time + (nK-1) * time(pkgen) + (#Oy-1) * time(f), n1))=> [computational] foreach iK <= nK do r <-R seed [unchanged]; ( Opk() := return(pkgen'(r)) | foreach iF <= nF do xow <-R Dow [unchanged]; xr <-R Dr [unchanged]; (Oy() := return(f'(pkgen'(r), concat(xow, xr))) | foreach i1 <= n1 do Oeq(xow':Dow) := if defined(kow) then return(xow' = xow) else if defined(kr) then return(xow' = xow) else return(false) | Oxow() := let kow:bool = true in return(xow) | Oxr() := let kr:bool = true in return(xr))). } def set_PD_OW_trapdoor_perm(seed, pkey, skey, D, Dow, Dr, pkgen, skgen, f, invf, concat, P_PD_OW) { expand set_PD_OW_trapdoor_perm_all_args(seed, pkey, skey, D, Dow, Dr, pkgen, pkgen', skgen, f, f', invf, concat, P_PD_OW). } (* Xor D: domain on which xor applies (should be "fixed") xor: the exclusive or function zero: the neutral element The type D must be declared before this macro is expanded. The function xor and the constant zero are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def Xor(D, xor, zero) { param nx. fun xor(D,D):D. const zero: D. equation builtin ACUN(xor,zero). (* Xor is a one-time pad *) equiv(remove_xor(xor)) foreach ix <= nx do a <-R D; Oxor(x:D) := return(xor(a,x)) <=(0)=> foreach ix <= nx do a <-R D; Oxor(x:D) := return(a). } (************ Composition of several primitives ******************** Authenticated encryption - from encrypt then MAC - from AEAD - from AEAD_nonce AEAD - from encrypt then MAC - from AEAD_nonce *) (* Authenticated encryption, built from encrypt-then-MAC. emkey: type of keys, must be "bounded" (to be able to generate random numbers from it), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property of the underlying encryption scheme in time t for one key and N encryption queries with cleartexts of length at most l. Pmac(t, N, N', Nu', l): probability of breaking the SUF-CMA property of the underlying MAC scheme in time t for one key, N MAC queries, N' modified verification queries and Nu' unchanged verification queries for messages of length at most l. The types emkey, cleartext, ciphertext and the probabilities Penc, Pmac must be declared before this macro is expanded. The functions enc, dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def Auth_Enc_from_Enc_then_MAC(emkey, cleartext, ciphertext, enc, dec, injbot, Z, Penc, Pmac) { type mkey [fixed,large]. (* Mac key *) type ekey [fixed,large]. (* Encryption key *) (* Extraction of encryption and MAC keys from the pair *) fun get_ekey(emkey):ekey. fun get_mkey(emkey):mkey. param Nk. equiv(emkey) foreach ik <= Nk do r <-R emkey; (O1():= return(get_ekey(r))| O2():= return(get_mkey(r))) <=(0)=> foreach ik <= Nk do (O1():= k1 <-R ekey; return(k1) | O2():= k2 <-R mkey; return(k2)). (* IND-CPA encryption *) type ciphertext_internal. expand IND_CPA_sym_enc(ekey, cleartext, ciphertext_internal, enc_internal, dec_internal, injbot, Z, Penc). (* MAC *) type macres [fixed]. expand SUF_CMA_det_mac(mkey, ciphertext_internal, macres, mac, check, Pmac). (* Concatenation of MAC *) fun concat_MAC(ciphertext_internal, macres): ciphertext [data]. letfun enc(c: cleartext, k: emkey) = let kENC = get_ekey(k) in let kMAC = get_mkey(k) in let e = enc_internal(c, kENC) in let m = mac(e, kMAC) in concat_MAC(e, m). letfun dec(e_MAC: ciphertext, k: emkey) = let kENC = get_ekey(k) in let kMAC = get_mkey(k) in let concat_MAC(e, m) = e_MAC in ( if check(e, kMAC, m) then dec_internal(e, kENC) else bottom ) else bottom. } (* Authenticated encryption, built from AEAD, by choosing the additional data nil. key: type of keys, must be "bounded" (to be able to generate random numbers from it), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property of the underlying encryption scheme in time t for one key and N encryption queries with cleartexts of length at most l. Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property of the underlying encryption scheme in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AuthEnc_from_AEAD(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc, Pencctxt) { type add_data. const nil: add_data. expand AEAD(key, cleartext, ciphertext, add_data, enc_internal, dec_internal, injbot, Z, Penc, Pencctxt). letfun enc(c: cleartext, k: key) = enc_internal(c, nil, k). letfun dec(e: ciphertext, k: key) = dec_internal(e, nil, k). } (* Authenticated encryption, built from AEAD_nonce by choosing the nonce randomly, and choosing the additional data nil. key: type of keys, must be "bounded" (to be able to generate random numbers from it), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property of the underlying encryption scheme in time t for one key and N encryption queries with cleartexts of length at most l. Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property of the underlying encryption scheme in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AuthEnc_from_AEAD_nonce(key, cleartext, ciphertext, enc, dec, injbot, Z, Penc, Pencctxt) { type nonce [large,fixed]. type ciphertext_internal. type add_data. const nil: add_data. expand AEAD_nonce(key, cleartext, ciphertext_internal, add_data, nonce, enc_internal, dec_internal, injbot, Z, Penc, Pencctxt). fun concat_nonce(nonce, ciphertext_internal): ciphertext [data]. letfun enc(c: cleartext, k: key) = new n: nonce; let e = enc_internal(c, nil, k, n) in concat_nonce(n, e). letfun dec(e_nonce: ciphertext, k: key) = let concat_nonce(n, e) = e_nonce in dec_internal(e, nil, k, n) else bottom. } (* Authenticated encryption with additional data, built from encrypt-then-MAC. emkey: type of keys, must be "bounded" (to be able to generate random numbers from it), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property of the underlying encryption scheme in time t for one key and N encryption queries with cleartexts of length at most l. Pmac(t, N, N', Nu', l): probability of breaking the SUF-CMA property of the underlying MAC scheme in time t for one key, N MAC queries, N' modified verification queries and Nu' unchanged verification queries for messages of length at most l. The types emkey, cleartext, ciphertext, add_data, and the probabilities Penc, Pmac must be declared before this macro is expanded. The functions enc, dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_from_Enc_then_MAC(emkey, cleartext, ciphertext, add_data, enc, dec, injbot, Z, Penc, Pmac) { type mkey [fixed,large]. (* Mac key *) type ekey [fixed,large]. (* Encryption key *) (* Extraction of encryption and MAC keys from the pair *) fun get_ekey(emkey):ekey. fun get_mkey(emkey):mkey. param Nk. equiv(emkey) foreach ik <= Nk do r <-R emkey; (O1():= return(get_ekey(r))| O2():= return(get_mkey(r))) <=(0)=> foreach ik <= Nk do (O1():= k1 <-R ekey; return(k1) | O2():= k2 <-R mkey; return(k2)). (* IND-CPA encryption *) type ciphertext_internal. expand IND_CPA_sym_enc(ekey, cleartext, ciphertext_internal, enc_internal, dec_internal, injbot, Z, Penc). (* MAC *) type macres [fixed]. expand SUF_CMA_det_mac(mkey, bitstring, macres, mac, check, Pmac). (* Concatenation of associated data *) fun concat_data(ciphertext_internal, add_data): bitstring [data]. (* Concatenation of MAC *) fun concat_MAC(ciphertext_internal, macres): ciphertext [data]. letfun enc(c: cleartext, d: add_data, k: emkey) = let kENC = get_ekey(k) in let kMAC = get_mkey(k) in let e = enc_internal(c, kENC) in let m = mac(concat_data(e, d), kMAC) in concat_MAC(e, m). letfun dec(e_MAC: ciphertext, d: add_data, k: emkey) = let kENC = get_ekey(k) in let kMAC = get_mkey(k) in let concat_MAC(e, m) = e_MAC in ( if check(concat_data(e,d), kMAC, m) then dec_internal(e, kENC) else bottom ) else bottom. } (* Authenticated encryption with additional data, built from AEAD_nonce by choosing the nonce randomly. key: type of keys, must be "bounded" (to be able to generate random numbers from it), typically "fixed" and "large". cleartext: type of cleartexts ciphertext: type of ciphertexts add_data: type of additional data enc: encryption function dec: decryption function injbot: natural injection from cleartext to bitstringbot Z: function that returns for each cleartext a cleartext of the same length consisting only of zeroes. Penc(t, N, l): probability of breaking the IND-CPA property of the underlying encryption scheme in time t for one key and N encryption queries with cleartexts of length at most l. Pencctxt(t, N, N', l, l', ld, ld'): probability of breaking the INT-CTXT property of the underlying encryption scheme in time t for one key, N encryption queries, N' decryption queries with cleartexts of length at most l and ciphertexts of length at most l', additional data for encryption of length at most ld, and additional data for decryption of length at most ld'. The types key, cleartext, ciphertext, add_data, and the probabilities Penc, Pencctxt must be declared before this macro is expanded. The functions enc, dec, injbot, and Z are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def AEAD_from_AEAD_nonce(key, cleartext, ciphertext, add_data, enc, dec, injbot, Z, Penc, Pencctxt) { type nonce [large,fixed]. type ciphertext_internal. expand AEAD_nonce(key, cleartext, ciphertext_internal, add_data, nonce, enc_internal, dec_internal, injbot, Z, Penc, Pencctxt). fun concat_nonce(nonce, ciphertext_internal): ciphertext [data]. letfun enc(c: cleartext, d: add_data, k: key) = new n: nonce; let e = enc_internal(c, d, k, n) in concat_nonce(n, e). letfun dec(e_nonce: ciphertext, d: add_data, k: key) = let concat_nonce(n, e) = e_nonce in dec_internal(e, d, k, n) else bottom. } (******************************* Hash functions (ROM) ****************************) (* Hash function in the random oracle model key: type of the key of the hash function, which models the choice of the hash function, must be "bounded", typically "fixed" input%: type of the %-th input of the hash function output: type of the output of the hash function, must be "bounded" or "nonuniform" (typically "fixed"). f: the hash function. WARNING: f is a keyed hash function. The key must be generated once and for all at the beginning of the game and the hash oracle must be made available to the adversary, by including the process f_oracle(k) where k is the key. qH is the number of calls to f_oracle. The types key, input%, and output must be declared before this macro. The function f, the process f_oracle, and the parameter qH are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def ROM_hash_1(key, input1, output, f, f_oracle, qH) { fun f(key, input1):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1) := return(f(k, x1)). } def ROM_hash_2(key, input1, input2, output, f, f_oracle, qH) { fun f(key, input1, input2):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2) := return(f(k, x1, x2)). } def ROM_hash_3(key, input1, input2, input3, output, f, f_oracle, qH) { fun f(key, input1, input2, input3):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3) := return(f(k, x1, x2, x3)). } def ROM_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4) := return(f(k, x1, x2, x3, x4)). } def ROM_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5) := return(f(k, x1, x2, x3, x4, x5)). } def ROM_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6) := return(f(k, x1, x2, x3, x4, x5, x6)). } def ROM_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)). } def ROM_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def ROM_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def ROM_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u)). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u)) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9, x10: input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def ROM_hash(key, input, output, f, f_oracle, qH) { expand ROM_hash_1(key, input, output, f, f_oracle, qH). } (* ROM with large output. The only difference with ROM is that we eliminate collisions on the output. The interface is the same as for ROMs. *) def ROM_hash_large_1(key, input1, output, f, f_oracle, qH) { fun f(key, input1):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1) := return(f(k, x1)). } def ROM_hash_large_2(key, input1, input2, output, f, f_oracle, qH) { fun f(key, input1, input2):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2) := return(f(k, x1, x2)). } def ROM_hash_large_3(key, input1, input2, input3, output, f, f_oracle, qH) { fun f(key, input1, input2, input3):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3) := return(f(k, x1, x2, x3)). } def ROM_hash_large_4(key, input1, input2, input3, input4, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4) := return(f(k, x1, x2, x3, x4)). } def ROM_hash_large_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5) := return(f(k, x1, x2, x3, x4, x5)). } def ROM_hash_large_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6) := return(f(k, x1, x2, x3, x4, x5, x6)). } def ROM_hash_large_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)). } def ROM_hash_large_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def ROM_hash_large_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def ROM_hash_large_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, qH) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. equiv(rom(f)) special rom("key_first", f, (hk, r, x, y, z, u), ("large")). equiv(rom_partial(f)) special rom_partial("key_first", f, (hk, r, x, y, z, u), ("large")) [manual]. param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9, x10: input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def ROM_hash_large(key, input, output, f, f_oracle, qH) { expand ROM_hash_large_1(key, input, output, f, f_oracle, qH). } (* Collision resistant hash function key: type of the key of the hash function, must be "bounded" or "nonuniform", typically "fixed" input%: type of the %-th input of the hash function output: type of the output of the hash function f: the hash function. Phash: probability of breaking collision resistance. WARNING: A collision resistant hash function is a keyed hash function. The key must be generated once and for all at the beginning of the game, and immediately made available to the adversary, for instance by including the process f_oracle(k), where k is the key. The types key, input%, output, and the probability Phash must be declared before this macro. The function f and the process f_oracle are defined by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def CollisionResistant_hash_1(key, input1, output, f, f_oracle, Phash) { fun f(key, input1):output. collision k <-R key; forall x1:input1, y1:input1; return(f(k, x1) = f(k, y1)) <=(Phash(time))=> return((x1 = y1)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_2(key, input1, input2, output, f, f_oracle, Phash) { fun f(key, input1, input2):output. collision k <-R key; forall x1:input1, x2:input2, y1:input1, y2:input2; return(f(k, x1, x2) = f(k, y1, y2)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, y1:input1, y2:input2, y3:input3; return(f(k, x1, x2, x3) = f(k, y1, y2, y3)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, y1:input1, y2:input2, y3:input3, y4:input4; return(f(k, x1, x2, x3, x4) = f(k, y1, y2, y3, y4)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5; return(f(k, x1, x2, x3, x4, x5) = f(k, y1, y2, y3, y4, y5)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6; return(f(k, x1, x2, x3, x4, x5, x6) = f(k, y1, y2, y3, y4, y5, y6)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7; return(f(k, x1, x2, x3, x4, x5, x6, x7) = f(k, y1, y2, y3, y4, y5, y6, y7)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8) = f(k, y1, y2, y3, y4, y5, y6, y7, y8)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. collision k <-R key; forall x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)). let f_oracle(k: key) = OH() := return(k). } def CollisionResistant_hash(key, input, output, f, f_oracle, Phash) { expand CollisionResistant_hash_1(key, input, output, f, f_oracle, Phash). } (* Hidden-key collision resistant hash function The interface is similar to collision-resistant hash functions, except for the addition of qH. WARNING: A hidden-key collision resistant hash function is a keyed hash function. The key must be generated once and for all at the beginning of the game, and the hash oracle must be made available to the adversary, by including the process f_oracle(k) where k is the key. qH is the number of calls to f_oracle. Phash(t,N): probability of breaking collision resistance for an adversary that runs in time at most t and calls the hash oracle at most N times. *) def HiddenKeyCollisionResistant_hash_1(key, input1, output, f, f_oracle, qH, Phash) { fun f(key, input1):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1) := return(f(k, x1)) | foreach i <= Ncoll do Ocoll(x1:input1, y1:input1) [useful_change] := return(f(k, x1) = f(k, y1))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1) := return(f(k, x1)) | foreach i <= Ncoll do Ocoll(x1:input1, y1:input1) := return((x1 = y1))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1) := return(f(k, x1)). } def HiddenKeyCollisionResistant_hash_2(key, input1, input2, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2) := return(f(k, x1, x2)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, y1:input1, y2:input2) [useful_change] := return(f(k, x1, x2) = f(k, y1, y2))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2) := return(f(k, x1, x2)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, y1:input1, y2:input2) := return((x1 = y1) && (x2 = y2))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2) := return(f(k, x1, x2)). } def HiddenKeyCollisionResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3) := return(f(k, x1, x2, x3)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, y1:input1, y2:input2, y3:input3) [useful_change] := return(f(k, x1, x2, x3) = f(k, y1, y2, y3))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3) := return(f(k, x1, x2, x3)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, y1:input1, y2:input2, y3:input3) := return((x1 = y1) && (x2 = y2) && (x3 = y3))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3) := return(f(k, x1, x2, x3)). } def HiddenKeyCollisionResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4) := return(f(k, x1, x2, x3, x4)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, y1:input1, y2:input2, y3:input3, y4:input4) [useful_change] := return(f(k, x1, x2, x3, x4) = f(k, y1, y2, y3, y4))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4) := return(f(k, x1, x2, x3, x4)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, y1:input1, y2:input2, y3:input3, y4:input4) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4) := return(f(k, x1, x2, x3, x4)). } def HiddenKeyCollisionResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := return(f(k, x1, x2, x3, x4, x5)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5) [useful_change] := return(f(k, x1, x2, x3, x4, x5) = f(k, y1, y2, y3, y4, y5))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := return(f(k, x1, x2, x3, x4, x5)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5) := return(f(k, x1, x2, x3, x4, x5)). } def HiddenKeyCollisionResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := return(f(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6) = f(k, y1, y2, y3, y4, y5, y6))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := return(f(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6) := return(f(k, x1, x2, x3, x4, x5, x6)). } def HiddenKeyCollisionResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7) = f(k, y1, y2, y3, y4, y5, y6, y7))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)). } def HiddenKeyCollisionResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8) = f(k, y1, y2, y3, y4, y5, y6, y7, y8))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def HiddenKeyCollisionResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def HiddenKeyCollisionResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. param N, Ncoll. equiv(collision_res(f)) k <-R key; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10))) <=(Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Ncoll do Ocoll(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10, y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9, x10: input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def HiddenKeyCollisionResistant_hash(key, input, output, f, f_oracle, qH, Phash) { expand HiddenKeyCollisionResistant_hash_1(key, input, output, f, f_oracle, qH, Phash). } (* Second-preimage-resistant hash function The interface is the same as for collision-resistant hash functions. *) def SecondPreimageResistant_hash_1(key, input1, output, f, f_oracle, Phash) { fun f(key, input1):output. collision k <-R key; x1 <-R input1; forall y1:input1; return(f(k, x1) = f(k, y1)) <=(Phash(time))=> return((x1 = y1)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_2(key, input1, input2, output, f, f_oracle, Phash) { fun f(key, input1, input2):output. collision k <-R key; x1 <-R input1; x2 <-R input2; forall y1:input1, y2:input2; return(f(k, x1, x2) = f(k, y1, y2)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; forall y1:input1, y2:input2, y3:input3; return(f(k, x1, x2, x3) = f(k, y1, y2, y3)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; forall y1:input1, y2:input2, y3:input3, y4:input4; return(f(k, x1, x2, x3, x4) = f(k, y1, y2, y3, y4)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5; return(f(k, x1, x2, x3, x4, x5) = f(k, y1, y2, y3, y4, y5)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6; return(f(k, x1, x2, x3, x4, x5, x6) = f(k, y1, y2, y3, y4, y5, y6)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7; return(f(k, x1, x2, x3, x4, x5, x6, x7) = f(k, y1, y2, y3, y4, y5, y6, y7)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8) = f(k, y1, y2, y3, y4, y5, y6, y7, y8)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. collision k <-R key; x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10; return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)). let f_oracle(k: key) = OH() := return(k). } def SecondPreimageResistant_hash(key, input, output, f, f_oracle, Phash) { expand SecondPreimageResistant_hash_1(key, input, output, f, f_oracle, Phash). } (* Hidden key second-preimage-resistant hash function The interface is the same as for hidden-key collision-resistant hash functions. *) def HiddenKeySecondPreimageResistant_hash_1(key, input1, output, f, f_oracle, qH, Phash) { fun f(key, input1):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1) := return(f(k, z1)) | foreach i <= Nx do x1 <-R input1; (Ox1() := return(x1) | foreach i <= Ncoll do Ocoll(y1:input1) [useful_change] := return(f(k, x1) = f(k, y1)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1) := return(f(k, z1)) | foreach i <= Nx do x1 <-R input1 [unchanged]; (Ox1() := return(x1) | foreach i <= Ncoll do Ocoll(y1:input1) := return((x1 = y1)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1) := return(f(k, x1)). } def HiddenKeySecondPreimageResistant_hash_2(key, input1, input2, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2) := return(f(k, z1, z2)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; (Ox1() := return(x1) | Ox2() := return(x2) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2) [useful_change] := return(f(k, x1, x2) = f(k, y1, y2)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2) := return(f(k, z1, z2)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2) := return((x1 = y1) && (x2 = y2)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2) := return(f(k, x1, x2)). } def HiddenKeySecondPreimageResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3) := return(f(k, z1, z2, z3)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3) [useful_change] := return(f(k, x1, x2, x3) = f(k, y1, y2, y3)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3) := return(f(k, z1, z2, z3)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3) := return((x1 = y1) && (x2 = y2) && (x3 = y3)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3) := return(f(k, x1, x2, x3)). } def HiddenKeySecondPreimageResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4) := return(f(k, z1, z2, z3, z4)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4) [useful_change] := return(f(k, x1, x2, x3, x4) = f(k, y1, y2, y3, y4)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4) := return(f(k, z1, z2, z3, z4)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4) := return(f(k, x1, x2, x3, x4)). } def HiddenKeySecondPreimageResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return(f(k, z1, z2, z3, z4, z5)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5) [useful_change] := return(f(k, x1, x2, x3, x4, x5) = f(k, y1, y2, y3, y4, y5)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return(f(k, z1, z2, z3, z4, z5)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5) := return(f(k, x1, x2, x3, x4, x5)). } def HiddenKeySecondPreimageResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return(f(k, z1, z2, z3, z4, z5, z6)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6) = f(k, y1, y2, y3, y4, y5, y6)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return(f(k, z1, z2, z3, z4, z5, z6)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; x6 <-R input6 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6) := return(f(k, x1, x2, x3, x4, x5, x6)). } def HiddenKeySecondPreimageResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return(f(k, z1, z2, z3, z4, z5, z6, z7)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7) = f(k, y1, y2, y3, y4, y5, y6, y7)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return(f(k, z1, z2, z3, z4, z5, z6, z7)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; x6 <-R input6 [unchanged]; x7 <-R input7 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)). } def HiddenKeySecondPreimageResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8) = f(k, y1, y2, y3, y4, y5, y6, y7, y8)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; x6 <-R input6 [unchanged]; x7 <-R input7 [unchanged]; x8 <-R input8 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def HiddenKeySecondPreimageResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; x6 <-R input6 [unchanged]; x7 <-R input7 [unchanged]; x8 <-R input8 [unchanged]; x9 <-R input9 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def HiddenKeySecondPreimageResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. param N, Nx, Ncoll. equiv(second_pre_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | Ox10() := return(x10) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10) [useful_change] := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10)))) <=(Nx * Phash(time, N))=> [computational] k <-R key [unchanged]; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10)) | foreach i <= Nx do x1 <-R input1 [unchanged]; x2 <-R input2 [unchanged]; x3 <-R input3 [unchanged]; x4 <-R input4 [unchanged]; x5 <-R input5 [unchanged]; x6 <-R input6 [unchanged]; x7 <-R input7 [unchanged]; x8 <-R input8 [unchanged]; x9 <-R input9 [unchanged]; x10 <-R input10 [unchanged]; (Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | Ox10() := return(x10) | foreach i <= Ncoll do Ocoll(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9, x10: input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def HiddenKeySecondPreimageResistant_hash(key, input, output, f, f_oracle, qH, Phash) { expand HiddenKeySecondPreimageResistant_hash_1(key, input, output, f, f_oracle, qH, Phash). } (* Fixed-hash second-preimage-resistant hash function input%: type of the %-th input of the hash function output: type of the output of the hash function f(input...):output : the hash function. (It is not keyed.) Phash: probability of breaking second-preimage resistance. The types input%, output, and the probability Phash must be declared before this macro. The function f is defined by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. *) def FixedSecondPreimageResistant_hash_1(input1, output, f, Phash) { fun f(input1):output. collision x1 <-R input1; forall y1:input1; return(f(x1) = f(y1)) <=(Phash(time))=> return((x1 = y1)). } def FixedSecondPreimageResistant_hash_2(input1, input2, output, f, Phash) { fun f(input1, input2):output. collision x1 <-R input1; x2 <-R input2; forall y1:input1, y2:input2; return(f(x1, x2) = f(y1, y2)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2)). } def FixedSecondPreimageResistant_hash_3(input1, input2, input3, output, f, Phash) { fun f(input1, input2, input3):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; forall y1:input1, y2:input2, y3:input3; return(f(x1, x2, x3) = f(y1, y2, y3)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3)). } def FixedSecondPreimageResistant_hash_4(input1, input2, input3, input4, output, f, Phash) { fun f(input1, input2, input3, input4):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; forall y1:input1, y2:input2, y3:input3, y4:input4; return(f(x1, x2, x3, x4) = f(y1, y2, y3, y4)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)). } def FixedSecondPreimageResistant_hash_5(input1, input2, input3, input4, input5, output, f, Phash) { fun f(input1, input2, input3, input4, input5):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5; return(f(x1, x2, x3, x4, x5) = f(y1, y2, y3, y4, y5)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)). } def FixedSecondPreimageResistant_hash_6(input1, input2, input3, input4, input5, input6, output, f, Phash) { fun f(input1, input2, input3, input4, input5, input6):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6; return(f(x1, x2, x3, x4, x5, x6) = f(y1, y2, y3, y4, y5, y6)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)). } def FixedSecondPreimageResistant_hash_7(input1, input2, input3, input4, input5, input6, input7, output, f, Phash) { fun f(input1, input2, input3, input4, input5, input6, input7):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7; return(f(x1, x2, x3, x4, x5, x6, x7) = f(y1, y2, y3, y4, y5, y6, y7)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)). } def FixedSecondPreimageResistant_hash_8(input1, input2, input3, input4, input5, input6, input7, input8, output, f, Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8; return(f(x1, x2, x3, x4, x5, x6, x7, x8) = f(y1, y2, y3, y4, y5, y6, y7, y8)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)). } def FixedSecondPreimageResistant_hash_9(input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8, input9):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9; return(f(x1, x2, x3, x4, x5, x6, x7, x8, x9) = f(y1, y2, y3, y4, y5, y6, y7, y8, y9)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)). } def FixedSecondPreimageResistant_hash_10(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. collision x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; forall y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10; return(f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = f(y1, y2, y3, y4, y5, y6, y7, y8, y9, y10)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)). } def FixedSecondPreimageResistant_hash(input, output, f, Phash) { expand FixedSecondPreimageResistant_hash_1(input, output, f, Phash). } (* preimage-resistant hash function The interface is the same as for collision-resistant hash functions. *) def PreimageResistant_hash_all_args_1(key, input1, output, f, f', f_oracle, Phash) { fun f(key, input1):output. fun f'(key, input1):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; (Oim() := return(f(k, x1)) | foreach i <= Neq do Oeq(y1: input1) := return((x1 = y1)) | Ox1() := return(x1))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; (Oim() := return(f'(k, x1)) | foreach i <= Neq do Oeq(y1: input1) := let r = (x1 = y1) in find suchthat defined(comp1) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_1(key, input1, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_1(key, input1, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_2(key, input1, input2, output, f, f', f_oracle, Phash) { fun f(key, input1, input2):output. fun f'(key, input1, input2):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; (Oim() := return(f(k, x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := return((x1 = y1) && (x2 = y2)) | Ox1() := return(x1) | Ox2() := return(x2))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; (Oim() := return(f'(k, x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := let r = (x1 = y1) && (x2 = y2) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_2(key, input1, input2, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_2(key, input1, input2, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_3(key, input1, input2, input3, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3):output. fun f'(key, input1, input2, input3):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f(k, x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := return((x1 = y1) && (x2 = y2) && (x3 = y3)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f'(k, x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_3(key, input1, input2, input3, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_4(key, input1, input2, input3, input4, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4):output. fun f'(key, input1, input2, input3, input4):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f(k, x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f'(k, x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_4(key, input1, input2, input3, input4, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_5(key, input1, input2, input3, input4, input5, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5):output. fun f'(key, input1, input2, input3, input4, input5):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f(k, x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f'(k, x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_5(key, input1, input2, input3, input4, input5, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_6(key, input1, input2, input3, input4, input5, input6, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. fun f'(key, input1, input2, input3, input4, input5, input6):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_6(key, input1, input2, input3, input4, input5, input6, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', f_oracle, Phash). } def PreimageResistant_hash_all_args_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', f_oracle, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. param Nx, Neq. equiv(preimage_res(f)) k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | Ox10() := return(x10))) <=(Nx * Phash(time))=> k <-R key; (Ok() := return(k) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) orfind suchthat defined(comp10) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9) | Ox10() := let comp10: bool = true in return(x10))). let f_oracle(k: key) = OH() := return(k). } def PreimageResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, Phash) { expand PreimageResistant_hash_all_args_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', f_oracle, Phash). } def PreimageResistant_hash(key, input, output, f, f_oracle, Phash) { expand PreimageResistant_hash_1(key, input, output, f, f_oracle, Phash). } def PreimageResistant_hash_all_args(key, input, output, f, f', f_oracle, Phash) { expand PreimageResistant_hash_all_args_1(key, input, output, f, f', f_oracle, Phash). } (* Hidden key preimage-resistant hash function The interface is the same as for hidden-key collision-resistant hash functions. *) def HiddenKeyPreimageResistant_hash_all_args_1(key, input1, output, f, f', f_oracle, qH, Phash) { fun f(key, input1):output. fun f'(key, input1):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1) := return(f(k, z1)) | foreach i <= Nx do x1 <-R input1; (Oim() := return(f(k, x1)) | foreach i <= Neq do Oeq(y1: input1) := return((x1 = y1)) | Ox1() := return(x1))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1) := return(f(k, z1)) | foreach i <= Nx do x1 <-R input1; (Oim() := return(f'(k, x1)) | foreach i <= Neq do Oeq(y1: input1) := let r = (x1 = y1) in find suchthat defined(comp1) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1) := return(f(k, x1)). } def HiddenKeyPreimageResistant_hash_1(key, input1, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_1(key, input1, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_2(key, input1, input2, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2):output. fun f'(key, input1, input2):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2) := return(f(k, z1, z2)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; (Oim() := return(f(k, x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := return((x1 = y1) && (x2 = y2)) | Ox1() := return(x1) | Ox2() := return(x2))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2) := return(f(k, z1, z2)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; (Oim() := return(f'(k, x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := let r = (x1 = y1) && (x2 = y2) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2) := return(f(k, x1, x2)). } def HiddenKeyPreimageResistant_hash_2(key, input1, input2, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_2(key, input1, input2, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_3(key, input1, input2, input3, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3):output. fun f'(key, input1, input2, input3):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3) := return(f(k, z1, z2, z3)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f(k, x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := return((x1 = y1) && (x2 = y2) && (x3 = y3)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3) := return(f(k, z1, z2, z3)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f'(k, x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3) := return(f(k, x1, x2, x3)). } def HiddenKeyPreimageResistant_hash_3(key, input1, input2, input3, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_3(key, input1, input2, input3, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_4(key, input1, input2, input3, input4, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4):output. fun f'(key, input1, input2, input3, input4):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4) := return(f(k, z1, z2, z3, z4)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f(k, x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4) := return(f(k, z1, z2, z3, z4)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f'(k, x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4) := return(f(k, x1, x2, x3, x4)). } def HiddenKeyPreimageResistant_hash_4(key, input1, input2, input3, input4, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_4(key, input1, input2, input3, input4, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_5(key, input1, input2, input3, input4, input5, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5):output. fun f'(key, input1, input2, input3, input4, input5):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return(f(k, z1, z2, z3, z4, z5)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f(k, x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return(f(k, z1, z2, z3, z4, z5)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f'(k, x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5) := return(f(k, x1, x2, x3, x4, x5)). } def HiddenKeyPreimageResistant_hash_5(key, input1, input2, input3, input4, input5, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_5(key, input1, input2, input3, input4, input5, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_6(key, input1, input2, input3, input4, input5, input6, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6):output. fun f'(key, input1, input2, input3, input4, input5, input6):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return(f(k, z1, z2, z3, z4, z5, z6)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return(f(k, z1, z2, z3, z4, z5, z6)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6) := return(f(k, x1, x2, x3, x4, x5, x6)). } def HiddenKeyPreimageResistant_hash_6(key, input1, input2, input3, input4, input5, input6, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_6(key, input1, input2, input3, input4, input5, input6, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return(f(k, z1, z2, z3, z4, z5, z6, z7)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return(f(k, z1, z2, z3, z4, z5, z6, z7)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)). } def HiddenKeyPreimageResistant_hash_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def HiddenKeyPreimageResistant_hash_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def HiddenKeyPreimageResistant_hash_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', f_oracle, qH, Phash) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. fun f'(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. param N, Nx, Neq. equiv(preimage_res(f)) k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | Ox10() := return(x10))) <=(Nx * Phash(time, N))=> k <-R key; (foreach i <= N do O(z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return(f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10)) | foreach i <= Nx do x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f'(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) orfind suchthat defined(comp10) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9) | Ox10() := let comp10: bool = true in return(x10))). param qH [noninteractive]. let f_oracle(k: key) = foreach iH <= qH do OH(x1: input1, x2: input2, x3: input3, x4: input4, x5: input5, x6: input6, x7: input7, x8: input8, x9: input9, x10: input10) := return(f(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def HiddenKeyPreimageResistant_hash_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash(key, input, output, f, f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_1(key, input, output, f, f_oracle, qH, Phash). } def HiddenKeyPreimageResistant_hash_all_args(key, input, output, f, f', f_oracle, qH, Phash) { expand HiddenKeyPreimageResistant_hash_all_args_1(key, input, output, f, f', f_oracle, qH, Phash). } (* Fixed-hash preimage-resistant hash function The interface is the same as for fixed-hash second-preimage-resistant hash functions. *) def FixedPreimageResistant_hash_all_args_1(input1, output, f, f', Phash) { fun f(input1):output. fun f'(input1):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; (Oim() := return(f(x1)) | foreach i <= Neq do Oeq(y1: input1) := return((x1 = y1)) | Ox1() := return(x1)) <=(Phash(time))=> x1 <-R input1; (Oim() := return(f'(x1)) | foreach i <= Neq do Oeq(y1: input1) := let r = (x1 = y1) in find suchthat defined(comp1) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1)). } def FixedPreimageResistant_hash_1(input1, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_1(input1, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_2(input1, input2, output, f, f', Phash) { fun f(input1, input2):output. fun f'(input1, input2):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; (Oim() := return(f(x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := return((x1 = y1) && (x2 = y2)) | Ox1() := return(x1) | Ox2() := return(x2)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; (Oim() := return(f'(x1, x2)) | foreach i <= Neq do Oeq(y1: input1, y2: input2) := let r = (x1 = y1) && (x2 = y2) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2)). } def FixedPreimageResistant_hash_2(input1, input2, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_2(input1, input2, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_3(input1, input2, input3, output, f, f', Phash) { fun f(input1, input2, input3):output. fun f'(input1, input2, input3):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f(x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := return((x1 = y1) && (x2 = y2) && (x3 = y3)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; (Oim() := return(f'(x1, x2, x3)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3)). } def FixedPreimageResistant_hash_3(input1, input2, input3, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_3(input1, input2, input3, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_4(input1, input2, input3, input4, output, f, f', Phash) { fun f(input1, input2, input3, input4):output. fun f'(input1, input2, input3, input4):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f(x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; (Oim() := return(f'(x1, x2, x3, x4)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4)). } def FixedPreimageResistant_hash_4(input1, input2, input3, input4, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_4(input1, input2, input3, input4, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_5(input1, input2, input3, input4, input5, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5):output. fun f'(input1, input2, input3, input4, input5):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f(x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; (Oim() := return(f'(x1, x2, x3, x4, x5)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5)). } def FixedPreimageResistant_hash_5(input1, input2, input3, input4, input5, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_5(input1, input2, input3, input4, input5, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_6(input1, input2, input3, input4, input5, input6, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5, input6):output. fun f'(input1, input2, input3, input4, input5, input6):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f(x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; (Oim() := return(f'(x1, x2, x3, x4, x5, x6)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6)). } def FixedPreimageResistant_hash_6(input1, input2, input3, input4, input5, input6, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_6(input1, input2, input3, input4, input5, input6, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_7(input1, input2, input3, input4, input5, input6, input7, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5, input6, input7):output. fun f'(input1, input2, input3, input4, input5, input6, input7):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f(x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; (Oim() := return(f'(x1, x2, x3, x4, x5, x6, x7)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7)). } def FixedPreimageResistant_hash_7(input1, input2, input3, input4, input5, input6, input7, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_7(input1, input2, input3, input4, input5, input6, input7, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_8(input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8):output. fun f'(input1, input2, input3, input4, input5, input6, input7, input8):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f(x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; (Oim() := return(f'(x1, x2, x3, x4, x5, x6, x7, x8)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8)). } def FixedPreimageResistant_hash_8(input1, input2, input3, input4, input5, input6, input7, input8, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_8(input1, input2, input3, input4, input5, input6, input7, input8, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_9(input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8, input9):output. fun f'(input1, input2, input3, input4, input5, input6, input7, input8, input9):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f(x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; (Oim() := return(f'(x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9)). } def FixedPreimageResistant_hash_9(input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_9(input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, f', Phash). } def FixedPreimageResistant_hash_all_args_10(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', Phash) { fun f(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. fun f'(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. param Neq. equiv(preimage_res(f)) x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10)) | Ox1() := return(x1) | Ox2() := return(x2) | Ox3() := return(x3) | Ox4() := return(x4) | Ox5() := return(x5) | Ox6() := return(x6) | Ox7() := return(x7) | Ox8() := return(x8) | Ox9() := return(x9) | Ox10() := return(x10)) <=(Phash(time))=> x1 <-R input1; x2 <-R input2; x3 <-R input3; x4 <-R input4; x5 <-R input5; x6 <-R input6; x7 <-R input7; x8 <-R input8; x9 <-R input9; x10 <-R input10; (Oim() := return(f'(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach i <= Neq do Oeq(y1: input1, y2: input2, y3: input3, y4: input4, y5: input5, y6: input6, y7: input7, y8: input8, y9: input9, y10: input10) := let r = (x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6) && (x7 = y7) && (x8 = y8) && (x9 = y9) && (x10 = y10) in find suchthat defined(comp1) then return(r) orfind suchthat defined(comp2) then return(r) orfind suchthat defined(comp3) then return(r) orfind suchthat defined(comp4) then return(r) orfind suchthat defined(comp5) then return(r) orfind suchthat defined(comp6) then return(r) orfind suchthat defined(comp7) then return(r) orfind suchthat defined(comp8) then return(r) orfind suchthat defined(comp9) then return(r) orfind suchthat defined(comp10) then return(r) else return(false) | Ox1() := let comp1: bool = true in return(x1) | Ox2() := let comp2: bool = true in return(x2) | Ox3() := let comp3: bool = true in return(x3) | Ox4() := let comp4: bool = true in return(x4) | Ox5() := let comp5: bool = true in return(x5) | Ox6() := let comp6: bool = true in return(x6) | Ox7() := let comp7: bool = true in return(x7) | Ox8() := let comp8: bool = true in return(x8) | Ox9() := let comp9: bool = true in return(x9) | Ox10() := let comp10: bool = true in return(x10)). } def FixedPreimageResistant_hash_10(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Phash) { expand FixedPreimageResistant_hash_all_args_10(input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, f', Phash). } def FixedPreimageResistant_hash(input, output, f, Phash) { expand FixedPreimageResistant_hash_1(input, output, f, Phash). } def FixedPreimageResistant_hash_all_args(input, output, f, f', Phash) { expand FixedPreimageResistant_hash_all_args_1(input, output, f, f', Phash). } (* Pseudo random function (PRF) key: type of keys, must be "bounded" (to be able to generate random numbers from it, and to talk about the runtime of f without mentioned the length of the key), typically "fixed" and "large". input%: type of the %-th input of the PRF. output: type of the output of the PRF, must be "bounded" or "nonuniform", typically "fixed". f: PRF function Pprf(t, N, l): probability of breaking the PRF property in time t, for one key, N queries to the PRF of length at most l. The types key, input, output and the probability Pprf must be declared before this macro is expanded. The function f is declared by this macro. It must not be declared elsewhere, and it can be used only after expanding the macro. *) def PRF_1(key, input1, output, f, Pprf) { fun f(key, input1):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_2(key, input1, input2, output, f, Pprf) { fun f(key, input1, input2):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_3(key, input1, input2, input3, output, f, Pprf) { fun f(key, input1, input2, input3):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_4(key, input1, input2, input3, input4, output, f, Pprf) { fun f(key, input1, input2, input3, input4):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_5(key, input1, input2, input3, input4, input5, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_6(key, input1, input2, input3, input4, input5, input6, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u)). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u)) [manual]. } def PRF(key, input, output, f, Pprf) { expand PRF_1(key, input, output, f, Pprf). } (* Pseudo random function (PRF) with large output. The only difference with PRF is that we eliminate collisions on the output. The interface is the same as for PRFs. *) def PRF_large_1(key, input1, output, f, Pprf) { fun f(key, input1):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_2(key, input1, input2, output, f, Pprf) { fun f(key, input1, input2):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_3(key, input1, input2, input3, output, f, Pprf) { fun f(key, input1, input2, input3):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_4(key, input1, input2, input3, input4, output, f, Pprf) { fun f(key, input1, input2, input3, input4):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_5(key, input1, input2, input3, input4, input5, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_6(key, input1, input2, input3, input4, input5, input6, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Pprf) { fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10):output. equiv(prf(f)) special prf("key_first", f, Pprf, (k, r, x, y, z, u), ("large")). equiv(prf_partial(f)) special prf_partial("key_first", f, Pprf, (k, r, x, y, z, u), ("large")) [manual]. } def PRF_large(key, input, output, f, Pprf) { expand PRF_large_1(key, input, output, f, Pprf). } (* Ideal Cipher Model cipherkey: type of keys that correspond to the choice of the scheme, must be "bounded" or "nonuniform", typically "fixed". key: type of keys (typically "large") blocksize: type of the input and output of the cipher, must be "bounded" or "nonuniform" (to be able to generate random numbers from it; typically "fixed") and "large". (The modeling of the ideal cipher model is not perfect in that, in order to encrypt a new message, one chooses a fresh random number, not necessarily different from previously generated random numbers. Then CryptoVerif needs to eliminate collisions between those random numbers, so blocksize must really be "large".) enc: encryption function dec: decryption function WARNING: the encryption and decryption functions take 2 keys as input: the key of type cipherkey that corresponds to the choice of the scheme, and the normal encryption/decryption key. The cipherkey must be chosen once and for all at the beginning of the game and the encryption and decryption oracles must be made available to the adversary, by including a process enc_dec_oracle(ck) where ck is the cipherkey. qE is the number of calls of the encryption oracle qD is the number of calls of the decryption oracle The types cipherkey, key, blocksize must be declared before this macro is expanded. The functions enc, dec, the process enc_dec_oracle, and the parameters qE, qD are declared by this macro. They must not be declared elsewhere, and they can be used only after expanding the macro. *) def ICM_cipher(cipherkey, key, blocksize, enc, dec, enc_dec_oracle, qE, qD) { fun enc(cipherkey, blocksize, key): blocksize. fun dec(cipherkey, blocksize, key): blocksize. equation forall ck:cipherkey, m:blocksize, k:key; dec(ck, enc(ck, m, k), k) = m. equation forall ck:cipherkey, m:blocksize, k:key; enc(ck, dec(ck, m, k), k) = m. equation forall ck:cipherkey, m1:blocksize, m2:blocksize, k:key; (dec(ck, m1, k) = dec(ck, m2, k)) = (m1 = m2). equation forall ck:cipherkey, m1:blocksize, m2:blocksize, k:key; (enc(ck, m1, k) = enc(ck, m2, k)) = (m1 = m2). equiv(icm(enc)) special icm(("key", "msg", "local_key"), enc, dec, (ck, k, me, md, u), ("large")). equiv(icm_partial(enc)) special icm_partial(("key", "msg", "local_key"), enc, dec, (ck, k, me, md, u), ("large")) [manual]. (* The difference of probability is the probability of collision between two random numbers in blocksize among the N+N2 chosen random numbers. *) param qE, qD [noninteractive]. let enc_dec_oracle(ck: cipherkey) = (foreach iE <= qE do Oenc(x:blocksize, ke:key) := return(enc(ck,x,ke))) | (foreach iD <= qD do Odec(m:blocksize, kd:key) := return(dec(ck,m,kd))). } (* random_split_N defines functions to split a random value into N values. input_t: type of the input value part%_t: types of the output parts tuple_t: type of a tuple of the output parts tuple(part1_t, ..., partN_t): tuple_t builds a tuple from N parts. split(input_t): tuple_t splits the input into N parts and returns a tuple of these parts Usage: let tuple(x1, ..., xN) = split(y) in ... input_t, part%_t, and tuple_t must be defined before. tuple and split are defined by this macro. *) def random_split_1(input_t, part1_t, tuple_t, tuple, split) { fun tuple(part1_t): tuple_t [data]. fun get1(input_t): part1_t. letfun split(r: input_t) = tuple(get1(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r))) <=(0)=> part1 <-R part1_t; (O1() := return(part1)). } def random_split_2(input_t, part1_t, part2_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. letfun split(r: input_t) = tuple(get1(r), get2(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; (O1() := return(part1) | O2() := return(part2)). } def random_split_3(input_t, part1_t, part2_t, part3_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3)). } def random_split_4(input_t, part1_t, part2_t, part3_t, part4_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4)). } def random_split_5(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5)). } def random_split_6(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. fun get6(input_t): part6_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r), get6(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r)) | O6() := return(get6(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5) | O6() := return(part6)). } def random_split_7(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. fun get6(input_t): part6_t. fun get7(input_t): part7_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r), get6(r), get7(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r)) | O6() := return(get6(r)) | O7() := return(get7(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; part7 <-R part7_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5) | O6() := return(part6) | O7() := return(part7)). } def random_split_8(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. fun get6(input_t): part6_t. fun get7(input_t): part7_t. fun get8(input_t): part8_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r), get6(r), get7(r), get8(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r)) | O6() := return(get6(r)) | O7() := return(get7(r)) | O8() := return(get8(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; part7 <-R part7_t; part8 <-R part8_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5) | O6() := return(part6) | O7() := return(part7) | O8() := return(part8)). } def random_split_9(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. fun get6(input_t): part6_t. fun get7(input_t): part7_t. fun get8(input_t): part8_t. fun get9(input_t): part9_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r), get6(r), get7(r), get8(r), get9(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r)) | O6() := return(get6(r)) | O7() := return(get7(r)) | O8() := return(get8(r)) | O9() := return(get9(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; part7 <-R part7_t; part8 <-R part8_t; part9 <-R part9_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5) | O6() := return(part6) | O7() := return(part7) | O8() := return(part8) | O9() := return(part9)). } def random_split_10(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, part10_t, tuple_t, tuple, split) { fun tuple(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, part10_t): tuple_t [data]. fun get1(input_t): part1_t. fun get2(input_t): part2_t. fun get3(input_t): part3_t. fun get4(input_t): part4_t. fun get5(input_t): part5_t. fun get6(input_t): part6_t. fun get7(input_t): part7_t. fun get8(input_t): part8_t. fun get9(input_t): part9_t. fun get10(input_t): part10_t. letfun split(r: input_t) = tuple(get1(r), get2(r), get3(r), get4(r), get5(r), get6(r), get7(r), get8(r), get9(r), get10(r)). equiv(splitter(split)) r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r)) | O5() := return(get5(r)) | O6() := return(get6(r)) | O7() := return(get7(r)) | O8() := return(get8(r)) | O9() := return(get9(r)) | O10() := return(get10(r))) <=(0)=> part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; part7 <-R part7_t; part8 <-R part8_t; part9 <-R part9_t; part10 <-R part10_t; (O1() := return(part1) | O2() := return(part2) | O3() := return(part3) | O4() := return(part4) | O5() := return(part5) | O6() := return(part6) | O7() := return(part7) | O8() := return(part8) | O9() := return(part9) | O10() := return(part10)). }